您好,登錄后才能下訂單哦!
這篇文章主要為大家分析了如何進行go-ethereum區塊存儲的源碼剖析的相關知識點,內容詳細易懂,操作細節合理,具有一定參考價值。如果感興趣的話,不妨跟著跟隨小編一起來看看,下面跟著小編一起深入學習“如何進行go-ethereum區塊存儲的源碼剖析”的知識吧。
區塊和交易等數據最終都是存儲在leveldb數據庫中的,數據庫的存儲位置在datadir/geth/chaindata
中,本文介紹區塊和交易在leveldb中的存儲格式。在core/database_util.go
中封裝了所有與區塊存儲和讀取相關的代碼,通過這些代碼可以弄清楚區塊、交易等數據結構在數據庫中是如何存儲的。
leveldb是一個key-value數據庫,所有數據都是以鍵-值對的形式存儲。key一般與hash相關,value一般是要存儲的數據結構的RLP編碼。區塊存儲時將區塊頭和區塊體分開存儲。
區塊頭的存儲格式為:
headerPrefix + num (uint64 big endian) + hash -> rlpEncode(header)
其中key由區塊頭前綴、區塊號(uint64大端格式)、區塊hash構成,value是區塊頭的RLP編碼。
區塊體的存儲格式為:
bodyPrefix + num (uint64 big endian) + hash -> rlpEncode(block body)
其中key由區塊體前綴、區塊號(uint64大端格式)、區塊hash構成,value是區塊體的RLP編碼。
key中的前綴可以用來區分數據的類型,在core/database_util.go
中定義了各種前綴:
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header tdSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + tdSuffix -> td numSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + numSuffix -> hash blockHashPrefix = []byte("H") // blockHashPrefix + hash -> num (uint64 big endian) bodyPrefix = []byte("b") // bodyPrefix + num (uint64 big endian) + hash -> block body
其中headerPrefix
定義了區塊頭key的前綴為h
,bodyPrefix
定義了區塊體key的前綴為b
。
下面是存儲區塊頭的函數:
// WriteHeader serializes a block header into the database. func WriteHeader(db ethdb.Database, header *types.Header) error { data, err := rlp.EncodeToBytes(header) if err != nil { return err } hash := header.Hash().Bytes() num := header.Number.Uint64() encNum := encodeBlockNumber(num) key := append(blockHashPrefix, hash...) if err := db.Put(key, encNum); err != nil { glog.Fatalf("failed to store hash to number mapping into database: %v", err) } key = append(append(headerPrefix, encNum...), hash...) if err := db.Put(key, data); err != nil { glog.Fatalf("failed to store header into database: %v", err) } glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, hash[:4]) return nil }
它是先對區塊頭進行RLP編碼,encodeBlockNumber
將區塊號轉換成大端格式,然后組裝key。這里先向數據庫中存儲一條 區塊hash->區塊號
的記錄,最后將區塊頭的RLP編碼寫到數據庫中。
下面是存儲區塊體的函數:
// WriteBody serializes the body of a block into the database. func WriteBody(db ethdb.Database, hash common.Hash, number uint64, body *types.Body) error { data, err := rlp.EncodeToBytes(body) if err != nil { return err } return WriteBodyRLP(db, hash, number, data) } // WriteBodyRLP writes a serialized body of a block into the database. func WriteBodyRLP(db ethdb.Database, hash common.Hash, number uint64, rlp rlp.RawValue) error { key := append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...) if err := db.Put(key, rlp); err != nil { glog.Fatalf("failed to store block body into database: %v", err) } glog.V(logger.Debug).Infof("stored block body [%x…]", hash.Bytes()[:4]) return nil }
WriteBody
先對區塊體進行RLP編碼,然后調用WriteBodyRLP
將區塊體的RLP編碼寫到數據庫中。WriteBodyRLP
根據上面的規則組裝key,然后向數據庫中寫入一條記錄。
還有一個WriteBlock
函數分別調用WriteBody
和WriteHeader
將區塊寫到數據庫中。此外還有GetHeader
GetBody
GetBlock
函數用于從數據庫中讀取區塊。
除了區塊外,數據庫中還存儲了所有的交易,每條交易的存儲格式如下:
txHash -> rlpEncode(tx) txHash + txMetaSuffix -> rlpEncode(txMeta)
每條交易對應存儲兩條數據,一條是交易本身,一條是交易的元信息(meta)。交易以交易的hash為key、交易的RLP編碼為value存儲;元信息以txHash+txMetaSuffix為key、元信息的RLP編碼為value存儲。元信息中包含交易所在區塊的區塊hash、區塊號、交易在區塊中的索引。具體可以看WriteTransactions
函數:
// WriteTransactions stores the transactions associated with a specific block // into the given database. Beside writing the transaction, the function also // stores a metadata entry along with the transaction, detailing the position // of this within the blockchain. func WriteTransactions(db ethdb.Database, block *types.Block) error { batch := db.NewBatch() // Iterate over each transaction and encode it with its metadata for i, tx := range block.Transactions() { // Encode and queue up the transaction for storage data, err := rlp.EncodeToBytes(tx) if err != nil { return err } if err := batch.Put(tx.Hash().Bytes(), data); err != nil { return err } // Encode and queue up the transaction metadata for storage meta := struct { BlockHash common.Hash BlockIndex uint64 Index uint64 }{ BlockHash: block.Hash(), BlockIndex: block.NumberU64(), Index: uint64(i), } data, err = rlp.EncodeToBytes(meta) if err != nil { return err } if err := batch.Put(append(tx.Hash().Bytes(), txMetaSuffix...), data); err != nil { return err } } // Write the scheduled data into the database if err := batch.Write(); err != nil { glog.Fatalf("failed to store transactions into database: %v", err) } return nil }
此外還有GetTransaction
函數,根據交易hash從數據庫中讀取交易,它返回對應的交易、交易所在區塊的區塊hash、交易所在區塊的區塊號、交易在區塊中的索引。
關于“如何進行go-ethereum區塊存儲的源碼剖析”就介紹到這了,更多相關內容可以搜索億速云以前的文章,希望能夠幫助大家答疑解惑,請多多支持億速云網站!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。