JavaScript로 블록체인 만들기 #3

JavaScript 로 블록체인 3단계입니다.

Min Seo Park
CAU_CLink
12 min readJun 26, 2019

--

#3 거래구현 시작

이번 3단계에는 transaction.ts 이 추가되었고
blockchain.ts 에는 약간의 변화가 생겼다.
소제목에서도 보면 알 수 있듯이, 이번 단계에서는 거래를 구현한다.

<그림 3_1 _ 3단계 결과물 5가지 파일들>

transaction.ts 에는 UTXO 관련 내용이 대부분이다. 해당 거래의 txId, txIn, txOut 등 UTXO 의 유효성, 거래의 유효성 그리고 coinbase 거래의 유효성을 검증하는 과정이 담겨있다. [1]

transaction.ts 이 추가된만큼 blockchain.ts 에서도 일정 내용의 변화가 있다. unspentTxOuts 와 block 이 blockchain 에 추가되는 과정이 변화하였다. [2]

이번 3 단계에서는

  • 거래 출력값
  • 거래 입력값
  • 거래 구조
  • 거래 id (tx Id)
  • 거래 서명
  • UTXO
  • UTXO 업데이트
  • 거래 유효성 검증
  • Coinbase 거래
  • Coinbase 거래 유효성 검증

순으로 구현해 나갈 것이다.

개요

이번에는 거래의 개념을 도입하려고 한다. 이번 챕터를 마무리하면 구현체는 블록들이 체인으로 연결되어 있는 블록-체인(Block-Chain)의 모습에서 소유권의 개념이 더해진 암호화폐로 바뀐다.

화폐의 소유권을 증명 해낼 수 있으면 특정 주소로 코인들을 보낼 수 있다는 것이다.

이번 챕터에서는 많은 기능들이 구현된다 : 공개키 암호, 서명 그리고 거래의 input , output 등

구현할 전체 코드는 여기서 볼 수 있다.

공개키 암호화와 서명

공개키 암호화에는 하나의 키 쌍이 필요하다 : 개인키와 공개키.

개인키로 공개키를 만들 수는 있어도, 공개키로 개인키를 만들거나 역추적할 수는 없다. 공개키는 그 이름에서 볼 수 있듯이 공개되어도 문제 없다.

모든 Message 는 개인키를 사용하여 Signature로 생성된다. 이 Signature와 그에 상응하는 공개키로, 누구나 그 서명이 공개키와 짝 지어진 개인키로 만들어졌다는 것을 증명해낼 수 있다.

<그림 3_2 Message, Signature, 개인키와 공개키>

우리는 공개키 암호화에 타원 곡선(elliptic curves)를 사용하는 elliptic 라는 라이브러리를 사용할 것이다. (= ECDSA)

결론적으로, 2가지 암호화 함수가 각기 다른 목적으로 사용될 것이다 :

  • 해시 함수(SHA 256) 는 POW 채굴에 쓰인다. (해시는 또한 블록의 무결성을 지키는데 사용된다.)
  • 공개키 암호화 (ECDSA) 는 거래에 쓰인다. (이 부분을 이번 챕터에서 구현할 것이다.)

개인키와 공개키 (ECDSA)

개인키는 임의의 32바이트 문자열이다. 아래에 그 예시가 있다.

19f128debc1b9122da0635954488b208b829879cf13b3d6cac5d1260c0fd967c

공개키는 ‘04’로 시작되는 64 바이트 문자열이다. 아래에 그 예시가 있다.

04bfcab8722991ae774db48f934ca79cfb7dd991229153b9f732ba5334aafcd8e7266e47076996b55a14bf9913ee3145ce0cfc1372ada8ada74bd287450313534a

공개키는 개인키로부터 생성된다.
공개키는 코인을 받을 때 혹은 보낼 때(주소로) 사용된다.

거래 개요

구현된 코드를 보기 전에, 거래의 구조에 대해서 개괄적으로 얘기해보자.

거래에는 2가지 구성 요소가 있다 : 입력값(input) 과 출력값(output).

출력값은 코인이 보내진 곳을 명시하고 입력값은 전송된 코인이 존재했다는 사실과 “송신자”가 소유했다는 것을 증명해준다.
추가로, 입력값은 항상 (사용되지 않은) 출력값을 참조한다.

<그림 3_3 거래의 구조>

거래 출력값 (outputs)

거래 출력값(output)은 주소와 코인의 양으로 구성되어있다.
이 주소는 공개키이다. 이는 참조된 공개키와 한 쌍의 개인키를 가진 유저가 코인에 접근할 수 있다는 것을 의미한다.

<code 3_1 : 거래의 출력값>

거래 입력값 (inputs)

