Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need send the best bid back to channel

}

// 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 @@ -144,6 +145,7 @@ const (
commitInterruptResubmit
commitInterruptTimeout
commitInterruptOutOfGas
commitInterruptBetterBid
)

// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
Expand Down Expand Up @@ -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))
}
Expand Down
Loading