core/blockchain.go¶
| Type | MOD |
| Upstream Lines | 2997 |
| Changed | +87 -7 |
Add TD write/delete hooks and PoW fork choice integration
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
}