如何进行go-ethereum区块存储的源码剖析
这篇文章主要为大家分析了如何进行go-ethereum区块存储的源码剖析的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“如何进行go-ethereum区块存储的源码剖析”的知识吧。
成都创新互联公司10多年成都企业网站建设服务;为您提供网站建设,网站制作,网页设计及高端网站定制服务,成都企业网站建设及推广,对成都岗亭等多个领域拥有多年的网站营销经验的网站建设公司。
区块和交易等数据最终都是存储在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区块存储的源码剖析”就介绍到这了,更多相关内容可以搜索创新互联以前的文章,希望能够帮助大家答疑解惑,请多多支持创新互联网站!
本文名称:如何进行go-ethereum区块存储的源码剖析
本文URL:http://cdiso.cn/article/jdoods.html