Blockchain

#16 transaction ch.6 - tx pool

Sila 2022. 6. 27. 18:54

tx 객체가 생성되면 이 객체는 바로 블록에 기록되는 것이 아니라 다음 블록이 생성될 때까지 기다려야 한다.

 

이런 tx들을 임시로 저장하는 곳을 txpool이라고 하는데 이 또한 Chain class 내에 저장하고 사용할 수 있다.

 

우선 chain class 안에 transactionPool 속성과 간단한 기능을 가진 tx pool 관련 함수를 추가해주자.

 

1. tx pool 속성 추가

 

/*  src/core/blockchain/chain.ts  */

// ...중략

export class Chain {
    public blockchain : Block[]
    private unspentTxOuts : unspentTxOut[]
    private transactionPool : ITransaction[]
    
    constructor() {
        this.blockchain = [Block.getGenesis()]
        this.unspentTxOuts = []
        this.transactionPool = []
    }
    
    public getTransactionPool() : ITransaction[] {
        return this.transactionPool
    }
    // 현재 체인의 tx pool 리턴
    
    public appendTransactionPool( _Transaction: ITransaction) : void {
        this.transactionPool.push(_Transaction)
    }
    // tx를 tx pool에 추가
}

 

2. tx pool update

 

다시 한 번 채굴자 시점에서 생각해보자.

 

블럭을 추가 할 때, txPool에 있던 tx들을 새 블럭의 data로 넘겨줘야 한다.

 

이를 위해서는 miningBlock 함수에서 리턴으로 addBlock함수를 실행할 때 tx pool에 있던 데이터들을 같이 넘겨주면 된다.

 

(원래 있던 transaction은 채굴 보상을 의미)

 

/*  src/core/blockchain/chain.ts  */

// ...중략

public miningBlock ( _account : string ) {
    // ...중략
    
    return this.addBlock(([transaction, ...this.getTransactionPool()])
}

 

이렇게 새로운 블럭을 생성되었다면 txpool에선 넘겨준 tx 데이터들을 다음 블럭이 생성되기 전에 제거해야 한다.

 

updateTransactionPool 함수를 작성해 이 기능을 넣어준다.

 

/*  src/core/blockchain/chain.ts  */

// ...중략

public updateTransactionPool ( _newBlock : Block ) : void {
    let txPool : ITransaction[] = this.getTransactionPool()
    // 현재 tx Pool에 있는 tx들을 가져온다
    
    _newBlock.data.forEach((tx : ITransaction) => {
        txPool = txPool.filter((txp) => {
            txp.hash !== tx.hash
            // 새롭게 생성된 블록의 tx들을 tx pool과 비교해
            // 동일한 tx를 제거한다. (hash값이 같다면 동일한 tx라는걸 이용)
        })
    })
    this.transactionPool = txPool
    // 걸러진 tx들로 transactionPool을 업데이트한다
}

 

채굴자의 경우, addBlock 함수에서 이 updateTransactionPool을 호출한다.

 

/*  src/core/blockchain/chain.ts  */

public addBlock (data : ITransaction[]) : Failable < Block, string> {
    // ...중략
    
    newBlock.data.forEach((_tx : ITransaction) => {
        this.updateUTXO(_tx)
    })
    
    this.updateTransactionPool(newBlock)
    // utxo 업데이트 후 tx pool을 업데이트
    
    return { isError : false, value : newBlock }
}

 

3. tx pool 업데이트 내용 전파

이 후 과정은 #15에서 utxo를 업데이트 해주는 것과 크게 다를 것이 없다.

 

다른 사람의 정보를 받아 내 체인을 최신화 할 때는 경우에 따라 addToChain, replaceChain 함수에서

 

updateTransactionPool 함수를 호출하면 된다.

 

/*  src/core/blockchain/chain.ts  */

public addToChain ( _receivedBlock : Block ) ; Failable < undefined, string > {
    // ...중략
    
    this.updateTransactionPool(_receivedBlock)
    // utxo 업데이트 후 tx pool 최신화
    
    return { isError : false, value : undefined }
}

public replaceChain ( _receivedChain : Block[] ) : Failable < undefined, string > {
    // ...중략
    
    this.blockchain = _receivedChain
    
    this.blockchain.forEach((_block : IBlock) => {
        this.updateTransactionPool(_block) // txpool update 코드 추가
        _block.data.forEach((_tx) => {
            this.updateUTXO(_tx)
        })
    })
    
    return { isError : false, value : undefined }
}

 

이를 다른 피어들과 주고 받으려면 P2PServer class에서 txpool 업데이트를 지시할 코드를 추가하고

 

index.ts의 sendTransaction 라우터에서 broadcast 함수를 실행하면 된다.

 

/*  src/core/p2p.ts */

// ...중략

export enum MessageType {
    // ...중략
    receivedTx = 3
}

export class P2PServer extends Chain {
    // ...중략
    
    public messageHandler(socket : WebSocket) {
        // ...중략
        
        case MessageType.receivedTx : {
            // 수신인 기준으로 생각한다
            const receivedChain : ITransaction = result.payload
            // 만들어진 tx에 대한 정보를 받는다
            
            if ( receivedChain : ITransaction === null ) break
            
            const withTransaction = this.getTransactionPool().find((_tx:ITransaction) => {
                return _tx.hash == receivedTransaction.hash
            })
            // 내 txpool을 뒤져서 방금 받은 tx와 일치하는 tx가 txpool에 있는지 확인
            
            if ( !withTransaction ) {
                this.appendTransactionPool(receivedTransaction)
            }
            // 없다면 내 tx pool에 추가
            
            const message : Message = {
                type : MessageType.receivedTx,
                payload : receivedTransaction
            }
            this.broadcast(message)
            // 전달받은 tx 정보를 나와 연결된 다른 노드들에게 broadcast
            
            break
        }
    }
}

 

/*  index.tx  */

app.post('/sendTransaction', (req, res) => {
    try {
        const receivedTx : ReceivedTx = req.body
        
        const tx = Wallet.sendTransaction(receivedTx, ws.getUnspentTxOuts())
        
        ws.appendTransaction(tx)
        
        // 라우터에서는 응답을 주는 입장이므로 발신인 입장에서 생각한다
        const message : Message = {
            type : MessageType.receivedTx,
            payload : tx
        }
        ws.broadcast(message)
    }
    catch (e) {
        if( e instanceof Error ) console.error(e.message)
    }
    res.json([])
})

 

여기까지하면 tx에 관한 내용도 어느 정도 구현이 끝났다.

'Blockchain' 카테고리의 다른 글

#18 metamask 활용  (0) 2022.06.29
#17 ethereum  (0) 2022.06.29
#15 transaction ch.5 - utxo update  (0) 2022.06.27
#14 transaction ch4. - transaction  (0) 2022.06.24
#13 tx ch3. 채굴자 보상  (0) 2022.06.23