블록체인 Block-Chain - 비트코인 코인베이스 Transaction

dev_swan·2022년 6월 25일
0

블록체인

목록 보기
8/36
post-thumbnail

Transaction Interface

declare interface ITxIn {
    txOutId: string; // 트랜잭션 해시값 ( 보내는 사람, 금액 좌표 )
    txOutIndex: number; // 배열의 인덱스 값 ( 보내는 사람, 금액 좌표 )
    signature?: string | undefined; // 서명 ( 보내는 사람의 서명 )
}

declare interface ITxOut {
    account: string; // 지갑 주소
    amount: number; // 금액
}

declare interface ITransaction {
    hash: string; // Transaction Hash 값
    txOuts: ITxOut[]; // Output 정보들 , 배열안에 객체로 들어간다.
    txIns: ITxIn[]; // Input 정보들 , 배열안에 객체로 들어간다.
}

declare interface unspentTxOut {
    txOutId: string; // Transaction Hash  값
    txOutIndex: number; // UTXO 리스트의 index값
    account: string; // 지갑 주소
    amount: number; // 금액
}

지난 시간에 만들어둔 TransactionType을 정해주는 Transaction interface를 활용하여 Transaction class를 만들어주도록 하겠습니다.

txin.ts

export class TxIn {
    public txOutId: string; // Transaction 해시값
    public txOutIndex: number; // Transaction Output 리스트의 배열의 인덱스 값
    public signature?: string; // 코인베이스는 signature가 없으니 ? (Optional chaining) 연산자를 사용하여 값이 null 또는 undefined라도 error가 아닌 undefined를 리턴하도록 합니다.

    constructor(_txOutId: string, _txOutIndex: number, _signature: string | undefined = undefined) {
        this.txOutId = _txOutId;
        this.txOutIndex = _txOutIndex;
        this.signature = _signature;
    }
}

TransactionInput 내용인 Txin에서는 Transaction의 고유한 hash값인 txOutId와 이 hash에 해당하는 TransactionOuputs 배열안에 몇번째 값을 사용한것인지 알 수 있도록 txOutIndex값을 추가하였습니다.
signature는 코인베이스에서는 Input의 내용이 없기 때문에 ? (Optional chaining) 연산자를 사용하여 값이 null or undefined라도 errorreturn하지 않고 undefinedreturn하도록 하였습니다.

txout.ts

export class TxOut {
    public account: string; // 지갑 주소
    public amount: number; // 금액

    constructor(_account: string, _amount: number) {
        this.account = _account;
        this.amount = _amount;
    }
}

Transaction의 Output 내용인 Txout에서는 지갑주소와 금액 인스턴스를 생성하도록 해주었습니다.

unspentTxOut.ts

export class unspentTxOut {
    public txOutId: string;
    public txOutIndex: number;
    public account: string;
    public amount: number;

    constructor(_txOutId: string, _txOutIndex: number, _account: string, _amount: number){
        this.txOutId = _txOutId; // Transaction의 해쉬값
        this.txOutIndex = _txOutIndex; // Transaction의 Output에 있는 각 객체의 Index번호
        this.account = _account; // 지갑 주소
        this.amount = _amount; // 금액
    }
}

unspentTxOuttxOut 객체에 있는 내용과 Transaction의 내용을 가지고 만들어줍니다.
txOutIdtxOutIndex로 해당 Transaction의 고유한 hash값과 해당 hash값에 해당하는 TransactionOutput 배열안에 몇번째 인덱스인지 알 수 있도록 txOutIndex값을 추가하였습니다.
마지막으로 어느 지갑에 얼마의 금액이 들어있는지 알아야 하니 accountamount를 추가해주었습니다.

transaction.ts

import { SHA256 } from 'crypto-js';
import { TxIn } from './txin';
import { TxOut } from './txout';
import { unspentTxOut } from './unspentTxOut';

export class Transaction {
    public hash: string;
    public txIns: TxIn[];
    public txOuts: TxOut[];

    constructor(_txIns: TxIn[], _txOuts: TxOut[]) {
        this.txIns = _txIns; // Transaction의 input의 내용들
        this.txOuts = _txOuts; // Transaction의 output의 내용들
        this.hash = this.createTransactionHash(); // Transaction의 고유한 값
    }

