From a686b62dafbea5a85ebc856bb83495f297a5b7ee Mon Sep 17 00:00:00 2001 From: irrun Date: Wed, 24 Apr 2024 12:04:05 +0800 Subject: [PATCH 1/4] feat: recommit bid when newBidCh is empty to maximize mev reward --- miner/bid_simulator.go | 47 ++++++++++++++++++++++++------------------ miner/worker.go | 12 +++++++---- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index e3cf5c6e9b..a592998b81 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -30,8 +30,6 @@ const ( // maxBidPerBuilderPerBlock is the max bid number per builder maxBidPerBuilderPerBlock = 3 - commitInterruptBetterBid = 1 - // 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 @@ -311,8 +309,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) { - log.Debug("BidSimulator: start", "bidHash", bidRuntime.bid.Hash().Hex()) - // 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 { @@ -320,7 +316,8 @@ func (b *bidSimulator) newBidLoop() { } if time.Until(b.bidMustBefore(bidRuntime.bid.ParentHash)) <= simDuration*leftOverTimeRate/leftOverTimeScale { - log.Debug("BidSimulator: abort commit, not enough time to simulate", "bidHash", bidRuntime.bid.Hash().Hex()) + log.Debug("BidSimulator: abort commit, not enough time to simulate", + "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) return } @@ -332,6 +329,7 @@ func (b *bidSimulator) newBidLoop() { interruptCh = make(chan int32, 1) select { case b.simBidCh <- &simBidReq{interruptCh: interruptCh, bid: bidRuntime}: + log.Debug("BidSimulator: commit", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) case <-b.exitCh: return } @@ -352,7 +350,8 @@ func (b *bidSimulator) newBidLoop() { if expectedValidatorReward.Cmp(big.NewInt(0)) < 0 { // damage self profit, ignore - log.Debug("BidSimulator: invalid bid, validator reward is less than 0, ignore", "bidHash", newBid.Hash().Hex()) + log.Debug("BidSimulator: invalid bid, validator reward is less than 0, ignore", + "builder", newBid.Builder, "bidHash", newBid.Hash().Hex()) continue } @@ -375,26 +374,27 @@ func (b *bidSimulator) newBidLoop() { } // if bestBid is not nil, check if newBid is better than bestBid - if bidRuntime.expectedBlockReward.Cmp(bestBid.expectedBlockReward) > 0 && - bidRuntime.expectedValidatorReward.Cmp(bestBid.expectedValidatorReward) > 0 { + if bidRuntime.expectedBlockReward.Cmp(bestBid.expectedBlockReward) >= 0 && + bidRuntime.expectedValidatorReward.Cmp(bestBid.expectedValidatorReward) >= 0 { // if both reward are better than last simulating newBid, commit for simulation commit(commitInterruptBetterBid, bidRuntime) continue } - log.Debug("BidSimulator: lower reward, ignore", "bidHash", newBid.Hash().Hex()) + log.Debug("BidSimulator: lower reward, ignore", + "builder", bidRuntime.bid.Builder, "bidHash", newBid.Hash().Hex()) continue } // simulatingBid must be better than bestBid, if newBid is better than simulatingBid, commit for simulation - if bidRuntime.expectedBlockReward.Cmp(simulatingBid.expectedBlockReward) > 0 && - bidRuntime.expectedValidatorReward.Cmp(simulatingBid.expectedValidatorReward) > 0 { + if bidRuntime.expectedBlockReward.Cmp(simulatingBid.expectedBlockReward) >= 0 && + bidRuntime.expectedValidatorReward.Cmp(simulatingBid.expectedValidatorReward) >= 0 { // if both reward are better than last simulating newBid, commit for simulation commit(commitInterruptBetterBid, bidRuntime) continue } - log.Debug("BidSimulator: lower reward, ignore", "bidHash", newBid.Hash().Hex()) + log.Debug("BidSimulator: lower reward, ignore", "builder", newBid.Builder, "bidHash", newBid.Hash().Hex()) case <-b.exitCh: return } @@ -546,12 +546,17 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { go b.reportIssue(bidRuntime, err) } + b.RemoveSimulatingBid(parentHash) + bidSimTimer.UpdateSince(start) + if success { bidRuntime.duration = time.Since(simStart) - } - b.RemoveSimulatingBid(parentHash) - bidSimTimer.UpdateSince(start) + if len(b.newBidCh) == 0 { + log.Debug("BidSimulator: recommit", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) + b.newBidCh <- bidRuntime.bid + } + } }(time.Now()) // prepareWork will configure header with a suitable time according to consensus @@ -612,7 +617,8 @@ 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 { - log.Debug("BidSimulator: greedy merge tx stopTimer", "block", bidRuntime.env.header.Number, + log.Debug("BidSimulator: greedy merge stopTimer", "block", bidRuntime.env.header.Number, + "builder", bidRuntime.bid.Builder, "header time", time.Until(time.Unix(int64(bidRuntime.env.header.Time), 0)), "commit delay", *delay, "DelayLeftOver", b.delayLeftOver) @@ -624,8 +630,8 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { } fillErr := b.bidWorker.fillTransactions(interruptCh, bidRuntime.env, stopTimer, bidTxsSet) - log.Info("BidSimulator: greedy merge tx fill transactions", "block", bidRuntime.env.header.Number, - "tx count", bidRuntime.env.tcount-bidTxLen+1, "err", fillErr) + log.Info("BidSimulator: greedy merge stopped", "block", bidRuntime.env.header.Number, + "builder", bidRuntime.bid.Builder, "tx count", bidRuntime.env.tcount-bidTxLen+1, "err", fillErr) // recalculate the packed reward bidRuntime.packReward(b.config.ValidatorCommission) @@ -635,7 +641,8 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { bidRuntime.env.gasPool.AddGas(params.PayBidTxGasLimit) err = bidRuntime.commitTransaction(b.chain, b.chainConfig, payBidTx) if err != nil { - log.Error("BidSimulator: failed to commit tx", "bidHash", bidRuntime.bid.Hash(), "tx", payBidTx.Hash(), "err", err) + log.Error("BidSimulator: failed to commit tx", "builder", bidRuntime.bid.Builder, + "bidHash", bidRuntime.bid.Hash(), "tx", payBidTx.Hash(), "err", err) err = fmt.Errorf("invalid tx in bid, %v", err) return } @@ -649,7 +656,7 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { } // this is the simplest strategy: best for all the delegators. - if bidRuntime.packedBlockReward.Cmp(bestBid.packedBlockReward) > 0 { + if bidRuntime.packedBlockReward.Cmp(bestBid.packedBlockReward) >= 0 { b.SetBestBid(bidRuntime.bid.ParentHash, bidRuntime) success = true return diff --git a/miner/worker.go b/miner/worker.go index 2cc2ff00ab..6efcf3496a 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -73,10 +73,11 @@ var ( writeBlockTimer = metrics.NewRegisteredTimer("worker/writeblock", nil) finalizeBlockTimer = metrics.NewRegisteredTimer("worker/finalizeblock", nil) - errBlockInterruptedByNewHead = errors.New("new head arrived while building block") - errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") - errBlockInterruptedByTimeout = errors.New("timeout while building block") - errBlockInterruptedByOutOfGas = errors.New("out of gas while building block") + errBlockInterruptedByNewHead = errors.New("new head arrived while building block") + errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") + errBlockInterruptedByTimeout = errors.New("timeout while building block") + errBlockInterruptedByOutOfGas = errors.New("out of gas while building block") + errBlockInterruptedByBetterBid = errors.New("better bid arrived while building block") ) // environment is the worker's current environment and holds all @@ -144,6 +145,7 @@ const ( commitInterruptResubmit commitInterruptTimeout commitInterruptOutOfGas + commitInterruptBetterBid ) // newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. @@ -1480,6 +1482,8 @@ func signalToErr(signal int32) error { return errBlockInterruptedByTimeout case commitInterruptOutOfGas: return errBlockInterruptedByOutOfGas + case commitInterruptBetterBid: + return errBlockInterruptedByBetterBid default: panic(fmt.Errorf("undefined signal %d", signal)) } From c1bfadab6c9859b2d7643ec00a33184def1b859c Mon Sep 17 00:00:00 2001 From: irrun Date: Fri, 26 Apr 2024 16:37:20 +0800 Subject: [PATCH 2/4] fix: nonblocking recommit --- miner/bid_simulator.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index a592998b81..411c7bf71c 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -552,9 +552,10 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { if success { bidRuntime.duration = time.Since(simStart) - if len(b.newBidCh) == 0 { + select { + case b.newBidCh <- bidRuntime.bid: log.Debug("BidSimulator: recommit", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) - b.newBidCh <- bidRuntime.bid + default: } } }(time.Now()) @@ -661,6 +662,13 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { success = true return } + + // recommit last best bid + select { + case b.newBidCh <- bestBid.bid: + log.Debug("BidSimulator: recommit last bid", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) + default: + } } // reportIssue reports the issue to the mev-sentry From e36d165bff6265cffc11e2ffd2d2205bde8b3ab7 Mon Sep 17 00:00:00 2001 From: irrun Date: Fri, 26 Apr 2024 20:30:13 +0800 Subject: [PATCH 3/4] fix: do not recommit when newBidCh has element --- miner/bid_simulator.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index 411c7bf71c..88589fea3f 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -552,6 +552,10 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { if success { bidRuntime.duration = time.Since(simStart) + if len(b.newBidCh) > 0 { + return + } + select { case b.newBidCh <- bidRuntime.bid: log.Debug("BidSimulator: recommit", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex()) From d4f3f19c29f45ccd907e15ca9c3c49a51f8609a0 Mon Sep 17 00:00:00 2001 From: irrun Date: Fri, 26 Apr 2024 20:36:33 +0800 Subject: [PATCH 4/4] fix: do not recommit when newBidCh has element --- miner/bid_simulator.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index 88589fea3f..477f2052ea 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -552,6 +552,7 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { if success { bidRuntime.duration = time.Since(simStart) + // only recommit self bid when newBidCh is empty if len(b.newBidCh) > 0 { return } @@ -667,7 +668,11 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { return } - // recommit last best bid + // only recommit last best bid when newBidCh is empty + if len(b.newBidCh) > 0 { + return + } + select { case b.newBidCh <- bestBid.bid: log.Debug("BidSimulator: recommit last bid", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex())