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