以太坊原始碼分析(19)core-blockchain分析

尹成發表於2018-05-14
從測試案例來看,blockchain的主要功能點有下面幾點.

1. import.
2. GetLastBlock的功能.
3. 如果有多條區塊鏈,可以選取其中難度最大的一條作為規範的區塊鏈.
4. BadHashes 可以手工禁止接受一些區塊的hash值.在blocks.go裡面.
5. 如果新配置了BadHashes. 那麼區塊啟動的時候會自動禁止並進入有效狀態.
6. 錯誤的nonce會被拒絕.
7. 支援Fast importing.
8. Light vs Fast vs Full processing 在處理區塊頭上面的效果相等.

可以看到blockchain的主要功能是維護區塊鏈的狀態, 包括區塊的驗證,插入和狀態查詢.

名詞解釋:

什麼是規範的區塊鏈

因為在區塊的建立過程中,可能在短時間內產生一些分叉, 在我們的資料庫裡面記錄的其實是一顆區塊樹.我們會認為其中總難度最高的一條路徑認為是我們的規範的區塊鏈. 這樣有很多區塊雖然也能形成區塊鏈,但是不是規範的區塊鏈.

資料庫結構:

    區塊的hash值和區塊頭的hash值是同樣的麼。所謂的區塊的Hash值其實就是Header的區塊值。
    // key -> value
    // + 代表連線

    "LastHeader" 最新的區塊頭 HeaderChain中使用
    "LastBlock" 最新的區塊頭 BlockChain 中使用
    "LastFast" 最新的快速同步的區塊頭

    "h"+num+"n" -> hash 用來儲存規範的區塊鏈的高度和區塊頭的hash值
    
    "h" + num + hash -> header 高度+hash值 -> 區塊頭
    
    "h" + num + hash + "t" -> td 高度+hash值 -> 總難度
    
    "H" + hash -> num 區塊體hash -> 高度
    
    "b" + num + hash -> block body 高度+hash值 -> 區塊體
    
    "r" + num + hash -> block receipts 高度 + hash值 -> 區塊收據
    
    "l" + hash -> transaction/receipt lookup metadata

