diff --git a/oracle/pkg/l1Listener/l1Listener.go b/oracle/pkg/l1Listener/l1Listener.go index c15b6b9c3..58eb4857b 100644 --- a/oracle/pkg/l1Listener/l1Listener.go +++ b/oracle/pkg/l1Listener/l1Listener.go @@ -12,6 +12,7 @@ import ( blocktracker "github.com/primev/mev-commit/contracts-abi/clients/BlockTracker" "github.com/primev/mev-commit/oracle/pkg/store" "github.com/primev/mev-commit/x/contracts/events" + "github.com/primev/mev-commit/x/evmclients" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/errgroup" ) @@ -27,15 +28,9 @@ type WinnerRegister interface { LastWinnerBlock() (int64, error) } -type EthClient interface { - BlockNumber(ctx context.Context) (uint64, error) - HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) - BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) -} - type L1Listener struct { logger *slog.Logger - l1Client EthClient + l1Client evmclients.EthClient winnerRegister WinnerRegister eventMgr events.EventManager recorder L1Recorder @@ -44,7 +39,7 @@ type L1Listener struct { func NewL1Listener( logger *slog.Logger, - l1Client EthClient, + l1Client evmclients.EthClient, winnerRegister WinnerRegister, evtMgr events.EventManager, recorder L1Recorder, diff --git a/oracle/pkg/node/node.go b/oracle/pkg/node/node.go index 43fcf48f8..ed4be2f89 100644 --- a/oracle/pkg/node/node.go +++ b/oracle/pkg/node/node.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "log/slog" - "math/big" "net/url" "strings" "time" @@ -15,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" bidderregistry "github.com/primev/mev-commit/contracts-abi/clients/BidderRegistry" blocktracker "github.com/primev/mev-commit/contracts-abi/clients/BlockTracker" @@ -30,6 +28,7 @@ import ( "github.com/primev/mev-commit/x/contracts/events/publisher" "github.com/primev/mev-commit/x/contracts/transactor" "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/primev/mev-commit/x/keysigner" ) @@ -107,7 +106,7 @@ func NewNode(opts *Options) (*Node, error) { monitor := txmonitor.New( owner, settlementClient, - txmonitor.NewEVMHelperWithLogger(settlementClient.Client(), nd.logger), + evmclients.NewEVMHelperWithLogger(settlementClient.Client(), nd.logger), st, nd.logger.With("component", "tx_monitor"), 1024, @@ -158,14 +157,14 @@ func NewNode(opts *Options) (*Node, error) { ) } - var listenerL1Client l1Listener.EthClient + var listenerL1Client evmclients.EthClient listenerL1Client = l1Client if opts.LaggerdMode > 0 { - listenerL1Client = &laggerdL1Client{EthClient: listenerL1Client, amount: opts.LaggerdMode} + listenerL1Client = evmclients.NewLaggerdL1Client(listenerL1Client, opts.LaggerdMode) } - listenerL1Client = &infiniteRetryL1Client{EthClient: listenerL1Client, logger: nd.logger} + listenerL1Client = evmclients.NewInfiniteRetryL1Client(listenerL1Client, nd.logger) blockTracker, err := blocktracker.NewBlocktrackerTransactor( opts.BlockTrackerContractAddr, @@ -205,7 +204,7 @@ func NewNode(opts *Options) (*Node, error) { } if opts.OverrideWinners != nil && len(opts.OverrideWinners) > 0 { - listenerL1Client = &winnerOverrideL1Client{EthClient: listenerL1Client, winners: opts.OverrideWinners} + listenerL1Client = evmclients.NewWinnerOverrideL1Client(listenerL1Client, opts.OverrideWinners) for _, winner := range opts.OverrideWinners { nd.logger.Info("setting builder mapping", "builderName", winner, "builderAddress", winner) err := setBuilderMapping( @@ -239,7 +238,7 @@ func NewNode(opts *Options) (*Node, error) { st, evtMgr, oracleTransactorSession, - txmonitor.NewEVMHelperWithLogger(l1Client.Client(), nd.logger), + evmclients.NewEVMHelperWithLogger(l1Client.Client(), nd.logger), ) if err != nil { nd.logger.Error("failed to instantiate updater", "error", err) @@ -398,93 +397,6 @@ func getContractABIs(opts *Options) (map[common.Address]*abi.ABI, error) { return abis, nil } -type laggerdL1Client struct { - l1Listener.EthClient - amount int -} - -func (l *laggerdL1Client) BlockNumber(ctx context.Context) (uint64, error) { - blkNum, err := l.EthClient.BlockNumber(ctx) - if err != nil { - return 0, err - } - - return blkNum - uint64(l.amount), nil -} - -type winnerOverrideL1Client struct { - l1Listener.EthClient - winners []string -} - -func (w *winnerOverrideL1Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { - hdr, err := w.EthClient.HeaderByNumber(ctx, number) - if err != nil { - return nil, err - } - - idx := number.Int64() % int64(len(w.winners)) - hdr.Extra = []byte(w.winners[idx]) - - return hdr, nil -} - -type infiniteRetryL1Client struct { - l1Listener.EthClient - logger *slog.Logger -} - -func (i *infiniteRetryL1Client) BlockNumber(ctx context.Context) (uint64, error) { - var blkNum uint64 - var err error - for retries := 50; retries > 0; retries-- { - blkNum, err = i.EthClient.BlockNumber(ctx) - if err == nil { - break - } - i.logger.Error("failed to get block number, retrying...", "error", err) - time.Sleep(2 * time.Second) - } - if err != nil { - return 0, err - } - return blkNum, nil -} - -func (i *infiniteRetryL1Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { - var hdr *types.Header - var err error - for retries := 50; retries > 0; retries-- { - hdr, err = i.EthClient.HeaderByNumber(ctx, number) - if err == nil { - break - } - i.logger.Error("failed to get header by number, retrying...", "error", err) - time.Sleep(2 * time.Second) - } - if err != nil { - return nil, err - } - return hdr, nil -} - -func (i *infiniteRetryL1Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { - var blk *types.Block - var err error - for retries := 50; retries > 0; retries-- { - blk, err = i.EthClient.BlockByNumber(ctx, number) - if err == nil { - break - } - i.logger.Error("failed to get block by number, retrying...", "error", err) - time.Sleep(2 * time.Second) - } - if err != nil { - return nil, err - } - return blk, nil -} - func setBuilderMapping( ctx context.Context, bt *blocktracker.BlocktrackerTransactorSession, diff --git a/oracle/pkg/updater/updater.go b/oracle/pkg/updater/updater.go index 3b1f841c5..65d4ff00a 100644 --- a/oracle/pkg/updater/updater.go +++ b/oracle/pkg/updater/updater.go @@ -20,7 +20,7 @@ import ( blocktracker "github.com/primev/mev-commit/contracts-abi/clients/BlockTracker" preconf "github.com/primev/mev-commit/contracts-abi/clients/PreConfCommitmentStore" "github.com/primev/mev-commit/x/contracts/events" - "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/errgroup" ) @@ -105,7 +105,7 @@ type Updater struct { openedCmts chan *preconf.PreconfcommitmentstoreCommitmentStored currentWindow atomic.Int64 metrics *metrics - receiptBatcher txmonitor.BatchReceiptGetter + receiptBatcher evmclients.BatchReceiptGetter } func NewUpdater( @@ -114,7 +114,7 @@ func NewUpdater( winnerRegister WinnerRegister, evtMgr events.EventManager, oracle Oracle, - receiptBatcher txmonitor.BatchReceiptGetter, + receiptBatcher evmclients.BatchReceiptGetter, ) (*Updater, error) { l1BlockCache, err := lru.New[uint64, map[string]TxMetadata](1024) if err != nil { diff --git a/oracle/pkg/updater/updater_test.go b/oracle/pkg/updater/updater_test.go index fcc030d9f..d752a8594 100644 --- a/oracle/pkg/updater/updater_test.go +++ b/oracle/pkg/updater/updater_test.go @@ -22,7 +22,7 @@ import ( preconf "github.com/primev/mev-commit/contracts-abi/clients/PreConfCommitmentStore" "github.com/primev/mev-commit/oracle/pkg/updater" "github.com/primev/mev-commit/x/contracts/events" - "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/primev/mev-commit/x/util" "golang.org/x/crypto/sha3" ) @@ -37,14 +37,14 @@ type testBatcher struct { failedReceipts map[common.Hash]bool } -func (t *testBatcher) BatchReceipts(ctx context.Context, txns []common.Hash) ([]txmonitor.Result, error) { - var results []txmonitor.Result +func (t *testBatcher) BatchReceipts(ctx context.Context, txns []common.Hash) ([]evmclients.Result, error) { + var results []evmclients.Result for _, txn := range txns { status := types.ReceiptStatusSuccessful if t.failedReceipts[txn] { status = types.ReceiptStatusFailed } - results = append(results, txmonitor.Result{ + results = append(results, evmclients.Result{ Receipt: &types.Receipt{ TxHash: txn, Status: status, diff --git a/p2p/pkg/node/node.go b/p2p/pkg/node/node.go index 68d96ebb1..b964c9fa4 100644 --- a/p2p/pkg/node/node.go +++ b/p2p/pkg/node/node.go @@ -49,6 +49,7 @@ import ( "github.com/primev/mev-commit/x/contracts/events/publisher" "github.com/primev/mev-commit/x/contracts/transactor" "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/primev/mev-commit/x/keysigner" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" @@ -190,7 +191,7 @@ func NewNode(opts *Options) (*Node, error) { monitor := txmonitor.New( opts.KeySigner.GetAddress(), contractRPC, - txmonitor.NewEVMHelperWithLogger(contractRPC.Client(), opts.Logger.With("component", "txmonitor")), + evmclients.NewEVMHelperWithLogger(contractRPC.Client(), opts.Logger.With("component", "txmonitor")), store, opts.Logger.With("component", "txmonitor"), 1024, @@ -342,7 +343,7 @@ func NewNode(opts *Options) (*Node, error) { evtMgr, store, commitmentDA, - txmonitor.NewEVMHelperWithLogger(contractRPC.Client(), opts.Logger.With("component", "evm_helper")), + evmclients.NewEVMHelperWithLogger(contractRPC.Client(), opts.Logger.With("component", "evm_helper")), optsGetter, opts.Logger.With("component", "tracker"), ) diff --git a/p2p/pkg/preconfirmation/tracker/tracker.go b/p2p/pkg/preconfirmation/tracker/tracker.go index 0c4bf53aa..107d3c3ce 100644 --- a/p2p/pkg/preconfirmation/tracker/tracker.go +++ b/p2p/pkg/preconfirmation/tracker/tracker.go @@ -16,7 +16,7 @@ import ( "github.com/primev/mev-commit/p2p/pkg/p2p" "github.com/primev/mev-commit/p2p/pkg/store" "github.com/primev/mev-commit/x/contracts/events" - "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/errgroup" ) @@ -27,7 +27,7 @@ type Tracker struct { evtMgr events.EventManager store CommitmentStore preconfContract PreconfContract - receiptGetter txmonitor.BatchReceiptGetter + receiptGetter evmclients.BatchReceiptGetter optsGetter OptsGetter newL1Blocks chan *blocktracker.BlocktrackerNewL1Block enryptedCmts chan *preconfcommstore.PreconfcommitmentstoreEncryptedCommitmentStored @@ -75,7 +75,7 @@ func NewTracker( evtMgr events.EventManager, store CommitmentStore, preconfContract PreconfContract, - receiptGetter txmonitor.BatchReceiptGetter, + receiptGetter evmclients.BatchReceiptGetter, optsGetter OptsGetter, logger *slog.Logger, ) *Tracker { diff --git a/p2p/pkg/preconfirmation/tracker/tracker_test.go b/p2p/pkg/preconfirmation/tracker/tracker_test.go index ee8aa229d..244f0ceea 100644 --- a/p2p/pkg/preconfirmation/tracker/tracker_test.go +++ b/p2p/pkg/preconfirmation/tracker/tracker_test.go @@ -23,7 +23,7 @@ import ( preconftracker "github.com/primev/mev-commit/p2p/pkg/preconfirmation/tracker" "github.com/primev/mev-commit/p2p/pkg/store" "github.com/primev/mev-commit/x/contracts/events" - "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/primev/mev-commit/x/util" ) @@ -364,13 +364,13 @@ type testReceiptGetter struct { count int } -func (t *testReceiptGetter) BatchReceipts(_ context.Context, txns []common.Hash) ([]txmonitor.Result, error) { +func (t *testReceiptGetter) BatchReceipts(_ context.Context, txns []common.Hash) ([]evmclients.Result, error) { if t.count != len(txns) { return nil, fmt.Errorf("expected %d txns, got %d", t.count, len(txns)) } - results := make([]txmonitor.Result, 0, len(txns)) + results := make([]evmclients.Result, 0, len(txns)) for range txns { - results = append(results, txmonitor.Result{ + results = append(results, evmclients.Result{ Err: errors.New("test error"), }) } diff --git a/x/contracts/txmonitor/txmonitor.go b/x/contracts/txmonitor/txmonitor.go index 575e4ed85..c4b7d9131 100644 --- a/x/contracts/txmonitor/txmonitor.go +++ b/x/contracts/txmonitor/txmonitor.go @@ -14,6 +14,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/prometheus/client_golang/prometheus" + + "github.com/primev/mev-commit/x/evmclients" ) var ( @@ -32,8 +34,8 @@ type Saver interface { } type EVMHelper interface { - BatchReceiptGetter - Debugger + evmclients.BatchReceiptGetter + evmclients.Debugger } type EVM interface { @@ -54,7 +56,7 @@ type waitCheck struct { type Monitor struct { owner common.Address mtx sync.Mutex - waitMap map[uint64]map[common.Hash][]chan Result + waitMap map[uint64]map[common.Hash][]chan evmclients.Result client EVM helper EVMHelper saver Saver @@ -86,7 +88,7 @@ func New( saver: saver, maxPendingTxs: maxPendingTxs, metrics: newMetrics(), - waitMap: make(map[uint64]map[common.Hash][]chan Result), + waitMap: make(map[uint64]map[common.Hash][]chan evmclients.Result), newTxAdded: make(chan struct{}), nonceUpdate: make(chan struct{}), blockUpdate: make(chan waitCheck), @@ -117,7 +119,7 @@ func (m *Monitor) Start(ctx context.Context) <-chan struct{} { for _, v := range m.waitMap { for _, c := range v { for _, c := range c { - c <- Result{nil, ErrMonitorClosed} + c <- evmclients.Result{nil, ErrMonitorClosed} close(c) } } @@ -236,15 +238,15 @@ func (m *Monitor) Sent(ctx context.Context, tx *types.Transaction) { }() } -func (m *Monitor) WatchTx(txHash common.Hash, nonce uint64) <-chan Result { +func (m *Monitor) WatchTx(txHash common.Hash, nonce uint64) <-chan evmclients.Result { m.mtx.Lock() defer m.mtx.Unlock() if m.waitMap[nonce] == nil { - m.waitMap[nonce] = make(map[common.Hash][]chan Result) + m.waitMap[nonce] = make(map[common.Hash][]chan evmclients.Result) } - c := make(chan Result, 1) + c := make(chan evmclients.Result, 1) m.waitMap[nonce][txHash] = append(m.waitMap[nonce][txHash], c) m.triggerNewTx() @@ -299,7 +301,7 @@ func (m *Monitor) getOlderTxns(nonce uint64) map[uint64][]common.Hash { func (m *Monitor) notify( nonce uint64, txn common.Hash, - res Result, + res evmclients.Result, ) { m.mtx.Lock() defer m.mtx.Unlock() @@ -350,7 +352,7 @@ func (m *Monitor) check(ctx context.Context, newBlock uint64, lastNonce uint64) nonce := nonceMap[txHashes[start+i]] if r.Err != nil { if errors.Is(r.Err, ethereum.NotFound) { - m.notify(nonce, txHashes[start+i], Result{nil, ErrTxnCancelled}) + m.notify(nonce, txHashes[start+i], evmclients.Result{nil, ErrTxnCancelled}) continue } m.logger.Error("failed to get receipt", "error", r.Err, "txHash", txHashes[start+i]) @@ -371,11 +373,11 @@ func (m *Monitor) check(ctx context.Context, newBlock uint64, lastNonce uint64) ) } m.logger.Error("failed to get receipt", "error", r.Err) - m.notify(nonce, txHashes[start+i], Result{r.Receipt, ErrTxnFailed}) + m.notify(nonce, txHashes[start+i], evmclients.Result{r.Receipt, ErrTxnFailed}) continue } - m.notify(nonce, txHashes[start+i], Result{r.Receipt, nil}) + m.notify(nonce, txHashes[start+i], evmclients.Result{r.Receipt, nil}) } } } diff --git a/x/contracts/txmonitor/txmonitor_test.go b/x/contracts/txmonitor/txmonitor_test.go index b08494345..66e24537b 100644 --- a/x/contracts/txmonitor/txmonitor_test.go +++ b/x/contracts/txmonitor/txmonitor_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/primev/mev-commit/x/contracts/txmonitor" + "github.com/primev/mev-commit/x/evmclients" "github.com/primev/mev-commit/x/util" ) @@ -39,9 +40,9 @@ func TestTxMonitor(t *testing.T) { )) } - results := make(map[common.Hash]txmonitor.Result) + results := make(map[common.Hash]evmclients.Result) for _, tx := range txns { - results[tx.Hash()] = txmonitor.Result{Receipt: &types.Receipt{Status: 1}} + results[tx.Hash()] = evmclients.Result{Receipt: &types.Receipt{Status: 1}} } evm := &testEVM{ @@ -144,14 +145,14 @@ func (t *testEVM) NonceAt(ctx context.Context, account common.Address, blockNumb } type testEVMHelper struct { - receipts map[common.Hash]txmonitor.Result + receipts map[common.Hash]evmclients.Result } -func (t *testEVMHelper) BatchReceipts(ctx context.Context, txns []common.Hash) ([]txmonitor.Result, error) { - results := make([]txmonitor.Result, 0, len(txns)) +func (t *testEVMHelper) BatchReceipts(ctx context.Context, txns []common.Hash) ([]evmclients.Result, error) { + results := make([]evmclients.Result, 0, len(txns)) for _, tx := range txns { if _, ok := t.receipts[tx]; !ok { - results = append(results, txmonitor.Result{Err: ethereum.NotFound}) + results = append(results, evmclients.Result{Err: ethereum.NotFound}) continue } results = append(results, t.receipts[tx]) diff --git a/x/contracts/txmonitor/eth_helper.go b/x/evmclients/eth_helper.go similarity index 64% rename from x/contracts/txmonitor/eth_helper.go rename to x/evmclients/eth_helper.go index a44ed9a12..e808c1053 100644 --- a/x/contracts/txmonitor/eth_helper.go +++ b/x/evmclients/eth_helper.go @@ -1,4 +1,4 @@ -package txmonitor +package evmclients import ( "context" @@ -165,3 +165,102 @@ func (e *evmHelper) BatchReceipts(ctx context.Context, txHashes []common.Hash) ( e.logger.Info("BatchReceipts completed successfully", "receipts", receipts) return receipts, nil } + +// FallbackEVMHelper is an EVM helper that attempts requests round-robin across multiple clients in the event of a failure. +type fallbackEVMHelper struct { + clients []*rpc.Client + logger *slog.Logger +} + +func NewFallbackEVMHelperWithLogger(clients []*rpc.Client, logger *slog.Logger) *fallbackEVMHelper { + return &fallbackEVMHelper{clients, logger} +} + +// TraceTransaction implements Debugger.TraceTransaction interface. +func (e *fallbackEVMHelper) TraceTransaction(ctx context.Context, txHash common.Hash) (*TransactionTrace, error) { + result := new(TransactionTrace) + traceOptions := map[string]interface{}{} // Empty map for default options. + var err error + + for i, client := range e.clients { + e.logger.Info("Attempting TraceTransaction", "client_index", i) + err = client.CallContext( + ctx, + result, + "debug_traceTransaction", + txHash, + traceOptions, + ) + if err == nil { + return result, nil + } + e.logger.Error("TraceTransaction attempt failed", "client_index", i, "error", err) + } + + return nil, err +} + +// BatchReceipts retrieves multiple receipts for a list of transaction hashes. +func (e *fallbackEVMHelper) BatchReceipts(ctx context.Context, txHashes []common.Hash) ([]Result, error) { + e.logger.Info("Starting BatchReceipts", "txHashes", txHashes) + batch := make([]rpc.BatchElem, len(txHashes)) + + for i, hash := range txHashes { + e.logger.Debug("Preparing batch element", "index", i, "hash", hash.Hex()) + batch[i] = rpc.BatchElem{ + Method: "eth_getTransactionReceipt", + Args: []interface{}{hash}, + Result: new(types.Receipt), + } + } + + var receipts []Result + var err error + + for i, client := range e.clients { + e.logger.Info("Attempting batch call", "client_index", i) + // Execute the batch request + err = client.BatchCallContext(context.Background(), batch) + if err != nil { + e.logger.Error("Batch call attempt failed", "client_index", i, "error", err) + } else { + e.logger.Info("Batch call attempt succeeded", "client_index", i) + break + } + } + + if err != nil { + e.logger.Error("All batch call attempts failed", "error", err) + return nil, err + } + + receipts = make([]Result, len(batch)) + for i, elem := range batch { + e.logger.Debug("Processing batch element", "index", i, "result", elem.Result, "error", elem.Error) + receipts[i].Receipt = elem.Result.(*types.Receipt) + if elem.Error != nil { + receipts[i].Err = elem.Error + } + } + + // Retry individual failed transactions + for i, receipt := range receipts { + if receipt.Err != nil { + e.logger.Info("Retrying failed transaction", "index", i, "hash", txHashes[i].Hex()) + for j, client := range e.clients { + e.logger.Info("Attempting individual call", "client_index", j, "hash", txHashes[i].Hex()) + err = client.CallContext(context.Background(), receipt.Receipt, "eth_getTransactionReceipt", txHashes[i]) + if err == nil { + e.logger.Info("Individual call succeeded", "client_index", j, "hash", txHashes[i].Hex()) + receipts[i].Err = nil + break + } + e.logger.Error("Individual call attempt failed", "client_index", j, "hash", txHashes[i].Hex(), "error", err) + time.Sleep(1 * time.Second) + } + } + } + + e.logger.Info("BatchReceipts completed successfully", "receipts", receipts) + return receipts, nil +} diff --git a/x/evmclients/ethclient.go b/x/evmclients/ethclient.go new file mode 100644 index 000000000..48d4dc208 --- /dev/null +++ b/x/evmclients/ethclient.go @@ -0,0 +1,14 @@ +package evmclients + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" +) + +type EthClient interface { + BlockNumber(ctx context.Context) (uint64, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) +} diff --git a/x/evmclients/infiniteretryclient.go b/x/evmclients/infiniteretryclient.go new file mode 100644 index 000000000..7a24686c1 --- /dev/null +++ b/x/evmclients/infiniteretryclient.go @@ -0,0 +1,73 @@ +package evmclients + +import ( + "context" + "log/slog" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/core/types" +) + +type infiniteRetryL1Client struct { + EthClient + logger *slog.Logger +} + +func NewInfiniteRetryL1Client(ethClient EthClient, logger *slog.Logger) EthClient { + return &infiniteRetryL1Client{ + EthClient: ethClient, + logger: logger, + } +} + +func (i *infiniteRetryL1Client) BlockNumber(ctx context.Context) (uint64, error) { + var blkNum uint64 + var err error + for retries := 50; retries > 0; retries-- { + blkNum, err = i.EthClient.BlockNumber(ctx) + if err == nil { + break + } + i.logger.Error("failed to get block number, retrying...", "error", err) + time.Sleep(2 * time.Second) + } + if err != nil { + return 0, err + } + return blkNum, nil +} + +func (i *infiniteRetryL1Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + var hdr *types.Header + var err error + for retries := 50; retries > 0; retries-- { + hdr, err = i.EthClient.HeaderByNumber(ctx, number) + if err == nil { + break + } + i.logger.Error("failed to get header by number, retrying...", "error", err) + time.Sleep(2 * time.Second) + } + if err != nil { + return nil, err + } + return hdr, nil +} + +func (i *infiniteRetryL1Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { + var blk *types.Block + var err error + for retries := 50; retries > 0; retries-- { + blk, err = i.EthClient.BlockByNumber(ctx, number) + if err == nil { + break + } + i.logger.Error("failed to get block by number, retrying...", "error", err) + time.Sleep(2 * time.Second) + } + if err != nil { + return nil, err + } + return blk, nil +} diff --git a/x/evmclients/laggeredclient.go b/x/evmclients/laggeredclient.go new file mode 100644 index 000000000..a1d6e610a --- /dev/null +++ b/x/evmclients/laggeredclient.go @@ -0,0 +1,26 @@ +package evmclients + +import ( + "context" +) + +type laggerdL1Client struct { + EthClient + amount int +} + +func NewLaggerdL1Client(ethClient EthClient, amount int) EthClient { + return &laggerdL1Client{ + EthClient: ethClient, + amount: amount, + } +} + +func (l *laggerdL1Client) BlockNumber(ctx context.Context) (uint64, error) { + blkNum, err := l.EthClient.BlockNumber(ctx) + if err != nil { + return 0, err + } + + return blkNum - uint64(l.amount), nil +} diff --git a/x/evmclients/winneroverrideclient.go b/x/evmclients/winneroverrideclient.go new file mode 100644 index 000000000..0845fc9a0 --- /dev/null +++ b/x/evmclients/winneroverrideclient.go @@ -0,0 +1,32 @@ +package evmclients + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" +) + +type winnerOverrideL1Client struct { + EthClient + winners []string +} + +func NewWinnerOverrideL1Client(ethClient EthClient, winners []string) *winnerOverrideL1Client { + return &winnerOverrideL1Client{ + EthClient: ethClient, + winners: winners, + } +} + +func (w *winnerOverrideL1Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + hdr, err := w.EthClient.HeaderByNumber(ctx, number) + if err != nil { + return nil, err + } + + idx := number.Int64() % int64(len(w.winners)) + hdr.Extra = []byte(w.winners[idx]) + + return hdr, nil +}