From 33efd645f375e2afa2e8433e19592e7739968377 Mon Sep 17 00:00:00 2001 From: irrun Date: Thu, 4 Jul 2024 17:50:36 +0800 Subject: [PATCH 1/2] opt: bidder timer --- miner/bidder.go | 111 +++++++++++++++++++++++------------------------- miner/worker.go | 2 +- 2 files changed, 55 insertions(+), 58 deletions(-) diff --git a/miner/bidder.go b/miner/bidder.go index 83a1afbc5..28989e787 100644 --- a/miner/bidder.go +++ b/miner/bidder.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner/validatorclient" "github.com/ethereum/go-ethereum/rlp" @@ -21,6 +22,8 @@ import ( const maxBid int64 = 3 +var bidSimulationLeftOver = 50 * time.Millisecond + type ValidatorConfig struct { Address common.Address URL string @@ -44,15 +47,17 @@ type Bidder struct { bestWorksMu sync.RWMutex bestWorks map[int64]*environment - newBidCh chan *environment - exitCh chan struct{} + chainHeadCh chan core.ChainHeadEvent + chainHeadSub event.Subscription + + exitCh chan struct{} wg sync.WaitGroup wallet accounts.Wallet } -func NewBidder(config *MevConfig, delayLeftOver time.Duration, engine consensus.Engine, eth Backend) *Bidder { +func NewBidder(config *MevConfig, chain *core.BlockChain, delayLeftOver time.Duration, engine consensus.Engine, eth Backend) *Bidder { b := &Bidder{ config: config, delayLeftOver: delayLeftOver, @@ -60,10 +65,12 @@ func NewBidder(config *MevConfig, delayLeftOver time.Duration, engine consensus. chain: eth.BlockChain(), validators: make(map[common.Address]*validator), bestWorks: make(map[int64]*environment), - newBidCh: make(chan *environment, 10), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), exitCh: make(chan struct{}), } + b.chainHeadSub = chain.SubscribeChainHeadEvent(b.chainHeadCh) + if !config.BuilderEnabled { return b } @@ -92,57 +99,47 @@ func NewBidder(config *MevConfig, delayLeftOver time.Duration, engine consensus. func (b *Bidder) mainLoop() { defer b.wg.Done() + defer b.chainHeadSub.Unsubscribe() - timer := time.NewTimer(0) - defer timer.Stop() - <-timer.C // discard the initial tick + for chainHeadEvent := range b.chainHeadCh { + if !b.enabled() { + continue + } - var ( - bidNum int64 = 0 - betterBidBefore time.Time - currentHeight = b.chain.CurrentBlock().Number.Int64() - ) - for { - select { - case work := <-b.newBidCh: - if work.header.Number.Int64() > currentHeight { - currentHeight = work.header.Number.Int64() - - bidNum = 0 - parentHeader := b.chain.GetHeaderByHash(work.header.ParentHash) - var bidSimulationLeftOver time.Duration - b.validatorsMu.RLock() - if b.validators[work.coinbase] != nil { - bidSimulationLeftOver = b.validators[work.coinbase].BidSimulationLeftOver + currentNumber := chainHeadEvent.Block.Number().Int64() + b.deleteBestWork(currentNumber) + + nextBlockNumber := currentNumber + 1 + betterBidBefore := bidutil.BidBetterBefore(chainHeadEvent.Block.Header(), b.chain.Config().Parlia.Period, + b.delayLeftOver, bidSimulationLeftOver) + + first := time.NewTimer(time.Until(betterBidBefore.Add(-10 * time.Millisecond))) + second := time.NewTimer(time.Until(betterBidBefore.Add(-20 * time.Millisecond))) + third := time.NewTimer(time.Until(betterBidBefore.Add(-30 * time.Millisecond))) + + for { + select { + case <-first.C: + case <-second.C: + work := b.getBestWork(nextBlockNumber) + if work != nil { + b.bid(work) } - b.validatorsMu.RUnlock() - betterBidBefore = bidutil.BidBetterBefore(parentHeader, b.chain.Config().Parlia.Period, b.delayLeftOver, - bidSimulationLeftOver) - - if time.Now().After(betterBidBefore) { - timer.Reset(0) - } else { - timer.Reset(time.Until(betterBidBefore) / time.Duration(maxBid)) + case <-third.C: + work := b.getBestWork(nextBlockNumber) + if work != nil { + b.bid(work) } + first.Stop() + second.Stop() + third.Stop() + break + case <-b.exitCh: + return + default: } - if bidNum < maxBid && b.isBestWork(work) { - // update the bestWork and do bid - b.setBestWork(work) - } - case <-timer.C: - go func() { - w := b.getBestWork(currentHeight) - if w != nil { - b.bid(w) - bidNum++ - if bidNum < maxBid && time.Now().Before(betterBidBefore) { - timer.Reset(time.Until(betterBidBefore) / time.Duration(maxBid-bidNum)) - } - } - }() - case <-b.exitCh: - return } + } } @@ -209,11 +206,9 @@ func (b *Bidder) newWork(work *environment) { return } - if work.profit.Cmp(common.Big0) <= 0 { - return + if b.isBestWork(work) { + b.setBestWork(work) } - - b.newBidCh <- work } func (b *Bidder) exit() { @@ -277,7 +272,6 @@ func (b *Bidder) bid(work *environment) { _, err := cli.SendBid(context.Background(), bidArgs) if err != nil { - b.deleteBestWork(work) log.Error("Bidder: bidding failed", "err", err) var bidErr rpc.Error @@ -289,7 +283,6 @@ func (b *Bidder) bid(work *environment) { return } - b.deleteBestWork(work) log.Info("Bidder: bidding success", "number", work.header.Number, "txs", len(work.txs)) } @@ -299,6 +292,10 @@ func (b *Bidder) isBestWork(work *environment) bool { return false } + if work.profit.Cmp(common.Big0) <= 0 { + return false + } + last := b.getBestWork(work.header.Number.Int64()) if last == nil { return true @@ -316,11 +313,11 @@ func (b *Bidder) setBestWork(work *environment) { } // deleteBestWork sets the best work -func (b *Bidder) deleteBestWork(work *environment) { +func (b *Bidder) deleteBestWork(number int64) { b.bestWorksMu.Lock() defer b.bestWorksMu.Unlock() - delete(b.bestWorks, work.header.Number.Int64()) + delete(b.bestWorks, number) } // getBestWork returns the best work diff --git a/miner/worker.go b/miner/worker.go index 3ac687b5f..f5db55d8f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -281,7 +281,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus exitCh: make(chan struct{}), resubmitIntervalCh: make(chan time.Duration), recentMinedBlocks: recentMinedBlocks, - bidder: NewBidder(&config.Mev, config.DelayLeftOver, engine, eth), + bidder: NewBidder(&config.Mev, eth.BlockChain(), config.DelayLeftOver, engine, eth), bundleCache: NewBundleCache(), } // Subscribe events for blockchain From 6757b5a885be5f6bf4b5f6c2d1a21458503a3166 Mon Sep 17 00:00:00 2001 From: irrun Date: Tue, 9 Jul 2024 11:29:41 +0800 Subject: [PATCH 2/2] opt: bidder timer --- miner/bidder.go | 66 +++++++++++++++++++++++++++++----------------- miner/miner_mev.go | 1 + 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/miner/bidder.go b/miner/bidder.go index 28989e787..d893fa634 100644 --- a/miner/bidder.go +++ b/miner/bidder.go @@ -23,6 +23,7 @@ import ( const maxBid int64 = 3 var bidSimulationLeftOver = 50 * time.Millisecond +var bidSendLeftOver = 20 * time.Millisecond type ValidatorConfig struct { Address common.Address @@ -52,8 +53,6 @@ type Bidder struct { exitCh chan struct{} - wg sync.WaitGroup - wallet accounts.Wallet } @@ -69,6 +68,11 @@ func NewBidder(config *MevConfig, chain *core.BlockChain, delayLeftOver time.Dur exitCh: make(chan struct{}), } + if config.BidSendLeftOver == 0 { + config.BidSendLeftOver = bidSendLeftOver + log.Info("Bidder: use default bid send left over", "duration", bidSendLeftOver) + } + b.chainHeadSub = chain.SubscribeChainHeadEvent(b.chainHeadCh) if !config.BuilderEnabled { @@ -90,7 +94,6 @@ func NewBidder(config *MevConfig, chain *core.BlockChain, delayLeftOver time.Dur log.Warn("Bidder: No valid validators") } - b.wg.Add(2) go b.mainLoop() go b.reconnectLoop() @@ -98,14 +101,13 @@ func NewBidder(config *MevConfig, chain *core.BlockChain, delayLeftOver time.Dur } func (b *Bidder) mainLoop() { - defer b.wg.Done() defer b.chainHeadSub.Unsubscribe() - for chainHeadEvent := range b.chainHeadCh { - if !b.enabled() { - continue - } + if !b.enabled() { + return + } + for chainHeadEvent := range b.chainHeadCh { currentNumber := chainHeadEvent.Block.Number().Int64() b.deleteBestWork(currentNumber) @@ -113,42 +115,56 @@ func (b *Bidder) mainLoop() { betterBidBefore := bidutil.BidBetterBefore(chainHeadEvent.Block.Header(), b.chain.Config().Parlia.Period, b.delayLeftOver, bidSimulationLeftOver) - first := time.NewTimer(time.Until(betterBidBefore.Add(-10 * time.Millisecond))) - second := time.NewTimer(time.Until(betterBidBefore.Add(-20 * time.Millisecond))) - third := time.NewTimer(time.Until(betterBidBefore.Add(-30 * time.Millisecond))) + if betterBidBefore.Before(time.Now()) { + continue + } + first := time.NewTimer(time.Until(betterBidBefore.Add(-3 * b.config.BidSendLeftOver))) + second := time.NewTimer(time.Until(betterBidBefore.Add(-2 * b.config.BidSendLeftOver))) + third := time.NewTimer(time.Until(betterBidBefore.Add(-b.config.BidSendLeftOver))) + exit := time.NewTimer(time.Until(betterBidBefore)) + + LOOP: for { select { case <-first.C: + work := b.getBestWork(nextBlockNumber) + if work != nil { + log.Info("Bidder: try to bid (first)", "number", nextBlockNumber) + go b.bid(work) + } case <-second.C: work := b.getBestWork(nextBlockNumber) if work != nil { - b.bid(work) + log.Info("Bidder: try to bid (second)", "number", nextBlockNumber) + go b.bid(work) } case <-third.C: work := b.getBestWork(nextBlockNumber) if work != nil { - b.bid(work) + log.Info("Bidder: try to bid (third)", "number", nextBlockNumber) + go b.bid(work) } - first.Stop() - second.Stop() - third.Stop() - break + case <-exit.C: + break LOOP case <-b.exitCh: return - default: + case <-b.chainHeadSub.Err(): + log.Error("Bidder: chain head sub error") + return } } - } } func (b *Bidder) reconnectLoop() { - defer b.wg.Done() - ticker := time.NewTicker(10 * time.Minute) defer ticker.Stop() + if !b.enabled() { + return + } + for { select { case <-ticker.C: @@ -213,7 +229,6 @@ func (b *Bidder) newWork(work *environment) { func (b *Bidder) exit() { close(b.exitCh) - b.wg.Wait() } // bid notifies the next in-turn validator the work @@ -270,9 +285,12 @@ func (b *Bidder) bid(work *environment) { } } + startSend := time.Now() _, err := cli.SendBid(context.Background(), bidArgs) + log.Info("Bidder: send bid", "number", work.header.Number, "duration", time.Since(startSend).Milliseconds()) if err != nil { - log.Error("Bidder: bidding failed", "err", err) + log.Error("Bidder: bidding failed", "number", work.header.Number, "txs", len(work.txs), + "validator", work.coinbase, "err", err) var bidErr rpc.Error ok := errors.As(err, &bidErr) @@ -283,7 +301,7 @@ func (b *Bidder) bid(work *environment) { return } - log.Info("Bidder: bidding success", "number", work.header.Number, "txs", len(work.txs)) + log.Info("Bidder: bidding success", "number", work.header.Number, "txs", len(work.txs), "validator", work.coinbase) } // isBestWork returns the work is better than the current best work diff --git a/miner/miner_mev.go b/miner/miner_mev.go index 3ebcde109..3a513ef93 100644 --- a/miner/miner_mev.go +++ b/miner/miner_mev.go @@ -25,6 +25,7 @@ type MevConfig struct { Builders []BuilderConfig // The list of builders ValidatorCommission uint64 // 100 means the validator claims 1% from block reward BidSimulationLeftOver time.Duration + BidSendLeftOver time.Duration BuilderEnabled bool // Whether to enable bidder or not Validators []ValidatorConfig // The list of validators