key | value|說明|插入|刪除|
---- | --- |---------------|-----------|-----------|
"LastHeader" | hash | 最新的區塊頭 HeaderChain中使用|當區塊被認為是當前最新的一個規範的區塊鏈頭|當有了更新的區塊鏈頭或者是分叉的兄弟區塊鏈替代了它
"LastBlock" | hash | 最新的區塊頭 BlockChain中使用|當區塊被認為是當前最新的一個規範的區塊鏈頭|當有了更新的區塊鏈頭或者是分叉的兄弟區塊鏈替代了它
"LastFast" | hash | 最新的區塊頭 BlockChain中使用|當區塊被認為是當前最新的規範的區塊鏈頭|當有了更新的區塊鏈頭或者是分叉的兄弟區塊鏈替代了它
"h"+num+"n"|hash|用來儲存規範的區塊鏈的高度和區塊頭的hash值 在HeaderChain中使用|當區塊在規範的區塊鏈中|當區塊不在規範的區塊鏈中
"h" + num + hash + "t"|td|總難度|WriteBlockAndState當驗證並執行完一個區塊之後(不管是不是規範的)|SetHead方法會呼叫。這種方法只會在兩種情況下被呼叫,1是當前區塊鏈包含了badhashs,需要刪除所有從badhashs開始的區塊, 2.是當前區塊的狀態錯誤,需要Reset到genesis。
"H" + hash | num| 區塊的高度 在HeaderChain中使用|WriteBlockAndState當驗證並執行完一個區塊之後|SetHead中被呼叫,同上
"b" + num + hash|block body| 區塊資料| WriteBlockAndState or InsertReceiptChain|SetHead中被刪除,同上
"r" + num + hash|block receipts|區塊收據|WriteBlockAndState or InsertReceiptChain|同上
"l" + txHash | {hash,num,TxIndex|交易hash可以找到區塊和交易|當區塊加入規範的區塊鏈|當區塊從規範的區塊鏈移除



資料結構

    
    // BlockChain represents the canonical chain given a database with a genesis
    // block. The Blockchain manages chain imports, reverts, chain reorganisations.
    // BlockChain 表示了一個規範的鏈,這個鏈通過一個包含了創世區塊的資料庫指定. BlockChain管理了鏈的插入,還原,重建等操作.
    // Importing blocks in to the block chain happens according to the set of rules
    // defined by the two stage Validator. Processing of blocks is done using the
    // Processor which processes the included transaction. The validation of the state
    // is done in the second part of the Validator. Failing results in aborting of
    // the import.
    // 插入一個區塊需要通過一系列指定的規則指定的兩階段的驗證器.
    // 使用Processor來對區塊的交易進行處理. 狀態的驗證是第二階段的驗證器. 錯誤將導致插入終止.
    // The BlockChain also helps in returning blocks from **any** chain included
    // in the database as well as blocks that represents the canonical chain. It's
    // important to note that GetBlock can return any block and does not need to be
    // included in the canonical one where as GetBlockByNumber always represents the
    // canonical chain.
    // 需要注意的是GetBlock可能返回任意不在當前規範區塊鏈中的區塊,
    // 但是GetBlockByNumber 總是返回當前規範區塊鏈中的區塊.
    type BlockChain struct {
        config *params.ChainConfig // chain & network configuration
    
        hc *HeaderChain      // 只包含了區塊頭的區塊鏈
        chainDb ethdb.Database    // 底層資料庫
        rmLogsFeed event.Feed        // 下面是很多訊息通知的元件
        chainFeed event.Feed
        chainSideFeed event.Feed
        chainHeadFeed event.Feed
        logsFeed event.Feed
        scope event.SubscriptionScope
        genesisBlock *types.Block      // 創世區塊
    
        mu sync.RWMutex // global mutex for locking chain operations
        chainmu sync.RWMutex // blockchain insertion lock
        procmu sync.RWMutex // block processor lock
    
        checkpoint int // checkpoint counts towards the new checkpoint
        currentBlock *types.Block // Current head of the block chain 當前的區塊頭
        currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) 當前的快速同步的區塊頭.
    
        stateCache state.Database // State database to reuse between imports (contains state cache)
        bodyCache *lru.Cache // Cache for the most recent block bodies
        bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
        blockCache *lru.Cache // Cache for the most recent entire blocks
        futureBlocks *lru.Cache // future blocks are blocks added for later processing 暫時還不能插入的區塊存放位置.
    
        quit chan struct{} // blockchain quit channel
        running int32 // running must be called atomically
        // procInterrupt must be atomically called
        procInterrupt int32 // interrupt signaler for block processing
        wg sync.WaitGroup // chain processing wait group for shutting down
    
        engine consensus.Engine  // 一致性引擎
        processor Processor // block processor interface // 區塊處理器介面
        validator Validator // block and state validator interface // 區塊和狀態驗證器介面
        vmConfig vm.Config //虛擬機器的配置
    
        badBlocks *lru.Cache // Bad block cache 錯誤區塊的快取.
    }
    


構造,NewBlockChain 使用資料庫裡面的可用資訊構造了一個初始化好的區塊鏈. 同時初始化了以太坊預設的 驗證器和處理器 (Validator and Processor)

    
    // NewBlockChain returns a fully initialised block chain using information
    // available in the database. It initialises the default Ethereum Validator and
    // Processor.
    func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
        bodyCache, _ := lru.New(bodyCacheLimit)
        bodyRLPCache, _ := lru.New(bodyCacheLimit)
        blockCache, _ := lru.New(blockCacheLimit)
        futureBlocks, _ := lru.New(maxFutureBlocks)
        badBlocks, _ := lru.New(badBlockLimit)
    
        bc := &BlockChain{
            config: config,
            chainDb: chainDb,
            stateCache: state.NewDatabase(chainDb),
            quit: make(chan struct{}),
            bodyCache: bodyCache,
            bodyRLPCache: bodyRLPCache,
            blockCache: blockCache,
            futureBlocks: futureBlocks,
            engine: engine,
            vmConfig: vmConfig,
            badBlocks: badBlocks,
        }
        bc.SetValidator(NewBlockValidator(config, bc, engine))
        bc.SetProcessor(NewStateProcessor(config, bc, engine))
    
        var err error
        bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt)
        if err != nil {
            return nil, err
        }
        bc.genesisBlock = bc.GetBlockByNumber(0) // 拿到創世區塊
        if bc.genesisBlock == nil {
            return nil, ErrNoGenesis
        }
        if err := bc.loadLastState(); err != nil { //載入最新的狀態
            return nil, err
        }
        // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
        // 檢查當前的狀態,確認我們的區塊鏈上面沒有非法的區塊.
        // BadHashes是一些手工配置的區塊hash值, 用來硬分叉使用的.
        for hash := range BadHashes {
            if header := bc.GetHeaderByHash(hash); header != nil {
                // get the canonical block corresponding to the offending header's number
                // 獲取規範的區塊鏈上面同樣高度的區塊頭,如果這個區塊頭確實是在我們的規範的區塊鏈上的話,我們需要回滾到這個區塊頭的高度 - 1
                headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
                // make sure the headerByNumber (if present) is in our current canonical chain
                if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
                    log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
                    bc.SetHead(header.Number.Uint64() - 1)
                    log.Error("Chain rewind was successful, resuming normal operation")
                }
            }
        }
        // Take ownership of this particular state
        go bc.update()
        return bc, nil
    }

