日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

以太坊源码分析——BlockChain

發(fā)布時間:2024/4/15 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 以太坊源码分析——BlockChain 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、BlockChain的insertChain方法

1.1 前言

?

本章節(jié)介紹BlockChain模塊插入一個新區(qū)塊的流程。一個新區(qū)塊的來源有兩種可能性,第一種可能性是本節(jié)點挖礦成功,要調(diào)用BlockChain模塊向本地區(qū)塊鏈上插入,第二種可能性是節(jié)點從網(wǎng)絡(luò)上的其他節(jié)點收到一個區(qū)塊,調(diào)用BlockChain模塊插入本地區(qū)塊鏈。將一個區(qū)塊插入?yún)^(qū)塊鏈?zhǔn)钦{(diào)用BlockChain的insertChain函數(shù),本章節(jié)詳細(xì)介紹insertChain流程。

?

1.2 insertChain函數(shù)

?

inertChain函數(shù)是功能是將一組區(qū)塊批量插入?yún)^(qū)塊鏈,inesrtChain函數(shù)會檢查這一組區(qū)塊是否是首尾相接。檢查無誤后會校驗區(qū)塊頭和區(qū)塊體,然后還需要校驗狀態(tài)是不是正確。最后將區(qū)塊插入?yún)^(qū)塊鏈,需要注意的是能插入?yún)^(qū)塊鏈不一定能插入規(guī)范鏈,在插入的時候會具體判斷是否能插入規(guī)范鏈,如果不能插入規(guī)范鏈就是一條分叉。

