Skip to content

Commit 83721a9

Browse files
holimanfjl
authored andcommitted
internal/ethapi: lock when auto-filling transaction nonce (#14483)
More context in the bug This solves the problems of transactions being submitted simultaneously, and getting the same nonce, due to the gap (due to signing) between nonce-issuance and nonce-update. With this PR, a lock will need to be acquired whenever a nonce is used, and released when the transaction is submitted or errors out.
1 parent a2f23ca commit 83721a9

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed

internal/ethapi/api.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"fmt"
2525
"math/big"
2626
"strings"
27+
"sync"
2728
"time"
2829

2930
"github.com/ethereum/go-ethereum/accounts"
@@ -890,6 +891,12 @@ type PublicTransactionPoolAPI struct {
890891
b Backend
891892
}
892893

894+
// nonceMutex is a global mutex for locking the nonce while a transaction
895+
// is being submitted. This should be used when a nonce has not been provided by the user,
896+
// and we get a nonce from the pools. The mutex prevents the (an identical nonce) from being
897+
// read again during the time that the first transaction is being signed.
898+
var nonceMutex sync.RWMutex
899+
893900
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
894901
func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI {
895902
return &PublicTransactionPoolAPI{b}
@@ -1170,6 +1177,14 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
11701177
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
11711178
// transaction pool.
11721179
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
1180+
1181+
if args.Nonce == nil {
1182+
// We'll need to set nonce from pool, and thus we need to lock here
1183+
nonceMutex.Lock()
1184+
defer nonceMutex.Unlock()
1185+
1186+
}
1187+
11731188
// Set some sanity defaults and terminate on failure
11741189
if err := args.setDefaults(ctx, s.b); err != nil {
11751190
return common.Hash{}, err
@@ -1257,6 +1272,14 @@ type SignTransactionResult struct {
12571272
// The node needs to have the private key of the account corresponding with
12581273
// the given from address and it needs to be unlocked.
12591274
func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) {
1275+
1276+
if args.Nonce == nil {
1277+
// We'll need to set nonce from pool, and thus we need to lock here
1278+
nonceMutex.Lock()
1279+
defer nonceMutex.Unlock()
1280+
1281+
}
1282+
12601283
if err := args.setDefaults(ctx, s.b); err != nil {
12611284
return nil, err
12621285
}

0 commit comments

Comments
 (0)