Skip to content
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
4 changes: 3 additions & 1 deletion spamoor/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,9 @@ func (wallet *Wallet) MarkSkippedNonce(nonce uint64) {
wallet.nonceMutex.Lock()
defer wallet.nonceMutex.Unlock()

if nonce >= wallet.confirmedTxCount {
// Only nonces at or above the confirmed count are still pending and can be
// reused. Nonces below it are already confirmed on chain, so ignore them.
if nonce < wallet.confirmedTxCount {
return
}

Expand Down
52 changes: 52 additions & 0 deletions spamoor/wallet_skippednonce_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package spamoor

import "testing"

// A transaction that fails to submit keeps a nonce at or above the confirmed
// count. MarkSkippedNonce should record it so the next allocation reuses it
// instead of leaving a permanent gap.
func TestMarkSkippedNonceReusesFailedNonce(t *testing.T) {
w := &Wallet{}
w.confirmedTxCount = 100
w.pendingTxCount.Store(100)

n := w.GetNextNonce() // allocates 100, pending count moves to 101
if n != 100 {
t.Fatalf("expected first allocation 100, got %d", n)
}

w.MarkSkippedNonce(n) // submit failed, make the nonce reusable

if reused := w.GetNextNonce(); reused != 100 {
t.Fatalf("expected nonce 100 to be reused, got %d", reused)
}
}

// Nonces at or above the confirmed count are still pending, so they should be
// recorded for reuse.
func TestMarkSkippedNonceRecordsPendingNonces(t *testing.T) {
w := &Wallet{}
w.confirmedTxCount = 100
w.pendingTxCount.Store(103)

w.MarkSkippedNonce(101)
w.MarkSkippedNonce(102)

if len(w.skippedNonces) != 2 {
t.Fatalf("expected 101 and 102 to be recorded, got %v", w.skippedNonces)
}
}

// Nonces below the confirmed count are already confirmed on chain and cannot be
// reused, so they should be ignored.
func TestMarkSkippedNonceIgnoresConfirmedNonces(t *testing.T) {
w := &Wallet{}
w.confirmedTxCount = 100
w.pendingTxCount.Store(105)

w.MarkSkippedNonce(50)

if len(w.skippedNonces) != 0 {
t.Fatalf("expected a confirmed nonce to be ignored, got %v", w.skippedNonces)
}
}