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

opt: bidder timer #1

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
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
145 changes: 80 additions & 65 deletions miner/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@
"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"
"github.com/ethereum/go-ethereum/rpc"
)

const maxBid int64 = 3

Check failure on line 23 in miner/bidder.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.21.x, ubuntu-latest)

const `maxBid` is unused (unused)

var bidSimulationLeftOver = 50 * time.Millisecond
var bidSendLeftOver = 20 * time.Millisecond

type ValidatorConfig struct {
Address common.Address
URL string
Expand All @@ -44,26 +48,33 @@
bestWorksMu sync.RWMutex
bestWorks map[int64]*environment

newBidCh chan *environment
exitCh chan struct{}
chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription

wg sync.WaitGroup
exitCh chan struct{}

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,
engine: engine,
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{}),
}

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 {
return b
}
Expand All @@ -83,75 +94,77 @@
log.Warn("Bidder: No valid validators")
}

b.wg.Add(2)
go b.mainLoop()
go b.reconnectLoop()

return b
}

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
if !b.enabled() {
return
}

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
for chainHeadEvent := range b.chainHeadCh {
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)

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)
}
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 <-second.C:
work := b.getBestWork(nextBlockNumber)
if work != nil {
log.Info("Bidder: try to bid (second)", "number", nextBlockNumber)
go b.bid(work)
}
}
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 <-third.C:
work := b.getBestWork(nextBlockNumber)
if work != nil {
log.Info("Bidder: try to bid (third)", "number", nextBlockNumber)
go b.bid(work)
}
}()
case <-b.exitCh:
return
case <-exit.C:
break LOOP
case <-b.exitCh:
return
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:
Expand Down Expand Up @@ -209,16 +222,13 @@
return
}

if work.profit.Cmp(common.Big0) <= 0 {
return
if b.isBestWork(work) {
b.setBestWork(work)
}

b.newBidCh <- work
}

func (b *Bidder) exit() {
close(b.exitCh)
b.wg.Wait()
}

// bid notifies the next in-turn validator the work
Expand Down Expand Up @@ -275,10 +285,12 @@
}
}

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 {
b.deleteBestWork(work)
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)
Expand All @@ -289,8 +301,7 @@
return
}

b.deleteBestWork(work)
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
Expand All @@ -299,6 +310,10 @@
return false
}

if work.profit.Cmp(common.Big0) <= 0 {
return false
}

last := b.getBestWork(work.header.Number.Int64())
if last == nil {
return true
Expand All @@ -316,11 +331,11 @@
}

// 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
Expand Down
1 change: 1 addition & 0 deletions miner/miner_mev.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading