以太坊区块设计分析(下)
字数 1048 2025-08-22 12:23:12

以太坊区块设计分析(下) - 教学文档

1. 链条构建

1.1 区块链初始化流程

以太坊启动时通过NewBlockChain函数创建区块链,调用流程如下:

geth → makeFullNode → RegisterEthService → eth.New → core.NewBlockChain

1.2 关键初始化步骤

  1. 创世区块加载

    • 使用SetupGenesisBlockWithOverride加载创世区块并获取链基本配置
    • 调用ReadDatabaseVersion获取DB版本
  2. 核心组件创建

    • NewBlockChain构建区块链
    • NewTxPool创建交易池
    • NewOracle进行价格预言
  3. 缓存配置

    • 创建各种LRU缓存(最近最少使用算法)
    • 初始化triegc(用于垃圾回收的区块number优先级队列)
    • 初始化stateCache

1.3 NewBlockChain函数详解

func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, 
    engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, 
    txLookupLimit *uint64) (*BlockChain, error) {
    // 初始化缓存
    bodyCache, _ := lru.New(bodyCacheLimit)
    bodyRLPCache, _ := lru.New(bodyCacheLimit)
    receiptsCache, _ := lru.New(receiptsCacheLimit)
    blockCache, _ := lru.New(blockCacheLimit)
    txLookupCache, _ := lru.New(txLookupCacheLimit)
    futureBlocks, _ := lru.New(maxFutureBlocks)
    
    // 创建区块链实例
    bc := &BlockChain{
        chainConfig: chainConfig,
        cacheConfig: cacheConfig,
        db:          db,
        triegc:      prque.New(nil),
        stateCache: state.NewDatabaseWithConfig(db, &trie.Config{
            Cache:     cacheConfig.TrieCleanLimit,
            Journal:   cacheConfig.TrieCleanJournal,
            Preimages: cacheConfig.Preimages,
        }),
        // ...其他字段初始化
    }
    
    // 初始化验证器、预取器和处理器
    bc.validator = NewBlockValidator(chainConfig, bc, engine)
    bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
    bc.processor = NewStateProcessor(chainConfig, bc, engine)
    
    // 初始化区块头链
    bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
    
    // 获取创世区块
    bc.genesisBlock = bc.GetBlockByNumber(0)
    if bc.genesisBlock == nil {
        return nil, ErrNoGenesis
    }
    
    // 加载最新状态
    if err := bc.loadLastState(); err != nil {
        return nil, err
    }
    
    // 启动后台任务
    go bc.update() // 定时处理future block
    if txLookupLimit != nil {
        go bc.maintainTxIndex(txIndexBlock)
    }
    
    return bc, nil
}

1.4 状态加载机制

loadLastState函数加载最新区块链状态:

  1. 获取最新区块及其hash
  2. 检查DB是否为空或损坏,必要时调用bc.Reset重置区块链
  3. 确定区块头的可用性
  4. 获取最新区块
  5. 通过日志记录状态信息
func (bc *BlockChain) loadLastState() error {
    head := rawdb.ReadHeadBlockHash(bc.db)
    if head == (common.Hash{}) {
        return bc.Reset() // 空数据库时重置
    }
    
    currentBlock := bc.GetBlockByHash(head)
    if currentBlock == nil {
        return bc.Reset() // 头块缺失时重置
    }
    
    bc.currentBlock.Store(currentBlock)
    currentHeader := currentBlock.Header()
    
    // 设置当前头部和快速区块
    bc.hc.SetCurrentHeader(currentHeader)
    bc.currentFastBlock.Store(currentBlock)
    
    // 记录状态信息
    log.Info("Loaded most recent local header", "number", currentHeader.Number, ...)
    log.Info("Loaded most recent local full block", "number", currentBlock.Number(), ...)
    log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), ...)
    
    return nil
}

1.5 区块回滚机制

SetHead函数用于回滚本地链到新的头部:

func (bc *BlockChain) SetHead(head uint64) error {
    _, err := bc.SetHeadBeyondRoot(head, common.Hash{})
    return err
}

func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) {
    // 更新函数:处理区块回滚逻辑
    updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (uint64, bool) {
        // 回滚逻辑实现...
    }
    
    // 删除函数:清除中间区块头所有数据和缓存
    delFn := func(db ethdb.KeyValueWriter, hash common.Hash, num uint64) {
        // 删除逻辑实现...
    }
    
    // 执行回滚操作
    bc.hc.SetHead(head, updateFn, delFn)
    
    // 清除缓存
    bc.bodyCache.Purge()
    bc.bodyRLPCache.Purge()
    bc.receiptsCache.Purge()
    bc.blockCache.Purge()
    bc.txLookupCache.Purge()
    bc.futureBlocks.Purge()
    
    // 重新加载状态
    return rootNumber, bc.loadLastState()
}

2. 分叉处理

2.1 reorg函数分析

reorg函数处理分叉,将原来的分叉链设置为规范链:

