以太坊blockchain源码分析 (3)

①:从数据库中恢复headblock,如果空的话,触发reset chain

head := rawdb.ReadHeadBlockHash(bc.db) if head == (common.Hash{}) { log.Warn("Empty database, resetting chain") return bc.Reset() }

②:确保整个head block是可以获取的,若为空,则触发reset chain

currentBlock := bc.GetBlockByHash(head) if currentBlock == nil { // Corrupt or empty database, init from scratch log.Warn("Head block missing, resetting chain", "hash", head) return bc.Reset() }

③:从stateDb中打开最新区块的状态trie,如果打开失败调用bc.repair(&currentBlock)方法进行修复。修复方法就是从当前区块一个个的往前面找,直到找到好的区块,然后赋值给currentBlock。

if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil { // Dangling block without a state associated, init from scratch log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash()) if err := bc.repair(&currentBlock); err != nil { return err } rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash()) }

④:存储当前的headblock和设置当前的headHeader以及头部快速块

bc.currentBlock.Store(currentBlock) .... bc.hc.SetCurrentHeader(currentHeader) ... bc.currentFastBlock.Store(currentBlock) 插入数据到blockchain中

①:如果链正在中断,直接返回

②:开启并行的签名恢复

③:校验header

abort, results := bc.engine.VerifyHeaders(bc, headers, seals)

④:循环校验body

block, err := it.next() -> ValidateBody -> VerifyUncles

包括以下错误:

block已知

uncle太多

重复的uncle

uncle是祖先块

uncle哈希不匹配

交易哈希不匹配

未知祖先

祖先块的状态无法获取

如果block存在,且是已知块,则写入已知块。

如果是祖先块的状态无法获取的错误,则作为侧链插入:

bc.insertSideChain(block, it)

如果是未来块或者未知祖先,则添加未来块:

bc.addFutureBlock(block);

如果是其他错误,直接中断,并且报告坏块。

bc.futureBlocks.Remove(block.Hash()) ... bc.reportBlock(block, nil, err)

⑤:没有校验错误

如果是坏块,则报告;如果是未知块,则写入未知块;根据给定trie,创建state;

执行块中的交易:

receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)

使用默认的validator校验状态:

bc.validator.ValidateState(block, statedb, receipts, usedGas);

将块写入到区块链中并获取状态:

status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false)

⑥:校验写入区块的状态

CanonStatTy : 插入成功新的block

SideStatTy:插入成功新的分叉区块

Default:插入未知状态的block

⑦:如果还有块,并且是未来块的话,那么将块添加到未来块的缓存中去

bc.addFutureBlock(block)

至此insertChain 大概介绍清楚。

将块和关联状态写入到数据库

函数:WriteBlockWithState

①:计算父块的total td

ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)

②:添加待插入块本身的td ,并将此时最新的total td 存储到数据库中。

bc.hc.WriteTd(block.Hash(), block.NumberU64(), externTd)

③:将块的header和body分别序列化到数据库

rawdb.WriteBlock(bc.db, block) ->WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) ->WriteHeader(db, block.Header())

④:将状态写入底层内存Trie数据库

state.Commit(bc.chainConfig.IsEIP158(block.Number()))

⑤:存储一个块的所有交易数据

rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)

⑥:将新的head块注入到当前链中

if status == CanonStatTy { bc.insert(block) }

存储分配给规范块的哈希

存储头块的哈希

存储最新的快

更新currentFastBlock

到此writeBlockWithState 结束,从上面可以知道,insertChain的最终还是调用了writeBlockWithState的insert方法完成了最终的插入动作。

思考

为什么还要导入已知块???writeKnownBlock

参考:

https://github.com/mindcarver/blockchain_guide (优秀的区块链学习营地)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsfyg.html