From 583231ea902e245c883259ba593bbb3aac5bc73a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 11 Mar 2024 21:18:11 -0500 Subject: [PATCH 1/2] Handle go-ethereum's blob tx already known handling --- arbnode/dataposter/data_poster.go | 3 ++- util/rpcclient/rpcclient.go | 21 ++++++++++++++++++++- util/rpcclient/rpcclient_test.go | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 1192139bb1..ed15a1549d 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -42,6 +42,7 @@ import ( "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/blobs" "github.com/offchainlabs/nitro/util/headerreader" + "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/signature" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/spf13/pflag" @@ -812,7 +813,7 @@ func (p *DataPoster) sendTx(ctx context.Context, prevTx *storage.QueuedTransacti return err } if err := p.client.SendTransaction(ctx, newTx.FullTx); err != nil { - if !strings.Contains(err.Error(), "already known") && !strings.Contains(err.Error(), "nonce too low") { + if !rpcclient.IsAlreadyKnownError(err) && !strings.Contains(err.Error(), "nonce too low") { log.Warn("DataPoster failed to send transaction", "err", err, "nonce", newTx.FullTx.Nonce(), "feeCap", newTx.FullTx.GasFeeCap(), "tipCap", newTx.FullTx.GasTipCap(), "blobFeeCap", newTx.FullTx.BlobGasFeeCap(), "gas", newTx.FullTx.Gas()) return err } diff --git a/util/rpcclient/rpcclient.go b/util/rpcclient/rpcclient.go index 275acdb283..9b5000d2fe 100644 --- a/util/rpcclient/rpcclient.go +++ b/util/rpcclient/rpcclient.go @@ -127,6 +127,25 @@ func (m limitedArgumentsMarshal) String() string { return res } +var blobTxUnderpricedRegexp = regexp.MustCompile(`replacement transaction underpriced: new tx gas fee cap (\d*) <= (\d*) queued`) + +// IsAlreadyKnownError returns true if the error appears to be an "already known" error. +// This check is based on the error's string form and is not precise. +func IsAlreadyKnownError(err error) bool { + s := err.Error() + if strings.Contains(s, "already known") { + return true + } + // go-ethereum returns "replacement transaction underpriced" instead of "already known" for blob txs. + // This is fixed in https://github.com/ethereum/go-ethereum/pull/29210 + // Once a new geth release is out with this fix, we can remove this check. + matches := blobTxUnderpricedRegexp.FindSubmatch([]byte(s)) + if len(matches) == 3 { + return string(matches[1]) == string(matches[2]) + } + return false +} + func (c *RpcClient) CallContext(ctx_in context.Context, result interface{}, method string, args ...interface{}) error { if c.client == nil { return errors.New("not connected") @@ -159,7 +178,7 @@ func (c *RpcClient) CallContext(ctx_in context.Context, result interface{}, meth cancelCtx() logger := log.Trace limit := int(c.config().ArgLogLimit) - if err != nil && err.Error() != "already known" { + if err != nil && !IsAlreadyKnownError(err) { logger = log.Info } logEntry := []interface{}{ diff --git a/util/rpcclient/rpcclient_test.go b/util/rpcclient/rpcclient_test.go index b885770f60..8613671d37 100644 --- a/util/rpcclient/rpcclient_test.go +++ b/util/rpcclient/rpcclient_test.go @@ -182,6 +182,25 @@ func TestRpcClientRetry(t *testing.T) { } } +func TestIsAlreadyKnownError(t *testing.T) { + for _, testCase := range []struct { + input string + expected bool + }{ + {"already known", true}, + {"insufficient balance", false}, + {"foo already known\nbar", true}, + {"replacement transaction underpriced: new tx gas fee cap 3824396284 \u003c= 3824396284 queued", true}, + {"replacement transaction underpriced: new tx gas fee cap 1234 \u003c= 5678 queued", false}, + {"foo replacement transaction underpriced: new tx gas fee cap 3824396284 \u003c= 3824396284 queued bar", true}, + } { + got := IsAlreadyKnownError(errors.New(testCase.input)) + if got != testCase.expected { + t.Errorf("IsAlreadyKnownError(%q) = %v expected %v", testCase.input, got, testCase.expected) + } + } +} + func Require(t *testing.T, err error, printables ...interface{}) { t.Helper() testhelpers.RequireImpl(t, err, printables...) From 86ab0c9c05e4f21c115abd1cfdf7ae7b8f4e1fcb Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 11 Mar 2024 21:27:38 -0500 Subject: [PATCH 2/2] Prefix comment with TODO: --- util/rpcclient/rpcclient.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/rpcclient/rpcclient.go b/util/rpcclient/rpcclient.go index 9b5000d2fe..02b41cf15d 100644 --- a/util/rpcclient/rpcclient.go +++ b/util/rpcclient/rpcclient.go @@ -138,7 +138,7 @@ func IsAlreadyKnownError(err error) bool { } // go-ethereum returns "replacement transaction underpriced" instead of "already known" for blob txs. // This is fixed in https://github.com/ethereum/go-ethereum/pull/29210 - // Once a new geth release is out with this fix, we can remove this check. + // TODO: Once a new geth release is out with this fix, we can remove this check. matches := blobTxUnderpricedRegexp.FindSubmatch([]byte(s)) if len(matches) == 3 { return string(matches[1]) == string(matches[2])