feat: recommit bid when newBidCh is empty to maximize mev reward (#2424)

This commit is contained in:
irrun 2024-04-28 11:05:09 +08:00 committed by GitHub
parent 6573254a62
commit ba6726325a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 25 deletions

@ -30,8 +30,6 @@ const (
// maxBidPerBuilderPerBlock is the max bid number per builder // maxBidPerBuilderPerBlock is the max bid number per builder
maxBidPerBuilderPerBlock = 3 maxBidPerBuilderPerBlock = 3
commitInterruptBetterBid = 1
// leftOverTimeRate is the rate of left over time to simulate a bid // leftOverTimeRate is the rate of left over time to simulate a bid
leftOverTimeRate = 11 leftOverTimeRate = 11
// leftOverTimeScale is the scale of left over time to simulate a bid // 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 aborts in-flight bid execution with given signal and resubmits a new one.
commit := func(reason int32, bidRuntime *BidRuntime) { 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 // if the left time is not enough to do simulation, return
var simDuration time.Duration var simDuration time.Duration
if lastBid := b.GetBestBid(bidRuntime.bid.ParentHash); lastBid != nil && lastBid.duration != 0 { 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 { 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 return
} }
@ -332,6 +329,7 @@ func (b *bidSimulator) newBidLoop() {
interruptCh = make(chan int32, 1) interruptCh = make(chan int32, 1)
select { select {
case b.simBidCh <- &simBidReq{interruptCh: interruptCh, bid: bidRuntime}: case b.simBidCh <- &simBidReq{interruptCh: interruptCh, bid: bidRuntime}:
log.Debug("BidSimulator: commit", "builder", bidRuntime.bid.Builder, "bidHash", bidRuntime.bid.Hash().Hex())
case <-b.exitCh: case <-b.exitCh:
return return
} }
@ -352,7 +350,8 @@ func (b *bidSimulator) newBidLoop() {
if expectedValidatorReward.Cmp(big.NewInt(0)) < 0 { if expectedValidatorReward.Cmp(big.NewInt(0)) < 0 {
// damage self profit, ignore // 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 continue
} }
@ -375,26 +374,27 @@ func (b *bidSimulator) newBidLoop() {
} }
// if bestBid is not nil, check if newBid is better than bestBid // if bestBid is not nil, check if newBid is better than bestBid
if bidRuntime.expectedBlockReward.Cmp(bestBid.expectedBlockReward) > 0 && if bidRuntime.expectedBlockReward.Cmp(bestBid.expectedBlockReward) >= 0 &&
bidRuntime.expectedValidatorReward.Cmp(bestBid.expectedValidatorReward) > 0 { bidRuntime.expectedValidatorReward.Cmp(bestBid.expectedValidatorReward) >= 0 {
// if both reward are better than last simulating newBid, commit for simulation // if both reward are better than last simulating newBid, commit for simulation
commit(commitInterruptBetterBid, bidRuntime) commit(commitInterruptBetterBid, bidRuntime)
continue 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 continue
} }
// simulatingBid must be better than bestBid, if newBid is better than simulatingBid, commit for simulation // simulatingBid must be better than bestBid, if newBid is better than simulatingBid, commit for simulation
if bidRuntime.expectedBlockReward.Cmp(simulatingBid.expectedBlockReward) > 0 && if bidRuntime.expectedBlockReward.Cmp(simulatingBid.expectedBlockReward) >= 0 &&
bidRuntime.expectedValidatorReward.Cmp(simulatingBid.expectedValidatorReward) > 0 { bidRuntime.expectedValidatorReward.Cmp(simulatingBid.expectedValidatorReward) >= 0 {
// if both reward are better than last simulating newBid, commit for simulation // if both reward are better than last simulating newBid, commit for simulation
commit(commitInterruptBetterBid, bidRuntime) commit(commitInterruptBetterBid, bidRuntime)
continue 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: case <-b.exitCh:
return return
} }
@ -546,12 +546,23 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) {
go b.reportIssue(bidRuntime, err) go b.reportIssue(bidRuntime, err)
} }
if success {
bidRuntime.duration = time.Since(simStart)
}
b.RemoveSimulatingBid(parentHash) b.RemoveSimulatingBid(parentHash)
bidSimTimer.UpdateSince(start) bidSimTimer.UpdateSince(start)
if success {
bidRuntime.duration = time.Since(simStart)
// 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()) }(time.Now())
// prepareWork will configure header with a suitable time according to consensus // prepareWork will configure header with a suitable time according to consensus
@ -612,7 +623,8 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) {
if b.config.GreedyMergeTx { if b.config.GreedyMergeTx {
delay := b.engine.Delay(b.chain, bidRuntime.env.header, &b.delayLeftOver) delay := b.engine.Delay(b.chain, bidRuntime.env.header, &b.delayLeftOver)
if delay != nil && *delay > 0 { 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)), "header time", time.Until(time.Unix(int64(bidRuntime.env.header.Time), 0)),
"commit delay", *delay, "DelayLeftOver", b.delayLeftOver) "commit delay", *delay, "DelayLeftOver", b.delayLeftOver)
@ -624,8 +636,8 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) {
} }
fillErr := b.bidWorker.fillTransactions(interruptCh, bidRuntime.env, stopTimer, bidTxsSet) fillErr := b.bidWorker.fillTransactions(interruptCh, bidRuntime.env, stopTimer, bidTxsSet)
log.Info("BidSimulator: greedy merge tx fill transactions", "block", bidRuntime.env.header.Number, log.Info("BidSimulator: greedy merge stopped", "block", bidRuntime.env.header.Number,
"tx count", bidRuntime.env.tcount-bidTxLen+1, "err", fillErr) "builder", bidRuntime.bid.Builder, "tx count", bidRuntime.env.tcount-bidTxLen+1, "err", fillErr)
// recalculate the packed reward // recalculate the packed reward
bidRuntime.packReward(b.config.ValidatorCommission) bidRuntime.packReward(b.config.ValidatorCommission)
@ -635,7 +647,8 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) {
bidRuntime.env.gasPool.AddGas(params.PayBidTxGasLimit) bidRuntime.env.gasPool.AddGas(params.PayBidTxGasLimit)
err = bidRuntime.commitTransaction(b.chain, b.chainConfig, payBidTx) err = bidRuntime.commitTransaction(b.chain, b.chainConfig, payBidTx)
if err != nil { 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) err = fmt.Errorf("invalid tx in bid, %v", err)
return return
} }
@ -649,11 +662,22 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) {
} }
// this is the simplest strategy: best for all the delegators. // 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) b.SetBestBid(bidRuntime.bid.ParentHash, bidRuntime)
success = true success = true
return 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 // reportIssue reports the issue to the mev-sentry

@ -77,6 +77,7 @@ var (
errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block")
errBlockInterruptedByTimeout = errors.New("timeout while building block") errBlockInterruptedByTimeout = errors.New("timeout while building block")
errBlockInterruptedByOutOfGas = errors.New("out of gas 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 // environment is the worker's current environment and holds all
@ -145,6 +146,7 @@ const (
commitInterruptResubmit commitInterruptResubmit
commitInterruptTimeout commitInterruptTimeout
commitInterruptOutOfGas commitInterruptOutOfGas
commitInterruptBetterBid
) )
// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. // newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
@ -1481,6 +1483,8 @@ func signalToErr(signal int32) error {
return errBlockInterruptedByTimeout return errBlockInterruptedByTimeout
case commitInterruptOutOfGas: case commitInterruptOutOfGas:
return errBlockInterruptedByOutOfGas return errBlockInterruptedByOutOfGas
case commitInterruptBetterBid:
return errBlockInterruptedByBetterBid
default: default:
panic(fmt.Errorf("undefined signal %d", signal)) panic(fmt.Errorf("undefined signal %d", signal))
} }