loadLastState, 載入資料庫裡面的最新的我們知道的區塊鏈狀態. 這個方法假設已經獲取到鎖了.
    
    // loadLastState loads the last known chain state from the database. This method
    // assumes that the chain manager mutex is held.
    func (bc *BlockChain) loadLastState() error {
        // Restore the last known head block
        // 返回我們知道的最新的區塊的hash
        head := GetHeadBlockHash(bc.chainDb)
        if head == (common.Hash{}) { // 如果獲取到了空.那麼認為資料庫已經被破壞.那麼設定區塊鏈為創世區塊.
            // Corrupt or empty database, init from scratch
            log.Warn("Empty database, resetting chain")
            return bc.Reset()
        }
        // Make sure the entire head block is available
        // 根據blockHash 來查詢block
        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()
        }
        // Make sure the state associated with the block is available
        // 確認和這個區塊的world state是否正確.
        if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
            // Dangling block without a state associated, init from scratch
            log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
            return bc.Reset()
        }
        // Everything seems to be fine, set as the head block
        bc.currentBlock = currentBlock
    
        // Restore the last known head header
        // 獲取最新的區塊頭的hash
        currentHeader := bc.currentBlock.Header()
        if head := GetHeadHeaderHash(bc.chainDb); head != (common.Hash{}) {
            if header := bc.GetHeaderByHash(head); header != nil {
                currentHeader = header
            }
        }
        // header chain 設定為當前的區塊頭.
        bc.hc.SetCurrentHeader(currentHeader)
    
        // Restore the last known head fast block
        bc.currentFastBlock = bc.currentBlock
        if head := GetHeadFastBlockHash(bc.chainDb); head != (common.Hash{}) {
            if block := bc.GetBlockByHash(head); block != nil {
                bc.currentFastBlock = block
            }
        }
    
        // Issue a status log for the user 用來列印日誌.
        headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
        blockTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
        fastTd := bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64())
    
        log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
        log.Info("Loaded most recent local full block", "number", bc.currentBlock.Number(), "hash", bc.currentBlock.Hash(), "td", blockTd)
        log.Info("Loaded most recent local fast block", "number", bc.currentFastBlock.Number(), "hash", bc.currentFastBlock.Hash(), "td", fastTd)
    
        return nil
    }

goroutine update的處理非常簡單. 定時處理future blocks.
    
    func (bc *BlockChain) update() {
        futureTimer := time.Tick(5 * time.Second)
        for {
            select {
            case <-futureTimer:
                bc.procFutureBlocks()
            case <-bc.quit:
                return
            }
        }
    }

Reset 方法 重置區塊鏈.
    
    // Reset purges the entire blockchain, restoring it to its genesis state.
    func (bc *BlockChain) Reset() error {
        return bc.ResetWithGenesisBlock(bc.genesisBlock)
    }
    
    // ResetWithGenesisBlock purges the entire blockchain, restoring it to the
    // specified genesis state.
    func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
        // Dump the entire block chain and purge the caches
        // 把整個區塊鏈轉儲並清楚快取
        if err := bc.SetHead(0); err != nil {
            return err
        }
        bc.mu.Lock()
        defer bc.mu.Unlock()
    
        // Prepare the genesis block and reinitialise the chain
        // 使用創世區塊重新初始化區塊鏈
        if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
            log.Crit("Failed to write genesis block TD", "err", err)
        }
        if err := WriteBlock(bc.chainDb, genesis); err != nil {
            log.Crit("Failed to write genesis block", "err", err)
        }
        bc.genesisBlock = genesis
        bc.insert(bc.genesisBlock)
        bc.currentBlock = bc.genesisBlock
        bc.hc.SetGenesis(bc.genesisBlock.Header())
        bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
        bc.currentFastBlock = bc.genesisBlock
    
        return nil
    }