func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*types.Log, error) {//如果傳入的區(qū)塊切片上長度為空,直接返回 if len(chain) == 0 {return 0, nil, nil, nil } //1確保這組區(qū)塊的是首尾相接的,并且區(qū)塊號連續(xù)遞增,如果不是則直接返回 // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ {if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {// Chain broke ancestry, log a message (programming error) and skip insertionlog.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])} } // Pre-checks passed, start the full block imports bc.wg.Add(1) defer bc.wg.Done()bc.chainmu.Lock() defer bc.chainmu.Unlock()// A queued approach to delivering events. This is generally // faster than direct delivery and requires much less mutex // acquiring. var (stats = insertStats{startTime: mclock.Now()}events = make([]interface{}, 0, len(chain))lastCanon *types.BlockcoalescedLogs []*types.Log ) // Start the parallel header verifier headers := make([]*types.Header, len(chain)) seals := make([]bool, len(chain)) //2并行驗證這組區(qū)塊的區(qū)塊頭 for i, block := range chain {headers[i] = block.Header()seals[i] = true } abort, results := bc.engine.VerifyHeaders(bc, headers, seals) defer close(abort)// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number ()), chain)// Iterate over the blocks and insert when the verifier permits //3驗證這組區(qū)塊的區(qū)塊體 for i, block := range chain {// If the chain is terminating, stop processing blocks//如果有人打斷驗證直接返回if atomic.LoadInt32(&bc.procInterrupt) == 1 {log.Debug("Premature abort during blocks processing")break} //如果這個區(qū)塊在bad區(qū)塊列表里,說明這個區(qū)塊不能插入?yún)^(qū)塊鏈,直接返回// If the header is a banned one, straight out abortif BadHashes[block.Hash()] {bc.reportBlock(block, nil, ErrBlacklistedHash)return i, events, coalescedLogs, ErrBlacklistedHash}// Wait for the block's verification to completebstart := time.Now()//4接收區(qū)塊頭的驗證結(jié)果 err := <-resultsif err == nil {//如果區(qū)塊頭沒有問題,則驗證區(qū)塊體err = bc.Validator().ValidateBody(block)}//5 處理區(qū)塊頭或區(qū)塊體的驗證錯誤switch {case err == ErrKnownBlock:// Block and state both already known. However if the current block is below// this number we did a rollback and we should reimport it nonetheless.//6 待插入的區(qū)塊在數(shù)據(jù)庫中已經(jīng)存在,如果當(dāng)前的區(qū)塊鏈的頭區(qū)塊高度比待插入的區(qū)塊大,則直接忽略這個區(qū)塊,否則繼續(xù)向下執(zhí)行插入流程if bc.CurrentBlock().NumberU64() >= block.NumberU64() {stats.ignored++continue}case err == consensus.ErrFutureBlock://7如果待插入?yún)^(qū)塊是一個未來區(qū)塊(大于當(dāng)前時間15秒),則判斷是否是小于30s,如果是則將區(qū)塊放入futureBlocks列表// Allow up to MaxFuture second in the future blocks. If this limit is exceeded// the chain is discarded and processed at a later time if given.max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)if block.Time().Cmp(max) > 0 {return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)}bc.futureBlocks.Add(block.Hash(), block)stats.queued++continuecase err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash())://8數(shù)據(jù)庫里面找不到這個區(qū)塊的父親區(qū)塊, 并且未來待處理區(qū)塊緩沖里面有它的父區(qū)塊, 就將它放入到futureBlocks列表中bc.futureBlocks.Add(block.Hash(), block)stats.queued++continuecase err == consensus.ErrPrunedAncestor://9如果待插入?yún)^(qū)塊的祖先是一個精簡分支(所謂精簡分支就是一條分叉,只有區(qū)塊頭和區(qū)塊體,但是沒有狀態(tài)),看這個區(qū)塊的總難度是否大于本地規(guī)范鏈頭區(qū)塊的總難度,如果大于,則將這條精簡分支上的所有沒有狀態(tài)的區(qū)塊重新做一次插入,插入的過程會產(chǎn)生狀態(tài),并將這條精簡分支升級為規(guī)范鏈,否則如果不大于則將這個區(qū)塊不帶狀態(tài)的情況下插入這條精簡分支。// Block competing with the canonical chain, store in the db, but don't process// until the competitor TD goes above the canonical TDcurrentBlock := bc.CurrentBlock()localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64 ()-1), block.Difficulty())if localTd.Cmp(externTd) > 0 {if err = bc.WriteBlockWithoutState(block, externTd); err != nil {return i, events, coalescedLogs, err}continue}// Competitor chain beat canonical, gather all blocks from the common ancestorvar winner []*types.Blockparent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)for !bc.HasState(parent.Root()) {winner = append(winner, parent)parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)}//將精簡分支上的區(qū)塊收集好后,做一個倒序,因為插入的時候要安從小到大的順序插入for j := 0; j < len(winner)/2; j++ {winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j]}// Import all the pruned blocks to make the state availablebc.chainmu.Unlock()_, evs, logs, err := bc.insertChain(winner)bc.chainmu.Lock()events, coalescedLogs = evs, logsif err != nil {return i, events, coalescedLogs, err}case err != nil://無法處理的錯誤,直接返回bc.reportBlock(block, nil, err)return i, events, coalescedLogs, err}//10 驗證區(qū)塊的狀態(tài)// Create a new statedb using the parent block and report an// error if it fails.var parent *types.Blockif i == 0 {parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)} else {parent = chain[i-1]}state, err := state.New(parent.Root(), bc.stateCache)if err != nil {return i, events, coalescedLogs, err}// Process block using the parent state as reference point.receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)if err != nil {bc.reportBlock(block, receipts, err)return i, events, coalescedLogs, err}// Validate the state using the default validatorerr = bc.Validator().ValidateState(block, parent, state, receipts, usedGas)if err != nil {bc.reportBlock(block, receipts, err)return i, events, coalescedLogs, err}proctime := time.Since(bstart)// 11 調(diào)用WriteBlockWithState將區(qū)塊寫入?yún)^(qū)塊鏈,返回值如果是CanonStatTy,表示寫入了規(guī)范鏈,如果是SideStatTy表示寫入了分叉// Write the block to the chain and get the status.status, err := bc.WriteBlockWithState(block, receipts, state)if err != nil {return i, events, coalescedLogs, err}switch status {case CanonStatTy: log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)))coalescedLogs = append(coalescedLogs, logs...)blockInsertTimer.UpdateSince(bstart)events = append(events, ChainEvent{block, block.Hash(), logs})lastCanon = block// Only count canonical blocks for GC processing timebc.gcproc += proctimecase SideStatTy:log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))blockInsertTimer.UpdateSince(bstart)events = append(events, ChainSideEvent{block})}stats.processed++stats.usedGas += usedGascache, _ := bc.stateCache.TrieDB().Size()stats.report(chain, i, cache) } // Append a single chain head event if we've progressed the chain if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {events = append(events, ChainHeadEvent{lastCanon}) } //返回插入過程中的事件,BlockChain會將這個事件post出去,讓其他監(jiān)聽的模塊可以獲知這些事件 return 0, events, coalescedLogs, nil }

?

上面代碼的第2步,使用了bc.engine.VerifyHeaders函數(shù)去驗證區(qū)塊的區(qū)塊頭,這個函數(shù)傳入了兩個切片:

?

headers := make([]*types.Header, len(chain)) seals := make([]bool, len(chain))

這兩個切片的長度是相同的,第一個切片傳入的是這組區(qū)塊的區(qū)塊頭 ,第二組切片指定要驗證哪些區(qū)塊頭,如果某個區(qū)塊要驗證,則在seals切片上對應(yīng)位置置為true。我們可以看到代碼里面把所有位置都置為了true,表示要驗證headers切片里的所有區(qū)塊頭。

?

for i, block := range chain {headers[i] = block.Header()seals[i] = true }

另外bc.engine.VerifyHeaders是異步檢查,調(diào)完之后會直接返回,繼續(xù)往下執(zhí)行。它會返回兩個管道abort和result,第一個管道可以命令VerifyHeaders函數(shù)停止驗證,第二個管道是可以從其中等待驗證結(jié)果,VerifyHeaders函數(shù)會保證驗證結(jié)果返回的順序和我們傳入的headers切片的順序相同。所以第3步驗證區(qū)塊頭的時候重新用for循環(huán)遍歷chain切片時,for循環(huán)的第一次執(zhí)行,result管道返回的必然是chain中第一個區(qū)塊的區(qū)塊頭的驗證結(jié)果。

?

第6步處理ErrFutureBlock錯誤返回值時,如果待插入的區(qū)塊在數(shù)據(jù)庫中已經(jīng)存在,說明它是有可能是一個分叉,如果它的區(qū)塊高度比當(dāng)前規(guī)范鏈的頭區(qū)塊要大,那么就重新在插入一下,因為有可能這個區(qū)塊的所在的分叉的總難度比當(dāng)前規(guī)范鏈大,如果真是這樣的話,需要把這條分叉升級為規(guī)范鏈,重新插入的過程會檢查是否是規(guī)范鏈,如果是就會升級。

?

第7步,當(dāng)收到一個區(qū)塊它的時間戳大于當(dāng)前時間15秒,小于30秒,節(jié)點不會將這個區(qū)塊丟棄, 而是將這個區(qū)塊放入到futureBlocks列表,我們在上一章節(jié)講到NewBlockChain函數(shù)最后會啟動一個go程定時來檢查這些區(qū)塊能不能插入到區(qū)塊鏈,如果能插,就會再次調(diào)用insertChain來插入。

?

第 10步是驗證區(qū)塊的狀態(tài),驗證區(qū)塊的狀態(tài)流程是基于父區(qū)塊的世界狀態(tài)去執(zhí)行待插入?yún)^(qū)塊的里的所有交易,生成新的世界狀態(tài),然后調(diào)用bc.Validator().ValidateState去驗證新的狀態(tài)是不是正確,其中重要的一個環(huán)節(jié)就是比較新生成的狀態(tài)樹樹根和區(qū)塊頭中的狀態(tài)樹樹根是否相同。

?

第11步調(diào)用WriteBlockWithState將區(qū)塊寫入?yún)^(qū)塊鏈,這個函數(shù)會去判斷這個區(qū)塊寫入的是一個分叉還是規(guī)范鏈,當(dāng)然這個插入的區(qū)塊有可能將一個原來的分叉升級為規(guī)范鏈,原來的規(guī)范鏈變成一條分叉。

?

1.3 總結(jié)

?

本章節(jié)主要介紹insertChain的流程,從上面的分析可以看出insertChain函數(shù)里面主要是實現(xiàn)了對區(qū)塊的校驗,包括區(qū)塊頭和區(qū)塊體,校驗通過之后會調(diào)用WriteBlockWithState函數(shù)將區(qū)塊寫入?yún)^(qū)塊鏈,而真正寫入的過程在WriteBlockWithState中。下一章節(jié)我們分析WriteBlockWithState函數(shù)流程。

?

?

-END-

轉(zhuǎn)載于:https://www.cnblogs.com/efish/p/eth-source-analyze.html

總結(jié)

以上是生活随笔為你收集整理的以太坊源码分析——BlockChain的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。