거래의 입력값(input) txIn은 코인이 “어디”에서 왔는지를 알려준다. 각 txIn 값은 이전 output 값을 참조한다, 그리고 이곳에서 코인들이 서명과 함께 ‘unlock’ 된다. 이 unlock 된 코인들은 이제 txOuts 으로 ‘사용될 수’ 있다. 이 서명은 공개키(=주소)가 참조한 개인키를 소유한 유저만이 거래를 만들 수 있게 한다.

<code 3_2 : 거래의 입력값>

txIn 은 오직 서명(개인키에 의해서 생성된)만 포함하고 있다, 절대로 개인키 자체는 포함되지 않는다. 즉, 블록체인에 담기는 정보는 공개키와 서명이다, 개인키는 절대 공개되지 않는다.

정리하자면, txIns 은 코인을 ‘unlock’ 하고 txOuts 은 코인을 다시 ‘lock’ 한다.

<그림 3_4 거래의 input 과 output>

거래 구조

거래 구조는 txIns 와 txOuts 로 정의되어 있고 굉장히 간단하다.

<code 3_3 : 거래의 구조>

거래 id

거래 id 는 거래의 내용을 기반으로 해시값을 계산한 결과이다. 그러나, 거래 id 의 서명들은 나중에 거래에 추가 되기에 거래 해시값에는 포함되지 않는다.

<code 3_4 : 거래 ID>

거래 서명

거래의 내용은 서명이 된 이후에는, 변경이 불가능하다. 거래의 내용은 공개되기 때문에, 누구나 거래에 접근할 수 있다. 심지어 블록체인에 담기기 전에도 말이다.

거래의 입력값을 서명할 때, 오직 txId 만 서명될 것이다. 만약 거래의 내용 중 하나라도 변화가 있다면, txId 도 반드시 변하게 된다. txId 가 변하게 된다면 거래와 서명이 모두 유효하지 않게 된다.

<code 3_5 : 거래 서명>

만약 누군가가 거래의 내용을 바꾸려고 한다면 어떤 일이 생기는지 알아보자 :

  1. 공격자가 노드를 돌리고 있는데, 다음과 같은 거래를 받는다 : 0x555.. 가 txId이고 “10코인을 AAA 에서 BBB 으로 보낸다” 가 그 내용이다.
  2. 공격자는 수신자의 주소를 CCC 로 바꾸고 바뀐 거래를 네트워크에 전파한다 . 거래의 내용은 “10코인을 AAA 에서 CCC 로 보낸다” 으로 바뀌었다.
  3. 수신자의 주소가 바뀌었기 때문에, 더 이상 기존 txId 는 유효하지 않다. txId은 0x567… 으로 바뀐다.
  4. txId 가 새로운 값으로 결정되었으니, 서명은 더 이상 유효하지 않다. 서명은 오직 원래의 txId 0x555.. 와만 일치한다.
  5. 수정된 거래는 다른 노드들에게 받아들여지지 않을 것이고 이는 검증되지 않은 것으로 취급된다.

사용되지 않은 출력값 (UTXO)

한 거래의 input 은 항상 사용되지 않은 output 을 참조한다(UTXO). 결과적으로, 만약 당신이 체인 상에서 어떤 코인을 소유하고 있다는 것은, 사실 코인 그 자체를 소유하고 있다는 것이 아니다.

당신이 소유한 개인키와 쌍을 이루는 공개키와 연관된 소비되지 않은 거래의 output 에 대한 소유권을 가지고 있는 것이다.

올바른 거래라는 것을 검증할 때, 사용되지 않은 거래의 출력값의 리스트만 살펴보면 된다. 사용되지 않은 거래의 출력값들은 항상 현 블록체인에서만 도출되어질 수 있다.

구현을 끝내면, 사용되지 않은 출력값의 리스트를 업데이트되고 거래는 체인에 포함될 것이다.

사용되지 않은 출력값 (UTXO , Unspent Transaction Output)의 구조는 아래와 같다 :

<code 3_6 : UTXO>

자료 구조 그 자체는 list 일 뿐이다 :

<code 3_7 : UTXO_list>

UTXO 업데이트 하기

블록이 체인에 더해질 때마다, 우리는 사용되지 않은 거래의 output 리스트를 업데이트 해야 한다. 왜냐하면 새로운 거래는 기존의 거래 output 을 소비하고 새로운 소비되지 않은 output을 만들기 때문이다.

이를 다루기 위해서는, 먼저 새로운 블록에서 새로운 사용되지 않은 거래의 출력값을 검색할 것입니다. (newUnspentTxOuts)

<code 3_8 : 새로운 UTXO>

또한, 새로운 거래에 의해서 소비된 거래 출력값을 알아야 한다. 이는 새로운 거래의 입력값을 통해서 파악한다.

<code 3_9 : 소비된 UTXO>

