From af7e9b95bda2fb90faead2db2d5c728529c4d7eb Mon Sep 17 00:00:00 2001 From: irrun Date: Fri, 7 Jun 2024 16:41:20 +0800 Subject: [PATCH] fix: waiting for the last simulation before pick best bid (#2507) --- miner/bid_simulator.go | 40 +++++++++++++--------------------------- miner/worker.go | 13 +++++++++++++ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index aa2afc812..e80dc90b1 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -29,11 +29,6 @@ import ( const ( // maxBidPerBuilderPerBlock is the max bid number per builder maxBidPerBuilderPerBlock = 3 - - // leftOverTimeRate is the rate of left over time to simulate a bid - leftOverTimeRate = 11 - // leftOverTimeScale is the scale of left over time to simulate a bid - leftOverTimeScale = 10 ) var ( @@ -318,18 +313,6 @@ func (b *bidSimulator) newBidLoop() { // commit aborts in-flight bid execution with given signal and resubmits a new one. commit := func(reason int32, bidRuntime *BidRuntime) { - // if the left time is not enough to do simulation, return - var simDuration time.Duration - if lastBid := b.GetBestBid(bidRuntime.bid.ParentHash); lastBid != nil && lastBid.duration != 0 { - simDuration = lastBid.duration - } - - if time.Until(b.bidMustBefore(bidRuntime.bid.ParentHash)) <= simDuration*leftOverTimeRate/leftOverTimeScale { - log.Debug("BidSimulator: abort commit, not enough time to simulate", - "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) - return - } - if interruptCh != nil { // each commit work will have its own interruptCh to stop work with a reason interruptCh <- reason @@ -370,6 +353,7 @@ func (b *bidSimulator) newBidLoop() { expectedValidatorReward: expectedValidatorReward, packedBlockReward: big.NewInt(0), packedValidatorReward: big.NewInt(0), + finished: make(chan struct{}), } simulatingBid := b.GetSimulatingBid(newBid.ParentHash) @@ -410,11 +394,6 @@ func (b *bidSimulator) newBidLoop() { } } -func (b *bidSimulator) bidMustBefore(parentHash common.Hash) time.Time { - parentHeader := b.chain.GetHeaderByHash(parentHash) - return bidutil.BidMustBefore(parentHeader, b.chainConfig.Parlia.Period, b.delayLeftOver) -} - func (b *bidSimulator) bidBetterBefore(parentHash common.Hash) time.Time { parentHeader := b.chain.GetHeaderByHash(parentHash) return bidutil.BidBetterBefore(parentHeader, b.chainConfig.Parlia.Period, b.delayLeftOver, b.config.BidSimulationLeftOver) @@ -530,7 +509,6 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { // ensure simulation exited then start next simulation b.SetSimulatingBid(parentHash, bidRuntime) - start := time.Now() defer func(simStart time.Time) { logCtx := []any{ @@ -556,10 +534,11 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { } b.RemoveSimulatingBid(parentHash) - bidSimTimer.UpdateSince(start) + close(bidRuntime.finished) if success { bidRuntime.duration = time.Since(simStart) + bidSimTimer.UpdateSince(simStart) // only recommit self bid when newBidCh is empty if len(b.newBidCh) > 0 { @@ -583,6 +562,14 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { return } + // if the left time is not enough to do simulation, return + delay := b.engine.Delay(b.chain, bidRuntime.env.header, &b.delayLeftOver) + if delay == nil || *delay <= 0 { + log.Info("BidSimulator: abort commit, not enough time to simulate", + "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) + return + } + gasLimit := bidRuntime.env.header.GasLimit if bidRuntime.env.gasPool == nil { bidRuntime.env.gasPool = new(core.GasPool).AddGas(gasLimit) @@ -650,14 +637,12 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { if b.config.GreedyMergeTx { delay := b.engine.Delay(b.chain, bidRuntime.env.header, &b.delayLeftOver) if delay != nil && *delay > 0 { - stopTimer := time.NewTimer(*delay) - bidTxsSet := mapset.NewSet[common.Hash]() for _, tx := range bidRuntime.bid.Txs { bidTxsSet.Add(tx.Hash()) } - fillErr := b.bidWorker.fillTransactions(interruptCh, bidRuntime.env, stopTimer, bidTxsSet) + fillErr := b.bidWorker.fillTransactions(interruptCh, bidRuntime.env, nil, bidTxsSet) log.Trace("BidSimulator: greedy merge stopped", "block", bidRuntime.env.header.Number, "builder", bidRuntime.bid.Builder, "tx count", bidRuntime.env.tcount-bidTxLen+1, "err", fillErr) @@ -733,6 +718,7 @@ type BidRuntime struct { packedBlockReward *big.Int packedValidatorReward *big.Int + finished chan struct{} duration time.Duration } diff --git a/miner/worker.go b/miner/worker.go index 1677dc12b..424a44935 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -67,6 +67,9 @@ const ( // the current 4 mining loops could have asynchronous risk of mining block with // save height, keep recently mined blocks to avoid double sign for safety, recentMinedCacheLimit = 20 + + // the default to wait for the mev miner to finish + waitMEVMinerEndTimeLimit = 50 * time.Millisecond ) var ( @@ -171,6 +174,7 @@ type getWorkReq struct { type bidFetcher interface { GetBestBid(parentHash common.Hash) *BidRuntime + GetSimulatingBid(prevBlockHash common.Hash) *BidRuntime } // worker is the main object which takes care of submitting new work to consensus engine @@ -1336,6 +1340,15 @@ LOOP: // when in-turn, compare with remote work. from := bestWork.coinbase if w.bidFetcher != nil && bestWork.header.Difficulty.Cmp(diffInTurn) == 0 { + if pendingBid := w.bidFetcher.GetSimulatingBid(bestWork.header.ParentHash); pendingBid != nil { + waitBidTimer := time.NewTimer(waitMEVMinerEndTimeLimit) + defer waitBidTimer.Stop() + select { + case <-waitBidTimer.C: + case <-pendingBid.finished: + } + } + bestBid := w.bidFetcher.GetBestBid(bestWork.header.ParentHash) if bestBid != nil {