①:从数据库中恢复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(¤tBlock)方法进行修复。修复方法就是从当前区块一个个的往前面找,直到找到好的区块,然后赋值给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(¤tBlock); 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 (优秀的区块链学习营地)