以太坊源码剖析:区块存储-量财经区块链学堂

区块和交易等数据最终都是存储在leveldb数据库中的,数据库的存储位置在datadir/geth/chaindata中梁君诺 ,本文介绍区块和交易在leveldb中的存储格式。在core/database_util.go中封装了所有与区块存储和读取相关的代码,通过这些代码可以弄清楚区块、交易等数据结构在数据库中是如何存储的名师测控 。区块存储
leveldb是一个key-value数据库,所有数据都是以键-值对的形式存储。key一般与hash相关怒之拳 ,value一般是要存储的数据结构的RLP编码。区块存储时将区块头和区块体分开存储。
区块头的存储格式为:
headerPrefix+num(uint64bigendian)+hash->rlpEncode(header)
其中key由区块头前缀、区块号(uint64大端格式)、区块hash构成,value是区块头的RLP编码。
区块体的存储格式为:
bodyPrefix+num(uint64bigendian)+hash->rlpEncode(blockbody)
其中key由区块体前缀、区块号(uint64大端格式)、区块hash构成,value是区块体的RLP编码食人族实录 。
key中的前缀可以用来区分数据的类型,在core/database_util.go中定义了各种前缀:
headerPrefix=[]byte("h")//headerPrefix+num(uint64bigendian)+hash->headertdSuffix=[]byte("t")//headerPrefix+num(uint64bigendian)+hash+tdSuffix->tdnumSuffix=[]byte("n")//headerPrefix+num(uint64bigendian)+numSuffix->hashblockHashPrefix=[]byte("H")//blockHashPrefix+hash->num(uint64bigendian)bodyPrefix=[]byte("b")//bodyPrefix+num(uint64bigendian)+hash->blockbody
其中headerPrefix定义了区块头key的前缀为h美梦成真泰剧 ,bodyPrefix定义了区块体key的前缀为b。
下面是存储区块头的函数:
//WriteHeaderserializesablockheaderintothedatabase.funcWriteHeader(dbethdb.Database,马秋子 header*types.Header)error{data,err:=rlp.EncodeToBytes(header)iferrmgb战队 !=nil{returnerr}hash:=header.Hash().Bytes()num:=header.Number.Uint64()encNum:=encodeBlockNumber(num)key:=append(blockHashPrefix岑梦凡,hash...)iferr:=db.Put(key,encNum);err!=nil{glog.Fatalf("failedtostorehashtonumbermappingintodatabase:%v",err)}key=append(append(headerPrefix华丽家族花园 ,encNum...)龙猫组合 ,hash...)iferr:=db.Put(key,data);err!=nil{glog.Fatalf("failedtostoreheaderintodatabase:%v",err)}glog.V(logger.Debug).Infof("storedheader#%v[%x…]"医仙王妃,header.Number,hash[:4])returnnil}
它是先对区块头进行RLP编码,encodeBlockNumber将区块号转换成大端格式,然后组装key。这里先向数据库中存储一条区块hash->区块号的记录老虎出监,最后将区块头的RLP编码写到数据库中。
下面是存储区块体的函数:
//WriteBodyserializesthebodyofablockintothedatabase.funcWriteBody(dbethdb.Database,hashcommon.Hash励志帝,numberuint64,body*types.Body)error{data,err:=rlp.EncodeToBytes(body)iferr!=nil{returnerr}returnWriteBodyRLP(db魏明亮,hash,number,data)}//WriteBodyRLPwritesaserializedbodyofablockintothedatabase.funcWriteBodyRLP(dbethdb.Database,hashcommon.Hash,numberuint64,rlprlp.RawValue)error{key:=append(append(bodyPrefix,encodeBlockNumber(number)...)戴潆萱 ,hash.Bytes()...)iferr:=db.Put(key,rlp);err!=nil{glog.Fatalf("failedtostoreblockbodyintodatabase:%v",err)}glog.V(logger.Debug).Infof("storedblockbody[%x…]",hash.Bytes()[:4])returnnil}
WriteBody先对区块体进行RLP编码,然后调用WriteBodyRLP将区块体的RLP编码写到数据库中。WriteBodyRLP根据上面的规则组装key,然后向数据库中写入一条记录。
还有一个WriteBlock函数分别调用WriteBody和WriteHeader将区块写到数据库中。此外还有GetHeaderGetBodyGetBlock函数用于从数据库中读取区块。交易存储
除了区块外,数据库中还存储了所有的交易,每条交易的存储格式如下:
txHash->rlpEncode(tx)txHash+txMetaSuffix->rlpEncode(txMeta)
每条交易对应存储两条数据,一条是交易本身街霸降龙 ,一条是交易的元信息(meta)。交易以交易的hash为key、交易的RLP编码为value存储;元信息以txHash+txMetaSuffix为key、元信息的RLP编码为value存储。元信息中包含交易所在区块的区块hash、区块号、交易在区块中的索引。具体可以看WriteTransactions函数:
//WriteTransactionsstoresthetransactionsassociatedwithaspecificblock//intothegivendatabase.Besidewritingthetransaction,thefunctionalso//storesametadataentryalongwiththetransaction,detailingtheposition//ofthiswithintheblockchain.funcWriteTransactions(dbethdb.Database,block*types.Block)error{batch:=db.NewBatch()//Iterateovereachtransactionandencodeitwithitsmetadatafori,tx:=rangeblock.Transactions(){//Encodeandqueueupthetransactionforstoragedata,err:=rlp.EncodeToBytes(tx)iferr!=nil{returnerr}iferr:=batch.Put(tx.Hash().Bytes(),data);err!=nil{returnerr}//Encodeandqueueupthetransactionmetadataforstoragemeta:=struct{BlockHashcommon.HashBlockIndexuint64Indexuint64}{BlockHash:block.Hash(),BlockIndex:block.NumberU64(),Index:uint64(i),}data,err=rlp.EncodeToBytes(meta)iferr!=nil{returnerr}iferr:=batch.Put(append(tx.Hash().Bytes(),txMetaSuffix...)聊斋之小谢 ,data);err!=nil{returnerr}}//Writethescheduleddataintothedatabaseiferr:=batch.Write();err!=nil{glog.Fatalf("failedtostoretransactionsintodatabase:%v",err)}returnnil}
此外还有GetTransaction函数,根据交易hash从数据库中读取交易,它返回对应的交易、交易所在区块的区块hash、交易所在区块的区块号、交易在区块中的索引。
声明:转载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不构成投资建议。投资者据此操作,风险自特工下堂妃 担。