From b6025425ac73c1fdda447a4cfacd4b745dac6823 Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:57:43 +0700 Subject: [PATCH 1/3] fix(rollup-relayer): check if previous bundle or batch is finalized in fake finalize mode (#1563) --- common/version/version.go | 2 +- .../internal/controller/relayer/l2_relayer.go | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/common/version/version.go b/common/version/version.go index 83bff50dea..57588b9c02 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.4.72" +var tag = "v4.4.73" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/rollup/internal/controller/relayer/l2_relayer.go b/rollup/internal/controller/relayer/l2_relayer.go index b90e70f9e0..879c4da9a7 100644 --- a/rollup/internal/controller/relayer/l2_relayer.go +++ b/rollup/internal/controller/relayer/l2_relayer.go @@ -568,8 +568,26 @@ func (r *Layer2Relayer) ProcessPendingBundles() { switch status { case types.ProvingTaskUnassigned, types.ProvingTaskAssigned: if r.cfg.EnableTestEnvBypassFeatures && utils.NowUTC().Sub(bundle.CreatedAt) > time.Duration(r.cfg.FinalizeBundleWithoutProofTimeoutSec)*time.Second { + // check if last batch is finalized, because in fake finalize bundle mode, the contract does not verify if the previous bundle or batch is finalized. + if bundle.StartBatchIndex == 0 { + log.Error("invalid args: start batch index of bundle is 0", "bundle index", bundle.Index, "start batch index", bundle.StartBatchIndex, "end batch index", bundle.EndBatchIndex) + return + } + + lastBatch, err := r.batchOrm.GetBatchByIndex(r.ctx, bundle.StartBatchIndex-1) + if err != nil { + log.Error("failed to get last batch", "batch index", bundle.StartBatchIndex-1, "err", err) + return + } + + if types.RollupStatus(lastBatch.RollupStatus) != types.RollupFinalized { + log.Error("previous bundle or batch is not finalized", "batch index", lastBatch.Index, "batch hash", lastBatch.Hash, "rollup status", types.RollupStatus(lastBatch.RollupStatus)) + return + } + if err := r.finalizeBundle(bundle, false); err != nil { - log.Error("Failed to finalize timeout bundle without proof", "index", bundle.Index, "start batch index", bundle.StartBatchIndex, "end batch index", bundle.EndBatchIndex, "err", err) + log.Error("failed to finalize timeout bundle without proof", "bundle index", bundle.Index, "start batch index", bundle.StartBatchIndex, "end batch index", bundle.EndBatchIndex, "err", err) + return } } @@ -577,7 +595,8 @@ func (r *Layer2Relayer) ProcessPendingBundles() { log.Info("Start to roll up zk proof", "bundle hash", bundle.Hash) r.metrics.rollupL2RelayerProcessPendingBundlesFinalizedTotal.Inc() if err := r.finalizeBundle(bundle, true); err != nil { - log.Error("Failed to finalize bundle with proof", "index", bundle.Index, "start batch index", bundle.StartBatchIndex, "end batch index", bundle.EndBatchIndex, "err", err) + log.Error("failed to finalize bundle with proof", "bundle index", bundle.Index, "start batch index", bundle.StartBatchIndex, "end batch index", bundle.EndBatchIndex, "err", err) + return } case types.ProvingTaskFailed: @@ -589,7 +608,7 @@ func (r *Layer2Relayer) ProcessPendingBundles() { // stop the ledger, fix the limit, revert all the violating blocks, // chunks, batches, bundles and all subsequent ones, and resume, // i.e. this case requires manual resolution. - log.Error("bundle proving failed", "index", bundle.Index, "hash", bundle.Hash, "proved at", bundle.ProvedAt, "proof time sec", bundle.ProofTimeSec) + log.Error("bundle proving failed", "bundle index", bundle.Index, "bundle hash", bundle.Hash, "proved at", bundle.ProvedAt, "proof time sec", bundle.ProofTimeSec) default: log.Error("encounter unreachable case in ProcessPendingBundles", "proving status", status) From e3cf2cb82b5b461f1f5a0c0c16358fa8d04ff221 Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:42:51 +0700 Subject: [PATCH 2/3] refactor(gas-oracle): remove outdated logic (#1560) Co-authored-by: colinlyguo --- common/version/version.go | 2 +- rollup/abi/bridge_abi.go | 2 +- rollup/abi/bridge_abi_test.go | 5 +- rollup/cmd/gas_oracle/app/app.go | 4 +- rollup/cmd/rollup_relayer/app/app.go | 4 +- rollup/conf/config.json | 2 - rollup/internal/config/relayer.go | 5 -- .../internal/controller/relayer/l1_relayer.go | 72 +++++------------- .../controller/relayer/l1_relayer_test.go | 8 -- rollup/mock_bridge/MockBridge.sol | 4 - rollup/tests/bridge_test.go | 1 - rollup/tests/gas_oracle_test.go | 75 +------------------ 12 files changed, 29 insertions(+), 155 deletions(-) diff --git a/common/version/version.go b/common/version/version.go index 57588b9c02..59a4c37319 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.4.73" +var tag = "v4.4.74" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/rollup/abi/bridge_abi.go b/rollup/abi/bridge_abi.go index 35f97824ff..ba7a6d6b20 100644 --- a/rollup/abi/bridge_abi.go +++ b/rollup/abi/bridge_abi.go @@ -34,5 +34,5 @@ var L2GasPriceOracleMetaData = &bind.MetaData{ // L1GasPriceOracleMetaData contains all meta data concerning the L1GasPriceOracle contract. var L1GasPriceOracleMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"scalar\",\"type\":\"uint256\"}],\"name\":\"BlobScalarUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"scalar\",\"type\":\"uint256\"}],\"name\":\"CommitScalarUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1BaseFee\",\"type\":\"uint256\"}],\"name\":\"L1BaseFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1BlobBaseFee\",\"type\":\"uint256\"}],\"name\":\"L1BlobBaseFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"overhead\",\"type\":\"uint256\"}],\"name\":\"OverheadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"scalar\",\"type\":\"uint256\"}],\"name\":\"ScalarUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"blobScalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"commitScalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"getL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"getL1GasUsed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l1BaseFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l1BlobBaseFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"overhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"scalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l1BaseFee\",\"type\":\"uint256\"}],\"name\":\"setL1BaseFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l1BaseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_l1BlobBaseFee\",\"type\":\"uint256\"}],\"name\":\"setL1BaseFeeAndBlobBaseFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"scalar\",\"type\":\"uint256\"}],\"name\":\"BlobScalarUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"scalar\",\"type\":\"uint256\"}],\"name\":\"CommitScalarUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1BaseFee\",\"type\":\"uint256\"}],\"name\":\"L1BaseFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1BlobBaseFee\",\"type\":\"uint256\"}],\"name\":\"L1BlobBaseFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"overhead\",\"type\":\"uint256\"}],\"name\":\"OverheadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"scalar\",\"type\":\"uint256\"}],\"name\":\"ScalarUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"blobScalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"commitScalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"getL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"getL1GasUsed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l1BaseFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l1BlobBaseFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"overhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"scalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l1BaseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_l1BlobBaseFee\",\"type\":\"uint256\"}],\"name\":\"setL1BaseFeeAndBlobBaseFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } diff --git a/rollup/abi/bridge_abi_test.go b/rollup/abi/bridge_abi_test.go index 70ab6564ba..e20fb726e1 100644 --- a/rollup/abi/bridge_abi_test.go +++ b/rollup/abi/bridge_abi_test.go @@ -52,14 +52,15 @@ func TestPackImportGenesisBatch(t *testing.T) { assert.NoError(err) } -func TestPackSetL1BaseFee(t *testing.T) { +func TestPackSetL1BaseFeeAndBlobBaseFee(t *testing.T) { assert := assert.New(t) l1GasOracleABI, err := L1GasPriceOracleMetaData.GetAbi() assert.NoError(err) baseFee := big.NewInt(2333) - _, err = l1GasOracleABI.Pack("setL1BaseFee", baseFee) + blobBaseFee := big.NewInt(1) + _, err = l1GasOracleABI.Pack("setL1BaseFeeAndBlobBaseFee", baseFee, blobBaseFee) assert.NoError(err) } diff --git a/rollup/cmd/gas_oracle/app/app.go b/rollup/cmd/gas_oracle/app/app.go index 5ad23819e9..156c6c394e 100644 --- a/rollup/cmd/gas_oracle/app/app.go +++ b/rollup/cmd/gas_oracle/app/app.go @@ -22,7 +22,7 @@ import ( "scroll-tech/rollup/internal/config" "scroll-tech/rollup/internal/controller/relayer" "scroll-tech/rollup/internal/controller/watcher" - butils "scroll-tech/rollup/internal/utils" + rutils "scroll-tech/rollup/internal/utils" ) var app *cli.App @@ -98,7 +98,7 @@ func action(ctx *cli.Context) error { go utils.LoopWithContext(subCtx, 10*time.Second, func(ctx context.Context) { // Fetch the latest block number to decrease the delay when fetching gas prices // Use latest block number - 1 to prevent frequent reorg - number, loopErr := butils.GetLatestConfirmedBlockNumber(ctx, l1client, rpc.LatestBlockNumber) + number, loopErr := rutils.GetLatestConfirmedBlockNumber(ctx, l1client, rpc.LatestBlockNumber) if loopErr != nil { log.Error("failed to get block number", "err", loopErr) return diff --git a/rollup/cmd/rollup_relayer/app/app.go b/rollup/cmd/rollup_relayer/app/app.go index 939d4b7798..4e086bd4ef 100644 --- a/rollup/cmd/rollup_relayer/app/app.go +++ b/rollup/cmd/rollup_relayer/app/app.go @@ -20,7 +20,7 @@ import ( "scroll-tech/rollup/internal/config" "scroll-tech/rollup/internal/controller/relayer" "scroll-tech/rollup/internal/controller/watcher" - butils "scroll-tech/rollup/internal/utils" + rutils "scroll-tech/rollup/internal/utils" ) var app *cli.App @@ -92,7 +92,7 @@ func action(ctx *cli.Context) error { // Watcher loop to fetch missing blocks go utils.LoopWithContext(subCtx, 2*time.Second, func(ctx context.Context) { - number, loopErr := butils.GetLatestConfirmedBlockNumber(ctx, l2client, cfg.L2Config.Confirmations) + number, loopErr := rutils.GetLatestConfirmedBlockNumber(ctx, l2client, cfg.L2Config.Confirmations) if loopErr != nil { log.Error("failed to get block number", "err", loopErr) return diff --git a/rollup/conf/config.json b/rollup/conf/config.json index 50d6b57262..fa9ba680ae 100644 --- a/rollup/conf/config.json +++ b/rollup/conf/config.json @@ -18,8 +18,6 @@ "gas_oracle_config": { "min_gas_price": 0, "gas_price_diff": 50000, - "l1_base_fee_weight": 0.132, - "l1_blob_base_fee_weight": 0.145, "check_committed_batches_window_minutes": 5, "l1_base_fee_default": 15000000000, "l1_blob_base_fee_default": 1 diff --git a/rollup/internal/config/relayer.go b/rollup/internal/config/relayer.go index 1a1d3a1a46..4cbee5f291 100644 --- a/rollup/internal/config/relayer.go +++ b/rollup/internal/config/relayer.go @@ -85,11 +85,6 @@ type GasOracleConfig struct { // AlternativeGasTokenConfig The configuration for handling token exchange rates when updating the gas price oracle. AlternativeGasTokenConfig *AlternativeGasTokenConfig `json:"alternative_gas_token_config"` - // The following configs are only for updating L1 gas price, used for sender in L2. - // The weight for L1 base fee. - L1BaseFeeWeight float64 `json:"l1_base_fee_weight"` - // The weight for L1 blob base fee. - L1BlobBaseFeeWeight float64 `json:"l1_blob_base_fee_weight"` // CheckCommittedBatchesWindowMinutes the time frame to check if we committed batches to decide to update gas oracle or not in minutes CheckCommittedBatchesWindowMinutes int `json:"check_committed_batches_window_minutes"` L1BaseFeeDefault uint64 `json:"l1_base_fee_default"` diff --git a/rollup/internal/controller/relayer/l1_relayer.go b/rollup/internal/controller/relayer/l1_relayer.go index ed2f39eb1f..14d34ebb70 100644 --- a/rollup/internal/controller/relayer/l1_relayer.go +++ b/rollup/internal/controller/relayer/l1_relayer.go @@ -36,12 +36,10 @@ type Layer1Relayer struct { gasOracleSender *sender.Sender l1GasOracleABI *abi.ABI - lastBaseFee uint64 - lastBlobBaseFee uint64 - minGasPrice uint64 - gasPriceDiff uint64 - l1BaseFeeWeight float64 - l1BlobBaseFeeWeight float64 + lastBaseFee uint64 + lastBlobBaseFee uint64 + minGasPrice uint64 + gasPriceDiff uint64 l1BlockOrm *orm.L1Block l2BlockOrm *orm.L2Block @@ -91,10 +89,8 @@ func NewLayer1Relayer(ctx context.Context, db *gorm.DB, cfg *config.RelayerConfi gasOracleSender: gasOracleSender, l1GasOracleABI: bridgeAbi.L1GasPriceOracleABI, - minGasPrice: minGasPrice, - gasPriceDiff: gasPriceDiff, - l1BaseFeeWeight: cfg.GasOracleConfig.L1BaseFeeWeight, - l1BlobBaseFeeWeight: cfg.GasOracleConfig.L1BlobBaseFeeWeight, + minGasPrice: minGasPrice, + gasPriceDiff: gasPriceDiff, } l1Relayer.metrics = initL1RelayerMetrics(reg) @@ -132,25 +128,13 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { block := blocks[0] if types.GasOracleStatus(block.GasOracleStatus) == types.GasOraclePending { - latestL2Height, err := r.l2BlockOrm.GetL2BlocksLatestHeight(r.ctx) - if err != nil { - log.Warn("Failed to fetch latest L2 block height from db", "err", err) + if block.BaseFee == 0 || block.BlobBaseFee == 0 { + log.Error("Invalid base fee or blob base fee", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", block.BaseFee, "block.BlobBaseFee", block.BlobBaseFee) return } - var isBernoulli = block.BlobBaseFee > 0 && r.chainCfg.IsBernoulli(new(big.Int).SetUint64(latestL2Height)) - var isCurie = block.BlobBaseFee > 0 && r.chainCfg.IsCurie(new(big.Int).SetUint64(latestL2Height)) - - var baseFee uint64 - var blobBaseFee uint64 - if isCurie { - baseFee = block.BaseFee - blobBaseFee = block.BlobBaseFee - } else if isBernoulli { - baseFee = uint64(math.Ceil(r.l1BaseFeeWeight*float64(block.BaseFee) + r.l1BlobBaseFeeWeight*float64(block.BlobBaseFee))) - } else { - baseFee = block.BaseFee - } + baseFee := block.BaseFee + blobBaseFee := block.BlobBaseFee // include the token exchange rate in the fee data if alternative gas token enabled if r.cfg.GasOracleConfig.AlternativeGasTokenConfig != nil && r.cfg.GasOracleConfig.AlternativeGasTokenConfig.Enabled { @@ -177,7 +161,7 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { blobBaseFee = uint64(math.Ceil(float64(blobBaseFee) / exchangeRate)) } - if r.shouldUpdateGasOracle(baseFee, blobBaseFee, isCurie) { + if r.shouldUpdateGasOracle(baseFee, blobBaseFee) { // It indicates the committing batch has been stuck for a long time, it's likely that the L1 gas fee spiked. // If we are not committing batches due to high fees then we shouldn't update fees to prevent users from paying high l1_data_fee // Also, set fees to some default value, because we have already updated fees to some high values, probably @@ -191,24 +175,15 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { } else if err != nil { return } - var data []byte - if isCurie { - data, err = r.l1GasOracleABI.Pack("setL1BaseFeeAndBlobBaseFee", new(big.Int).SetUint64(baseFee), new(big.Int).SetUint64(blobBaseFee)) - if err != nil { - log.Error("Failed to pack setL1BaseFeeAndBlobBaseFee", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "isBernoulli", isBernoulli, "isCurie", isCurie, "err", err) - return - } - } else { - data, err = r.l1GasOracleABI.Pack("setL1BaseFee", new(big.Int).SetUint64(baseFee)) - if err != nil { - log.Error("Failed to pack setL1BaseFee", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "isBernoulli", isBernoulli, "isCurie", isCurie, "err", err) - return - } + data, err := r.l1GasOracleABI.Pack("setL1BaseFeeAndBlobBaseFee", new(big.Int).SetUint64(baseFee), new(big.Int).SetUint64(blobBaseFee)) + if err != nil { + log.Error("Failed to pack setL1BaseFeeAndBlobBaseFee", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "err", err) + return } hash, err := r.gasOracleSender.SendTransaction(block.Hash, &r.cfg.GasPriceOracleContractAddress, data, nil, 0) if err != nil { - log.Error("Failed to send gas oracle update tx to layer2", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "isBernoulli", isBernoulli, "isCurie", isCurie, "err", err) + log.Error("Failed to send gas oracle update tx to layer2", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "err", err) return } @@ -222,7 +197,7 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { r.lastBlobBaseFee = blobBaseFee r.metrics.rollupL1RelayerLatestBaseFee.Set(float64(r.lastBaseFee)) r.metrics.rollupL1RelayerLatestBlobBaseFee.Set(float64(r.lastBlobBaseFee)) - log.Info("Update l1 base fee", "txHash", hash.String(), "baseFee", baseFee, "blobBaseFee", blobBaseFee, "isBernoulli", isBernoulli, "isCurie", isCurie) + log.Info("Update l1 base fee", "txHash", hash.String(), "baseFee", baseFee, "blobBaseFee", blobBaseFee) } } } @@ -271,9 +246,10 @@ func (r *Layer1Relayer) StopSenders() { } } -func (r *Layer1Relayer) shouldUpdateGasOracle(baseFee uint64, blobBaseFee uint64, isCurie bool) bool { +func (r *Layer1Relayer) shouldUpdateGasOracle(baseFee uint64, blobBaseFee uint64) bool { // Right after restarting. if r.lastBaseFee == 0 { + log.Info("First time to update gas oracle after restarting", "baseFee", baseFee, "blobBaseFee", blobBaseFee) return true } @@ -282,16 +258,6 @@ func (r *Layer1Relayer) shouldUpdateGasOracle(baseFee uint64, blobBaseFee uint64 return true } - // Omitting blob base fee checks before Curie. - if !isCurie { - return false - } - - // Right after enabling Curie. - if r.lastBlobBaseFee == 0 { - return true - } - expectedBlobBaseFeeDelta := r.lastBlobBaseFee * r.gasPriceDiff / gasPriceDiffPrecision // Plus a minimum of 0.01 gwei, since the blob base fee is usually low, preventing short-time flunctuation. expectedBlobBaseFeeDelta += 10000000 diff --git a/rollup/internal/controller/relayer/l1_relayer_test.go b/rollup/internal/controller/relayer/l1_relayer_test.go index 6ac67fdc5b..08f6f9b50a 100644 --- a/rollup/internal/controller/relayer/l1_relayer_test.go +++ b/rollup/internal/controller/relayer/l1_relayer_test.go @@ -141,14 +141,6 @@ func testL1RelayerProcessGasPriceOracle(t *testing.T) { return tmpInfo, nil }) - convey.Convey("setL1BaseFee failure", t, func() { - targetErr := errors.New("pack setL1BaseFee error") - patchGuard.ApplyMethodFunc(l1Relayer.l1GasOracleABI, "Pack", func(name string, args ...interface{}) ([]byte, error) { - return nil, targetErr - }) - l1Relayer.ProcessGasPriceOracle() - }) - patchGuard.ApplyMethodFunc(l1Relayer.l1GasOracleABI, "Pack", func(name string, args ...interface{}) ([]byte, error) { return []byte("for test"), nil }) diff --git a/rollup/mock_bridge/MockBridge.sol b/rollup/mock_bridge/MockBridge.sol index c94bc221b3..d1bddd4abc 100644 --- a/rollup/mock_bridge/MockBridge.sol +++ b/rollup/mock_bridge/MockBridge.sol @@ -102,10 +102,6 @@ contract MockBridge { mapping(uint256 => bytes32) public withdrawRoots; - function setL1BaseFee(uint256 _l1BaseFee) external { - l1BaseFee = _l1BaseFee; - } - function setL1BaseFeeAndBlobBaseFee(uint256 _l1BaseFee, uint256 _l1BlobBaseFee) external { l1BaseFee = _l1BaseFee; l1BlobBaseFee = _l1BlobBaseFee; diff --git a/rollup/tests/bridge_test.go b/rollup/tests/bridge_test.go index 3ab88f8439..afc137f8c4 100644 --- a/rollup/tests/bridge_test.go +++ b/rollup/tests/bridge_test.go @@ -213,7 +213,6 @@ func TestFunction(t *testing.T) { // l1/l2 gas oracle t.Run("TestImportL1GasPrice", testImportL1GasPrice) - t.Run("TestImportL1GasPriceAfterCurie", testImportL1GasPriceAfterCurie) t.Run("TestImportDefaultL1GasPriceDueToL1GasPriceSpike", testImportDefaultL1GasPriceDueToL1GasPriceSpike) t.Run("TestImportL2GasPrice", testImportL2GasPrice) } diff --git a/rollup/tests/gas_oracle_test.go b/rollup/tests/gas_oracle_test.go index cdcd5a3e01..c7a0b50e34 100644 --- a/rollup/tests/gas_oracle_test.go +++ b/rollup/tests/gas_oracle_test.go @@ -30,80 +30,7 @@ func testImportL1GasPrice(t *testing.T) { l1Cfg := rollupApp.Config.L1Config // Create L1Relayer - l1Relayer, err := relayer.NewLayer1Relayer(context.Background(), db, l1Cfg.RelayerConfig, ¶ms.ChainConfig{}, relayer.ServiceTypeL1GasOracle, nil) - assert.NoError(t, err) - defer l1Relayer.StopSenders() - - // Create L1Watcher - startHeight, err := l1Client.BlockNumber(context.Background()) - assert.NoError(t, err) - l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1Client, startHeight-1, db, nil) - - // fetch new blocks - number, err := l1Client.BlockNumber(context.Background()) - assert.Greater(t, number, startHeight-1) - assert.NoError(t, err) - err = l1Watcher.FetchBlockHeader(number) - assert.NoError(t, err) - - l1BlockOrm := orm.NewL1Block(db) - // check db status - latestBlockHeight, err := l1BlockOrm.GetLatestL1BlockHeight(context.Background()) - assert.NoError(t, err) - assert.Equal(t, number, latestBlockHeight) - blocks, err := l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{"number": latestBlockHeight}) - assert.NoError(t, err) - assert.Equal(t, len(blocks), 1) - assert.Empty(t, blocks[0].OracleTxHash) - assert.Equal(t, types.GasOracleStatus(blocks[0].GasOracleStatus), types.GasOraclePending) - - // add fake batch to pass check for commit batch timeout - chunk := &encoding.Chunk{ - Blocks: []*encoding.Block{ - { - Header: &gethTypes.Header{ - Number: big.NewInt(1), - ParentHash: common.Hash{}, - Difficulty: big.NewInt(0), - BaseFee: big.NewInt(0), - }, - Transactions: nil, - WithdrawRoot: common.Hash{}, - RowConsumption: &gethTypes.RowConsumption{}, - }, - }, - } - batch := &encoding.Batch{ - Index: 0, - TotalL1MessagePoppedBefore: 0, - ParentBatchHash: common.Hash{}, - Chunks: []*encoding.Chunk{chunk}, - } - batchOrm := orm.NewBatch(db) - dbBatch, err := batchOrm.InsertBatch(context.Background(), batch, encoding.CodecV0, utils.BatchMetrics{}) - assert.NoError(t, err) - err = batchOrm.UpdateCommitTxHashAndRollupStatus(context.Background(), dbBatch.Hash, common.Hash{}.String(), types.RollupCommitted) - assert.NoError(t, err) - - // relay gas price - l1Relayer.ProcessGasPriceOracle() - blocks, err = l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{"number": latestBlockHeight}) - assert.NoError(t, err) - assert.Equal(t, len(blocks), 1) - assert.NotEmpty(t, blocks[0].OracleTxHash) - assert.Equal(t, types.GasOracleStatus(blocks[0].GasOracleStatus), types.GasOracleImporting) -} - -func testImportL1GasPriceAfterCurie(t *testing.T) { - db := setupDB(t) - defer database.CloseDB(db) - - prepareContracts(t) - - l1Cfg := rollupApp.Config.L1Config - - // Create L1Relayer - l1Relayer, err := relayer.NewLayer1Relayer(context.Background(), db, l1Cfg.RelayerConfig, ¶ms.ChainConfig{BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0)}, relayer.ServiceTypeL1GasOracle, nil) + l1Relayer, err := relayer.NewLayer1Relayer(context.Background(), db, l1Cfg.RelayerConfig, ¶ms.ChainConfig{LondonBlock: big.NewInt(0), BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64), DarwinV2Time: new(uint64)}, relayer.ServiceTypeL1GasOracle, nil) assert.NoError(t, err) defer l1Relayer.StopSenders() From 54d823677fc64cade51865db147f6dafa005da26 Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:49:23 +0700 Subject: [PATCH 3/3] fix(rollup-relayer): graceful restart (#1564) Co-authored-by: colinlyguo --- common/version/version.go | 2 +- rollup/internal/controller/sender/sender.go | 134 ++++++++++-------- .../internal/controller/sender/sender_test.go | 38 +++-- 3 files changed, 101 insertions(+), 73 deletions(-) diff --git a/common/version/version.go b/common/version/version.go index 59a4c37319..f84f9b78cf 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.4.74" +var tag = "v4.4.75" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 051c01b79d..ff32750f72 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -175,7 +175,6 @@ func (s *Sender) SendTransaction(contextID string, target *common.Address, data s.metrics.sendTransactionTotal.WithLabelValues(s.service, s.name).Inc() var ( feeData *FeeData - tx *gethTypes.Transaction sidecar *gethTypes.BlobTxSidecar err error ) @@ -217,20 +216,35 @@ func (s *Sender) SendTransaction(contextID string, target *common.Address, data return common.Hash{}, fmt.Errorf("failed to get fee data, err: %w", err) } - if tx, err = s.createAndSendTx(feeData, target, data, sidecar, nil); err != nil { + signedTx, err := s.createTx(feeData, target, data, sidecar, nil) + if err != nil { s.metrics.sendTransactionFailureSendTx.WithLabelValues(s.service, s.name).Inc() - log.Error("failed to create and send tx (non-resubmit case)", "from", s.transactionSigner.GetAddr().String(), "nonce", s.transactionSigner.GetNonce(), "err", err) - return common.Hash{}, fmt.Errorf("failed to create and send transaction, err: %w", err) + log.Error("failed to create signed tx (non-resubmit case)", "from", s.transactionSigner.GetAddr().String(), "nonce", s.transactionSigner.GetNonce(), "err", err) + return common.Hash{}, fmt.Errorf("failed to create signed transaction, err: %w", err) } - if err = s.pendingTransactionOrm.InsertPendingTransaction(s.ctx, contextID, s.getSenderMeta(), tx, blockNumber); err != nil { + // Insert the transaction into the pending transaction table. + // A corner case is that the transaction is inserted into the table but not sent to the chain, because the server is stopped in the middle. + // This case will be handled by the checkPendingTransaction function. + if err = s.pendingTransactionOrm.InsertPendingTransaction(s.ctx, contextID, s.getSenderMeta(), signedTx, blockNumber); err != nil { log.Error("failed to insert transaction", "from", s.transactionSigner.GetAddr().String(), "nonce", s.transactionSigner.GetNonce(), "err", err) return common.Hash{}, fmt.Errorf("failed to insert transaction, err: %w", err) } - return tx.Hash(), nil + + if err := s.client.SendTransaction(s.ctx, signedTx); err != nil { + log.Error("failed to send tx", "tx hash", signedTx.Hash().String(), "from", s.transactionSigner.GetAddr().String(), "nonce", signedTx.Nonce(), "err", err) + // Check if contain nonce, and reset nonce + // only reset nonce when it is not from resubmit + if strings.Contains(err.Error(), "nonce too low") { + s.resetNonce(context.Background()) + } + return common.Hash{}, fmt.Errorf("failed to send transaction, err: %w", err) + } + + return signedTx.Hash(), nil } -func (s *Sender) createAndSendTx(feeData *FeeData, target *common.Address, data []byte, sidecar *gethTypes.BlobTxSidecar, overrideNonce *uint64) (*gethTypes.Transaction, error) { +func (s *Sender) createTx(feeData *FeeData, target *common.Address, data []byte, sidecar *gethTypes.BlobTxSidecar, overrideNonce *uint64) (*gethTypes.Transaction, error) { var ( nonce = s.transactionSigner.GetNonce() txData gethTypes.TxData @@ -292,14 +306,9 @@ func (s *Sender) createAndSendTx(feeData *FeeData, target *common.Address, data return nil, err } - if err = s.client.SendTransaction(s.ctx, signedTx); err != nil { - log.Error("failed to send tx", "tx hash", signedTx.Hash().String(), "from", s.transactionSigner.GetAddr().String(), "nonce", signedTx.Nonce(), "err", err) - // Check if contain nonce, and reset nonce - // only reset nonce when it is not from resubmit - if strings.Contains(err.Error(), "nonce too low") && overrideNonce == nil { - s.resetNonce(context.Background()) - } - return nil, err + // update nonce when it is not from resubmit + if overrideNonce == nil { + s.transactionSigner.SetNonce(nonce + 1) } if feeData.gasTipCap != nil { @@ -320,10 +329,6 @@ func (s *Sender) createAndSendTx(feeData *FeeData, target *common.Address, data s.metrics.currentGasLimit.WithLabelValues(s.service, s.name).Set(float64(feeData.gasLimit)) - // update nonce when it is not from resubmit - if overrideNonce == nil { - s.transactionSigner.SetNonce(nonce + 1) - } return signedTx, nil } @@ -337,7 +342,7 @@ func (s *Sender) resetNonce(ctx context.Context) { s.transactionSigner.SetNonce(nonce) } -func (s *Sender) resubmitTransaction(tx *gethTypes.Transaction, baseFee, blobBaseFee uint64) (*gethTypes.Transaction, error) { +func (s *Sender) createReplacingTransaction(tx *gethTypes.Transaction, baseFee, blobBaseFee uint64) (*gethTypes.Transaction, error) { escalateMultipleNum := new(big.Int).SetUint64(s.config.EscalateMultipleNum) escalateMultipleDen := new(big.Int).SetUint64(s.config.EscalateMultipleDen) maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice) @@ -468,12 +473,12 @@ func (s *Sender) resubmitTransaction(tx *gethTypes.Transaction, baseFee, blobBas nonce := tx.Nonce() s.metrics.resubmitTransactionTotal.WithLabelValues(s.service, s.name).Inc() - tx, err := s.createAndSendTx(&feeData, tx.To(), tx.Data(), tx.BlobTxSidecar(), &nonce) + signedTx, err := s.createTx(&feeData, tx.To(), tx.Data(), tx.BlobTxSidecar(), &nonce) if err != nil { - log.Error("failed to create and send tx (resubmit case)", "from", s.transactionSigner.GetAddr().String(), "nonce", nonce, "err", err) + log.Error("failed to create signed tx (resubmit case)", "from", s.transactionSigner.GetAddr().String(), "nonce", nonce, "err", err) return nil, err } - return tx, nil + return signedTx, nil } // checkPendingTransaction checks the confirmation status of pending transactions against the latest confirmed block number. @@ -500,30 +505,29 @@ func (s *Sender) checkPendingTransaction() { } for _, txnToCheck := range transactionsToCheck { - tx := new(gethTypes.Transaction) - if err := tx.DecodeRLP(rlp.NewStream(bytes.NewReader(txnToCheck.RLPEncoding), 0)); err != nil { + originalTx := new(gethTypes.Transaction) + if err := originalTx.DecodeRLP(rlp.NewStream(bytes.NewReader(txnToCheck.RLPEncoding), 0)); err != nil { log.Error("failed to decode RLP", "context ID", txnToCheck.ContextID, "sender meta", s.getSenderMeta(), "err", err) continue } - receipt, err := s.client.TransactionReceipt(s.ctx, tx.Hash()) + receipt, err := s.client.TransactionReceipt(s.ctx, originalTx.Hash()) if err == nil { // tx confirmed. if receipt.BlockNumber.Uint64() <= confirmed { - err := s.db.Transaction(func(dbTX *gorm.DB) error { + if dbTxErr := s.db.Transaction(func(dbTX *gorm.DB) error { // Update the status of the transaction to TxStatusConfirmed. - if err := s.pendingTransactionOrm.UpdatePendingTransactionStatusByTxHash(s.ctx, tx.Hash(), types.TxStatusConfirmed, dbTX); err != nil { - log.Error("failed to update transaction status by tx hash", "hash", tx.Hash().String(), "sender meta", s.getSenderMeta(), "from", s.transactionSigner.GetAddr().String(), "nonce", tx.Nonce(), "err", err) - return err + if updateErr := s.pendingTransactionOrm.UpdatePendingTransactionStatusByTxHash(s.ctx, originalTx.Hash(), types.TxStatusConfirmed, dbTX); updateErr != nil { + log.Error("failed to update transaction status by tx hash", "hash", originalTx.Hash().String(), "sender meta", s.getSenderMeta(), "from", s.transactionSigner.GetAddr().String(), "nonce", originalTx.Nonce(), "err", updateErr) + return updateErr } // Update other transactions with the same nonce and sender address as failed. - if err := s.pendingTransactionOrm.UpdateOtherTransactionsAsFailedByNonce(s.ctx, txnToCheck.SenderAddress, tx.Nonce(), tx.Hash(), dbTX); err != nil { - log.Error("failed to update other transactions as failed by nonce", "senderAddress", txnToCheck.SenderAddress, "nonce", tx.Nonce(), "excludedTxHash", tx.Hash(), "err", err) - return err + if updateErr := s.pendingTransactionOrm.UpdateOtherTransactionsAsFailedByNonce(s.ctx, txnToCheck.SenderAddress, originalTx.Nonce(), originalTx.Hash(), dbTX); updateErr != nil { + log.Error("failed to update other transactions as failed by nonce", "senderAddress", txnToCheck.SenderAddress, "nonce", originalTx.Nonce(), "excludedTxHash", originalTx.Hash(), "err", updateErr) + return updateErr } return nil - }) - if err != nil { - log.Error("db transaction failed after receiving confirmation", "err", err) + }); dbTxErr != nil { + log.Error("db transaction failed after receiving confirmation", "err", dbTxErr) return } @@ -531,7 +535,7 @@ func (s *Sender) checkPendingTransaction() { s.confirmCh <- &Confirmation{ ContextID: txnToCheck.ContextID, IsSuccessful: receipt.Status == gethTypes.ReceiptStatusSuccessful, - TxHash: tx.Hash(), + TxHash: originalTx.Hash(), SenderType: s.senderType, } } @@ -548,52 +552,60 @@ func (s *Sender) checkPendingTransaction() { // early return if the previous transaction has not been confirmed yet. // currentNonce is already the confirmed nonce + 1. - if tx.Nonce() > currentNonce { - log.Debug("previous transaction not yet confirmed, skip bumping gas price", "address", txnToCheck.SenderAddress, "currentNonce", currentNonce, "txNonce", tx.Nonce()) + if originalTx.Nonce() > currentNonce { + log.Debug("previous transaction not yet confirmed, skip bumping gas price", "address", txnToCheck.SenderAddress, "currentNonce", currentNonce, "txNonce", originalTx.Nonce()) continue } // It's possible that the pending transaction was marked as failed earlier in this loop (e.g., if one of its replacements has already been confirmed). // Therefore, we fetch the current transaction status again for accuracy before proceeding. - status, err := s.pendingTransactionOrm.GetTxStatusByTxHash(s.ctx, tx.Hash()) + status, err := s.pendingTransactionOrm.GetTxStatusByTxHash(s.ctx, originalTx.Hash()) if err != nil { - log.Error("failed to get transaction status by tx hash", "hash", tx.Hash().String(), "err", err) + log.Error("failed to get transaction status by tx hash", "hash", originalTx.Hash().String(), "err", err) return } if status == types.TxStatusConfirmedFailed { - log.Warn("transaction already marked as failed, skipping resubmission", "hash", tx.Hash().String()) + log.Warn("transaction already marked as failed, skipping resubmission", "hash", originalTx.Hash().String()) continue } log.Info("resubmit transaction", "service", s.service, "name", s.name, - "hash", tx.Hash().String(), + "hash", originalTx.Hash().String(), "from", s.transactionSigner.GetAddr().String(), - "nonce", tx.Nonce(), + "nonce", originalTx.Nonce(), "submitBlockNumber", txnToCheck.SubmitBlockNumber, "currentBlockNumber", blockNumber, "escalateBlocks", s.config.EscalateBlocks) - if newTx, err := s.resubmitTransaction(tx, baseFee, blobBaseFee); err != nil { + newSignedTx, err := s.createReplacingTransaction(originalTx, baseFee, blobBaseFee) + if err != nil { s.metrics.resubmitTransactionFailedTotal.WithLabelValues(s.service, s.name).Inc() - log.Error("failed to resubmit transaction", "context ID", txnToCheck.ContextID, "sender meta", s.getSenderMeta(), "from", s.transactionSigner.GetAddr().String(), "nonce", tx.Nonce(), "err", err) - } else { - err := s.db.Transaction(func(dbTX *gorm.DB) error { - // Update the status of the original transaction as replaced, while still checking its confirmation status. - if err := s.pendingTransactionOrm.UpdatePendingTransactionStatusByTxHash(s.ctx, tx.Hash(), types.TxStatusReplaced, dbTX); err != nil { - return fmt.Errorf("failed to update status of transaction with hash %s to TxStatusReplaced, err: %w", tx.Hash().String(), err) - } - // Record the new transaction that has replaced the original one. - if err := s.pendingTransactionOrm.InsertPendingTransaction(s.ctx, txnToCheck.ContextID, s.getSenderMeta(), newTx, blockNumber, dbTX); err != nil { - return fmt.Errorf("failed to insert new pending transaction with context ID: %s, nonce: %d, hash: %v, previous block number: %v, current block number: %v, err: %w", txnToCheck.ContextID, newTx.Nonce(), newTx.Hash().String(), txnToCheck.SubmitBlockNumber, blockNumber, err) - } - return nil - }) - if err != nil { - log.Error("db transaction failed after resubmitting", "err", err) - return + log.Error("failed to resubmit transaction", "context ID", txnToCheck.ContextID, "sender meta", s.getSenderMeta(), "from", s.transactionSigner.GetAddr().String(), "nonce", originalTx.Nonce(), "err", err) + return + } + + // Update the status of the original transaction as replaced, while still checking its confirmation status. + // Insert the new transaction that has replaced the original one, and set the status as pending. + // A corner case is that the transaction is inserted into the table but not sent to the chain, because the server is stopped in the middle. + // This case will be handled by the checkPendingTransaction function. + if dbTxErr := s.db.Transaction(func(dbTX *gorm.DB) error { + if updateErr := s.pendingTransactionOrm.UpdatePendingTransactionStatusByTxHash(s.ctx, originalTx.Hash(), types.TxStatusReplaced, dbTX); updateErr != nil { + return fmt.Errorf("failed to update status of transaction with hash %s to TxStatusReplaced, err: %w", newSignedTx.Hash().String(), updateErr) + } + if updateErr := s.pendingTransactionOrm.InsertPendingTransaction(s.ctx, txnToCheck.ContextID, s.getSenderMeta(), newSignedTx, blockNumber, dbTX); updateErr != nil { + return fmt.Errorf("failed to insert new pending transaction with context ID: %s, nonce: %d, hash: %v, previous block number: %v, current block number: %v, err: %w", txnToCheck.ContextID, newSignedTx.Nonce(), newSignedTx.Hash().String(), txnToCheck.SubmitBlockNumber, blockNumber, updateErr) } + return nil + }); dbTxErr != nil { + log.Error("db transaction failed after resubmitting", "err", dbTxErr) + return + } + + if err := s.client.SendTransaction(s.ctx, newSignedTx); err != nil { + log.Error("failed to send replacing tx", "tx hash", newSignedTx.Hash().String(), "from", s.transactionSigner.GetAddr().String(), "nonce", newSignedTx.Nonce(), "err", err) + return } } } diff --git a/rollup/internal/controller/sender/sender_test.go b/rollup/internal/controller/sender/sender_test.go index 6f2af46c27..83fb045376 100644 --- a/rollup/internal/controller/sender/sender_test.go +++ b/rollup/internal/controller/sender/sender_test.go @@ -282,13 +282,17 @@ func testResubmitZeroGasPriceTransaction(t *testing.T) { gasFeeCap: big.NewInt(0), gasLimit: 50000, } - tx, err := s.createAndSendTx(feeData, &common.Address{}, nil, nil, nil) + tx, err := s.createTx(feeData, &common.Address{}, nil, nil, nil) assert.NoError(t, err) assert.NotNil(t, tx) + err = s.client.SendTransaction(s.ctx, tx) + assert.NoError(t, err) // Increase at least 1 wei in gas price, gas tip cap and gas fee cap. // Bumping the fees enough times to let the transaction be included in a block. for i := 0; i < 30; i++ { - tx, err = s.resubmitTransaction(tx, 0, 0) + tx, err = s.createReplacingTransaction(tx, 0, 0) + assert.NoError(t, err) + err = s.client.SendTransaction(s.ctx, tx) assert.NoError(t, err) } @@ -369,10 +373,14 @@ func testResubmitNonZeroGasPriceTransaction(t *testing.T) { sidecar, err = makeSidecar(txBlob[i]) assert.NoError(t, err) } - tx, err := s.createAndSendTx(feeData, &common.Address{}, nil, sidecar, nil) + tx, err := s.createTx(feeData, &common.Address{}, nil, sidecar, nil) assert.NoError(t, err) assert.NotNil(t, tx) - resubmittedTx, err := s.resubmitTransaction(tx, 0, 0) + err = s.client.SendTransaction(s.ctx, tx) + assert.NoError(t, err) + resubmittedTx, err := s.createReplacingTransaction(tx, 0, 0) + assert.NoError(t, err) + err = s.client.SendTransaction(s.ctx, resubmittedTx) assert.NoError(t, err) assert.Eventually(t, func() bool { @@ -412,10 +420,14 @@ func testResubmitUnderpricedTransaction(t *testing.T) { gasFeeCap: big.NewInt(1000000000), gasLimit: 50000, } - tx, err := s.createAndSendTx(feeData, &common.Address{}, nil, nil, nil) + tx, err := s.createTx(feeData, &common.Address{}, nil, nil, nil) assert.NoError(t, err) assert.NotNil(t, tx) - _, err = s.resubmitTransaction(tx, 0, 0) + err = s.client.SendTransaction(s.ctx, tx) + assert.NoError(t, err) + resubmittedTx, err := s.createReplacingTransaction(tx, 0, 0) + assert.NoError(t, err) + err = s.client.SendTransaction(s.ctx, resubmittedTx) assert.Error(t, err, "replacement transaction underpriced") assert.Eventually(t, func() bool { @@ -462,7 +474,9 @@ func testResubmitDynamicFeeTransactionWithRisingBaseFee(t *testing.T) { // bump the basefee by 10x baseFeePerGas *= 10 // resubmit and check that the gas fee has been adjusted accordingly - newTx, err := s.resubmitTransaction(tx, baseFeePerGas, 0) + resubmittedTx, err := s.createReplacingTransaction(tx, baseFeePerGas, 0) + assert.NoError(t, err) + err = s.client.SendTransaction(s.ctx, resubmittedTx) assert.NoError(t, err) maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice) @@ -471,7 +485,7 @@ func testResubmitDynamicFeeTransactionWithRisingBaseFee(t *testing.T) { expectedGasFeeCap = maxGasPrice } - assert.Equal(t, expectedGasFeeCap.Uint64(), newTx.GasFeeCap().Uint64()) + assert.Equal(t, expectedGasFeeCap.Uint64(), resubmittedTx.GasFeeCap().Uint64()) s.Stop() } @@ -511,7 +525,9 @@ func testResubmitBlobTransactionWithRisingBaseFeeAndBlobBaseFee(t *testing.T) { baseFeePerGas *= 10 blobBaseFeePerGas *= 10 // resubmit and check that the gas fee has been adjusted accordingly - newTx, err := s.resubmitTransaction(tx, baseFeePerGas, blobBaseFeePerGas) + resubmittedTx, err := s.createReplacingTransaction(tx, baseFeePerGas, blobBaseFeePerGas) + assert.NoError(t, err) + err = s.client.SendTransaction(s.ctx, resubmittedTx) assert.NoError(t, err) maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice) @@ -526,8 +542,8 @@ func testResubmitBlobTransactionWithRisingBaseFeeAndBlobBaseFee(t *testing.T) { expectedBlobGasFeeCap = maxBlobGasPrice } - assert.Equal(t, expectedGasFeeCap.Uint64(), newTx.GasFeeCap().Uint64()) - assert.Equal(t, expectedBlobGasFeeCap.Uint64(), newTx.BlobGasFeeCap().Uint64()) + assert.Equal(t, expectedGasFeeCap.Uint64(), resubmittedTx.GasFeeCap().Uint64()) + assert.Equal(t, expectedBlobGasFeeCap.Uint64(), resubmittedTx.BlobGasFeeCap().Uint64()) s.Stop() }