Skip to content

feat(sender): do not bump gas price for nonce-gapped transactions #1458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"runtime/debug"
)

var tag = "v4.4.37"
var tag = "v4.4.38"

var commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
Expand Down
16 changes: 16 additions & 0 deletions rollup/internal/controller/sender/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,22 @@ func (s *Sender) checkPendingTransaction() {
}
} else if txnToCheck.Status == types.TxStatusPending && // Only try resubmitting a new transaction based on gas price of the last transaction (status pending) with same ContextID.
s.config.EscalateBlocks+txnToCheck.SubmitBlockNumber <= blockNumber {

// blockNumber is the block number with "latest" tag, so we need to check the current nonce of the sender address to ensure that the previous transaction has been confirmed.
// otherwise it's not very necessary to bump the gas price. Also worth noting is that, during bumping gas prices, the sender would consider the new basefee and blobbasefee of L1.
currentNonce, err := s.client.NonceAt(s.ctx, common.HexToAddress(txnToCheck.SenderAddress), new(big.Int).SetUint64(blockNumber))
if err != nil {
log.Error("failed to get current nonce from node", "address", txnToCheck.SenderAddress, "blockNumber", blockNumber, "err", err)
return
}

// 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())
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())
Expand Down
63 changes: 63 additions & 0 deletions rollup/internal/controller/sender/sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/rand"
"errors"
"fmt"
"math"
"math/big"
"os"
"testing"
Expand Down Expand Up @@ -130,6 +131,7 @@ func TestSender(t *testing.T) {
t.Run("test resubmit under priced transaction", testResubmitUnderpricedTransaction)
t.Run("test resubmit dynamic fee transaction with rising base fee", testResubmitDynamicFeeTransactionWithRisingBaseFee)
t.Run("test resubmit blob transaction with rising base fee and blob base fee", testResubmitBlobTransactionWithRisingBaseFeeAndBlobBaseFee)
t.Run("test resubmit nonce gapped transaction", testResubmitNonceGappedTransaction)
t.Run("test check pending transaction tx confirmed", testCheckPendingTransactionTxConfirmed)
t.Run("test check pending transaction resubmit tx confirmed", testCheckPendingTransactionResubmitTxConfirmed)
t.Run("test check pending transaction replaced tx confirmed", testCheckPendingTransactionReplacedTxConfirmed)
Expand Down Expand Up @@ -517,6 +519,67 @@ func testResubmitBlobTransactionWithRisingBaseFeeAndBlobBaseFee(t *testing.T) {
s.Stop()
}

func testResubmitNonceGappedTransaction(t *testing.T) {
for i, txType := range txTypes {
sqlDB, err := db.DB()
assert.NoError(t, err)
assert.NoError(t, migrate.ResetDB(sqlDB))

cfgCopy := *cfg.L2Config.RelayerConfig.SenderConfig

// Bump gas price, gas tip cap and gas fee cap just touch the minimum threshold of 10% (default config of geth).
cfgCopy.EscalateMultipleNum = 110
cfgCopy.EscalateMultipleDen = 100
cfgCopy.TxType = txType

// resubmit immediately if not nonce gapped
cfgCopy.Confirmations = rpc.LatestBlockNumber
cfgCopy.EscalateBlocks = 0

// stop background check pending transaction
cfgCopy.CheckPendingTime = math.MaxUint32

s, err := NewSender(context.Background(), &cfgCopy, privateKey, "test", "test", types.SenderTypeUnknown, db, nil)
assert.NoError(t, err)

patchGuard1 := gomonkey.ApplyMethodFunc(s.client, "SendTransaction", func(_ context.Context, _ *gethTypes.Transaction) error {
return nil
})

// simulating not confirmed transaction
patchGuard2 := gomonkey.ApplyMethodFunc(s.client, "TransactionReceipt", func(_ context.Context, hash common.Hash) (*gethTypes.Receipt, error) {
return nil, errors.New("simulated transaction receipt error")
})

_, err = s.SendTransaction("test-1", &common.Address{}, nil, txBlob[i], 0)
assert.NoError(t, err)

_, err = s.SendTransaction("test-2", &common.Address{}, nil, txBlob[i], 0)
assert.NoError(t, err)

s.checkPendingTransaction()

txs, err := s.pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), s.senderType, 10)
assert.NoError(t, err)
assert.Len(t, txs, 3)

assert.Equal(t, txs[0].Nonce, txs[1].Nonce)
assert.Equal(t, txs[0].Nonce+1, txs[2].Nonce)

// the first 2 transactions have the same nonce, with one replaced and another pending
assert.Equal(t, types.TxStatusReplaced, txs[0].Status)
assert.Equal(t, types.TxStatusPending, txs[1].Status)

// the third transaction has nonce + 1, which will not be replaced due to the nonce gap,
// thus the status should be pending
assert.Equal(t, types.TxStatusPending, txs[2].Status)

s.Stop()
patchGuard1.Reset()
patchGuard2.Reset()
}
}

func testCheckPendingTransactionTxConfirmed(t *testing.T) {
for _, txType := range txTypes {
sqlDB, err := db.DB()
Expand Down
Loading