func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
    var (
        newChain    types.Blocks
        oldChain    types.Blocks
        commonBlock *types.Block
        deletedTxs  types.Transactions
        addedTxs    types.Transactions
        deletedLogs [][]*types.Log
        rebirthLogs [][]*types.Log
    )
    
    // 步骤1:找到共同祖先
    // 如果老链比新链长,减少老链高度
    if oldBlock.NumberU64() > newBlock.NumberU64() {
        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(), true)
        }
    } else {
        // 新链比老链长,暂存新链区块
        for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) {
            newChain = append(newChain, newBlock)
        }
    }
    
    // 步骤2:找到共同祖先
    for {
        if oldBlock.Hash() == newBlock.Hash() {
            commonBlock = oldBlock
            break
        }
        oldChain = append(oldChain, oldBlock)
        deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
        collectLogs(oldBlock.Hash(), true)
        
        newChain = append(newChain, newBlock)
        
        oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1)
        newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
    }
    
    // 步骤3:插入新链到规范链中
    for i := len(newChain) - 1; i >= 1; i-- {
        bc.writeHeadBlock(newChain[i])
        collectLogs(newChain[i].Hash(), false)
        addedTxs = append(addedTxs, newChain[i].Transactions()...)
    }
    
    // 步骤4:清理无用索引
    indexesBatch := bc.db.NewBatch()
    for _, tx := range types.TxDifference(deletedTxs, addedTxs) {
        rawdb.DeleteTxLookupEntry(indexesBatch, tx.Hash())
    }
    
    // 步骤5:发送事件通知
    if len(deletedLogs) > 0 {
        bc.rmLogsFeed.Send(RemovedLogsEvent{mergeLogs(deletedLogs, true)})
    }
    if len(rebirthLogs) > 0 {
        bc.logsFeed.Send(mergeLogs(rebirthLogs, false))
    }
    if len(oldChain) > 0 {
        for i := len(oldChain) - 1; i >= 0; i-- {
            bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]})
        }
    }
    
    return nil
}

2.2 分叉处理关键点

  1. 共同祖先查找

    • 比较两条链的高度,将较长链回退到与较短链相同高度
    • 然后同时回退两条链直到找到共同祖先
  2. 数据清理

    • 删除不再属于规范链的交易索引
    • 清理规范哈希映射
  3. 事件通知

    • 发送日志删除事件(rmLogsFeed)
    • 发送新日志事件(logsFeed)
    • 发送被替换的侧链区块事件(chainSideFeed)

3. 关键安全考量

  1. 区块验证

    • 区块头验证(共识引擎的VerifyHeader)
    • 交易执行验证(StateProcessor)
  2. 状态一致性

    • 确保区块状态可用(state.New)
    • 处理状态缺失时的恢复逻辑
  3. 异常处理

    • 数据库损坏检测与恢复
    • 硬分叉处理机制
    • 坏块(bad block)检测与回滚
  4. 性能优化

    • 缓存策略(LRU缓存)
    • 并行处理(future block处理)
    • 批量写入(索引清理)

4. 总结

本教学文档详细分析了以太坊区块设计的核心机制,包括:

  1. 区块链初始化流程和关键组件
  2. 状态加载和回滚机制
  3. 分叉处理的核心算法
  4. 关键安全考量和性能优化点

这些机制共同构成了以太坊区块链稳定运行的基础,理解这些底层原理对于区块链开发和安全审计至关重要。

以太坊区块设计分析(下) - 教学文档 1. 链条构建 1.1 区块链初始化流程 以太坊启动时通过 NewBlockChain 函数创建区块链,调用流程如下: 1.2 关键初始化步骤 创世区块加载 : 使用 SetupGenesisBlockWithOverride 加载创世区块并获取链基本配置 调用 ReadDatabaseVersion 获取DB版本 核心组件创建 : NewBlockChain 构建区块链 NewTxPool 创建交易池 NewOracle 进行价格预言 缓存配置 : 创建各种LRU缓存(最近最少使用算法) 初始化 triegc (用于垃圾回收的区块number优先级队列) 初始化 stateCache 1.3 NewBlockChain函数详解 1.4 状态加载机制 loadLastState 函数加载最新区块链状态: 获取最新区块及其hash 检查DB是否为空或损坏,必要时调用 bc.Reset 重置区块链 确定区块头的可用性 获取最新区块 通过日志记录状态信息 1.5 区块回滚机制 SetHead 函数用于回滚本地链到新的头部: 2. 分叉处理 2.1 reorg函数分析 reorg 函数处理分叉,将原来的分叉链设置为规范链: 2.2 分叉处理关键点 共同祖先查找 : 比较两条链的高度,将较长链回退到与较短链相同高度 然后同时回退两条链直到找到共同祖先 数据清理 : 删除不再属于规范链的交易索引 清理规范哈希映射 事件通知 : 发送日志删除事件( rmLogsFeed ) 发送新日志事件( logsFeed ) 发送被替换的侧链区块事件( chainSideFeed ) 3. 关键安全考量 区块验证 : 区块头验证(共识引擎的 VerifyHeader ) 交易执行验证( StateProcessor ) 状态一致性 : 确保区块状态可用( state.New ) 处理状态缺失时的恢复逻辑 异常处理 : 数据库损坏检测与恢复 硬分叉处理机制 坏块(bad block)检测与回滚 性能优化 : 缓存策略(LRU缓存) 并行处理(future block处理) 批量写入(索引清理) 4. 总结 本教学文档详细分析了以太坊区块设计的核心机制,包括: 区块链初始化流程和关键组件 状态加载和回滚机制 分叉处理的核心算法 关键安全考量和性能优化点 这些机制共同构成了以太坊区块链稳定运行的基础,理解这些底层原理对于区块链开发和安全审计至关重要。