    // Transaction hash 만들기
    createTransactionHash(): string {
        // txoutput 내용의 각 객체의 value값들을 스트링으로 이어붙임
        const txoutConmtent: string = this.txOuts.map((v) => Object.values(v).join('')).join('');
        // txinput 내용의 각 객체의 value값들을 스트링으로 이어붙임
        const txinContent: string = this.txIns.map((v) => Object.values(v).join('')).join('');
        // 이어붙인 txoutConmtent + txinContent 값을 해쉬화하여 리턴
        return SHA256(txoutConmtent + txinContent).toString();
    }

    // UTXO 생성하기 ->  배열안에 객체 형태로 저장
    createUTXO(): unspentTxOut[] {
        // txOutId = Transaction의 해쉬값
        // txOutIndex = Transaction의 Output에 있는 각 객체의 Index번호
        // account = 지갑 주소
        // amount = 금액

        return this.txOuts.map((v, i) => {
            return new unspentTxOut(this.hash, i, v.account, v.amount);
        });
    }
}

Transaction class에서는 Transaction의 고유한 값인 hash값과 txIns (Input 내용), txOuts (Output 내용)을 추가하였습니다.
Transaction의 고유한값인 hash값을 생성할때는 txIns 배열안에 있는 객체들의 value값과 txOuts 배열안에 있는 객체들의 value값을 문자열로 이어붙이고 해당 값을 해쉬화하여 만들어주었습니다.
마지막으로 Transaction을 생성하고 나서 UTXO (미사용 트랜잭션 출력값)에 넣어주기 위해 txOuts 배열안에 있는 객체들에 hash값과 index값을 추가하여 return해주는 createUTXO 메서드를 만들어 주었습니다.

블록체인 HTTP 서버

이제 블록을 생성할때 자동으로 코인베이스 (블록의 첫번째 Transaction값으로 채굴자에 대한 보상 내용이 들어갑니다.)를 생성하도록 HTTP 서버에 블록을 생성하는 API를 수정하도록 하겠습니다.

/* index.ts ( 블록체인 HTTP 서버 ) */

// 블록채굴 API
app.post('/mineBlock', (req, res) => {
    const { data } = req.body; // Transaction 객체를 채우기 위한 정보로 지갑주소값이 들어감
    const newBlock = ws.miningBlock(data); // 받는 사람의 지갑 주소를 인자값으로 보내줌
    if (newBlock.isError) return res.status(500).send(newBlock.error);

    res.json(newBlock.value);
});

mineBlock으로 요청이 들어오면 지갑주소를 req.body로 받아오고 해당 지갑주소를 인자값으로 넣어 miningBlock 함수를 실행합니다.

/* chain.ts */

public miningBlock(_account: string): Failable<Block, string> {
        const txin: ITxIn = new TxIn('', this.getLastestBlock().height + 1); // 해시값이 겹치지 않도록 this.getLastestBlock().height + 1를 넣어주었다.
        const txout: ITxOut = new TxOut(_account, 50); // 지갑주소와 블록을 생성했을때 받는 금액으로 OutPut내용을 만든다.
        const transaction: Transaction = new Transaction([txin], [txout]); // input 배열의 내용들과 output 배열의 내용들로 Transaction을 만든다.

        return this.addBlcok([transaction, ...this.getTransactionPool()]);
    }
    
// 블록을 추가할 때 실행할 함수
    public addBlcok(data: ITransaction[]): Failable<Block, string> {
        const previousBlock = this.getLastestBlock();
        const adjustmentBlock: Block = this.getAdjustmentBlock();
        const newBlock = Block.generateBlock(previousBlock, data, adjustmentBlock);
        const isVaild = Block.isValidNewBlock(newBlock, previousBlock);

        if (isVaild.isError) return { isError: true, error: isVaild.error };
        this.blockchain.push(newBlock);

        newBlock.data.forEach((_tx: ITransaction) => {
            this.updateUTXO(_tx);
        });

        // 사용한 트랜잭션을 트랜잭션풀에서 제거해주어야 합니다.
        this.updateTransactionPool(newBlock);
        return { isError: false, value: newBlock };
    }

chain.ts에 있는 miningBlock을 보면 txin의 내용과 txout의 내용을 가지고 채굴에 대한 보상내용이 있는 Transaction을 생성하고 이 TransactionaddBlock 함수의 인자값으로 넣어주면 Blockdata부분에 miningBlock에서 채굴에 보상에 대한 내용으로 생성한 Transaction이 들어간 상태로 블록을 생성하게 됩니다.

참고 자료

0개의 댓글