이제, 우리는 consumedTxOuts 을 제거하고 거래 출력값에 newUnspentTxOuts 을 추가하여 새로운 사용되지 않은 출력값(UTXO)을 만들 수 있게 되었다.

<code 3_10 : UTXO 업데이트>

묘사된 코드와 기능들은 updateUnspentTxOuts 에 포함되어 있다. 해당 메소드는 해당 거래와 거래가 속해있는 블록이 유효하다고 검증이 된 이후에만 호출된다.

거래 검증

거래 검증은 발생한 거래가 유효한 형태인지를 확인하는 과정이다.
크게, 거래 구조, 거래 id, txIn, txOut 의 유효성을 검증한다.

올바른 거래 구조
거래의 Transaction, TxIn, TxOut가 정의된 클래스를 따랐는지 확인한다.

<code 3_11 : 거래 구조 확인>

거래 내용의 유효성 확인
거래 구조 자체의 유효성을 확인한 후에는 거래 내용의 유효성도 확인한다. tx Id, txIns 의 유효성 그리고 txIn 과 txOut 의 값의 일치여부를 통해 거래 내용의 유효성을 검증한다.

<code 3_12 : 거래 내용의 유효성 검증>

유효한 txIn 들
txIn 에 있는 서명들은 유효해야하고, 참조한 output 들은 사용되지 않았어야 한다.

<code 3_13 : txIn 의 유효성 검증>

유효한 txOut 값 들
output 으로 명시된 값의 합은 input 으로 명시된 값의 합과 일치해야 한다. 만약 출력값이 50개의 코인을 포함하고 있다고 설정되면, 새로운 출력값의 합도 반드시 50개의 코인을 가지고 있어야 한다.

<code 3_14 : txOut 값의 유효성 검증>

코인베이스 거래

거래 input은 항상 거래 output을 항상 참조해야 한다, 그렇다면 initial coin(채굴 보상으로 주어지는 코인) 들은 어느 것을 참조해야 할까?

사실상, 참조해야할 것이 없기에 특별한 종류의 거래가 설정한다.
코인베이스 거래이다.

코인 베이스 거래는 오직 출력값만 포함하고 있고, 입력값은 포함되어 있지 않다. 이는 코인베이스 거래가 circulation 에 있어서 새로운 코인들을 추가한다는 것을 의미한다. 우리는 코인베이스 거래의 출력값을 50으로 설정하였다.

<code 3_15 : 코인베이스 거래 크기>

코인베이스 거래는 블록의 첫번째 거래이고 블록의 채굴자에 의해서 담긴다 : 만약 당신이 블록을 채굴했으면, 그 대가로 50 코인을 받게 된다.

코인베이스 거래에 block height 를 input 값으로 넣을 것이다. 이는 각 코인 베이스 거래가 고유한 txId 를 가질 수 있게 한다. 이러한 규칙이 없으면, 예를 들면, “주소 0xabc 에게 50개의 코인을 전송한다” 를 선언하는 코인베이스 거래는 항상 같은 txId 를 가지게 된다.

코인베이스 거래의 검증은 “일반”거래의 검증과는 약간 다르다.

<code 3_16 : 코인베이스 거래의 유효성 검증>

결론

이번에는 거래의 개념을 블록체인에 넣었다. 기본적인 아이디어는 간단하다 : 거래의 입력값들은 사용되지 않은 거래의 출력값(UTXO)을 참조하고 unlock하는데 서명을 사용한다. 우리가 그들을 송신자의 주소로 “relock” 하기 위해서는 출력값을 사용한다.

그러나, 지금 거래를 생성하는 것은 상당히 어렵다. 모든 것을 수동적으로 해야한다. 거래의 입력갑과 출력값을 만들고 개인키를 이용하여 서명해야 한다. 이 부분은 지갑이 구현되는 다음 챕터 부터는 달라질 것이다.

이번 챕터에 구현된 전체 코드는 여기서 볼 수 있다.

Javascript 로 블록체인 만들기 1편 , 2편 , 3편 , 4편 , 5편

[1] : transaction.ts 에는 아래의 것들이 추가되었다.

class UnspentTxOut
class TxIn
class TxOut
class Transaction
const getTransactionId
const validateTransaction
const validateBlockTransactions
const hasDuplicates
const validateCoinbaseTx
const validateTxIn
const getTxInAmount
const signTxIn
const findUnspentTxOut
const getPublicKey
const updateUnspentTxOuts
const getCoinbaseTransaction
const processTransactions
const toHexString
const getPublicKey
const isValidTxInStructure
const isValidTxOutStructure
const isValidAddress

[2] : blockchain.ts에는 아래의 것들이 변화하였다.

let unspentTxOuts
const generateNextBlock 내 add block 삭제 및 변화
addBlockToChain 변화

--

--

Min Seo Park
CAU_CLink

Interested in Blockchain, Project Financing and Smart city and Love DJing and EDM