chore: read and use nonce from network instead from the local cache when sending tx

Reading the Nonce from the local cache may be incorrect if the tx is made out of the Status app or
if Status app sends a tx prepared by the dapp (via WalletConnect). A submitted tx with a wrong Nonce
results in a failing tx, that's why we need to read the Nonce from the network.
This commit is contained in:
Sale Djenic 2023-12-19 14:38:01 +01:00 committed by saledjenic
parent 195982c950
commit 6bfc1bed08
10 changed files with 58 additions and 255 deletions

View File

@ -96,5 +96,5 @@ type Bridge interface {
CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error)
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
GetContractAddress(network *params.Network, token *token.Token) *common.Address
BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, transactions.UnlockNonceFunc, error)
BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error)
}

View File

@ -362,9 +362,8 @@ func (s *CBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.Sel
return types.Hash(tx.Hash()), nil
}
func (s *CBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, transactions.UnlockNonceFunc, error) {
tx, err := s.sendOrBuild(sendArgs, nil)
return tx, nil, err
func (s *CBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
return s.sendOrBuild(sendArgs, nil)
}
func (s *CBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {

View File

@ -98,20 +98,20 @@ func (s *ERC721TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwor
return uint64(increasedEstimation), nil
}
func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, unlock transactions.UnlockNonceFunc, err error) {
func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
if err != nil {
return tx, nil, err
return tx, err
}
contract, err := collectibles.NewCollectibles(common.Address(*sendArgs.ERC721TransferTx.To), ethClient)
if err != nil {
return tx, nil, err
return tx, err
}
nonce, unlock, err := s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC721TransferTx.From)
nonce, err := s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC721TransferTx.From)
if err != nil {
return tx, nil, err
return tx, err
}
argNonce := hexutil.Uint64(nonce)
@ -120,23 +120,18 @@ func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn
tx, err = contract.SafeTransferFrom(txOpts, common.Address(sendArgs.ERC721TransferTx.From),
sendArgs.ERC721TransferTx.Recipient,
sendArgs.ERC721TransferTx.TokenID.ToInt())
return tx, unlock, err
return tx, err
}
func (s *ERC721TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
tx, unlock, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount))
defer func() {
if unlock != nil {
unlock(err == nil, tx.Nonce())
}
}()
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount))
if err != nil {
return hash, err
}
return types.Hash(tx.Hash()), nil
}
func (s *ERC721TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, transactions.UnlockNonceFunc, error) {
func (s *ERC721TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
return s.sendOrBuild(sendArgs, nil)
}

View File

@ -226,15 +226,15 @@ func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Tok
return &address
}
func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, unlock transactions.UnlockNonceFunc, err error) {
func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
fromNetwork := h.contractMaker.RPCClient.NetworkManager.Find(sendArgs.ChainID)
if fromNetwork == nil {
return tx, nil, err
return tx, err
}
nonce, unlock, err := h.transactor.NextNonce(h.contractMaker.RPCClient, sendArgs.ChainID, sendArgs.HopTx.From)
nonce, err := h.transactor.NextNonce(h.contractMaker.RPCClient, sendArgs.ChainID, sendArgs.HopTx.From)
if err != nil {
return tx, nil, err
return tx, err
}
argNonce := hexutil.Uint64(nonce)
@ -243,26 +243,21 @@ func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.Signe
token := h.tokenManager.FindToken(fromNetwork, sendArgs.HopTx.Symbol)
if fromNetwork.Layer == 1 {
tx, err = h.sendToL2(sendArgs.ChainID, sendArgs.HopTx, signerFn, token)
return tx, unlock, err
return tx, err
}
tx, err = h.swapAndSend(sendArgs.ChainID, sendArgs.HopTx, signerFn, token)
return tx, unlock, err
return tx, err
}
func (h *HopBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
tx, unlock, err := h.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.HopTx.From, verifiedAccount))
defer func() {
if unlock != nil {
unlock(err == nil, tx.Nonce())
}
}()
tx, err := h.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.HopTx.From, verifiedAccount))
if err != nil {
return types.Hash{}, err
}
return types.Hash(tx.Hash()), nil
}
func (h *HopBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, transactions.UnlockNonceFunc, error) {
func (h *HopBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
return h.sendOrBuild(sendArgs, nil)
}

View File

@ -88,7 +88,7 @@ func (s *TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *acco
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, transactions.UnlockNonceFunc, error) {
func (s *TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
}

View File

@ -40,7 +40,6 @@ type TransactionDescription struct {
chainID uint64
builtTx *ethTypes.Transaction
signature []byte
unlock transactions.UnlockNonceFunc
}
type TransactionManager struct {
@ -160,13 +159,7 @@ func (tm *TransactionManager) BuildTransaction(chainID uint64, sendArgs transact
return nil, err
}
txBeingSigned, unlock, err := tm.transactor.ValidateAndBuildTransaction(chainID, sendArgs)
// We have to unlock the nonce, cause we don't know what will happen on the client side (will user accept/reject) an action.
if unlock != nil {
defer func() {
unlock(false, 0)
}()
}
txBeingSigned, err := tm.transactor.ValidateAndBuildTransaction(chainID, sendArgs)
if err != nil {
return nil, err
}
@ -214,13 +207,7 @@ func (tm *TransactionManager) BuildTransaction(chainID uint64, sendArgs transact
}
func (tm *TransactionManager) BuildRawTransaction(chainID uint64, sendArgs transactions.SendTxArgs, signature []byte) (response *TxResponse, err error) {
tx, unlock, err := tm.transactor.BuildTransactionWithSignature(chainID, sendArgs, signature)
// We have to unlock the nonce, cause we don't know what will happen on the client side (will user accept/reject) an action.
if unlock != nil {
defer func() {
unlock(false, 0)
}()
}
tx, err := tm.transactor.BuildTransactionWithSignature(chainID, sendArgs, signature)
if err != nil {
return nil, err
}

View File

@ -293,11 +293,6 @@ func (tm *TransactionManager) ProceedWithTransactionsSignatures(ctx context.Cont
hashes := make(map[uint64][]types.Hash)
for _, desc := range tm.transactionsForKeycardSingning {
hash, err := tm.transactor.AddSignatureToTransactionAndSend(desc.chainID, desc.builtTx, desc.signature)
if desc.unlock != nil {
defer func() {
desc.unlock(err == nil, desc.builtTx.Nonce())
}()
}
if err != nil {
return nil, err
}
@ -376,11 +371,8 @@ func (tm *TransactionManager) buildTransactions(bridges map[string]bridge.Bridge
tm.transactionsForKeycardSingning = make(map[common.Hash]*TransactionDescription)
var hashes []string
for _, bridgeTx := range tm.transactionsBridgeData {
builtTx, unlock, err := bridges[bridgeTx.BridgeName].BuildTransaction(bridgeTx)
builtTx, err := bridges[bridgeTx.BridgeName].BuildTransaction(bridgeTx)
if err != nil {
if unlock != nil {
unlock(false, 0) // unlock nonce in case of an error, otherwise keep it locked, until the transaction is sent
}
return hashes, err
}
@ -390,7 +382,6 @@ func (tm *TransactionManager) buildTransactions(bridges map[string]bridge.Bridge
tm.transactionsForKeycardSingning[txHash] = &TransactionDescription{
chainID: bridgeTx.ChainID,
builtTx: builtTx,
unlock: unlock,
}
hashes = append(hashes, txHash.String())

View File

@ -1,73 +0,0 @@
package transactions
import (
"context"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/eth-node/types"
)
type UnlockNonceFunc func(inc bool, n uint64)
type Nonce struct {
addrLock *AddrLocker
localNonce map[uint64]*sync.Map
}
func NewNonce() *Nonce {
return &Nonce{
addrLock: &AddrLocker{},
localNonce: make(map[uint64]*sync.Map),
}
}
func (n *Nonce) Next(rpcWrapper *rpcWrapper, from types.Address) (uint64, UnlockNonceFunc, error) {
n.addrLock.LockAddr(from)
current, err := n.GetCurrent(rpcWrapper, from)
if err != nil {
return 0, nil, err
}
unlock := func(inc bool, nonce uint64) {
if inc {
if _, ok := n.localNonce[rpcWrapper.chainID]; !ok {
n.localNonce[rpcWrapper.chainID] = &sync.Map{}
}
n.localNonce[rpcWrapper.chainID].Store(from, nonce+1)
}
n.addrLock.UnlockAddr(from)
}
return current, unlock, nil
}
func (n *Nonce) GetCurrent(rpcWrapper *rpcWrapper, from types.Address) (uint64, error) {
var (
localNonce uint64
remoteNonce uint64
)
if _, ok := n.localNonce[rpcWrapper.chainID]; !ok {
n.localNonce[rpcWrapper.chainID] = &sync.Map{}
}
// get the local nonce
if val, ok := n.localNonce[rpcWrapper.chainID].Load(from); ok {
localNonce = val.(uint64)
}
// get the remote nonce
ctx := context.Background()
remoteNonce, err := rpcWrapper.PendingNonceAt(ctx, common.Address(from))
if err != nil {
return 0, err
}
// if upstream node returned nonce higher than ours we will use it, as it probably means
// that another client was used for sending transactions
if remoteNonce > localNonce {
return remoteNonce, nil
}
return localNonce, nil
}

View File

@ -49,7 +49,6 @@ type Transactor struct {
sendTxTimeout time.Duration
rpcCallTimeout time.Duration
networkID uint64
nonce *Nonce
log log.Logger
}
@ -57,7 +56,6 @@ type Transactor struct {
func NewTransactor() *Transactor {
return &Transactor{
sendTxTimeout: sendTxTimeout,
nonce: NewNonce(),
log: log.New("package", "status-go/transactions.Manager"),
}
}
@ -77,9 +75,10 @@ func (t *Transactor) SetRPC(rpcClient *rpc.Client, timeout time.Duration) {
t.rpcCallTimeout = timeout
}
func (t *Transactor) NextNonce(rpcClient *rpc.Client, chainID uint64, from types.Address) (uint64, UnlockNonceFunc, error) {
func (t *Transactor) NextNonce(rpcClient *rpc.Client, chainID uint64, from types.Address) (uint64, error) {
wrapper := newRPCWrapper(rpcClient, chainID)
return t.nonce.Next(wrapper, from)
ctx := context.Background()
return wrapper.PendingNonceAt(ctx, common.Address(from))
}
func (t *Transactor) EstimateGas(network *params.Network, from common.Address, to common.Address, value *big.Int, input []byte) (uint64, error) {
@ -110,9 +109,9 @@ func (t *Transactor) SendTransactionWithChainID(chainID uint64, sendArgs SendTxA
return
}
func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs) (tx *gethtypes.Transaction, unlock UnlockNonceFunc, err error) {
func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs) (tx *gethtypes.Transaction, err error) {
wrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
tx, unlock, err = t.validateAndBuildTransaction(wrapper, sendArgs)
tx, err = t.validateAndBuildTransaction(wrapper, sendArgs)
return
}
@ -167,16 +166,7 @@ func (t *Transactor) AddSignatureToTransactionAndSend(chainID uint64, tx *gethty
// It's different from eth_sendRawTransaction because it receives a signature and not a serialized transaction with signature.
// Since the transactions is already signed, we assume it was validated and used the right nonce.
func (t *Transactor) BuildTransactionAndSendWithSignature(chainID uint64, args SendTxArgs, sig []byte) (hash types.Hash, err error) {
txWithSignature, unlock, err := t.BuildTransactionWithSignature(chainID, args, sig)
if unlock != nil {
defer func() {
var nonce uint64
if txWithSignature != nil {
nonce = txWithSignature.Nonce()
}
unlock(err == nil, nonce)
}()
}
txWithSignature, err := t.BuildTransactionWithSignature(chainID, args, sig)
if err != nil {
return hash, err
}
@ -185,32 +175,31 @@ func (t *Transactor) BuildTransactionAndSendWithSignature(chainID uint64, args S
return hash, err
}
func (t *Transactor) BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, UnlockNonceFunc, error) {
func (t *Transactor) BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, error) {
if !args.Valid() {
return nil, nil, ErrInvalidSendTxArgs
return nil, ErrInvalidSendTxArgs
}
if len(sig) != ValidSignatureSize {
return nil, nil, ErrInvalidSignatureSize
return nil, ErrInvalidSignatureSize
}
tx := t.buildTransaction(args)
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
expectedNonce, unlock, err := t.nonce.Next(rpcWrapper, args.From)
expectedNonce, err := t.NextNonce(t.rpcWrapper.RPCClient, chainID, args.From)
if err != nil {
return nil, nil, err
return nil, err
}
if tx.Nonce() != expectedNonce {
return nil, unlock, &ErrBadNonce{tx.Nonce(), expectedNonce}
return nil, &ErrBadNonce{tx.Nonce(), expectedNonce}
}
txWithSignature, err := t.AddSignatureToTransaction(chainID, tx, sig)
if err != nil {
return nil, unlock, err
return nil, err
}
return txWithSignature, unlock, nil
return txWithSignature, nil
}
func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs, hash types.Hash, err error) {
@ -220,13 +209,10 @@ func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs,
validatedArgs = args
nonce, unlock, err := t.nonce.Next(t.rpcWrapper, args.From)
nonce, err := t.NextNonce(t.rpcWrapper.RPCClient, t.rpcWrapper.chainID, args.From)
if err != nil {
return validatedArgs, hash, err
}
if unlock != nil {
defer unlock(false, 0)
}
gasPrice := (*big.Int)(args.GasPrice)
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
@ -315,18 +301,18 @@ func (t *Transactor) validateAccount(args SendTxArgs, selectedAccount *account.S
return nil
}
func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args SendTxArgs) (tx *gethtypes.Transaction, unlock UnlockNonceFunc, err error) {
func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args SendTxArgs) (tx *gethtypes.Transaction, err error) {
if !args.Valid() {
return tx, nil, ErrInvalidSendTxArgs
return tx, ErrInvalidSendTxArgs
}
var nonce uint64
if args.Nonce != nil {
nonce = uint64(*args.Nonce)
} else {
nonce, unlock, err = t.nonce.Next(rpcWrapper, args.From)
nonce, err = t.NextNonce(rpcWrapper.RPCClient, rpcWrapper.chainID, args.From)
if err != nil {
return tx, nil, err
return tx, err
}
}
@ -337,7 +323,7 @@ func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args Se
if !args.IsDynamicFeeTx() && args.GasPrice == nil {
gasPrice, err = rpcWrapper.SuggestGasPrice(ctx)
if err != nil {
return tx, unlock, err
return tx, err
}
}
@ -365,15 +351,16 @@ func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args Se
Data: args.GetInput(),
})
if err != nil {
return tx, unlock, err
return tx, err
}
if gas < defaultGas {
t.log.Info("default gas will be used because estimated is lower", "estimated", gas, "default", defaultGas)
gas = defaultGas
}
}
tx = t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
return tx, unlock, nil
return tx, nil
}
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs) (hash types.Hash, err error) {
@ -381,12 +368,7 @@ func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccoun
return hash, err
}
tx, unlock, err := t.validateAndBuildTransaction(rpcWrapper, args)
defer func() {
if unlock != nil {
unlock(err == nil, tx.Nonce())
}
}()
tx, err := t.validateAndBuildTransaction(rpcWrapper, args)
if err != nil {
return hash, err
}

View File

@ -1,11 +1,9 @@
package transactions
import (
"errors"
"fmt"
"math/big"
"reflect"
"sync"
"testing"
"time"
@ -246,92 +244,30 @@ func (s *TransactorSuite) TestAccountMismatch() {
s.EqualError(err, ErrInvalidTxSender.Error())
}
// TestLocalNonce verifies that local nonce will be used unless
// upstream nonce is updated and higher than a local
// in test we will run 3 transaction with nonce zero returned by upstream
// node, after each call local nonce will be incremented
// then, we return higher nonce, as if another node was used to send 2 transactions
// upstream nonce will be equal to 5, we update our local counter to 5+1
// as the last step, we verify that if tx failed nonce is not updated
func (s *TransactorSuite) TestLocalNonce() {
txCount := 3
chainID := s.nodeConfig.NetworkID
key, _ := gethcrypto.GenerateKey()
selectedAccount := &account.SelectedExtKey{
Address: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
AccountKey: &types.Key{PrivateKey: key},
}
nonce := hexutil.Uint64(0)
for i := 0; i < txCount; i++ {
args := SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
}
s.setupTransactionPoolAPI(args, nonce, hexutil.Uint64(i), selectedAccount, nil)
_, err := s.manager.SendTransaction(args, selectedAccount)
s.NoError(err)
resultNonce, _ := s.manager.nonce.localNonce[chainID].Load(args.From)
s.Equal(uint64(i)+1, resultNonce.(uint64))
}
nonce = hexutil.Uint64(5)
args := SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
}
s.setupTransactionPoolAPI(args, nonce, nonce, selectedAccount, nil)
_, err := s.manager.SendTransaction(args, selectedAccount)
s.NoError(err)
resultNonce, _ := s.manager.nonce.localNonce[chainID].Load(args.From)
s.Equal(uint64(nonce)+1, resultNonce.(uint64))
testErr := errors.New("test")
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(selectedAccount.Address)), gethrpc.PendingBlockNumber).Return(nil, testErr)
args = SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
}
_, err = s.manager.SendTransaction(args, selectedAccount)
s.EqualError(err, testErr.Error())
resultNonce, _ = s.manager.nonce.localNonce[chainID].Load(args.From)
s.Equal(uint64(nonce)+1, resultNonce.(uint64))
}
func (s *TransactorSuite) TestSendTransactionWithSignature() {
privKey, err := crypto.GenerateKey()
s.Require().NoError(err)
address := crypto.PubkeyToAddress(privKey.PublicKey)
scenarios := []struct {
localNonce hexutil.Uint64
txNonce hexutil.Uint64
expectError bool
nonceFromNetwork hexutil.Uint64
txNonce hexutil.Uint64
expectError bool
}{
{
localNonce: hexutil.Uint64(0),
txNonce: hexutil.Uint64(0),
expectError: false,
nonceFromNetwork: hexutil.Uint64(0),
txNonce: hexutil.Uint64(0),
expectError: false,
},
{
localNonce: hexutil.Uint64(1),
txNonce: hexutil.Uint64(0),
expectError: true,
},
{
localNonce: hexutil.Uint64(0),
txNonce: hexutil.Uint64(1),
expectError: true,
nonceFromNetwork: hexutil.Uint64(0),
txNonce: hexutil.Uint64(1),
expectError: true,
},
}
for _, scenario := range scenarios {
desc := fmt.Sprintf("local nonce: %d, tx nonce: %d, expect error: %v", scenario.localNonce, scenario.txNonce, scenario.expectError)
desc := fmt.Sprintf("nonceFromNetwork: %d, tx nonce: %d, expect error: %v", scenario.nonceFromNetwork, scenario.txNonce, scenario.expectError)
s.T().Run(desc, func(t *testing.T) {
nonce := scenario.txNonce
from := address
@ -341,8 +277,6 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
gasPrice := (*hexutil.Big)(big.NewInt(2000000000))
data := []byte{}
chainID := big.NewInt(int64(s.nodeConfig.NetworkID))
s.manager.nonce.localNonce[s.nodeConfig.NetworkID] = &sync.Map{}
s.manager.nonce.localNonce[s.nodeConfig.NetworkID].Store(address, uint64(scenario.localNonce))
args := SendTxArgs{
From: from,
To: &to,
@ -366,7 +300,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
s.txServiceMock.EXPECT().
GetTransactionCount(gomock.Any(), common.Address(address), gethrpc.PendingBlockNumber).
Return(&scenario.localNonce, nil)
Return(&scenario.nonceFromNetwork, nil)
if !scenario.expectError {
s.txServiceMock.EXPECT().
@ -377,15 +311,8 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
_, err = s.manager.BuildTransactionAndSendWithSignature(s.nodeConfig.NetworkID, args, sig)
if scenario.expectError {
s.Error(err)
// local nonce should not be incremented
resultNonce, _ := s.manager.nonce.localNonce[s.nodeConfig.NetworkID].Load(args.From)
s.Equal(uint64(scenario.localNonce), resultNonce.(uint64))
} else {
s.NoError(err)
// local nonce should be incremented
resultNonce, _ := s.manager.nonce.localNonce[s.nodeConfig.NetworkID].Load(args.From)
s.Equal(uint64(nonce)+1, resultNonce.(uint64))
}
})
}