Skip to content

Commit

Permalink
test(geometric txmgr): add test for sending tx with incorrect nonce
Browse files Browse the repository at this point in the history
  • Loading branch information
samlaf committed Aug 9, 2024
1 parent e8fa6a0 commit 3a12fcc
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 19 deletions.
15 changes: 13 additions & 2 deletions chainio/txmgr/geometric/geometric.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func (t *GeometricTxManager) ensureAnyTransactionConfirmed(
// TODO(samlaf): how to maintain these better? How do we know which errors to use and where they are
// returned from?
if errors.Is(err, ethereum.NotFound) || errors.Is(err, wallet.ErrReceiptNotYetAvailable) {
t.logger.Debug("Transaction not yet mined", "txID", txID, "txHash", tx.Hash().Hex(), "err", err)
// t.logger.Debug("Transaction not yet mined", "nonce", tx.Nonce(), "txID", txID, "txHash", tx.Hash().Hex(), "err", err)
} else if errors.Is(err, wallet.ErrTransactionFailed) {
t.logger.Debug("Transaction failed", "txID", txID, "txHash", tx.Hash().Hex(), "err", err)
// Remove the transaction from the list of transactions to query.
Expand Down Expand Up @@ -411,6 +411,8 @@ func (t *GeometricTxManager) monitorTransaction(ctx context.Context, req *txnReq
return err
}

queryTicker := time.NewTicker(t.params.GetTxReceiptTickerDuration)
defer queryTicker.Stop()
for {
err = rpcCallAttempt()
if err == nil {
Expand All @@ -437,7 +439,7 @@ func (t *GeometricTxManager) monitorTransaction(ctx context.Context, req *txnReq
"nonce",
req.tx.Nonce(),
)
newTx, err := t.speedUpTxn(ctx, req.tx)
newTx, err := t.speedUpTxn(ctx, req.tx, numSpeedUps)
if err != nil {
t.logger.Error("failed to speed up transaction", "err", err)
t.metrics.IncrementProcessedTxsTotal("failure")
Expand Down Expand Up @@ -478,6 +480,13 @@ func (t *GeometricTxManager) monitorTransaction(ctx context.Context, req *txnReq
t.metrics.IncrementProcessedTxsTotal("failure")
return nil, err
}

// Wait for the next round.
select {
case <-ctx.Done():
return receipt, ctx.Err()
case <-queryTicker.C:
}
}
}

Expand All @@ -486,6 +495,7 @@ func (t *GeometricTxManager) monitorTransaction(ctx context.Context, req *txnReq
func (t *GeometricTxManager) speedUpTxn(
ctx context.Context,
tx *types.Transaction,
numSpeedUps int,
) (*types.Transaction, error) {
// bump the current gasTip, and also reestimate it from the node, and take the highest value
var newGasTipCap *big.Int
Expand All @@ -512,6 +522,7 @@ func (t *GeometricTxManager) speedUpTxn(
}
t.logger.Info(
"increasing gas price",
"numSpeedUps", numSpeedUps,
"prevTxHash", tx.Hash().Hex(), "newTxHash", newTx.Hash().Hex(),
"nonce", tx.Nonce(),
"prevGasTipCap", tx.GasTipCap(), "newGasTipCap", newGasTipCap,
Expand Down
48 changes: 31 additions & 17 deletions chainio/txmgr/geometric/geometric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (h *testHarness) validateTxReceipt(t *testing.T, txReceipt *types.Receipt)
require.Equal(t, txReceipt, receiptFromEthBackend)
}

func newTestHarness(t *testing.T) *testHarness {
func newTestHarness(t *testing.T, geometricTxnManagerParams *GeometricTxnManagerParams) *testHarness {
logger := testutils.NewTestLogger()
ethBackend := NewFakeEthBackend()

Expand All @@ -49,16 +49,19 @@ func newTestHarness(t *testing.T) *testHarness {
skWallet, err := wallet.NewPrivateKeyWallet(ethBackend, signerFn, ecdsaAddr, logger)
require.NoError(t, err)

txmgr := NewGeometricTxnManager(ethBackend, skWallet, logger, NewNoopMetrics(), GeometricTxnManagerParams{
GetTxReceiptTickerDuration: 100 * time.Millisecond,
// set to 100 so that no buffer is added to the gasTipCap
// this way we can test that the txmgr will bump the gasTipCap to a working value
// and also simulate a congested network (with fakeEthBackend.congestedBlocks) where txs won't be mined
GasTipMultiplier: 100,
// set to 1 second (instead of default 2min) so that we can test that the txmgr will bump the gasTipCap to a
// working value
TxnBroadcastTimeout: 1 * time.Second,
})
if geometricTxnManagerParams == nil {
geometricTxnManagerParams = &GeometricTxnManagerParams{
GetTxReceiptTickerDuration: 100 * time.Millisecond,
// set to 100 so that no buffer is added to the gasTipCap
// this way we can test that the txmgr will bump the gasTipCap to a working value
// and also simulate a congested network (with fakeEthBackend.congestedBlocks) where txs won't be mined
GasTipMultiplier: 100,
// set to 1 second (instead of default 2min) so that we can test that the txmgr will bump the gasTipCap to a
// working value
TxnBroadcastTimeout: 1 * time.Second,
}
}
txmgr := NewGeometricTxnManager(ethBackend, skWallet, logger, NewNoopMetrics(), *geometricTxnManagerParams)

return &testHarness{
fakeEthBackend: ethBackend,
Expand All @@ -68,7 +71,7 @@ func newTestHarness(t *testing.T) *testHarness {

func TestGeometricTxManager(t *testing.T) {
t.Run("Send 1 tx", func(t *testing.T) {
h := newTestHarness(t)
h := newTestHarness(t, nil)

unsignedTx := newUnsignedEthTransferTx(0, nil)
ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
Expand All @@ -80,7 +83,7 @@ func TestGeometricTxManager(t *testing.T) {
})

t.Run("Send 1 tx to congested network", func(t *testing.T) {
h := newTestHarness(t)
h := newTestHarness(t, nil)
h.fakeEthBackend.setCongestedBlocks(3)
h.txmgr.params.TxnConfirmationTimeout = 1 * time.Second

Expand All @@ -94,7 +97,7 @@ func TestGeometricTxManager(t *testing.T) {
})

t.Run("gasFeeCap gets overwritten when sending tx", func(t *testing.T) {
h := newTestHarness(t)
h := newTestHarness(t, nil)

unsignedTx := newUnsignedEthTransferTx(0, big.NewInt(1))
ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
Expand All @@ -111,7 +114,7 @@ func TestGeometricTxManager(t *testing.T) {

t.Run("Send n=3 txs in parallel", func(t *testing.T) {
n := 3
h := newTestHarness(t)
h := newTestHarness(t, nil)

g := new(errgroup.Group)

Expand Down Expand Up @@ -140,7 +143,7 @@ func TestGeometricTxManager(t *testing.T) {

t.Run("Send n=3 txs sequentially", func(t *testing.T) {
n := uint64(3)
h := newTestHarness(t)
h := newTestHarness(t, nil)

for nonce := uint64(0); nonce < n; nonce++ {
tx := newUnsignedEthTransferTx(nonce, nil)
Expand All @@ -152,8 +155,19 @@ func TestGeometricTxManager(t *testing.T) {

})

t.Run("Send tx with incorrect nonce should result in context.DeadlineExceeded error", func(t *testing.T) {
h := newTestHarness(t, nil)

tx := newUnsignedEthTransferTx(uint64(100), nil)
ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err := h.txmgr.Send(ctxWithTimeout, tx)
require.Equal(t, context.DeadlineExceeded, err)

})

t.Run("Send 2 txs with same nonce", func(t *testing.T) {
h := newTestHarness(t)
h := newTestHarness(t, nil)

unsignedTx := newUnsignedEthTransferTx(0, nil)
ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
Expand Down

0 comments on commit 3a12fcc

Please sign in to comment.