以太坊區塊資料儲存及查詢流程
阿新 • • 發佈:2018-12-14
以太坊區塊資料儲存
字首
core/rawdb/schema.go // databaseVerisionKey tracks the current database version. databaseVerisionKey = []byte("DatabaseVersion") // headHeaderKey tracks the latest know header's hash. headHeaderKey = []byte("LastHeader") // headBlockKey tracks the latest know full block's hash. headBlockKey = []byte("LastBlock") // headFastBlockKey tracks the latest known incomplete block's hash during fast sync. headFastBlockKey = []byte("LastFast") // fastTrieProgressKey tracks the number of trie entries imported during fast sync. fastTrieProgressKey = []byte("TrieSync") // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian) blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db // Chain index prefixes (use `i` + single byte to avoid mixing data types). BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
根據區塊頭hash將區塊高度儲存,再根據區塊高度和區塊頭hash將區塊頭RLP編碼後儲存
core/rawdb/accessors_chain.go
func WriteHeader(db DatabaseWriter, header *types.Header) {
// Write the hash -> number mapping
var (
hash = header.Hash()
number = header.Number.Uint64()
encoded = encodeBlockNumber(number) // 將number轉化為大端
)
key := headerNumberKey(hash) // key="H"+hash
if err := db.Put(key, encoded); err != nil { // 將 number 存入資料庫
log.Crit("Failed to store hash to number mapping", "err", err)
}
// Write the encoded header
data, err := rlp.EncodeToBytes(header) // 對區塊頭進行RLP編碼
if err != nil {
log.Crit("Failed to RLP encode header", "err", err)
}
key = headerKey(number, hash) // key="h"+number+hash
if err := db.Put(key, data); err != nil {
log.Crit("Failed to store header", "err", err)
}
}
將區塊體的RLP編碼後儲存
func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
data, err := rlp.EncodeToBytes(body)
if err != nil {
log.Crit("Failed to RLP encode body", "err", err)
}
WriteBodyRLP(db, hash, number, data)
}
func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil { // key="b"+number+hash
log.Crit("Failed to store block body", "err", err)
}
}
區塊體和區塊頭是分開儲存
func WriteBlock(db DatabaseWriter, block *types.Block) {
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
WriteHeader(db, block.Header())
}
根據區塊高度寫入區塊頭hash
func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil { // key="h"+number+"n"
log.Crit("Failed to store number to hash mapping", "err", err)
}
}
根據區塊高度刪除區塊頭hash
func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
if err := db.Delete(headerHashKey(number)); err != nil { // key="h"+number+"n"
log.Crit("Failed to delete number to hash mapping", "err", err)
}
}
以太坊區塊查詢
通過塊高查詢整個區塊資料
根據區塊號獲取區塊頭hash
func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
data, _ := db.Get(headerHashKey(number)) // key="h"+number+"n"
if len(data) == 0 {
return common.Hash{}
}
return common.BytesToHash(data)
}
根據區塊號,區塊頭hash,獲取區塊頭的RLP編碼後的值
func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
data, _ := db.Get(headerKey(number, hash)) // key="h"+number+hash
return data
}
根據區塊號,區塊頭hash,獲取區塊體的RLP編碼後的值
func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
data, _ := db.Get(blockBodyKey(number, hash)) // key="b"+number+hash
return data
}
通過區塊hash查詢整個區塊資料
根據區塊頭hash獲取區塊高度
func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
data, _ := db.Get(headerNumberKey(hash)) // key="H"+hash
if len(data) != 8 {
return nil
}
number := binary.BigEndian.Uint64(data)
return &number
}
儲存區塊資料
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
...
rawdb.WriteBlock(bc.db, block) // 將區塊體和區塊頭分別儲存
bc.insert(block)
...
}
func (bc *BlockChain) insert(block *types.Block) {
...
rawdb.WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64()) // 將區塊頭hash存入區塊高度
rawdb.WriteHeadBlockHash(bc.db, block.Hash())
...
}
小結:
- 儲存區塊體,將區塊體RLP編碼後儲存 : “b”+number+hash = rlp(body)
- 儲存區塊頭,將區塊高度儲存 : “H”+hash = number
- 儲存區塊頭,將區塊頭RLP編碼後儲存 : “h”+number+hash = rlp(header)
- 儲存區塊頭hash,將區塊頭hash儲存 : key=“h”+number+“n” = hash
至此就可以通過塊高或hash查詢區塊資料