From 2873b0df63bdea2db95f46087877060cf8744c8c Mon Sep 17 00:00:00 2001 From: ironbeer <7997273+ironbeer@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:48:03 +0900 Subject: [PATCH 1/2] Imported fast finality reorganization from bsc@v1.4.6 --- core/blockchain.go | 28 ++++++++++++++++++++-------- core/forkchoice.go | 32 ++++++++++++++++++++++++++++++++ core/headerchain.go | 2 +- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 3e79a33f6..6d6349487 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1151,7 +1151,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ // Rewind may have occurred, skip in that case. if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 { - reorg, err := bc.forker.ReorgNeeded(bc.CurrentSnapBlock(), head.Header()) + reorg, err := bc.forker.ReorgNeededWithFastFinality(bc.CurrentSnapBlock(), head.Header()) if err != nil { log.Warn("Reorg failed", "err", err) return false @@ -1510,7 +1510,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types return NonStatTy, err } currentBlock := bc.CurrentBlock() - reorg, err := bc.forker.ReorgNeeded(currentBlock, block.Header()) + reorg, err := bc.forker.ReorgNeededWithFastFinality(currentBlock, block.Header()) if err != nil { return NonStatTy, err } @@ -1669,7 +1669,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) current = bc.CurrentBlock() ) for block != nil && bc.skipBlock(err, it) { - reorg, err = bc.forker.ReorgNeeded(current, block.Header()) + reorg, err = bc.forker.ReorgNeededWithFastFinality(current, block.Header()) if err != nil { return it.index, err } @@ -2034,7 +2034,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i // // If the externTd was larger than our local TD, we now need to reimport the previous // blocks to regenerate the required state - reorg, err := bc.forker.ReorgNeeded(current, lastBlock.Header()) + reorg, err := bc.forker.ReorgNeededWithFastFinality(current, lastBlock.Header()) if err != nil { return it.index, err } @@ -2265,6 +2265,12 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { // rewind the canonical chain to a lower point. log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain)) } + // Reset the tx lookup cache in case to clear stale txlookups. + // This is done before writing any new chain data to avoid the + // weird scenario that canonical chain is changed while the + // stale lookups are still cached. + bc.txLookupCache.Purge() + // Insert the new chain(except the head block(reverse order)), // taking care of the proper incremental order. for i := len(newChain) - 1; i >= 1; i-- { @@ -2279,11 +2285,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { // Delete useless indexes right now which includes the non-canonical // transaction indexes, canonical chain indexes which above the head. - indexesBatch := bc.db.NewBatch() - for _, tx := range types.HashDifference(deletedTxs, addedTxs) { + var ( + indexesBatch = bc.db.NewBatch() + diffs = types.HashDifference(deletedTxs, addedTxs) + blockBatch = bc.db.NewBatch() + ) + for _, tx := range diffs { rawdb.DeleteTxLookupEntry(indexesBatch, tx) } - // Delete all hash markers that are not part of the new canonical chain. // Because the reorg function does not handle new chain head, all hash // markers greater than or equal to new chain head should be deleted. @@ -2296,11 +2305,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { if hash == (common.Hash{}) { break } - rawdb.DeleteCanonicalHash(indexesBatch, i) + rawdb.DeleteCanonicalHash(blockBatch, i) } if err := indexesBatch.Write(); err != nil { log.Crit("Failed to delete useless indexes", "err", err) } + if err := blockBatch.Write(); err != nil { + log.Crit("Failed to delete useless indexes use block batch", "err", err) + } // Send out events for logs from the old canon chain, and 'reborn' // logs from the new canon chain. The number of logs can be very diff --git a/core/forkchoice.go b/core/forkchoice.go index b293c851b..e64c78233 100644 --- a/core/forkchoice.go +++ b/core/forkchoice.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -36,6 +37,12 @@ type ChainReader interface { // Config retrieves the header chain's chain configuration. Config() *params.ChainConfig + // Engine retrieves the blockchain's consensus engine. + Engine() consensus.Engine + + // GetJustifiedNumber returns the highest justified blockNumber on the branch including and before `header` + GetJustifiedNumber(header *types.Header) uint64 + // GetTd returns the total difficulty of a local block. GetTd(common.Hash, uint64) *big.Int } @@ -111,3 +118,28 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b } return reorg, nil } + +// ReorgNeededWithFastFinality compares justified block numbers firstly, backoff to compare tds when equal +func (f *ForkChoice) ReorgNeededWithFastFinality(current *types.Header, header *types.Header) (bool, error) { + _, ok := f.chain.Engine().(consensus.PoS) + if !ok { + return f.ReorgNeeded(current, header) + } + + justifiedNumber, curJustifiedNumber := uint64(0), uint64(0) + if f.chain.Config().IsFastFinalityEnabled(header.Number) { + justifiedNumber = f.chain.GetJustifiedNumber(header) + } + if f.chain.Config().IsFastFinalityEnabled(current.Number) { + curJustifiedNumber = f.chain.GetJustifiedNumber(current) + } + if justifiedNumber == curJustifiedNumber { + return f.ReorgNeeded(current, header) + } + + if justifiedNumber > curJustifiedNumber && header.Number.Cmp(current.Number) <= 0 { + log.Info("Chain find higher justifiedNumber", "fromHeight", current.Number, "fromHash", current.Hash(), "fromMiner", current.Coinbase, "fromJustified", curJustifiedNumber, + "toHeight", header.Number, "toHash", header.Hash(), "toMiner", header.Coinbase, "toJustified", justifiedNumber) + } + return justifiedNumber > curJustifiedNumber, nil +} diff --git a/core/headerchain.go b/core/headerchain.go index e13a405bd..3f2c74a5c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -306,7 +306,7 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *F } ) // Ask the fork choicer if the reorg is necessary - if reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader); err != nil { + if reorg, err := forker.ReorgNeededWithFastFinality(hc.CurrentHeader(), lastHeader); err != nil { return nil, err } else if !reorg { if inserted != 0 { From caeba34d3fd5edcfd71f03c945544bf8707cfac9 Mon Sep 17 00:00:00 2001 From: ironbeer <7997273+ironbeer@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:57:29 +0900 Subject: [PATCH 2/2] Fix errors in test code --- core/headerchain.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/headerchain.go b/core/headerchain.go index 3f2c74a5c..da1300b2c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -104,14 +104,14 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c } hc.currentHeaderHash = hc.CurrentHeader().Hash() headHeaderGauge.Update(hc.CurrentHeader().Number.Int64()) - headJustifiedBlockGauge.Update(int64(hc.getJustifiedNumber(hc.CurrentHeader()))) + headJustifiedBlockGauge.Update(int64(hc.GetJustifiedNumber(hc.CurrentHeader()))) headFinalizedBlockGauge.Update(int64(hc.getFinalizedNumber(hc.CurrentHeader()))) return hc, nil } // getJustifiedNumber returns the highest justified blockNumber on the branch including and before `header`. -func (hc *HeaderChain) getJustifiedNumber(header *types.Header) uint64 { +func (hc *HeaderChain) GetJustifiedNumber(header *types.Header) uint64 { if p, ok := hc.engine.(consensus.PoS); ok { justifiedBlockNumber, _, err := p.GetJustifiedNumberAndHash(hc, []*types.Header{header}) if err == nil { @@ -561,7 +561,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { hc.currentHeader.Store(head) hc.currentHeaderHash = head.Hash() headHeaderGauge.Update(head.Number.Int64()) - headJustifiedBlockGauge.Update(int64(hc.getJustifiedNumber(head))) + headJustifiedBlockGauge.Update(int64(hc.GetJustifiedNumber(head))) headFinalizedBlockGauge.Update(int64(hc.getFinalizedNumber(head))) } @@ -649,7 +649,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat hc.currentHeader.Store(parent) hc.currentHeaderHash = parentHash headHeaderGauge.Update(parent.Number.Int64()) - headJustifiedBlockGauge.Update(int64(hc.getJustifiedNumber(parent))) + headJustifiedBlockGauge.Update(int64(hc.GetJustifiedNumber(parent))) headFinalizedBlockGauge.Update(int64(hc.getFinalizedNumber(parent))) // If this is the first iteration, wipe any leftover data upwards too so