【區塊鏈開發指南】區塊鏈基礎之區塊和交易

在區塊鏈網絡當中,全部的數據都以區塊的形式記錄在各個節點上。而每一個區塊又以單獨的文件保存在節點本地磁盤上,在比特幣(Linux系統)中全部的區塊信息都保存在~/.bitcoin/blocks/目錄下面,並以blk***.dat文件名標示,以下圖所示:c++

blocks

區塊結構

根據https://en.bitcoin.it/wiki/Block所描述的,區塊的結構以下:git

Field 描述 大小
Magic no 」魔法數「,常數0xD9B4BEF9 4 bytes
Blocksize 區塊大小 4 bytes
Blockheader 區塊頭 80 bytes
Transaction counter 交易數量,正整數 1 - 9 bytes
transactions 交易列表 -many transactions

首先是一個「魔法數」,根據描述這是個常數佔4個字節,而後是4個字節的區塊大小,而後是區塊頭80字節,而後是1-9字節的交易數量,最後是全部的交易。可是在實際的比特幣代碼當中卻並非這麼定義的,github

class CBlock : public CBlockHeader
{
public:
    // network and disk
    std::vector<CTransactionRef> vtx;

    // memory only
    mutable bool fChecked;

    // ...

    CBlockHeader GetBlockHeader() const
    {
        CBlockHeader block;
        block.nVersion       = nVersion;
        block.hashPrevBlock  = hashPrevBlock;
        block.hashMerkleRoot = hashMerkleRoot;
        block.nTime          = nTime;
        block.nBits          = nBits;
        block.nNonce         = nNonce;
        return block;
    }

    std::string ToString() const;
};

能夠發現,實際上只定義了區塊頭和全部的交易,或許是不一樣版本的差別,可是看github上過去版本的bitcoin代碼中也都沒有定義上述表格中的變量,因此我的認爲實際的結構應該是代碼中定義的。web

再來看看區塊頭部的結構,https://en.bitcoin.it/wiki/Block_hashing_algorithm網絡

Field 目的 更新時間 大小 (Bytes)
Version 區塊版本號 升級軟件並指定新版本號時 4
hashPrevBlock 前一個區塊的256-bit 哈希值 產生新的區塊 32
hashMerkleRoot Merkle樹根的256-bit 哈希值 收到了新的交易 32
Time 從 1970-01-01 00:00 UTC到如今爲止的時間間隔,單位爲秒 每幾秒 4
Bits 當前POW的目標哈希值的壓縮形式 難度調整時 4
Nonce 32-bit 隨機數 嘗試新的hash時 4

每一個區塊頭都包含前一個區塊的哈希值,因此全部的區塊就像鏈表同樣連成了一條鏈,鏈的頭部就是創世塊(Genesis Block)。全部的交易都以Merkle tree的形式進行索引,並在block header中保存Merkle tree的樹根,若是當前交易數量是奇數的話,那麼最後一個交易將會被計算兩次哈希值。當前POW的難度以壓縮的形式保存在4個字節的Bits中,最後經過mining找到的隨機數記錄在最後的Nonce中。數據結構

交易結構

關於比特幣交易,更好的參考是比特幣官方文檔https://en.bitcoin.it/wiki/Transaction,解釋的很詳細。svg

交易是在區塊鏈網絡中傳輸的最基本的數據結構,全部有效的交易最終都會被打包進區塊中並保存在區塊鏈上,比特幣中交易的數據結構以下,區塊鏈

Field 描述 大小
Version no 版本號,當前爲1 4 bytes
In-counter 輸入交易數量,正整數 1 - 9 bytes
list of inputs 輸入列表,每一個區塊中第一個交易被稱爲「Coinbase」 -many inputs
Out-counter 輸出交易數量,正整數 1 - 9 bytes
list of outputs 輸出列表,每一個區塊中第一個輸出交易是給礦工的獎勵 -many outputs
lock_time 鎖定時間,若是非0而且小於0xFFFFFFFF,那麼就是指塊序號;若是交易已經終結,那麼就是指時間戳 4 bytes

一個簡單的交易輸入以下:ui

Input:
Previous tx: f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6
Index: 0
scriptSig: 304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d10
90db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501

首先是前一筆交易的哈希值(Previous tx),而後是花費的是第幾個輸出(Index),最後是解鎖腳本(scriptSig),其中解鎖腳本=簽名+公鑰,只有提供正確的解鎖腳本,才能花費對應的交易。spa

一個簡單的輸出以下:

Output:
Value: 5000000000
scriptPubKey: OP_DUP OP_HASH160 404371705fa9bd789a2fcd52d2c580b65d35549d
OP_EQUALVERIFY OP_CHECKSIG

首先是輸出金額(Value),而後是鎖定腳本,鎖定腳本包括腳本系統中的一系列操做符。

交易類型

比特幣目前提供了兩種不一樣的交易類型,以下所示。經過這二者類型的交易能夠組合出更加複雜的交易,稱之爲合約。

(1)Pay-to-PubkeyHash

scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig: <sig> <pubKey>

這個也是最多見的交易,目標地址就是比特幣地址,花費時須要提供簽名和公鑰。

(2)Pay-to-Script-Hash(P2SH)

scriptPubKey: OP_HASH160 <scriptHash> OP_EQUAL 
scriptSig: ..signatures... <serialized script>

在P2SH中,目標地址由腳本哈希取代,解鎖腳本中才包含簽名和腳本內容。下面有兩種類型的交易的對比,首先是普通的多簽名腳本,解鎖腳本中只須要5個公鑰中任意兩個私鑰的簽名便可。

Locking Script 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG
Unlocking Script Sig1 Sig2

而後是P2SH腳本,

Redeem Script 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG
Locking Script OP_HASH160 <20-byte hash of redeem script> OP_EQUAL
Unlocking Script Sig1 Sig2 redeem script

能夠看出鎖定腳本中只須要包括Redeem Script的哈希便可,而不用包含長長的一串公鑰信息,具體的腳本內容由解鎖腳本提供,至關因而將交易的大部份內容交給了交易花費者去處理,從而減小了網絡傳輸的數據。根據《Master Bitcoin》所述,與直接使⽤複雜腳本以鎖定輸出的⽅式相⽐,P2SH具備如下特色:

  • 在交易輸出中,複雜腳本由簡短電⼦指紋取代,使得交易代碼變短。
  • 腳本能被編譯爲地址,⽀付指令的發出者和⽀付者的⽐特幣錢包不須要複雜⼯序就能夠執⾏P2SH。
  • P2SH將構建腳本的重擔轉移⾄接收⽅,⽽⾮發送⽅。
  • P2SH將⻓腳本數據存儲的負擔從輸出⽅(存儲於UTXO集,影響內存)轉移⾄輸⼊⽅(僅存儲於區塊鏈)。
  • P2SH將⻓腳本數據存儲的重擔從當前(⽀付時)轉移⾄將來(花費時)。
  • P2SH將⻓腳本的交易費成本從發送⽅轉移⾄接收⽅,接收⽅在使⽤該筆資⾦時必須含有贖回腳本。