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