Skip to content

Commit

Permalink
core/node/crypto: refetch tx nonce in case chain rpc node reports non…
Browse files Browse the repository at this point in the history
…ce issues
  • Loading branch information
bas-vk committed Sep 10, 2024
1 parent 8bbec46 commit 197c075
Showing 1 changed file with 30 additions and 11 deletions.
41 changes: 30 additions & 11 deletions core/node/crypto/chain_txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"errors"
"math/big"
"strings"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -79,9 +80,9 @@ type (
walletBalanceLastTimeChecked time.Time
walletBalance prometheus.Gauge

// mu guards lastPendingTx that is used to determine the tx nonce
mu sync.Mutex
lastPendingTx *txPoolPendingTransaction
// mu guards lastNonce that is used to determine the tx nonce
mu sync.Mutex
lastNonce *uint64
}

// txPoolPendingTransaction represents a transaction that is submitted to the chain but no receipt was retrieved.
Expand Down Expand Up @@ -478,8 +479,8 @@ func (tx *txPoolPendingTransaction) TransactionHash() common.Hash {

// caller is expected to hold a lock on r.mu
func (r *transactionPool) nextNonce(ctx context.Context) (uint64, error) {
if r.lastPendingTx != nil {
return r.lastPendingTx.tx.Nonce() + 1, nil
if r.lastNonce != nil {
return *r.lastNonce + 1, nil
}
return r.client.PendingNonceAt(ctx, r.wallet.Address)
}
Expand Down Expand Up @@ -507,17 +508,24 @@ func (r *transactionPool) Submit(
name string,
createTx CreateTransaction,
) (TransactionPoolPendingTransaction, error) {
var span trace.Span
// lock to prevent tx.Nonce collisions
r.mu.Lock()
defer r.mu.Unlock()

return r.submitNoLock(ctx, name, createTx, true)
}
func (r *transactionPool) submitNoLock(
ctx context.Context,
name string,
createTx CreateTransaction,
canRetry bool,
) (TransactionPoolPendingTransaction, error) {
var span trace.Span
if r.tracer != nil {
ctx, span = r.tracer.Start(ctx, "txpool_submit")
defer span.End()
}

// lock to prevent tx.Nonce collisions
r.mu.Lock()
defer r.mu.Unlock()

nonce, err := r.nextNonce(ctx)
if err != nil {
return nil, err
Expand Down Expand Up @@ -548,6 +556,13 @@ func (r *transactionPool) Submit(
}

if err := r.client.SendTransaction(ctx, tx); err != nil {
// force fetching the latest nonce from the rpc node again when it was reported to be too low. This can be
// caused by the chain rpc node lagging behind when the tx pool fetched the nonce. When the chain rpc node
// caught up the fetched nonce can be too low. Fetch the nonce again recovers from this scenario.
if canRetry && strings.Contains(strings.ToLower(err.Error()), "nonce too low") {
r.lastNonce = nil
return r.submitNoLock(ctx, name, createTx, false)
}
return nil, err
}

Expand All @@ -564,7 +579,11 @@ func (r *transactionPool) Submit(
listener: make(chan *types.Receipt, 1),
}

r.lastPendingTx = pendingTx
if r.lastNonce == nil {
r.lastNonce = new(uint64)
}
*r.lastNonce = pendingTx.tx.Nonce()

r.pendingTransactionPool.addPendingTx <- pendingTx

// metrics
Expand Down

0 comments on commit 197c075

Please sign in to comment.