JavaScript로 블록체인 만들기 #5
JavaScript 로 블록체인 5단계입니다.
#5 거래 풀
이번 5단계에서는 거래 풀을 중심으로 실질적으로 거래가 어떻게 풀 안에 포함되고 제거되는지, 그 후에 블록 및 체인에 어떻게 담기게 되는지에 대해서 구현하였다.
transactionpool.ts 가 추가되었고[1]
blockchain.ts , main.ts, p2p.ts , wallet.ts 에 약간의 변화가 생겼다.[2]
이번 5단계에서는
- 거래 풀
- 거래 전파
- 거래 검증
- 거래 체인에 담는 과정
- 거래풀 업데이트
순으로 구현할 예정이다.
개요
이번 단계에서는 아직 블록체인에는 들어가 있지 않은 거래들을 relaying 하는 것을 구현하려고 한다. 비트코인에서는 이러한 거래들을 “검증되지 않은 거래(unconfirmed transactions)” 라고 부른다. 보통은 누군가가 거래를 블록체인에 넣고 싶을 때는 거래를 네트워크에 전파하고 다른 어떤 노드가 이를 채굴해주기를 기다린다.
사실 이 특징은 꽤나 중요하다, 왜냐하면 블록체인에 거래를 넣기 위해서 내가 거래를 만든 후에 꼭 스스로 채굴을 할 필요는 없기 때문이다.
결론적으로, 이제 노드는 상호 통신을 할 때 2가지 종류의 데이터를 주고 받을 것이다 :
- 블록체인의 상태 (= 블록체인에 포함된 블록들과 거래들)
- 검증되지 않은 거래 (=체인에 아직 포함되지 않은 거래들)
이번에 구현할 전체 코드는 여기서 볼 수 있다.
거래 풀
“거래 풀” 이라는 새로운 entity 를 생성하고 거기에 검증되지 않은 거래 들을 저장하려고 한다. 비트코인에서는 “mempool” 이라고 한다. Transaction 은 나의 노드가 알고 있는 모든 “검증되지 않은 거래” 를 포함하는 자료구조이다. 이 간단한 구현에는 list 를 사용해서 할 예정이다.
노드에게 새로운 endpoint 하나를 제공할 것이다 : POST /sendTransaction
이 방법은 우리의 로컬 거래 풀에 거래를 생성할 것이다. 지금으로썬 블록체인에 거래를 더할 때, 우리는 이를 “선호된” 인터페이스로 사용할 것이다.
거래 생성은 chapter4 에서 한 것처럼 진행할 것이다. 단지 바로 블록을 채굴하는 대신 생성된 거래를 풀에 담을 것이다.
전파 (broadcasting)
검증되지 않은 거래들은 네트워크에 전파되고 결국엔 어떤 노드에 의해서 채굴되어 블록체인에 담길 것이다. 이를 실행시키기 위해서 몇가지 간단한 규칙들을 만들고자 한다 :
- 노드가 처음보는 검증되지 않은 거래를 받았을 때, 거래 풀 전체를 모든 피어들에게 전파한다.
- 노드가 처음으로 다른 노드와 이어졌을 때, 해당 노드의 거래 풀을 받는 통신을 시도할 것이다.
위의 기능을 구현하기위해서 2가지 종류의 새로운 MessageTypes 을 추가할 것이다.
: QUERY_TRANSACTION_POOL
와RESPONSE_TRANACTION_POOL
이다.
메세지 타입은 아래와 같이 정의된다 :
거래 풀 메세지는 다음과 같이 생성될 것이다 :
위에서 묘사된 거래 전파 논리를 구현하기 위해서, 새로운 메세지 타입 RESPONSE_TRANSACTION_POOL 을 다루는 코드를 더하려고 한다.
확인되지 않은 거래를 받을 때마다, 그들을 풀에 넣으려고 한다.
거래가 풀에 들어간다는 것은 그 거래는 유효한 것이고 노드에게 이전에는 전파된 적이 없다는 것을 의미한다. 이번의 경우에는 우리의 거래 풀을 다른 모든 피어들에게 전파하려고 한다.
수신한 검증되지 않은 거래 검증
피어는 어떤 종류의 거래도 보낼 수 있기 때문에, 거래를 거래 풀에 넣기 전에 이를 먼저 검증해야 할 것이다. 현존하는 모든 거래에게 적용된다. 예를 들면, 거래는 올바른 형식을 갖추어야 하고, 입력값, 출력값 그리고 서명이 일치해야 한다.
여기에 더해서, 1가지 새로운 규칙을 적용시키려고 한다 : 거래 입력값 중 하나라도 이미 거래 풀에 있다면 해당 거래는 풀에 담길 수 없다. 이 새로운 규칙은 아래의 코드와 같이 구현하였다.
거래 풀에서 거래를 제거할 수 있는 방법은 없다. 거래 풀은 새로운 블록이 채굴될 때마다 업데이트 된다.
거래 풀에서 블록체인으로
이번에는 거래가 로컬 거래 풀에 있다가 채굴이 되어 블록 및 체인에 담기는 과정을 구현할 것이다. 과정은 간단하다.
노드가 채굴하기 시작하면, 거래 풀에 있는 거래를 새로운 후보 블록에 담을 것이다.
거래가 풀에 담기기 전 이미 검증되었기에, 이 과정에서 추가적인 검증 과정은 존재하지 않는다.
거래 풀 업데이트
새로운 블록이 채굴되어 체인에 연결되면, 새 블록이 채굴될 때마다 거래 풀을 반드시 재검증 되어야한다. 새로운 블록에 담기는 거래들이 거래 풀에 담기는 거래들을 유효하지 않은 것으로 만들 수 있다. 그 대표적인 예로는 아래와 같은 상황이 있다 :
- 풀에 있는 거래가 이미 채굴 되었을 때
- 검증되지 않은 거래에서 나온 UTXO 가 이미 다른 거래에 의해서 사용되었을 때
거래 풀은 아래의 코드와 같이 업데이트 된다 :
보이는 것처럼, 거래가 풀에서 제거되어야 할지 여부를 결정할 때 우리가 알아야 할 점은 UTXO 하나이다.
결론
우리는 이제 블록을 스스로 채굴하지 않더라도 거래를 블록체인에 넣을 수 있다. 그러나 우리가 거래 수수료의 개념을 도입하지 않았기에, 거래를 블록에 담은 노드에게 주어지는 별도의 수수료는 존재하지 않는다.
다음 단계 에서는 지갑 UI 와 간단한 블록체인 탐색기를 만들려고 한다.
[1] 새로 추가된 transactionpool.ts 는 아래와 같이 구성되어있다.
transactionpool.ts
let transactionPool
const getTransactionPool
const addToTransactionPool
const hasTxIn
const updateTransactionPool
const getTxPoolIns
const isValidTxForPool
[2] blockchain.ts, main.ts, p2p.ts, wallet.ts 는 아래와 같이 변화하였다.
blockchain.ts
const genesisTransaction
const setUnspentTxOuts
const getLatestBlock
const getMyUnspentTransactionOutputs
const sendTransaction
main.ts
const initHttpServer_app.get(‘/unspentTransactionOutputs’)
const initHttpServer_app.get(‘/myUnspentTransactionOutputs’)
const initHttpServer_app.get(‘/address’)
const initHttpServer_app.get(‘/sendTransaction’)
const initHttpServer_app.get(‘/transactionPool’)
const initHttpServer_app.get(‘/stop’)
p2p.ts
enum MessageType 변경
const initConnection 변경
const initMessageHandler 변경
const queryTransactionPoolMsg
const responseTransactionPoolMsg
wallet.ts
const findTxOutsForAmount 변경
const filterTxPoolTxs
원저자 : Lauri Hartikka
원본 : https://lhartikk.github.io