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:
parent
195982c950
commit
6bfc1bed08
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue