Skip to content

Commit

Permalink
feat: recommit bid when newBidCh is empty to maximize mev reward (#2424)
Browse files Browse the repository at this point in the history
  • Loading branch information
irrun authored Apr 28, 2024
1 parent 6573254 commit ba67263
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 24 deletions.
64 changes: 44 additions & 20 deletions miner/bid_simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -311,16 +309,15 @@ 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 {
simDuration = lastBid.duration
}

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
}

Expand All @@ -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
}
Expand All @@ -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
}

Expand All @@ -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
}
Expand Down Expand Up @@ -546,12 +546,23 @@ 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)
// only recommit self bid when newBidCh is empty
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())
default:
}
}
}(time.Now())

// prepareWork will configure header with a suitable time according to consensus
Expand Down Expand Up @@ -612,7 +623,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)

Expand All @@ -624,8 +636,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)
Expand All @@ -635,7 +647,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
}
Expand All @@ -649,11 +662,22 @@ 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
}

// 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())
default:
}
}

// reportIssue reports the issue to the mev-sentry
Expand Down
12 changes: 8 additions & 4 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -145,6 +146,7 @@ const (
commitInterruptResubmit
commitInterruptTimeout
commitInterruptOutOfGas
commitInterruptBetterBid
)

// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
Expand Down Expand Up @@ -1481,6 +1483,8 @@ func signalToErr(signal int32) error {
return errBlockInterruptedByTimeout
case commitInterruptOutOfGas:
return errBlockInterruptedByOutOfGas
case commitInterruptBetterBid:
return errBlockInterruptedByBetterBid
default:
panic(fmt.Errorf("undefined signal %d", signal))
}
Expand Down

0 comments on commit ba67263

Please sign in to comment.