Skip to content

core/blockchain.go — Version Comparison

v1.16.8 v1.17.3
Branch etc/v1.16.8-full-node etc/v1.17.3-full-node
Delta +75 -5 +87 -7
ETC delta on v1.16.8 (+75 -5)
diff --git a/core/blockchain.go b/core/blockchain.go
index b7acd12ac..6e0143265 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -329,6 +329,7 @@ type BlockChain struct {

    stopping      atomic.Bool // false if chain is running, true when stopped
    procInterrupt atomic.Bool // interrupt signaler for block processing
+   forker        *ForkChoice // ETC: PoW fork choice (nil for non-PoW chains)

    engine     consensus.Engine
    validator  Validator // Block and state validator interface
@@ -395,6 +396,9 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
    bc.validator = NewBlockValidator(chainConfig, bc)
    bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
    bc.processor = NewStateProcessor(bc.hc)
+   if chainConfig.IsPow() {
+       bc.forker = NewForkChoiceWithMESS(bc, nil)
+   }

    genesisHeader := bc.GetHeaderByNumber(0)
    if genesisHeader == nil {
@@ -1161,6 +1165,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {

    // Prepare the genesis block and reinitialise the chain
    batch := bc.db.NewBatch()
+   rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) // ETC: write genesis TD
    rawdb.WriteBlock(batch, genesis)
    if err := batch.Write(); err != nil {
        log.Crit("Failed to write genesis block", "err", err)
@@ -1425,7 +1430,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
        // Ensure genesis is in the ancient store
        if blockChain[0].NumberU64() == 1 {
            if frozen, _ := bc.db.Ancients(); frozen == 0 {
-               writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
+               // ETC: use PoW version to write real TD
+               td := bc.genesisBlock.Difficulty()
+               writeSize, err := rawdb.WriteAncientBlocksPoW(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList}, td)
                if err != nil {
                    log.Error("Error writing genesis to ancients", "err", err)
                    return 0, err
@@ -1434,8 +1441,16 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
                log.Info("Wrote genesis to ancients")
            }
        }
-       // Write all chain data to ancients.
-       writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
+       // ETC: use PoW version to write real TD if parent TD is available
+       var writeSize int64
+       var err error
+       parentTd := bc.GetTd(blockChain[0].ParentHash(), blockChain[0].NumberU64()-1)
+       if parentTd != nil {
+           td := new(big.Int).Add(parentTd, blockChain[0].Header().Difficulty)
+           writeSize, err = rawdb.WriteAncientBlocksPoW(bc.db, blockChain, receiptChain, td)
+       } else {
+           writeSize, err = rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
+       }
        if err != nil {
            log.Error("Error importing chain data to ancients", "err", err)
            return 0, err
@@ -1495,6 +1510,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
            rawdb.WriteBlock(batch, block)
            rawdb.WriteRawReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])

+           // ETC: Write TD for PoW chains - writeLive doesn't go through
+           // InsertHeaderChain, so TD must be written here
+           if ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1); ptd != nil {
+               externTd := new(big.Int).Add(block.Difficulty(), ptd)
+               rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), externTd)
+           }
+
            // Write everything belongs to the blocks into the database. So that
            // we can ensure all components of body is completed(body, receipts)
            // except transaction indexes(will be created once sync is finished).
@@ -1566,6 +1588,13 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block) (err error) {
        return errInsertionInterrupted
    }
    batch := bc.db.NewBatch()
+
+   // Write TD for sidechain blocks
+   if ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1); ptd != nil {
+       externTd := new(big.Int).Add(block.Difficulty(), ptd)
+       rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), externTd)
+   }
+
    rawdb.WriteBlock(batch, block)
    if err := batch.Write(); err != nil {
        log.Crit("Failed to write block into disk", "err", err)
@@ -1578,6 +1607,15 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block) (err error) {
 func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
    current := bc.CurrentBlock()
    if block.ParentHash() != current.Hash() {
+       if bc.forker != nil {
+           reorg, err := bc.forker.ReorgNeeded(current, block.Header())
+           if err != nil {
+               return err
+           }
+           if !reorg {
+               return nil
+           }
+       }
        if err := bc.reorg(current, block.Header()); err != nil {
            return err
        }
@@ -1597,6 +1635,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
    // Note all the components of block(hash->number map, header, body, receipts)
    // should be written atomically. BlockBatch is used for containing all components.
    blockBatch := bc.db.NewBatch()
+
+   // Calculate and write TD for chain sync (if parent TD exists)
+   if ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1); ptd != nil {
+       externTd := new(big.Int).Add(block.Difficulty(), ptd)
+       rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
+   }
    rawdb.WriteBlock(blockBatch, block)
    rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
    rawdb.WritePreimages(blockBatch, statedb.Preimages())
@@ -1682,6 +1726,15 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types

    // Reorganise the chain if the parent is not the head block
    if block.ParentHash() != currentBlock.Hash() {
+       if bc.forker != nil {
+           reorg, err := bc.forker.ReorgNeeded(currentBlock, block.Header())
+           if err != nil {
+               return NonStatTy, err
+           }
+           if !reorg {
+               return SideStatTy, nil
+           }
+       }
        if err := bc.reorg(currentBlock, block.Header()); err != nil {
            return NonStatTy, err
        }
@@ -2769,7 +2822,9 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
        first     = headers[0].Number.Uint64()
    )
    if first == 1 && frozen == 0 {
-       _, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
+       // ETC: use PoW version to write real TD
+       td := bc.genesisBlock.Difficulty()
+       _, err := rawdb.WriteAncientBlocksPoW(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList}, td)
        if err != nil {
            log.Error("Error writing genesis to ancients", "err", err)
            return 0, err
@@ -2779,9 +2834,24 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
        return 0, fmt.Errorf("headers are gapped with the ancient store, first: %d, ancient: %d", first, frozen)
    }

+   // ETC: compute parent TD for the header chain and use PoW version if available
+   var td *big.Int
+   if first == 1 {
+       td = new(big.Int).Add(bc.genesisBlock.Difficulty(), headers[0].Difficulty)
+   } else {
+       parentTd := bc.GetTd(headers[0].ParentHash, first-1)
+       if parentTd != nil {
+           td = new(big.Int).Add(parentTd, headers[0].Difficulty)
+       }
+   }
    // Write headers to the ancient store, with block bodies and receipts set to nil
    // to ensure consistency across tables in the freezer.
-   _, err := rawdb.WriteAncientHeaderChain(bc.db, headers)
+   var err error
+   if td != nil {
+       _, err = rawdb.WriteAncientHeaderChainPoW(bc.db, headers, td)
+   } else {
+       _, err = rawdb.WriteAncientHeaderChain(bc.db, headers)
+   }
    if err != nil {
        return 0, err
    }
ETC delta on v1.17.3 (+87 -7)
diff --git a/core/blockchain.go b/core/blockchain.go
index 2b4911112..1eccdc074 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -357,8 +357,10 @@ type BlockChain struct {
    txLookupLock  sync.RWMutex
    txLookupCache *lru.Cache[common.Hash, txLookup]

-   stopping      atomic.Bool // false if chain is running, true when stopped
-   procInterrupt atomic.Bool // interrupt signaler for block processing
+   stopping      atomic.Bool  // false if chain is running, true when stopped
+   procInterrupt atomic.Bool  // interrupt signaler for block processing
+   forker        *ForkChoice  // ETC: PoW fork choice (nil for non-PoW chains)
+   messEnabled   atomic.Int32 // ETC: MESS runtime activation state (per-chain, not global)

    engine     consensus.Engine
    validator  Validator // Block and state validator interface
@@ -427,6 +429,9 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
    bc.validator = NewBlockValidator(chainConfig, bc)
    bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
    bc.processor = NewStateProcessor(bc.hc)
+   if chainConfig.IsPow() {
+       bc.forker = NewForkChoiceWithMESS(bc, nil)
+   }

    genesisHeader := bc.GetHeaderByNumber(0)
    if genesisHeader == nil {
@@ -1212,6 +1217,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {

    // Prepare the genesis block and reinitialise the chain
    batch := bc.db.NewBatch()
+   rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) // ETC: write genesis TD
    rawdb.WriteBlock(batch, genesis)
    if err := batch.Write(); err != nil {
        log.Crit("Failed to write genesis block", "err", err)
@@ -1478,7 +1484,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
        // Ensure genesis is in the ancient store
        if blockChain[0].NumberU64() == 1 {
            if frozen, _ := bc.db.Ancients(); frozen == 0 {
-               writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
+               // ETC: use PoW version to write real TD
+               td := bc.genesisBlock.Difficulty()
+               writeSize, err := rawdb.WriteAncientBlocksPoW(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList}, td)
                if err != nil {
                    log.Error("Error writing genesis to ancients", "err", err)
                    return 0, err
@@ -1487,8 +1495,22 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
                log.Info("Wrote genesis to ancients")
            }
        }
-       // Write all chain data to ancients.
-       writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
+       // ETC: use PoW version to write real TD if parent TD is available.
+       // On PoW chains a missing parent TD means the ancestor is unknown
+       // (or the TD store is corrupt); falling back to the no-TD writer
+       // would leave the ancients without difficulty and break sync. Treat
+       // it as an unknown-ancestor error instead.
+       var writeSize int64
+       var err error
+       parentTd := bc.GetTd(blockChain[0].ParentHash(), blockChain[0].NumberU64()-1)
+       if parentTd != nil {
+           td := new(big.Int).Add(parentTd, blockChain[0].Header().Difficulty)
+           writeSize, err = rawdb.WriteAncientBlocksPoW(bc.db, blockChain, receiptChain, td)
+       } else if bc.chainConfig.IsPow() {
+           return 0, consensus.ErrUnknownAncestor
+       } else {
+           writeSize, err = rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
+       }
        if err != nil {
            log.Error("Error importing chain data to ancients", "err", err)
            return 0, err
@@ -1548,6 +1570,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
            rawdb.WriteBlock(batch, block)
            rawdb.WriteRawReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])

+           // ETC: Write TD for PoW chains - writeLive doesn't go through
+           // InsertHeaderChain, so TD must be written here
+           if ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1); ptd != nil {
+               externTd := new(big.Int).Add(block.Difficulty(), ptd)
+               rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), externTd)
+           }
+
            // Write everything belongs to the blocks into the database. So that
            // we can ensure all components of body is completed(body, receipts)
            // except transaction indexes(will be created once sync is finished).