SetHead將本地鏈回捲到新的頭部。 在給定新header之上的所有內容都將被刪除,新的header將被設定。 如果塊體丟失(快速同步之後的非歸檔節點),頭部可能被進一步倒回。
    
    // SetHead rewinds the local chain to a new head. In the case of headers, everything
    // above the new head will be deleted and the new one set. In the case of blocks
    // though, the head may be further rewound if block bodies are missing (non-archive
    // nodes after a fast sync).
    func (bc *BlockChain) SetHead(head uint64) error {
        log.Warn("Rewinding blockchain", "target", head)
    
        bc.mu.Lock()
        defer bc.mu.Unlock()
    
        // Rewind the header chain, deleting all block bodies until then
        delFn := func(hash common.Hash, num uint64) {
            DeleteBody(bc.chainDb, hash, num)
        }
        bc.hc.SetHead(head, delFn)
        currentHeader := bc.hc.CurrentHeader()
    
        // Clear out any stale content from the caches
        bc.bodyCache.Purge()
        bc.bodyRLPCache.Purge()
        bc.blockCache.Purge()
        bc.futureBlocks.Purge()
    
        // Rewind the block chain, ensuring we don't end up with a stateless head block
        if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
            bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
        }
        if bc.currentBlock != nil {
            if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil {
                // Rewound state missing, rolled back to before pivot, reset to genesis
                bc.currentBlock = nil
            }
        }
        // Rewind the fast block in a simpleton way to the target head
        if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
            bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
        }
        // If either blocks reached nil, reset to the genesis state
        if bc.currentBlock == nil {
            bc.currentBlock = bc.genesisBlock
        }
        if bc.currentFastBlock == nil {
            bc.currentFastBlock = bc.genesisBlock
        }
        if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil {
            log.Crit("Failed to reset head full block", "err", err)
        }
        if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil {
            log.Crit("Failed to reset head fast block", "err", err)
        }
        return bc.loadLastState()
    }

InsertChain,插入區塊鏈, 插入區塊鏈嘗試把給定的區塊插入到規範的鏈條,或者是建立一個分叉. 如果發生錯誤,那麼會返回錯誤發生時候的index和具體的錯誤資訊.
    
    // InsertChain attempts to insert the given batch of blocks in to the canonical
    // chain or, otherwise, create a fork. If an error is returned it will return
    // the index number of the failing block as well an error describing what went
    // wrong.
    //
    // After insertion is done, all accumulated events will be fired.
    // 在插入完成之後,所有累計的事件將被觸發.
    func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
        n, events, logs, err := bc.insertChain(chain)
        bc.PostChainEvents(events, logs)
        return n, err
    }

insertChain方法會執行區塊鏈插入,並收集事件資訊. 因為需要使用defer來處理解鎖,所以把這個方法作為一個單獨的方法.

    // insertChain will execute the actual chain insertion and event aggregation. The
    // only reason this method exists as a separate one is to make locking cleaner
    // with deferred statements.
    func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*types.Log, error) {
        // 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 messge (programming error) and skip insertion
                log.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.Block
            coalescedLogs []*types.Log
        )
        // Start the parallel header verifier
        headers := make([]*types.Header, len(chain))
        seals := make([]bool, len(chain))
    
        for i, block := range chain {
            headers[i] = block.Header()
            seals[i] = true
        }
        // 呼叫一致性引擎來驗證區塊頭是有效的.
        abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
        defer close(abort)
    
        // Iterate over the blocks and insert when the verifier permits
        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
            }
            // If the header is a banned one, straight out abort
            // 如果區塊頭被禁止了.
            if BadHashes[block.Hash()] {
                bc.reportBlock(block, nil, ErrBlacklistedHash)
                return i, events, coalescedLogs, ErrBlacklistedHash
            }
            // Wait for the block's verification to complete
            bstart := time.Now()
    
            err := <-results
            if err == nil { // 如果沒有錯誤. 驗證body
                err = bc.Validator().ValidateBody(block)
            }
            if err != nil {
                if err == ErrKnownBlock { // 如果區塊已經插入, 直接繼續
                    stats.ignored++
                    continue
                }
    
                if err == consensus.ErrFutureBlock {
                    // 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++
                    continue
                }
    
                if err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()) { 如果區塊沒有找到祖先 而在future blocks 包含了這個區塊的祖先,那麼也存放在future
                    bc.futureBlocks.Add(block.Hash(), block)
                    stats.queued++
                    continue
                }
    
                bc.reportBlock(block, nil, err)
                return i, events, coalescedLogs, err
            }
            // Create a new statedb using the parent block and report an
            // error if it fails.
            var parent *types.Block
            if 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.
            // 處理區塊,生成交易,收據,日誌等資訊.
            // 實際上呼叫了state_processor.go 裡面的 Process方法.
            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 validator
            // 二次驗證,驗證狀態是否合法
            err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas)
            if err != nil {
                bc.reportBlock(block, receipts, err)
                return i, events, coalescedLogs, err
            }
            // Write the block to the chain and get the status      
            // 寫入區塊和狀態.
            status, err := bc.WriteBlockAndState(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
    
            case SideStatTy: // 插入了一個forked 區塊
                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 += usedGas.Uint64()
            stats.report(chain, i)
        }
        // Append a single chain head event if we've progressed the chain
        // 如果我們生成了一個新的區塊頭, 而且最新的區塊頭等於lastCanon
        // 那麼我們公佈一個新的 ChainHeadEvent
        if lastCanon != nil && bc.LastBlockHash() == lastCanon.Hash() {
            events = append(events, ChainHeadEvent{lastCanon})
        }
        return 0, events, coalescedLogs, nil
    }

