From 823916810edf8e6b8f75361beb1eda4095bf003c Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Tue, 5 Mar 2024 10:47:15 -0600 Subject: [PATCH 01/13] Add backup blob client --- util/headerreader/blob_client.go | 84 +++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/util/headerreader/blob_client.go b/util/headerreader/blob_client.go index 8989a321c7..2da379d507 100644 --- a/util/headerreader/blob_client.go +++ b/util/headerreader/blob_client.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/blobs" "github.com/offchainlabs/nitro/util/jsonapi" @@ -26,10 +27,11 @@ import ( ) type BlobClient struct { - ec arbutil.L1Interface - beaconUrl *url.URL - httpClient *http.Client - authorization string + ec arbutil.L1Interface + beaconUrl *url.URL + secondaryBeaconUrl *url.URL + httpClient *http.Client + authorization string // Filled in in Initialize() genesisTime uint64 @@ -40,19 +42,22 @@ type BlobClient struct { } type BlobClientConfig struct { - BeaconUrl string `koanf:"beacon-url"` - BlobDirectory string `koanf:"blob-directory"` - Authorization string `koanf:"authorization"` + BeaconUrl string `koanf:"beacon-url"` + SecondaryBeaconUrl string `koanf:"secondary-beacon-url"` + BlobDirectory string `koanf:"blob-directory"` + Authorization string `koanf:"authorization"` } var DefaultBlobClientConfig = BlobClientConfig{ - BeaconUrl: "", - BlobDirectory: "", - Authorization: "", + BeaconUrl: "", + SecondaryBeaconUrl: "", + BlobDirectory: "", + Authorization: "", } func BlobClientAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".beacon-url", DefaultBlobClientConfig.BeaconUrl, "Beacon Chain RPC URL to use for fetching blobs (normally on port 3500)") + f.String(prefix+".secondary-beacon-url", DefaultBlobClientConfig.SecondaryBeaconUrl, "Backup beacon Chain RPC URL to use for fetching blobs (normally on port 3500) when unable to fetch from primary") f.String(prefix+".blob-directory", DefaultBlobClientConfig.BlobDirectory, "Full path of the directory to save fetched blobs") f.String(prefix+".authorization", DefaultBlobClientConfig.Authorization, "Value to send with the HTTP Authorization: header for Beacon REST requests, must include both scheme and scheme parameters") } @@ -62,6 +67,12 @@ func NewBlobClient(config BlobClientConfig, ec arbutil.L1Interface) (*BlobClient if err != nil { return nil, fmt.Errorf("failed to parse beacon chain URL: %w", err) } + var secondaryBeaconUrl *url.URL + if config.SecondaryBeaconUrl != "" { + if secondaryBeaconUrl, err = url.Parse(config.BeaconUrl); err != nil { + return nil, fmt.Errorf("failed to parse secondary beacon chain URL: %w", err) + } + } if config.BlobDirectory != "" { if _, err = os.Stat(config.BlobDirectory); err != nil { if os.IsNotExist(err) { @@ -74,11 +85,12 @@ func NewBlobClient(config BlobClientConfig, ec arbutil.L1Interface) (*BlobClient } } return &BlobClient{ - ec: ec, - beaconUrl: beaconUrl, - authorization: config.Authorization, - httpClient: &http.Client{}, - blobDirectory: config.BlobDirectory, + ec: ec, + beaconUrl: beaconUrl, + secondaryBeaconUrl: secondaryBeaconUrl, + authorization: config.Authorization, + httpClient: &http.Client{}, + blobDirectory: config.BlobDirectory, }, nil } @@ -91,22 +103,36 @@ func beaconRequest[T interface{}](b *BlobClient, ctx context.Context, beaconPath var empty T - // not really a deep copy, but copies the Path part we care about - url := *b.beaconUrl - url.Path = path.Join(url.Path, beaconPath) - - req, err := http.NewRequestWithContext(ctx, "GET", url.String(), http.NoBody) - if err != nil { - return empty, err - } - - if b.authorization != "" { - req.Header.Set("Authorization", b.authorization) + fetchData := func(url url.URL) (*http.Response, error) { + url.Path = path.Join(url.Path, beaconPath) + req, err := http.NewRequestWithContext(ctx, "GET", url.String(), http.NoBody) + if err != nil { + return nil, err + } + if b.authorization != "" { + req.Header.Set("Authorization", b.authorization) + } + resp, err := b.httpClient.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("response returned with status code %d, want 200 (OK)", resp.StatusCode) + } + return resp, nil } - resp, err := b.httpClient.Do(req) - if err != nil { - return empty, err + var resp *http.Response + var err error + if resp, err = fetchData(*b.beaconUrl); err != nil { + if b.secondaryBeaconUrl != nil { + log.Info("error fetching blob data from primary beacon URL, switching to secondary beacon URL", "err", err) + if resp, err = fetchData(*b.secondaryBeaconUrl); err != nil { + return empty, fmt.Errorf("error fetching blob data from secondary beacon URL: %w", err) + } + } else { + return empty, err + } } defer resp.Body.Close() From 966bcdb437b6ca56e3b1167f37b196a254b02c5d Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Mon, 18 Mar 2024 19:30:07 +0100 Subject: [PATCH 02/13] refactor: pass latestHeader into feeAndTipCaps fn --- arbnode/dataposter/data_poster.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index c26e7ab3b6..ba0434d335 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -469,13 +469,10 @@ func (p *DataPoster) evalMaxFeeCapExpr(backlogOfBatches uint64, elapsed time.Dur var big4 = big.NewInt(4) // The dataPosterBacklog argument should *not* include extraBacklog (it's added in in this function) -func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit uint64, numBlobs uint64, lastTx *types.Transaction, dataCreatedAt time.Time, dataPosterBacklog uint64) (*big.Int, *big.Int, *big.Int, error) { +func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit uint64, numBlobs uint64, lastTx *types.Transaction, dataCreatedAt time.Time, dataPosterBacklog uint64, latestHeader *types.Header) (*big.Int, *big.Int, *big.Int, error) { config := p.config() dataPosterBacklog += p.extraBacklog() - latestHeader, err := p.headerReader.LastHeader(ctx) - if err != nil { - return nil, nil, nil, err - } + if latestHeader.BaseFee == nil { return nil, nil, nil, fmt.Errorf("latest parent chain block %v missing BaseFee (either the parent chain does not have EIP-1559 or the parent chain node is not synced)", latestHeader.Number) } @@ -683,7 +680,12 @@ func (p *DataPoster) PostTransaction(ctx context.Context, dataCreatedAt time.Tim return nil, fmt.Errorf("failed to update data poster balance: %w", err) } - feeCap, tipCap, blobFeeCap, err := p.feeAndTipCaps(ctx, nonce, gasLimit, uint64(len(kzgBlobs)), nil, dataCreatedAt, 0) + latestHeader, err := p.headerReader.LastHeader(ctx) + if err != nil { + return nil, err + } + + feeCap, tipCap, blobFeeCap, err := p.feeAndTipCaps(ctx, nonce, gasLimit, uint64(len(kzgBlobs)), nil, dataCreatedAt, 0, latestHeader) if err != nil { return nil, err } @@ -868,7 +870,12 @@ func updateGasCaps(tx *types.Transaction, newFeeCap, newTipCap, newBlobFeeCap *b // The mutex must be held by the caller. func (p *DataPoster) replaceTx(ctx context.Context, prevTx *storage.QueuedTransaction, backlogWeight uint64) error { - newFeeCap, newTipCap, newBlobFeeCap, err := p.feeAndTipCaps(ctx, prevTx.FullTx.Nonce(), prevTx.FullTx.Gas(), uint64(len(prevTx.FullTx.BlobHashes())), prevTx.FullTx, prevTx.Created, backlogWeight) + latestHeader, err := p.headerReader.LastHeader(ctx) + if err != nil { + return err + } + + newFeeCap, newTipCap, newBlobFeeCap, err := p.feeAndTipCaps(ctx, prevTx.FullTx.Nonce(), prevTx.FullTx.Gas(), uint64(len(prevTx.FullTx.BlobHashes())), prevTx.FullTx, prevTx.Created, backlogWeight, latestHeader) if err != nil { return err } From 096b437472eea606cf32a4df27ec7365edc2c542 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Mon, 18 Mar 2024 22:21:19 +0100 Subject: [PATCH 03/13] Scaffolding for calling feeAndTipCaps --- arbnode/dataposter/dataposter_test.go | 95 +++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 06e3144ed1..302d5cd52f 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -9,9 +9,13 @@ import ( "time" "github.com/Knetic/govaluate" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" "github.com/google/go-cmp/cmp" "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbnode/dataposter/externalsigner" @@ -187,3 +191,94 @@ func TestMaxFeeCapFormulaCalculation(t *testing.T) { t.Fatalf("Unexpected result. Got: %d, want: >0", result) } } + +type stubL1Client struct { + nonce uint64 + gasTipCap *big.Int + + // Define most of the required methods that aren't used by feeAndTipCaps + backends.SimulatedBackend +} + +func (c *stubL1Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + return c.nonce, nil +} + +func (c *stubL1Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return c.gasTipCap, nil +} + +// Not used but we need to define +func (c *stubL1Client) BlockNumber(ctx context.Context) (uint64, error) { + return 0, nil +} + +func (c *stubL1Client) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) { + return []byte{}, nil +} + +func (c *stubL1Client) ChainID(ctx context.Context) (*big.Int, error) { + return nil, nil +} + +func (c *stubL1Client) Client() rpc.ClientInterface { + return nil +} + +func (c *stubL1Client) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) { + return common.Address{}, nil +} + +func TestFeeAndTipCaps(t *testing.T) { + conf := func() *DataPosterConfig { + return &DataPosterConfig{ + MaxMempoolTransactions: 0, + MaxMempoolWeight: 0, + MinTipCapGwei: 0, + MinBlobTxTipCapGwei: 0, + MaxTipCapGwei: 0, + MaxBlobTxTipCapGwei: 0, + MaxFeeBidMultipleBips: 0, + AllocateMempoolBalance: false, + } + } + expression, err := govaluate.NewEvaluableExpression(DefaultDataPosterConfig.MaxFeeCapFormula) + if err != nil { + t.Fatalf("error creating govaluate evaluable expression: %v", err) + } + + p := DataPoster{ + config: conf, + extraBacklog: func() uint64 { return 0 }, + balance: big.NewInt(1_000_000_000_000_000_000), + usingNoOpStorage: false, + client: &stubL1Client{ + nonce: 1, + gasTipCap: big.NewInt(2_000_000_000), + }, + auth: &bind.TransactOpts{ + From: common.Address{}, + }, + maxFeeCapExpression: expression, + } + + ctx := context.Background() + var nonce uint64 = 1 + var gasLimit uint64 = 30_000_000 + var numBlobs uint64 = 0 + var lastTx *types.Transaction + dataCreatedAt := time.Now() + var dataPosterBacklog uint64 = 0 + var blobGasUsed uint64 = 100 + var excessBlobGas uint64 = 100 + latestHeader := types.Header{ + Number: big.NewInt(1), + BaseFee: big.NewInt(1_000_000_000), + BlobGasUsed: &blobGasUsed, + ExcessBlobGas: &excessBlobGas, + } + + newGasFeeCap, newTipCap, newBlobFeeCap, err := p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, dataCreatedAt, dataPosterBacklog, &latestHeader) + _, _, _, _ = newGasFeeCap, newTipCap, newBlobFeeCap, err + +} From 5ddfaeb090c21740273f02b6a142c9bbae823f1a Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Thu, 21 Mar 2024 16:02:51 +0100 Subject: [PATCH 04/13] No backlog/replace, enough bal blob tx price test --- arbnode/dataposter/dataposter_test.go | 83 ++++++++++++++++++++------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 302d5cd52f..d5c8fc3fae 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -20,6 +20,7 @@ import ( "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbnode/dataposter/externalsigner" "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" + "github.com/offchainlabs/nitro/util/arbmath" ) func TestParseReplacementTimes(t *testing.T) { @@ -193,19 +194,19 @@ func TestMaxFeeCapFormulaCalculation(t *testing.T) { } type stubL1Client struct { - nonce uint64 - gasTipCap *big.Int + senderNonce uint64 + suggestedGasTipCap *big.Int // Define most of the required methods that aren't used by feeAndTipCaps backends.SimulatedBackend } func (c *stubL1Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { - return c.nonce, nil + return c.senderNonce, nil } func (c *stubL1Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return c.gasTipCap, nil + return c.suggestedGasTipCap, nil } // Not used but we need to define @@ -231,15 +232,22 @@ func (c *stubL1Client) TransactionSender(ctx context.Context, tx *types.Transact func TestFeeAndTipCaps(t *testing.T) { conf := func() *DataPosterConfig { + // Set only the fields that are used by feeAndTipCaps + // Start with defaults, maybe change for test. return &DataPosterConfig{ - MaxMempoolTransactions: 0, - MaxMempoolWeight: 0, - MinTipCapGwei: 0, - MinBlobTxTipCapGwei: 0, - MaxTipCapGwei: 0, - MaxBlobTxTipCapGwei: 0, - MaxFeeBidMultipleBips: 0, - AllocateMempoolBalance: false, + MaxMempoolTransactions: 18, + MaxMempoolWeight: 18, + MinTipCapGwei: 0.05, + MinBlobTxTipCapGwei: 1, + MaxTipCapGwei: 5, + MaxBlobTxTipCapGwei: 10, + MaxFeeBidMultipleBips: arbmath.OneInBips * 10, + AllocateMempoolBalance: true, + + UrgencyGwei: 2., + ElapsedTimeBase: 10 * time.Minute, + ElapsedTimeImportance: 10, + TargetPriceGwei: 60., } } expression, err := govaluate.NewEvaluableExpression(DefaultDataPosterConfig.MaxFeeCapFormula) @@ -250,11 +258,11 @@ func TestFeeAndTipCaps(t *testing.T) { p := DataPoster{ config: conf, extraBacklog: func() uint64 { return 0 }, - balance: big.NewInt(1_000_000_000_000_000_000), + balance: big.NewInt(0).Mul(big.NewInt(1_000_000_000_000_000_000), big.NewInt(10)), usingNoOpStorage: false, client: &stubL1Client{ - nonce: 1, - gasTipCap: big.NewInt(2_000_000_000), + senderNonce: 1, + suggestedGasTipCap: big.NewInt(2_000_000_000), }, auth: &bind.TransactOpts{ From: common.Address{}, @@ -264,13 +272,13 @@ func TestFeeAndTipCaps(t *testing.T) { ctx := context.Background() var nonce uint64 = 1 - var gasLimit uint64 = 30_000_000 - var numBlobs uint64 = 0 - var lastTx *types.Transaction + var gasLimit uint64 = 300_000 // reasonable upper bound for mainnet batches + var numBlobs uint64 = 6 + var lastTx *types.Transaction // PostTransaction leaves this nil, used when replacing dataCreatedAt := time.Now() - var dataPosterBacklog uint64 = 0 - var blobGasUsed uint64 = 100 - var excessBlobGas uint64 = 100 + var dataPosterBacklog uint64 = 0 // Zero backlog for PostTransaction + var blobGasUsed uint64 = 0xc0000 // 6 blobs of gas + var excessBlobGas uint64 = 0 // typical current mainnet conditions latestHeader := types.Header{ Number: big.NewInt(1), BaseFee: big.NewInt(1_000_000_000), @@ -279,6 +287,37 @@ func TestFeeAndTipCaps(t *testing.T) { } newGasFeeCap, newTipCap, newBlobFeeCap, err := p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, dataCreatedAt, dataPosterBacklog, &latestHeader) - _, _, _, _ = newGasFeeCap, newTipCap, newBlobFeeCap, err + t.Log("newGasFeeCap", newGasFeeCap, "newTipCap", newTipCap, "newBlobFeeCap", newBlobFeeCap, "err", err) + if err != nil { + t.Fatalf("%s", err) + } + + // There is no backlog and almost no time elapses since the batch data was + // created to when it was posted so the maxNormalizedFeeCap is ~60.01 gwei. + // This is multiplied with the normalizedGas to get targetMaxCost. + // This is greatly in excess of currentTotalCost * MaxFeeBidMultipleBips, + // so targetMaxCost is reduced to the current base fee + suggested tip cap + + // current blob fee multipled by MaxFeeBidMultipleBips (factor of 10). + // The blob and non blob factors are then proportionally split out and so + // the newGasFeeCap is set to (current base fee + suggested tip cap) * 10 + // and newBlobFeeCap is set to current blob gas base fee (1 wei + // since there is no excess blob gas) * 10. + expectedGasFeeCap := big.NewInt(30 * params.GWei) + expectedBlobFeeCap := big.NewInt(10) + if !arbmath.BigEquals(expectedGasFeeCap, newGasFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected gas fee cap. Was: %d, expected: %d", expectedGasFeeCap, newGasFeeCap) + } + if !arbmath.BigEquals(expectedBlobFeeCap, newBlobFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected blob gas fee cap. Was: %d, expected: %d", expectedBlobFeeCap, newBlobFeeCap) + } + + // 2 gwei is the amount suggested by the L1 client, so that is the value + // returned because it doesn't exceed the configured bounds, there is no + // lastTx to scale against with rbf, and it is not bigger than the computed + // gasFeeCap. + expectedTipCap := big.NewInt(2 * params.GWei) + if !arbmath.BigEquals(expectedTipCap, newTipCap) { + t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected: %d", expectedTipCap, newTipCap) + } } From 0d2e89ba9e17215a0849950bfb8b399b1e834121 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Thu, 21 Mar 2024 17:20:31 +0100 Subject: [PATCH 05/13] Test replacing tx pricing --- arbnode/dataposter/dataposter_test.go | 37 +++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index d5c8fc3fae..0bf0ef6ebf 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -230,7 +230,7 @@ func (c *stubL1Client) TransactionSender(ctx context.Context, tx *types.Transact return common.Address{}, nil } -func TestFeeAndTipCaps(t *testing.T) { +func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) { conf := func() *DataPosterConfig { // Set only the fields that are used by feeAndTipCaps // Start with defaults, maybe change for test. @@ -258,11 +258,11 @@ func TestFeeAndTipCaps(t *testing.T) { p := DataPoster{ config: conf, extraBacklog: func() uint64 { return 0 }, - balance: big.NewInt(0).Mul(big.NewInt(1_000_000_000_000_000_000), big.NewInt(10)), + balance: big.NewInt(0).Mul(big.NewInt(params.Ether), big.NewInt(10)), usingNoOpStorage: false, client: &stubL1Client{ senderNonce: 1, - suggestedGasTipCap: big.NewInt(2_000_000_000), + suggestedGasTipCap: big.NewInt(2 * params.GWei), }, auth: &bind.TransactOpts{ From: common.Address{}, @@ -272,7 +272,7 @@ func TestFeeAndTipCaps(t *testing.T) { ctx := context.Background() var nonce uint64 = 1 - var gasLimit uint64 = 300_000 // reasonable upper bound for mainnet batches + var gasLimit uint64 = 300_000 // reasonable upper bound for mainnet blob batches var numBlobs uint64 = 6 var lastTx *types.Transaction // PostTransaction leaves this nil, used when replacing dataCreatedAt := time.Now() @@ -287,7 +287,6 @@ func TestFeeAndTipCaps(t *testing.T) { } newGasFeeCap, newTipCap, newBlobFeeCap, err := p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, dataCreatedAt, dataPosterBacklog, &latestHeader) - t.Log("newGasFeeCap", newGasFeeCap, "newTipCap", newTipCap, "newBlobFeeCap", newBlobFeeCap, "err", err) if err != nil { t.Fatalf("%s", err) } @@ -320,4 +319,32 @@ func TestFeeAndTipCaps(t *testing.T) { t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected: %d", expectedTipCap, newTipCap) } + lastBlobTx := &types.BlobTx{} + updateTxDataGasCaps(lastBlobTx, newGasFeeCap, newTipCap, newBlobFeeCap) + lastTx = types.NewTx(lastBlobTx) + // Make creation time go backwards so elapsed time increases + retconnedCreationTime := dataCreatedAt.Add(-time.Minute) + + newGasFeeCap, newTipCap, newBlobFeeCap, err = p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, retconnedCreationTime, dataPosterBacklog, &latestHeader) + + // I think we expect an increase by *2 due to rbf rules for blob txs, + // currently appears to be broken since the increase exceeds the + // current cost (based on current basefees and tip) * config.MaxFeeBidMultipleBips + // since the previous attempt to send the tx was already using the current cost scaled by + // the multiple (* 10 bips). + expectedGasFeeCap = expectedGasFeeCap.Mul(expectedGasFeeCap, big.NewInt(2)) + expectedBlobFeeCap = expectedBlobFeeCap.Mul(expectedBlobFeeCap, big.NewInt(2)) + expectedTipCap = expectedTipCap.Mul(expectedTipCap, big.NewInt(2)) + + t.Log("newGasFeeCap", newGasFeeCap, "newTipCap", newTipCap, "newBlobFeeCap", newBlobFeeCap, "err", err) + if !arbmath.BigEquals(expectedGasFeeCap, newGasFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected gas fee cap. Was: %d, expected: %d", expectedGasFeeCap, newGasFeeCap) + } + if !arbmath.BigEquals(expectedBlobFeeCap, newBlobFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected blob gas fee cap. Was: %d, expected: %d", expectedBlobFeeCap, newBlobFeeCap) + } + if !arbmath.BigEquals(expectedTipCap, newTipCap) { + t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected: %d", expectedTipCap, newTipCap) + } + } From 4d144c756298a5b47106cedce62382a3f7fb418d Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Fri, 22 Mar 2024 14:33:08 +0100 Subject: [PATCH 06/13] Test RBF with diverging blob/non blob basefees --- arbnode/dataposter/dataposter_test.go | 126 ++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 0bf0ef6ebf..7c8b521b0e 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -324,6 +324,13 @@ func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) lastTx = types.NewTx(lastBlobTx) // Make creation time go backwards so elapsed time increases retconnedCreationTime := dataCreatedAt.Add(-time.Minute) + // Base fee needs to have increased to simulate conditions to not include prev tx + latestHeader = types.Header{ + Number: big.NewInt(2), + BaseFee: big.NewInt(32_000_000_000), + BlobGasUsed: &blobGasUsed, + ExcessBlobGas: &excessBlobGas, + } newGasFeeCap, newTipCap, newBlobFeeCap, err = p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, retconnedCreationTime, dataPosterBacklog, &latestHeader) @@ -348,3 +355,122 @@ func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) } } + +func TestFeeAndTipCaps_RBF_RisingBlobFee_FallingBaseFee(t *testing.T) { + conf := func() *DataPosterConfig { + // Set only the fields that are used by feeAndTipCaps + // Start with defaults, maybe change for test. + return &DataPosterConfig{ + MaxMempoolTransactions: 18, + MaxMempoolWeight: 18, + MinTipCapGwei: 0.05, + MinBlobTxTipCapGwei: 1, + MaxTipCapGwei: 5, + MaxBlobTxTipCapGwei: 10, + MaxFeeBidMultipleBips: arbmath.OneInBips * 10, + AllocateMempoolBalance: true, + + UrgencyGwei: 2., + ElapsedTimeBase: 10 * time.Minute, + ElapsedTimeImportance: 10, + TargetPriceGwei: 60., + } + } + expression, err := govaluate.NewEvaluableExpression(DefaultDataPosterConfig.MaxFeeCapFormula) + if err != nil { + t.Fatalf("error creating govaluate evaluable expression: %v", err) + } + + p := DataPoster{ + config: conf, + extraBacklog: func() uint64 { return 0 }, + balance: big.NewInt(0).Mul(big.NewInt(params.Ether), big.NewInt(10)), + usingNoOpStorage: false, + client: &stubL1Client{ + senderNonce: 1, + suggestedGasTipCap: big.NewInt(2 * params.GWei), + }, + auth: &bind.TransactOpts{ + From: common.Address{}, + }, + maxFeeCapExpression: expression, + } + + ctx := context.Background() + var nonce uint64 = 1 + var gasLimit uint64 = 300_000 // reasonable upper bound for mainnet blob batches + var numBlobs uint64 = 6 + var lastTx *types.Transaction // PostTransaction leaves this nil, used when replacing + dataCreatedAt := time.Now() + var dataPosterBacklog uint64 = 0 // Zero backlog for PostTransaction + var blobGasUsed uint64 = 0xc0000 // 6 blobs of gas + var excessBlobGas uint64 = 0 // typical current mainnet conditions + latestHeader := types.Header{ + Number: big.NewInt(1), + BaseFee: big.NewInt(1_000_000_000), + BlobGasUsed: &blobGasUsed, + ExcessBlobGas: &excessBlobGas, + } + + newGasFeeCap, newTipCap, newBlobFeeCap, err := p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, dataCreatedAt, dataPosterBacklog, &latestHeader) + if err != nil { + t.Fatalf("%s", err) + } + + // There is no backlog and almost no time elapses since the batch data was + // created to when it was posted so the maxNormalizedFeeCap is ~60.01 gwei. + // This is multiplied with the normalizedGas to get targetMaxCost. + // This is greatly in excess of currentTotalCost * MaxFeeBidMultipleBips, + // so targetMaxCost is reduced to the current base fee + suggested tip cap + + // current blob fee multipled by MaxFeeBidMultipleBips (factor of 10). + // The blob and non blob factors are then proportionally split out and so + // the newGasFeeCap is set to (current base fee + suggested tip cap) * 10 + // and newBlobFeeCap is set to current blob gas base fee (1 wei + // since there is no excess blob gas) * 10. + expectedGasFeeCap := big.NewInt(30 * params.GWei) + expectedBlobFeeCap := big.NewInt(10) + if !arbmath.BigEquals(expectedGasFeeCap, newGasFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected gas fee cap. Was: %d, expected: %d", expectedGasFeeCap, newGasFeeCap) + } + if !arbmath.BigEquals(expectedBlobFeeCap, newBlobFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected blob gas fee cap. Was: %d, expected: %d", expectedBlobFeeCap, newBlobFeeCap) + } + + // 2 gwei is the amount suggested by the L1 client, so that is the value + // returned because it doesn't exceed the configured bounds, there is no + // lastTx to scale against with rbf, and it is not bigger than the computed + // gasFeeCap. + expectedTipCap := big.NewInt(2 * params.GWei) + if !arbmath.BigEquals(expectedTipCap, newTipCap) { + t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected: %d", expectedTipCap, newTipCap) + } + + lastBlobTx := &types.BlobTx{} + updateTxDataGasCaps(lastBlobTx, newGasFeeCap, newTipCap, newBlobFeeCap) + lastTx = types.NewTx(lastBlobTx) + // Make creation time go backwards so elapsed time increases + retconnedCreationTime := dataCreatedAt.Add(-time.Minute) + // Base fee has decreased but blob fee has increased + blobGasUsed = 0xc0000 // 6 blobs of gas + excessBlobGas = 8295804 // this should set blob fee to 12 wei + latestHeader = types.Header{ + Number: big.NewInt(2), + BaseFee: big.NewInt(100_000_000), + BlobGasUsed: &blobGasUsed, + ExcessBlobGas: &excessBlobGas, + } + + newGasFeeCap, newTipCap, newBlobFeeCap, err = p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, retconnedCreationTime, dataPosterBacklog, &latestHeader) + + t.Log("newGasFeeCap", newGasFeeCap, "newTipCap", newTipCap, "newBlobFeeCap", newBlobFeeCap, "err", err) + if arbmath.BigEquals(expectedGasFeeCap, newGasFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected gas fee cap. Was: %d, expected NOT: %d", expectedGasFeeCap, newGasFeeCap) + } + if arbmath.BigEquals(expectedBlobFeeCap, newBlobFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected blob gas fee cap. Was: %d, expected NOT: %d", expectedBlobFeeCap, newBlobFeeCap) + } + if arbmath.BigEquals(expectedTipCap, newTipCap) { + t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected NOT: %d", expectedTipCap, newTipCap) + } + +} From df0dd50e52d7c4196553d070c5dda67467fb6dac Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Mar 2024 09:23:50 -0500 Subject: [PATCH 07/13] Fix MaxFeeBidMultipleBips to respect rbf minimums --- arbnode/dataposter/data_poster.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index c26e7ab3b6..11e810f7f2 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -583,13 +583,8 @@ func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit u blobGasUsed := params.BlobTxBlobGasPerBlob * numBlobs currentBlobCost := arbmath.BigMulByUint(currentBlobFee, blobGasUsed) currentNonBlobCost := arbmath.BigMulByUint(currentNonBlobFee, gasLimit) - currentTotalCost := arbmath.BigAdd(currentBlobCost, currentNonBlobCost) - if config.MaxFeeBidMultipleBips > 0 { - targetMaxCost = arbmath.BigMin(targetMaxCost, arbmath.BigMulByBips(currentTotalCost, config.MaxFeeBidMultipleBips)) - } - newBlobFeeCap := arbmath.BigMul(targetMaxCost, currentBlobFee) - newBlobFeeCap.Div(newBlobFeeCap, currentTotalCost) + newBlobFeeCap.Div(newBlobFeeCap, arbmath.BigAdd(currentBlobCost, currentNonBlobCost)) if lastTx != nil && lastTx.BlobGasFeeCap() != nil { newBlobFeeCap = arbmath.BigMax(newBlobFeeCap, arbmath.BigMulByBips(lastTx.BlobGasFeeCap(), minRbfIncrease)) } @@ -606,6 +601,18 @@ func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit u newBlobFeeCap = arbmath.BigDivByUint(newBlobCost, blobGasUsed) } + // Limit the fee caps to be no greater than max(MaxFeeBidMultipleBips, minRbf) + maxNonBlobFee := arbmath.BigMulByBips(currentNonBlobFee, config.MaxFeeBidMultipleBips) + if lastTx != nil { + maxNonBlobFee = arbmath.BigMax(maxNonBlobFee, arbmath.BigMulByBips(lastTx.GasFeeCap(), minRbfIncrease)) + } + maxBlobFee := arbmath.BigMulByBips(currentBlobFee, config.MaxFeeBidMultipleBips) + if lastTx != nil && lastTx.BlobGasFeeCap() != nil { + maxBlobFee = arbmath.BigMax(maxBlobFee, arbmath.BigMulByBips(lastTx.BlobGasFeeCap(), minRbfIncrease)) + } + newBaseFeeCap = arbmath.BigMin(newBaseFeeCap, maxNonBlobFee) + newBlobFeeCap = arbmath.BigMin(newBlobFeeCap, maxBlobFee) + if arbmath.BigGreaterThan(newTipCap, newBaseFeeCap) { log.Info( "reducing new tip cap to new basefee cap", From c38a0eea44be9deecdb1bfcab8b5b989da4fada7 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Mar 2024 09:33:24 -0500 Subject: [PATCH 08/13] Fix MaxFeeBidMultipleBips == 0 --- arbnode/dataposter/data_poster.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 11e810f7f2..298cfd548a 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -601,17 +601,19 @@ func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit u newBlobFeeCap = arbmath.BigDivByUint(newBlobCost, blobGasUsed) } - // Limit the fee caps to be no greater than max(MaxFeeBidMultipleBips, minRbf) - maxNonBlobFee := arbmath.BigMulByBips(currentNonBlobFee, config.MaxFeeBidMultipleBips) - if lastTx != nil { - maxNonBlobFee = arbmath.BigMax(maxNonBlobFee, arbmath.BigMulByBips(lastTx.GasFeeCap(), minRbfIncrease)) - } - maxBlobFee := arbmath.BigMulByBips(currentBlobFee, config.MaxFeeBidMultipleBips) - if lastTx != nil && lastTx.BlobGasFeeCap() != nil { - maxBlobFee = arbmath.BigMax(maxBlobFee, arbmath.BigMulByBips(lastTx.BlobGasFeeCap(), minRbfIncrease)) + if config.MaxFeeBidMultipleBips > 0 { + // Limit the fee caps to be no greater than max(MaxFeeBidMultipleBips, minRbf) + maxNonBlobFee := arbmath.BigMulByBips(currentNonBlobFee, config.MaxFeeBidMultipleBips) + if lastTx != nil { + maxNonBlobFee = arbmath.BigMax(maxNonBlobFee, arbmath.BigMulByBips(lastTx.GasFeeCap(), minRbfIncrease)) + } + maxBlobFee := arbmath.BigMulByBips(currentBlobFee, config.MaxFeeBidMultipleBips) + if lastTx != nil && lastTx.BlobGasFeeCap() != nil { + maxBlobFee = arbmath.BigMax(maxBlobFee, arbmath.BigMulByBips(lastTx.BlobGasFeeCap(), minRbfIncrease)) + } + newBaseFeeCap = arbmath.BigMin(newBaseFeeCap, maxNonBlobFee) + newBlobFeeCap = arbmath.BigMin(newBlobFeeCap, maxBlobFee) } - newBaseFeeCap = arbmath.BigMin(newBaseFeeCap, maxNonBlobFee) - newBlobFeeCap = arbmath.BigMin(newBlobFeeCap, maxBlobFee) if arbmath.BigGreaterThan(newTipCap, newBaseFeeCap) { log.Info( From 6c042f04ded87452492dacb198772f23310fb70d Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Fri, 22 Mar 2024 16:23:58 +0100 Subject: [PATCH 09/13] Temp disable rbf part of 1st TestFeeAndTipCaps test --- arbnode/dataposter/dataposter_test.go | 40 ++++++++++++++------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 7c8b521b0e..13566e3c34 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -334,25 +334,27 @@ func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) newGasFeeCap, newTipCap, newBlobFeeCap, err = p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, retconnedCreationTime, dataPosterBacklog, &latestHeader) - // I think we expect an increase by *2 due to rbf rules for blob txs, - // currently appears to be broken since the increase exceeds the - // current cost (based on current basefees and tip) * config.MaxFeeBidMultipleBips - // since the previous attempt to send the tx was already using the current cost scaled by - // the multiple (* 10 bips). - expectedGasFeeCap = expectedGasFeeCap.Mul(expectedGasFeeCap, big.NewInt(2)) - expectedBlobFeeCap = expectedBlobFeeCap.Mul(expectedBlobFeeCap, big.NewInt(2)) - expectedTipCap = expectedTipCap.Mul(expectedTipCap, big.NewInt(2)) - - t.Log("newGasFeeCap", newGasFeeCap, "newTipCap", newTipCap, "newBlobFeeCap", newBlobFeeCap, "err", err) - if !arbmath.BigEquals(expectedGasFeeCap, newGasFeeCap) { - t.Fatalf("feeAndTipCaps didn't return expected gas fee cap. Was: %d, expected: %d", expectedGasFeeCap, newGasFeeCap) - } - if !arbmath.BigEquals(expectedBlobFeeCap, newBlobFeeCap) { - t.Fatalf("feeAndTipCaps didn't return expected blob gas fee cap. Was: %d, expected: %d", expectedBlobFeeCap, newBlobFeeCap) - } - if !arbmath.BigEquals(expectedTipCap, newTipCap) { - t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected: %d", expectedTipCap, newTipCap) - } + /* + // I think we expect an increase by *2 due to rbf rules for blob txs, + // currently appears to be broken since the increase exceeds the + // current cost (based on current basefees and tip) * config.MaxFeeBidMultipleBips + // since the previous attempt to send the tx was already using the current cost scaled by + // the multiple (* 10 bips). + expectedGasFeeCap = expectedGasFeeCap.Mul(expectedGasFeeCap, big.NewInt(2)) + expectedBlobFeeCap = expectedBlobFeeCap.Mul(expectedBlobFeeCap, big.NewInt(2)) + expectedTipCap = expectedTipCap.Mul(expectedTipCap, big.NewInt(2)) + + t.Log("newGasFeeCap", newGasFeeCap, "newTipCap", newTipCap, "newBlobFeeCap", newBlobFeeCap, "err", err) + if !arbmath.BigEquals(expectedGasFeeCap, newGasFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected gas fee cap. Was: %d, expected: %d", expectedGasFeeCap, newGasFeeCap) + } + if !arbmath.BigEquals(expectedBlobFeeCap, newBlobFeeCap) { + t.Fatalf("feeAndTipCaps didn't return expected blob gas fee cap. Was: %d, expected: %d", expectedBlobFeeCap, newBlobFeeCap) + } + if !arbmath.BigEquals(expectedTipCap, newTipCap) { + t.Fatalf("feeAndTipCaps didn't return expected tip cap. Was: %d, expected: %d", expectedTipCap, newTipCap) + } + */ } From 6bd27214527d94ce81948ba4d699748b40124182 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 22 Mar 2024 11:56:54 -0500 Subject: [PATCH 10/13] Add backtrace to help debug flaky relay test --- broadcaster/broadcaster.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/broadcaster/broadcaster.go b/broadcaster/broadcaster.go index ed3088ca2e..242b8f9eeb 100644 --- a/broadcaster/broadcaster.go +++ b/broadcaster/broadcaster.go @@ -7,6 +7,7 @@ import ( "context" "errors" "net" + "runtime/debug" "github.com/gobwas/ws" @@ -60,7 +61,7 @@ func (b *Broadcaster) NewBroadcastFeedMessage(message arbostypes.MessageWithMeta func (b *Broadcaster) BroadcastSingle(msg arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) (err error) { defer func() { if r := recover(); r != nil { - log.Error("recovered error in BroadcastSingle", "recover", r) + log.Error("recovered error in BroadcastSingle", "recover", r, "backtrace", string(debug.Stack())) err = errors.New("panic in BroadcastSingle") } }() @@ -84,7 +85,7 @@ func (b *Broadcaster) BroadcastSingleFeedMessage(bfm *m.BroadcastFeedMessage) { func (b *Broadcaster) BroadcastMessages(messages []arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) (err error) { defer func() { if r := recover(); r != nil { - log.Error("recovered error in BroadcastMessages", "recover", r) + log.Error("recovered error in BroadcastMessages", "recover", r, "backtrace", string(debug.Stack())) err = errors.New("panic in BroadcastMessages") } }() From f3e74d24864322dd7b0096fbde834e5babd6d9a0 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Mon, 25 Mar 2024 14:34:20 +0100 Subject: [PATCH 11/13] Fix lint errors --- arbnode/dataposter/dataposter_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 13566e3c34..9bd54141cd 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -320,7 +320,10 @@ func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) } lastBlobTx := &types.BlobTx{} - updateTxDataGasCaps(lastBlobTx, newGasFeeCap, newTipCap, newBlobFeeCap) + err = updateTxDataGasCaps(lastBlobTx, newGasFeeCap, newTipCap, newBlobFeeCap) + if err != nil { + t.Fatal(err) + } lastTx = types.NewTx(lastBlobTx) // Make creation time go backwards so elapsed time increases retconnedCreationTime := dataCreatedAt.Add(-time.Minute) @@ -333,7 +336,7 @@ func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) } newGasFeeCap, newTipCap, newBlobFeeCap, err = p.feeAndTipCaps(ctx, nonce, gasLimit, numBlobs, lastTx, retconnedCreationTime, dataPosterBacklog, &latestHeader) - + _, _, _, _ = newGasFeeCap, newTipCap, newBlobFeeCap, err /* // I think we expect an increase by *2 due to rbf rules for blob txs, // currently appears to be broken since the increase exceeds the @@ -448,7 +451,10 @@ func TestFeeAndTipCaps_RBF_RisingBlobFee_FallingBaseFee(t *testing.T) { } lastBlobTx := &types.BlobTx{} - updateTxDataGasCaps(lastBlobTx, newGasFeeCap, newTipCap, newBlobFeeCap) + err = updateTxDataGasCaps(lastBlobTx, newGasFeeCap, newTipCap, newBlobFeeCap) + if err != nil { + t.Fatal(err) + } lastTx = types.NewTx(lastBlobTx) // Make creation time go backwards so elapsed time increases retconnedCreationTime := dataCreatedAt.Add(-time.Minute) From daaf7ca163319e2729d165f627fb8f784e5540d4 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Mon, 25 Mar 2024 10:57:55 -0500 Subject: [PATCH 12/13] address PR comments --- util/headerreader/blob_client.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/headerreader/blob_client.go b/util/headerreader/blob_client.go index bccda04496..664dbb5e30 100644 --- a/util/headerreader/blob_client.go +++ b/util/headerreader/blob_client.go @@ -117,7 +117,14 @@ func beaconRequest[T interface{}](b *BlobClient, ctx context.Context, beaconPath return nil, err } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("response returned with status code %d, want 200 (OK)", resp.StatusCode) + body, _ := io.ReadAll(resp.Body) + bodyStr := string(body) + log.Debug("beacon request returned response with non 200 OK status", "status", resp.Status, "body", bodyStr) + if len(bodyStr) > 100 { + return nil, fmt.Errorf("response returned with status %s, want 200 OK. body: %s ", resp.Status, bodyStr[len(bodyStr)-trailingCharsOfResponse:]) + } else { + return nil, fmt.Errorf("response returned with status %s, want 200 OK. body: %s", resp.Status, bodyStr) + } } return resp, nil } From eece5988dc6d57884496f67bb3485078198a1979 Mon Sep 17 00:00:00 2001 From: Tristan-Wilson <87238672+Tristan-Wilson@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:40:11 +0100 Subject: [PATCH 13/13] Update arbnode/dataposter/dataposter_test.go Co-authored-by: Ganesh Vanahalli --- arbnode/dataposter/dataposter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 9bd54141cd..a8e2e110a0 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -230,7 +230,7 @@ func (c *stubL1Client) TransactionSender(ctx context.Context, tx *types.Transact return common.Address{}, nil } -func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUncofirmed_BlobTx(t *testing.T) { +func TestFeeAndTipCaps_EnoughBalance_NoBacklog_NoUnconfirmed_BlobTx(t *testing.T) { conf := func() *DataPosterConfig { // Set only the fields that are used by feeAndTipCaps // Start with defaults, maybe change for test.