@@ -1619,6 +1648,13 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block) (err error) {
        return errInsertionInterrupted
    }
    batch := bc.db.NewBatch()
+
+   // Write TD for sidechain blocks
+   if ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1); ptd != nil {
+       externTd := new(big.Int).Add(block.Difficulty(), ptd)
+       rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), externTd)
+   }
+
    rawdb.WriteBlock(batch, block)
    if err := batch.Write(); err != nil {
        log.Crit("Failed to write block into disk", "err", err)
@@ -1631,6 +1667,15 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block) (err error) {
 func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
    current := bc.CurrentBlock()
    if block.ParentHash() != current.Hash() {
+       if bc.forker != nil {
+           reorg, err := bc.forker.ReorgNeeded(current, block.Header())
+           if err != nil {
+               return err
+           }
+           if !reorg {
+               return nil
+           }
+       }
        if err := bc.reorg(current, block.Header()); err != nil {
            return err
        }
@@ -1655,6 +1700,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
    )
    defer batch.Close()

+   // Calculate and write TD for chain sync (if parent TD exists)
+   if ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1); ptd != nil {
+       externTd := new(big.Int).Add(block.Difficulty(), ptd)
+       rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), externTd)
+   }
    rawdb.WriteBlock(batch, block)
    rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
    rawdb.WritePreimages(batch, statedb.Preimages())
@@ -1763,6 +1813,15 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types

    // Reorganise the chain if the parent is not the head block
    if block.ParentHash() != currentBlock.Hash() {
+       if bc.forker != nil {
+           reorg, err := bc.forker.ReorgNeeded(currentBlock, block.Header())
+           if err != nil {
+               return NonStatTy, err
+           }
+           if !reorg {
+               return SideStatTy, nil
+           }
+       }
        if err := bc.reorg(currentBlock, block.Header()); err != nil {
            return NonStatTy, err
        }
@@ -2926,7 +2985,9 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
        first     = headers[0].Number.Uint64()
    )
    if first == 1 && frozen == 0 {
-       _, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
+       // ETC: use PoW version to write real TD
+       td := bc.genesisBlock.Difficulty()
+       _, err := rawdb.WriteAncientBlocksPoW(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList}, td)
        if err != nil {
            log.Error("Error writing genesis to ancients", "err", err)
            return 0, err
@@ -2936,9 +2997,28 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
        return 0, fmt.Errorf("headers are gapped with the ancient store, first: %d, ancient: %d", first, frozen)
    }

+   // ETC: compute parent TD for the header chain and use PoW version if
+   // available. On PoW chains a missing parent TD is corruption / unknown
+   // ancestor; the no-TD writer would leave the freezer inconsistent.
+   var td *big.Int
+   if first == 1 {
+       td = new(big.Int).Add(bc.genesisBlock.Difficulty(), headers[0].Difficulty)
+   } else {
+       parentTd := bc.GetTd(headers[0].ParentHash, first-1)
+       if parentTd != nil {
+           td = new(big.Int).Add(parentTd, headers[0].Difficulty)
+       }
+   }
    // Write headers to the ancient store, with block bodies and receipts set to nil
    // to ensure consistency across tables in the freezer.
-   _, err := rawdb.WriteAncientHeaderChain(bc.db, headers)
+   var err error
+   if td != nil {
+       _, err = rawdb.WriteAncientHeaderChainPoW(bc.db, headers, td)
+   } else if bc.chainConfig.IsPow() {
+       return 0, consensus.ErrUnknownAncestor
+   } else {
+       _, err = rawdb.WriteAncientHeaderChain(bc.db, headers)
+   }
    if err != nil {
        return 0, err
    }

← Back to Version Comparison