From d1c86b3f3dcccc3ff67dbe2c9a891d9ea8c41f34 Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:01:59 +0800 Subject: [PATCH 1/6] miner: avoid to commit a bid twice --- core/blockchain.go | 2 ++ internal/ethapi/api_mev.go | 10 +++++----- miner/worker.go | 22 +++++++++++----------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index d9a1987d78..bac2280243 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1430,6 +1430,8 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { bc.currentSnapBlock.Store(block.Header()) headFastBlockGauge.Update(int64(block.NumberU64())) + // rawdb.WriteX may block if the database is busy, + // but bid simulation can start once currentBlock is set. bc.currentBlock.Store(block.Header()) headBlockGauge.Update(int64(block.NumberU64())) justifiedBlockGauge.Update(int64(bc.GetJustifiedNumber(block.Header()))) diff --git a/internal/ethapi/api_mev.go b/internal/ethapi/api_mev.go index ff18f69009..c002571c4b 100644 --- a/internal/ethapi/api_mev.go +++ b/internal/ethapi/api_mev.go @@ -31,8 +31,8 @@ func (m *MevAPI) SendBid(ctx context.Context, args types.BidArgs) (common.Hash, } var ( - rawBid = args.RawBid - currentHeader = m.b.CurrentHeader() // `currentHeader` might change during use. + rawBid = args.RawBid + currentBlock = m.b.CurrentBlock() // `CurrentBlock` might change during use. ) if rawBid == nil { @@ -40,7 +40,7 @@ func (m *MevAPI) SendBid(ctx context.Context, args types.BidArgs) (common.Hash, } // only support bidding for the next block not for the future block - if latestBlockNumber := currentHeader.Number.Uint64(); rawBid.BlockNumber < latestBlockNumber+1 { + if latestBlockNumber := currentBlock.Number.Uint64(); rawBid.BlockNumber < latestBlockNumber+1 { return common.Hash{}, types.NewInvalidBidError( fmt.Sprintf("stale block number: %d, latest block: %d", rawBid.BlockNumber, latestBlockNumber)) } else if rawBid.BlockNumber > latestBlockNumber+1 { @@ -55,9 +55,9 @@ func (m *MevAPI) SendBid(ctx context.Context, args types.BidArgs) (common.Hash, return common.Hash{}, types.ErrMevNotInTurn } - if rawBid.ParentHash != currentHeader.Hash() { + if rawBid.ParentHash != currentBlock.Hash() { return common.Hash{}, types.NewInvalidBidError( - fmt.Sprintf("non-aligned parent hash: %v", currentHeader.Hash())) + fmt.Sprintf("non-aligned parent hash: %v", currentBlock.Hash())) } if rawBid.GasFee == nil || rawBid.GasFee.Cmp(common.Big0) == 0 || rawBid.GasUsed == 0 { diff --git a/miner/worker.go b/miner/worker.go index 6bb64b42e5..33d3b128d6 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -705,14 +705,8 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co } else { if prevEnv == nil { state.StartPrefetcher("miner", nil) - } else if prevEnv.header.Number.Uint64() == header.Number.Uint64() { - state.TransferPrefetcher(prevEnv.state) } else { - // in some corner case, new block was just imported and preEnv was for the previous block - // in this case, the prefetcher can not be transferred - log.Debug("new block was just imported, start prefetcher from scratch", - "prev number", prevEnv.header.Number.Uint64(), "cur number", header.Number.Uint64()) - state.StartPrefetcher("miner", nil) + state.TransferPrefetcher(prevEnv.state) } } @@ -1285,6 +1279,7 @@ func (w *worker) commitWork(interruptCh chan int32, timestamp int64) { // validator can try several times to get the most profitable block, // as long as the timestamp is not reached. workList := make([]*environment, 0, 10) + parentHash := w.chain.CurrentBlock().Hash() var prevWork *environment // workList clean up defer func() { @@ -1300,9 +1295,10 @@ func (w *worker) commitWork(interruptCh chan int32, timestamp int64) { LOOP: for { work, err := w.prepareWork(&generateParams{ - timestamp: uint64(timestamp), - coinbase: coinbase, - prevWork: prevWork, + timestamp: uint64(timestamp), + parentHash: parentHash, + coinbase: coinbase, + prevWork: prevWork, }, false) if err != nil { return @@ -1508,6 +1504,10 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti interval() } fees := env.state.GetBalance(consensus.SystemAddress).ToBig() + if len(env.txs) != env.tcount { + log.Warn("Invalid work commit: it may have already been committed", "number", env.header.Number.Uint64()) + return nil + } feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether)) // Withdrawals are set to nil here, because this is only called in PoW. finalizeStart := time.Now() @@ -1536,7 +1536,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti select { case w.taskCh <- &task{receipts: receipts, state: env.state, block: block, createdAt: time.Now(), miningStartAt: start}: log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), - "txs", env.tcount, "blobs", env.blobs, "gas", block.GasUsed(), "fees", feesInEther, "elapsed", common.PrettyDuration(time.Since(start))) + "txs", len(env.txs), "blobs", env.blobs, "gas", block.GasUsed(), "fees", feesInEther, "elapsed", common.PrettyDuration(time.Since(start))) case <-w.exitCh: log.Info("Worker has exited") From be9131ea9ac531bc208730bbb5457e6876c20839 Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:22:05 +0800 Subject: [PATCH 2/6] core: wait db writing before setting head --- core/blockchain.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index bac2280243..41eee02709 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1398,7 +1398,6 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { func (bc *BlockChain) writeHeadBlock(block *types.Block) { var dbWg sync.WaitGroup dbWg.Add(2) - defer dbWg.Wait() go func() { defer dbWg.Done() // Add the block to the canonical chain number scheme and mark as the head @@ -1423,6 +1422,7 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { log.Crit("Failed to update chain indexes in chain db", "err", err) } }() + dbWg.Wait() // Update all in-memory chain markers in the last step bc.hc.SetCurrentHeader(block.Header()) @@ -1430,8 +1430,6 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { bc.currentSnapBlock.Store(block.Header()) headFastBlockGauge.Update(int64(block.NumberU64())) - // rawdb.WriteX may block if the database is busy, - // but bid simulation can start once currentBlock is set. bc.currentBlock.Store(block.Header()) headBlockGauge.Update(int64(block.NumberU64())) justifiedBlockGauge.Update(int64(bc.GetJustifiedNumber(block.Header()))) From e1204cccbbdec5ca4441260d9c268c87888c6215 Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:48:44 +0800 Subject: [PATCH 3/6] miner: fix comment for tcount --- miner/worker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/worker.go b/miner/worker.go index 33d3b128d6..a1baf288b3 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -94,7 +94,7 @@ var ( type environment struct { signer types.Signer state *state.StateDB // apply state changes here - tcount int // tx count in cycle + tcount int // count of non-system transactions in cycle gasPool *core.GasPool // available gas used to pack transactions coinbase common.Address evm *vm.EVM From 7f82b173eb190254c86b5783dce80fce2e5afc02 Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:55:03 +0800 Subject: [PATCH 4/6] miner: improve warn log for committed work --- miner/worker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/worker.go b/miner/worker.go index a1baf288b3..41a3b9f489 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1505,7 +1505,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti } fees := env.state.GetBalance(consensus.SystemAddress).ToBig() if len(env.txs) != env.tcount { - log.Warn("Invalid work commit: it may have already been committed", "number", env.header.Number.Uint64()) + log.Warn("Invalid work commit: possibly already committed, now including system txs", "number", env.header.Number.Uint64()) return nil } feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether)) From 90c43926e28063057b75c77be4e709d13b748387 Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Wed, 3 Sep 2025 11:09:09 +0800 Subject: [PATCH 5/6] miner: add committed flag for environment --- miner/worker.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 41a3b9f489..0a177959c4 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -106,17 +106,20 @@ type environment struct { blobs int witness *stateless.Witness + + committed bool } // copy creates a deep copy of environment. func (env *environment) copy() *environment { cpy := &environment{ - signer: env.signer, - state: env.state.Copy(), - tcount: env.tcount, - coinbase: env.coinbase, - header: types.CopyHeader(env.header), - receipts: copyReceipts(env.receipts), + signer: env.signer, + state: env.state.Copy(), + tcount: env.tcount, + coinbase: env.coinbase, + header: types.CopyHeader(env.header), + receipts: copyReceipts(env.receipts), + committed: env.committed, } if env.gasPool != nil { gasPool := *env.gasPool @@ -1504,8 +1507,8 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti interval() } fees := env.state.GetBalance(consensus.SystemAddress).ToBig() - if len(env.txs) != env.tcount { - log.Warn("Invalid work commit: possibly already committed, now including system txs", "number", env.header.Number.Uint64()) + if env.committed { + log.Warn("Invalid work commit: already committed", "number", env.header.Number.Uint64()) return nil } feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether)) @@ -1516,6 +1519,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti body.Withdrawals = make([]*types.Withdrawal, 0) } block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, types.CopyHeader(env.header), env.state, &body, env.receipts, nil) + env.committed = true if err != nil { return err } From 814b8ad01f85cf93f6cb7f4f5f3aa57bbc05321a Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Wed, 3 Sep 2025 11:20:13 +0800 Subject: [PATCH 6/6] miner: improve func commit --- miner/worker.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 0a177959c4..6bbe6c7d7f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1503,14 +1503,14 @@ func (w *worker) inTurn() bool { // the deep copy first. func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error { if w.isRunning() { - if interval != nil { - interval() - } - fees := env.state.GetBalance(consensus.SystemAddress).ToBig() if env.committed { log.Warn("Invalid work commit: already committed", "number", env.header.Number.Uint64()) return nil } + if interval != nil { + interval() + } + fees := env.state.GetBalance(consensus.SystemAddress).ToBig() feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether)) // Withdrawals are set to nil here, because this is only called in PoW. finalizeStart := time.Now()