WriteBlockAndState,把區塊寫入區塊鏈.
    
    // WriteBlock writes the block to the chain.
    func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
        bc.wg.Add(1)
        defer bc.wg.Done()
    
        // Calculate the total difficulty of the block
        // 計算待插入的區塊的總難度
        ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
        if ptd == nil {
            return NonStatTy, consensus.ErrUnknownAncestor
        }
        // Make sure no inconsistent state is leaked during insertion
        // 確保在插入過程中沒有不一致的狀態洩漏
        bc.mu.Lock()
        defer bc.mu.Unlock()
        // 計算當前區塊的區塊鏈的總難度.
        localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
        // 計算新的區塊鏈的總難度
        externTd := new(big.Int).Add(block.Difficulty(), ptd)
    
        // Irrelevant of the canonical status, write the block itself to the database
        // 和規範區塊沒有關係的狀態, 寫入資料庫. 寫入區塊的hash 高度和對應的總難度.
        if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil {
            return NonStatTy, err
        }
        // Write other block data using a batch.
        batch := bc.chainDb.NewBatch()
        if err := WriteBlock(batch, block); err != nil { // 寫入區塊
            return NonStatTy, err
        }
        if _, err := state.CommitTo(batch, bc.config.IsEIP158(block.Number())); err != nil { //Commit
            return NonStatTy, err
        }
        if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil { // 寫入區塊收據
            return NonStatTy, err
        }
    
        // If the total difficulty is higher than our known, add it to the canonical chain
        // Second clause in the if statement reduces the vulnerability to selfish mining.
        // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
        // 如果新的區塊的總難度高於我們當前的區塊, 把這個區塊設定為規範的區塊.
        // 第二個表示式 ((externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5))
        // 是為了減少自私挖礦的可能性.
        if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) {
            // Reorganise the chain if the parent is not the head block
            // 如果這個區塊的父區塊不是當前的區塊, 說明存在一個分叉.需要呼叫reorg重新組織區塊鏈.
            if block.ParentHash() != bc.currentBlock.Hash() {
                if err := bc.reorg(bc.currentBlock, block); err != nil {
                    return NonStatTy, err
                }
            }
            // Write the positional metadata for transaction and receipt lookups
            // "l" + txHash -> {blockHash,blockNum,txIndex}
            // 根據交易的hash值來找到對應的區塊以及對應的交易。
            if err := WriteTxLookupEntries(batch, block); err != nil {
                return NonStatTy, err
            }
            // Write hash preimages
            // hash(Keccak-256) -> 對應的資料 這個功能是用來測試的。如果開啟了dev模式,
            // 或者是 vmdebug引數, 如果執行 SHA3 指令就會新增Preimage
            if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil {
                return NonStatTy, err
            }
            status = CanonStatTy
        } else {
            status = SideStatTy
        }
        if err := batch.Write(); err != nil {
            return NonStatTy, err
        }
    
        // Set new head.
        if status == CanonStatTy {
            bc.insert(block)
        }
        bc.futureBlocks.Remove(block.Hash())
        return status, nil
    }


