如何进行go-ethereum区块存储的源码剖析
如何进行go-ethereum区块存储的源码剖析
这篇文章主要为大家分析了如何进行go-ethereum区块存储的源码剖析的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“如何进行go-ethereum区块存储的源码剖析”的知识吧。
区块和交易等数据最终都是存储在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)iferr!=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
将区块写到数据库中。此外还有GetHeader
GetBody
GetBlock
函数用于从数据库中读取区块。
交易存储
除了区块外,数据库中还存储了所有的交易,每条交易的存储格式如下:
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、交易所在区块的区块号、交易在区块中的索引。
关于“如何进行go-ethereum区块存储的源码剖析”就介绍到这了,更多相关内容可以搜索恰卡编程网以前的文章,希望能够帮助大家答疑解惑,请多多支持恰卡编程网网站!
推荐阅读
-
polyfills怎么按需加载
polyfills怎么按需加载本篇内容主要讲解“polyfills...
-
C#数据类型怎么实现背包、队列和栈
-
C#怎么实现冒泡排序和插入排序算法
C#怎么实现冒泡排序和插入排序算法这篇文章主要讲解了“C#怎么实现...
-
C#如何实现希尔排序
-
C#如何实现归并排序
-
C#怎么使用符号表实现查找算法
-
C#类的静态成员怎么用
C#类的静态成员怎么用这篇“C#类的静态成员怎么用”文章的知识点大...
-
C#的静态函数怎么用
C#的静态函数怎么用这篇文章主要讲解了“C#的静态函数怎么用”,文...
-
C#中的析构函数怎么用
C#中的析构函数怎么用这篇文章主要讲解了“C#中的析构函数怎么用”...
-
怎么用CZGL.ProcessMetrics监控.NET应用