From 5e69e506fbf1a42bb525a090ed540c8a116baa04 Mon Sep 17 00:00:00 2001 From: Matt <98158711+BedrockSquirrel@users.noreply.github.com> Date: Fri, 31 May 2024 11:09:38 +0100 Subject: [PATCH] Host: avoid spamming stuck L1 transactions (#1941) --- go/ethadapter/geth_rpc_client.go | 14 ++++++-------- go/ethadapter/interface.go | 2 +- go/host/l1/publisher.go | 9 ++++++++- integration/ethereummock/node.go | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/go/ethadapter/geth_rpc_client.go b/go/ethadapter/geth_rpc_client.go index 17745cd903..983ab08877 100644 --- a/go/ethadapter/geth_rpc_client.go +++ b/go/ethadapter/geth_rpc_client.go @@ -246,12 +246,16 @@ func (e *gethRPCClient) FetchLastBatchSeqNo(address gethcommon.Address) (*big.In // PrepareTransactionToSend takes a txData type and overrides the From, Gas and Gas Price field with current values func (e *gethRPCClient) PrepareTransactionToSend(ctx context.Context, txData types.TxData, from gethcommon.Address) (types.TxData, error) { - return e.PrepareTransactionToRetry(ctx, txData, from, 0) + nonce, err := e.EthClient().PendingNonceAt(ctx, from) + if err != nil { + return nil, fmt.Errorf("could not get nonce - %w", err) + } + return e.PrepareTransactionToRetry(ctx, txData, from, nonce, 0) } // PrepareTransactionToRetry takes a txData type and overrides the From, Gas and Gas Price field with current values // it bumps the price by a multiplier for retries. retryNumber is zero on first attempt (no multiplier on price) -func (e *gethRPCClient) PrepareTransactionToRetry(ctx context.Context, txData types.TxData, from gethcommon.Address, retryNumber int) (types.TxData, error) { +func (e *gethRPCClient) PrepareTransactionToRetry(ctx context.Context, txData types.TxData, from gethcommon.Address, nonce uint64, retryNumber int) (types.TxData, error) { unEstimatedTx := types.NewTx(txData) gasPrice, err := e.EthClient().SuggestGasPrice(ctx) if err != nil { @@ -279,12 +283,6 @@ func (e *gethRPCClient) PrepareTransactionToRetry(ctx context.Context, txData ty return nil, fmt.Errorf("could not estimate gas - %w", err) } - // we fetch the current nonce on every retry to avoid any risk of nonce reuse/conflicts - nonce, err := e.EthClient().PendingNonceAt(ctx, from) - if err != nil { - return nil, fmt.Errorf("could not fetch nonce - %w", err) - } - return &types.LegacyTx{ Nonce: nonce, GasPrice: retryPrice, diff --git a/go/ethadapter/interface.go b/go/ethadapter/interface.go index 281f1e6abf..31a1071869 100644 --- a/go/ethadapter/interface.go +++ b/go/ethadapter/interface.go @@ -35,7 +35,7 @@ type EthClient interface { // PrepareTransactionToSend updates the tx with from address, current nonce and current estimates for the gas and the gas price PrepareTransactionToSend(ctx context.Context, txData types.TxData, from gethcommon.Address) (types.TxData, error) - PrepareTransactionToRetry(ctx context.Context, txData types.TxData, from gethcommon.Address, retries int) (types.TxData, error) + PrepareTransactionToRetry(ctx context.Context, txData types.TxData, from gethcommon.Address, nonce uint64, retries int) (types.TxData, error) FetchLastBatchSeqNo(address gethcommon.Address) (*big.Int, error) diff --git a/go/host/l1/publisher.go b/go/host/l1/publisher.go index 94d0ea8220..5971f92bda 100644 --- a/go/host/l1/publisher.go +++ b/go/host/l1/publisher.go @@ -391,12 +391,19 @@ func (p *Publisher) publishTransaction(tx types.TxData) error { retries := -1 + // we keep trying to send the transaction with this nonce until it is included in a block + // note: this is only safe because of the sendingLock guaranteeing only one transaction in-flight at a time + nonce, err := p.ethClient.Nonce(p.hostWallet.Address()) + if err != nil { + return fmt.Errorf("could not get nonce for L1 tx: %w", err) + } + // while the publisher service is still alive we keep trying to get the transaction into the L1 for !p.hostStopper.IsStopping() { retries++ // count each attempt so we can increase gas price // update the tx gas price before each attempt - tx, err := p.ethClient.PrepareTransactionToRetry(p.sendingContext, tx, p.hostWallet.Address(), retries) + tx, err := p.ethClient.PrepareTransactionToRetry(p.sendingContext, tx, p.hostWallet.Address(), nonce, retries) if err != nil { return errors.Wrap(err, "could not estimate gas/gas price for L1 tx") } diff --git a/integration/ethereummock/node.go b/integration/ethereummock/node.go index cb41bac416..669c2de5c1 100644 --- a/integration/ethereummock/node.go +++ b/integration/ethereummock/node.go @@ -99,7 +99,7 @@ func (m *Node) PrepareTransactionToSend(_ context.Context, txData types.TxData, }, nil } -func (m *Node) PrepareTransactionToRetry(ctx context.Context, txData types.TxData, from gethcommon.Address, _ int) (types.TxData, error) { +func (m *Node) PrepareTransactionToRetry(ctx context.Context, txData types.TxData, from gethcommon.Address, _ uint64, _ int) (types.TxData, error) { return m.PrepareTransactionToSend(ctx, txData, from) }