以太坊区块和交易的存储结构分析
来源:互联网 发布:一级建造师网络教育 编辑:程序博客网 时间:2024/05/18 20:31
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 thedatabase.
funcWriteHeader(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 numbermapping into database: %v", err)
}
key =append(append(headerPrefix, encNum...), hash...)
if err := db.Put(key, data); err !=nil {
glog.Fatalf("failed to store header intodatabase: %v", err)
}
glog.V(logger.Debug).Infof("stored header#%v [%x…]", header.Number, hash[:4])
returnnil
}
它是先对区块头进行RLP编码,encodeBlockNumber将区块号转换成大端格式,然后组装key。这里先向数据库中存储一条 区块hash->区块号 的记录,最后将区块头的RLP编码写到数据库中。
下面是存储区块体的函数:
// WriteBody serializes the body of a block intothe database.
funcWriteBody(db ethdb.Database, hash common.Hash, numberuint64, 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 ablock into the database.
funcWriteBodyRLP(db ethdb.Database, hash common.Hash,numberuint64, 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 intodatabase: %v", err)
}
glog.V(logger.Debug).Infof("stored blockbody [%x…]", hash.Bytes()[:4])
returnnil
}
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 transactionsassociated with a specific block
// into the given database. Beside writing thetransaction, the function also
// stores a metadata entry along with thetransaction, detailing the position
// of this within the blockchain.
funcWriteTransactions(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
BlockIndexuint64
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)
}
returnnil
}
此外还有GetTransaction函数,根据交易hash从数据库中读取交易,它返回对应的交易、交易所在区块的区块hash、交易所在区块的区块号、交易在区块中的索引。
- 以太坊区块和交易的存储结构分析
- [以太坊源代码分析] I.区块和交易,合约和虚拟机
- [以太坊源代码分析] I.区块和交易,合约和虚拟机
- 以太坊中的账户、交易、Gas和区块Gas Limit
- 区块链入门(3):在以太坊私有网络中建立节点集群,并发生交易
- 区块链开发(十四)以太坊go-ethereum客户端查询交易列表探讨
- 区块链和以太坊用于 JavaScript 和 React 开发
- 以太坊客户端mist和geth加快区块同步速度的方法
- 以太坊客户端mist和geth加快区块同步速度的方法(星火节点计划)
- [Introducing Ethereum and Solidity]以太坊和solidity介绍----第一章-连接区块链知识的断点
- 以太坊客户端mist和geth加快区块同步速度的方法(星火节点计划)
- 以太坊区块链白皮书
- 以太坊代币开发深入理解交易的脚本
- 以太坊的公链支持挖到空的区块
- 基于以太坊的区块链浏览器搭建
- 基于以太坊的区块链浏览器搭建
- 发力于以太坊商业应用开发,他们提供基于以太坊区块链技术的行业解决方案和咨询服务
- 基于区块链的价值交易和管理系统
- HBase HFile与Prefix Compression内部实现全解
- vue入门环境搭建
- Oracle 创建dblink
- poj-1014 多重背包问题
- 隐私窗口
- 以太坊区块和交易的存储结构分析
- 多行文本溢出最后一行显示省略号
- "_OBJC_CLASS_$_XXXXXXXXXX", referenced from:
- sprintf与snprintf区别
- enable_shared_from_this
- 第十三章干系人管理
- 直接插入排序C++实现
- mysql安装、配置、启动(windows)
- 简单的几步,让php实现邮件发送