【以太坊原始碼解析】-區塊資料結構
區塊資料結構
在區塊鏈中,區塊是儲存有價值資訊的塊。這是任何一種加密貨幣的本質。除此之外,區塊還包含一些技術資訊,比如它的版本、當前時間戳和前一區塊的雜湊值(雜湊值)
Block(區塊)是Ethereum的核心資料結構之一
*所有賬戶的相關活動,以交易(Transaction)的格式儲存,每個Block有一個交易物件的列表
*每個交易的執行結果,由一個Receipt物件與其包含的一組Log物件記錄
*所有交易執行完後生成的Receipt列表,儲存在Block中(經過壓縮加密)
*不同Block之間,通過前向指標ParentHash一個一個串聯起來成為一個單向連結串列,BlockChain 結構體管理著這個連結串列
*Block結構體基本可分為Header和Body兩個部分
Block: 表示以太坊區塊鏈中的一個完整塊
type Block struct { header*Header uncles[]*Header// 塊頭(Header) transactions Transactions // caches hash atomic.Value size atomic.Value // Td is used by package core to store the total difficulty // of the chain up to and including the block. td *big.Int // These fields are used by package eth to track // inter-peer block relay. ReceivedAttime.Time ReceivedFrom interface{}// 塊體(Body) }
在儲存區塊資訊時,會將區塊頭和區塊體分開進行儲存。因此在區塊的結構體中,能夠看到Header和Body兩個結構體
Header: 表示以太坊區塊鏈中的塊頭
type Header struct { ParentHashcommon.Hash`json:"parentHash"gencodec:"required"` UncleHashcommon.Hash`json:"sha3Uncles"gencodec:"required"` Coinbasecommon.Address `json:"miner"gencodec:"required"` Rootcommon.Hash`json:"stateRoot"gencodec:"required"` TxHashcommon.Hash`json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash`json:"receiptsRoot"gencodec:"required"` BloomBloom`json:"logsBloom"gencodec:"required"` Difficulty*big.Int`json:"difficulty"gencodec:"required"` Number*big.Int`json:"number"gencodec:"required"` GasLimituint64`json:"gasLimit"gencodec:"required"` GasUseduint64`json:"gasUsed"gencodec:"required"` Time*big.Int`json:"timestamp"gencodec:"required"` Extra[]byte`json:"extraData"gencodec:"required"` MixDigestcommon.Hash`json:"mixHash"gencodec:"required"` NonceBlockNonce`json:"nonce"gencodec:"required"` }
Body: 以太坊區塊鏈中的交易資訊
// Body is a simple (mutable, non-safe) data container for storing and moving // a block's data contents (transactions and uncles) together. type Body struct { Transactions []*Transaction Uncles[]*Header }
Header部分
Header是Block的核心,它的成員變數全都是公共的,可以很方便的向呼叫者提供關於Block屬性的操作。Header的成員變數全都很重要,值得細細理解:
- ParentHash:指向父區塊(parentBlock)的指標。除了創世塊(Genesis Block)外,每個區塊有且只有一個父區塊。
- UncleHash:Block結構體的成員uncles的RLP雜湊值。uncles是一個Header陣列,它的存在,頗具匠心。
- Coinbase:挖掘出這個區塊的作者地址。在每次執行交易時系統會給與一定補償的Ether,這筆金額就是發給這個地址的。
- Root:StateDB中的“state Trie”的根節點的RLP雜湊值。Block中,每個賬戶以stateObject物件表示,賬戶以Address為唯一標示,其資訊在相關交易(Transaction)的執行中被修改。所有賬戶物件可以逐個插入一個Merkle-PatricaTrie(MPT)結構裡,形成“state Trie”。
- TxHash: Block中 “tx Trie”的根節點的RLP雜湊值。Block的成員變數transactions中所有的tx物件,被逐個插入一個MPT結構,形成“tx Trie”。
- ReceiptHash:Block中的 "Receipt Trie”的根節點的RLP雜湊值。Block的所有Transaction執行完後會生成一個Receipt陣列,這個陣列中的所有Receipt被逐個插入一個MPT結構中,形成"Receipt Trie"。
- Bloom:Bloom過濾器(Filter),用來快速判斷一個引數Log物件是否存在於一組已知的Log集合中。
- Difficulty:區塊的難度。Block的Difficulty由共識演算法基於parentBlock的Time和Difficulty計算得出,它會應用在區塊的‘挖掘’階段。
- Number:區塊的序號。Block的Number等於其父區塊Number +1。
- GasLimit:區塊內所有Gas消耗的理論上限。該數值在區塊建立時設定,與父區塊有關。具體來說,根據父區塊的GasUsed同GasLimit * 2/3的大小關係來計算得出。
- GasUsed:區塊內所有Transaction執行時所實際消耗的Gas總和。
- Time:區塊“應該”被建立的時間。由共識演算法確定,一般來說,要麼等於parentBlock.Time + 10s,要麼等於當前系統時間。
- Nonce:一個64bit的雜湊數,它被應用在區塊的"挖掘"階段,並且在使用中會被修改
Body結構體
Block的成員變數td 表示的是整個區塊連結串列從源頭創世塊開始,到當前區塊截止,累積的所有區塊Difficulty之和,td 取名totalDifficulty。從概念上可知,某個區塊與父區塊的td之差,就等於該區塊Header帶有的Difficulty值。
Body可以理解為Block裡的陣列成員集合,它相對於Header需要更多的記憶體空間,所以在資料傳輸和驗證時,往往與Header是分開進行的。
Uncles是Body非常特別的一個成員,從業務功能上說,它並不是Block結構體必須的,它的出現當然會佔用整個Block計算雜湊值時更長的時間,目的是為了抵消整個Ethereum網路中那些計算能力特別強大的節點會對區塊的產生有過大的影響力,防止這些節點破壞“去中心化”這個根本宗旨。官方描述可見ofollow,noindex" target="_blank">ethereum-wiki
Block的唯一識別符號
Block物件的唯一識別符號,就是它的(RLP)雜湊值。需要注意的是,Block的雜湊值,等於其Header成員的(RLP)雜湊值
// core/types/block.go // Hash returns the keccak256 hash of b's header. // The hash is computed on the first call and cached thereafter. func (b *Block) Hash() common.Hash { if hash := b.hash.Load(); hash != nil {// 獲取最近所設定的儲存值 return hash.(common.Hash) } v := b.header.Hash()// 呼叫Head成員的Hash b.hash.Store(v) return v }
Block的成員hash會快取上一次Header計算出的雜湊值,以避免不必要的計算
// Hash returns the block hash of the header, which is simply the keccak256 hash of its // RLP encoding. func (h *Header) Hash() common.Hash { return rlpHash(h) }
func rlpHash(x interface{}) (h common.Hash) { hw := sha3.NewKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h }
新增新塊:
// NewBlock creates a new block. The input data is copied, // changes to header and to the field values will not affect the // block. // // The values of TxHash, UncleHash, ReceiptHash and Bloom in header // are ignored and set to values derived from the given txs, uncles // and receipts. func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { b := &Block{header: CopyHeader(header), td: new(big.Int)} // TODO: panic if len(txs) != len(receipts) if len(txs) == 0 { b.header.TxHash = EmptyRootHash } else { b.header.TxHash = DeriveSha(Transactions(txs)) b.transactions = make(Transactions, len(txs)) copy(b.transactions, txs) } if len(receipts) == 0 { b.header.ReceiptHash = EmptyRootHash } else { b.header.ReceiptHash = DeriveSha(Receipts(receipts)) b.header.Bloom = CreateBloom(receipts) } if len(uncles) == 0 { b.header.UncleHash = EmptyUncleHash } else { b.header.UncleHash = CalcUncleHash(uncles) b.uncles = make([]*Header, len(uncles)) for i := range uncles { b.uncles[i] = CopyHeader(uncles[i]) } } return b }