reorgs方法是在新的鏈的總難度大於本地鏈的總難度的情況下,需要用新的區塊鏈來替換本地的區塊鏈為規範鏈。
    
    // reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
    // to be part of the new canonical chain and accumulates potential missing transactions and post an
    // event about them
    // reorgs 接受兩個區塊作為引數,一個是老的區塊鏈,一個新的區塊鏈,這個方法會把他們插入
    // 以便重新構建出一條規範的區塊鏈。 同時會累計潛在會丟失的交易並把它們作為事件釋出出去。
    func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
        var (
            newChain types.Blocks
            oldChain types.Blocks
            commonBlock *types.Block
            deletedTxs types.Transactions
            deletedLogs []*types.Log
            // collectLogs collects the logs that were generated during the
            // processing of the block that corresponds with the given hash.
            // These logs are later announced as deleted.
            // collectLogs 會收集我們已經生成的日誌資訊,這些日誌稍後會被宣告刪除(實際上在資料庫中並沒有被刪除)。
            collectLogs = func(h common.Hash) {
                // Coalesce logs and set 'Removed'.
                receipts := GetBlockReceipts(bc.chainDb, h, bc.hc.GetBlockNumber(h))
                for _, receipt := range receipts {
                    for _, log := range receipt.Logs {
                        del := *log
                        del.Removed = true
                        deletedLogs = append(deletedLogs, &del)
                    }
                }
            }
        )
    
        // first reduce whoever is higher bound
        if oldBlock.NumberU64() > newBlock.NumberU64() {
            // reduce old chain 如果老的鏈比新的鏈高。那麼需要減少老的鏈,讓它和新鏈一樣高
            for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
                oldChain = append(oldChain, oldBlock)
                deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
    
                collectLogs(oldBlock.Hash())
            }
        } else {
            // reduce new chain and append new chain blocks for inserting later on
            // 如果新鏈比老鏈要高,那麼減少新鏈。
            for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) {
                newChain = append(newChain, newBlock)
            }
        }
        if oldBlock == nil {
            return fmt.Errorf("Invalid old chain")
        }
        if newBlock == nil {
            return fmt.Errorf("Invalid new chain")
        }
    
        for { //這個for迴圈裡面需要找到共同的祖先。
            if oldBlock.Hash() == newBlock.Hash() {
                commonBlock = oldBlock
                break
            }
    
            oldChain = append(oldChain, oldBlock)
            newChain = append(newChain, newBlock)
            deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
            collectLogs(oldBlock.Hash())
    
            oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
            if oldBlock == nil {
                return fmt.Errorf("Invalid old chain")
            }
            if newBlock == nil {
                return fmt.Errorf("Invalid new chain")
            }
        }
        // Ensure the user sees large reorgs
        if len(oldChain) > 0 && len(newChain) > 0 {
            logFn := log.Debug
            if len(oldChain) > 63 {
                logFn = log.Warn
            }
            logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(),
                "drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash())
        } else {
            log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
        }
        var addedTxs types.Transactions
        // insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
        for _, block := range newChain {
            // insert the block in the canonical way, re-writing history
            // 插入區塊 更新記錄規範區塊鏈的key
            bc.insert(block)
            // write lookup entries for hash based transaction/receipt searches
            // 寫入交易的查詢資訊。
            if err := WriteTxLookupEntries(bc.chainDb, block); err != nil {
                return err
            }
            addedTxs = append(addedTxs, block.Transactions()...)
        }
    
        // calculate the difference between deleted and added transactions
        diff := types.TxDifference(deletedTxs, addedTxs)
        // When transactions get deleted from the database that means the
        // receipts that were created in the fork must also be deleted
        // 刪除那些需要刪除的交易查詢資訊。
        // 這裡並沒有刪除那些需要刪除的區塊,區塊頭,收據等資訊。
        for _, tx := range diff {
            DeleteTxLookupEntry(bc.chainDb, tx.Hash())
        }
        if len(deletedLogs) > 0 { // 傳送訊息通知
            go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
        }
        if len(oldChain) > 0 {
            go func() {
                for _, block := range oldChain { // 傳送訊息通知。
                    bc.chainSideFeed.Send(ChainSideEvent{Block: block})
                }
            }()
        }
    
        return nil
    }




網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN








網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN








網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN






網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN





網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN


相關文章