(三) 區塊鏈數據結構 – 交易

區塊由交易組成。區塊體中包含若干項交易數據。java


交易

交易主要包含兩類數據:交易輸入和交易輸出。
- 交易輸入用來指明錢的來源
- 交易輸出用來指明錢的去向數組

除了交易輸入和交易輸出外,交易中還包含版本號和鎖定時間。交易數據的存儲結構以下:數據結構

交易數據存儲格式

交易對象中的各個字段含義以下:ide

字段 大小 描述
version 4個字節 明確該筆交易參考的版本規則
numInputs 1-9個字節 交易中包含的輸入數量
inputs 不定長 交易中包含的輸入數據
numOutputs 1-9個字節 交易中包含的輸出數量
outputs 不定長 交易中包含的輸出數據
lockTime 4個字節 交易鎖定時間

注:若是鎖定時間爲0,表明當即執行,無需鎖定。若是該值小於5億,則表明區塊鏈到達該高度前,交易不會被執行,若是該值大於5億,則該值表明Unix紀元時間戳,交易在該時間以後執行。區塊鏈

核心代碼 - 變量定義

// These are bitcoin serialized.
    private long version;                           //交易遵循的版本號
    private ArrayList<TransactionInput> inputs;     //交易的金錢來源
    private ArrayList<TransactionOutput> outputs;   //交易的金錢去向

    private long lockTime;                          //交易的鎖定時間

核心代碼 - 數據解析

//解析區塊原始字節數據,構造交易對象
    @Override
    protected void parse() throws ProtocolException {
        cursor = offset;

        version = readUint32();         //讀取版本號
        optimalEncodingMessageSize = 4;

        // First come the inputs.
        long numInputs = readVarInt();  //讀取輸入交易量
        optimalEncodingMessageSize += VarInt.sizeOf(numInputs);

        inputs = new ArrayList<>((int) numInputs);

        //逐一解析輸入交易對象
        for (long i = 0; i < numInputs; i++) {
            //構造輸入交易對象
            TransactionInput input = new TransactionInput(params, this, payload, cursor, serializer);
            inputs.add(input);
            long scriptLen = readVarInt(TransactionOutPoint.MESSAGE_LENGTH);
            optimalEncodingMessageSize += TransactionOutPoint.MESSAGE_LENGTH + VarInt.sizeOf(scriptLen) + scriptLen + 4;
            cursor += scriptLen + 4;
        }

        // Now the outputs
        long numOutputs = readVarInt(); //讀取輸出交易量
        optimalEncodingMessageSize += VarInt.sizeOf(numOutputs);
        outputs = new ArrayList<>((int) numOutputs);

        //逐一解析輸出交易對象
        for (long i = 0; i < numOutputs; i++) {
            //構造輸出交易對象
            TransactionOutput output = new TransactionOutput(params, this, payload, cursor, serializer);
            outputs.add(output);
            long scriptLen = readVarInt(8);
            optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen;
            cursor += scriptLen;
        }
        lockTime = readUint32();
        optimalEncodingMessageSize += 4;
        length = cursor - offset;
    }

交易輸入

交易輸入指明瞭交易的金錢來源。在比特幣系統中,交易的金錢來源,並不來自於某一特定帳戶,而來自於其餘人的輸出。
好比A和B分別轉給C一筆金錢,則當C向D支付時,須要用A和B的輸出做爲輸入。this

交易輸入中,存儲了其關聯的交易輸出指針,同時還存儲了該輸出的解鎖腳本。加密

交易輸入結構以下:idea

交易輸入存儲格式

交易輸入中,各個字段的含義以下:spa

字段 大小 描述
hash 32個字節 關聯輸出所在的交易Hash地址
index 4個字節 關聯輸出在其交易輸出集合中的索引
scriptLen 1-9個字節 解鎖腳本長度
scriptBytes 不定長 解鎖腳本數據
sequence 4個字節 序列號,暫未使用

注:只有解鎖腳本正確,才能對輸出進行消費。解鎖腳本須要使用當前用戶的祕鑰進行簽名,所以只有當前用戶,能使用別人來的輸出。指針

核心代碼 - 變量定義

// Allows for altering transactions after they were broadcast. Values below NO_SEQUENCE-1 mean it can be altered.
    private long sequence;
    // Data needed to connect to the output of the transaction we're gathering coins from.
    //關聯的交易輸出
    private TransactionOutPoint outpoint;
    // The "script bytes" might not actually be a script. In coinbase transactions where new coins are minted there
    // is no input transaction, so instead the scriptBytes contains some extra stuff (like a rollover nonce) that we
    // don't care about much. The bytes are turned into a Script object (cached below) on demand via a getter.
    // 腳本字節數組可能並非實際的腳本,在創世區塊中,沒有輸入交易,該字段可能存儲其餘數據。正常狀況下,該數據會轉換成腳本對象
    private byte[] scriptBytes;

核心代碼 - 數據解析

//經過區塊的原始字節數據,構造輸入交易對象
    @Override
    protected void parse() throws ProtocolException {
        //構造該輸入交易對應的輸出(接收地址)
        //解析數據時,會解析出對應輸出所在交易的Hash地址以及交易輸出對應的索引值
        outpoint = new TransactionOutPoint(params, payload, cursor, this, serializer);
        cursor += outpoint.getMessageSize();
        int scriptLen = (int) readVarInt(); //讀取腳本長度
        length = cursor - offset + scriptLen + 4;

        scriptBytes = readBytes(scriptLen); //讀取解鎖腳本數據
        sequence = readUint32();            //序列號,目前未使用
    }

交易輸出

交易輸出指明瞭錢的去向,擁有該輸出,並用私鑰解密後,方可做爲其餘交易的輸入,進行消費。

交易輸出中,存儲了支付的金額以及鎖定腳本。交易輸出的存儲結構以下:

交易輸出存儲格式

交易輸出中,各個字段含義以下:

字段 大小 描述
value 8個字節 支付金額(單位爲聰)
scriptLen 1-9個字節 鎖定腳本長度
scriptBytes 1-9個字節 鎖定腳本數據

注:只有接收者,經過祕鑰加密生成解鎖腳本後,方可繼續使用該輸出做爲輸入,繼續消費。

核心代碼 - 變量定義

// The output's value is kept as a native type in order to save class instances.
    //支付金額
    private long value;

    // A transaction output has a script used for authenticating that the redeemer is allowed to spend
    // this output.
    //鎖定腳本
    private byte[] scriptBytes;

核心代碼 - 數據解析

//解析交易輸出數據,包含輸出金額和鎖定腳本(接收者能夠解鎖該腳本)
    @Override
    protected void parse() throws ProtocolException {
        value = readInt64();                    //獲取交易輸出的金額
        scriptLen = (int) readVarInt();         //鎖定腳本的長度
        length = cursor - offset + scriptLen;
        scriptBytes = readBytes(scriptLen);     //鎖定腳本的內容
    }

上一篇:(二) 區塊鏈數據結構-區塊

下一篇:(四) 區塊鏈數據結構 – 腳本