Managing nonces with Nethereum
What are nonces?
The nonce is an important component of a transaction, it is an attribute of a an address that represents the number of transactions sent by that address. Nonces act as counters that keeps track of the number of transactions sent by an account.
Nonces have two functions: 1- Allowing to choose the order in which transactions will be executed. 2- Avoiding replay attacks.
In case 1, nonces enable to choose the order in which transactions will be executed by simply assigning nonces reflecting the order in which we want them processed (
0for the first
1 for the second, etc…).
In case 2, nonces prevent an attacker from copying one of our transactions and resending it until the account is drained (replay attack). Nonces make each transaction unique: there can only be one single transaction with a specific nonce, once it’s confirmed it cannot be “replayed”.
Common errors when working with nonces
Each node will process transactions from a specific account in a strict order according to the value of its nonce, hence the nonce value needs to be incremented precisely.
Keeping track of nonces is straightforward if all transactions originate from a single source/wallet handling the account, but things can get complicated if the account is managed by concurrent processes. When several wallets handle transactions for the same account, duplicates and gaps can happen, resulting in transactions being cancelled or held off.
Errors can also occur when Geth or Parity clients update their pending transactions queue too slowly.
Two main errors can occur with nonces:
Error 1/ Reusing nonce: if we send two transactions with the same nonce from the same account, one of the two will be rejected.
Error 2/ Gaps: if we leave a gap between the nonces that are attributed to two consecutive transactions, the last transaction will not be processed until this gap is closed.
Let’s take an example with a first transaction that would have nonce
123 and a second transaction with nonce
126. In that example, the transaction with nonce
126 wouldn’t be processed until transactions with nonces
125 are sent.
How Nethereum helps managing nonces
Nethereum simplifies nonce management thanks to the
NonceService keeps track of pending transactions thus preventing the errors mentionned above the below demonstrates how to leverage it.
In order to run the code contained in this document, we recommended the following setup: First, download the test chain matching your environment from https://github.com/nethereum/testchains
Start a geth chain (geth-clique-linux\, geth-clique-windows\ or geth-clique-mac\) using startgeth.bat (windows) or startgeth.sh (mac/linux). the chain is setup with the proof of authority consensus and will start the mining process immediately.
Then we will need to add
using Nethereum.Web3; using Nethereum.Web3.Accounts; using Nethereum.Web3.Accounts.Managed; using Nethereum.Signer; using Nethereum.Hex.HexConvertors.Extensions; using Nethereum.KeyStore; using Nethereum.Hex.HexConvertors; using Nethereum.Hex.HexTypes; using Nethereum.RPC.NonceServices; using Nethereum.RPC.TransactionReceipts; using System.Threading.Tasks; using Nethereum.RPC.Eth.Transactions; using Nethereum.RPC.Eth.DTOs;
In most cases, Nethereum takes care of incrementing the
nonce automatically (unless you need to sign a raw transaction manually, we’ll explain that in the next chapter).
Once you have loaded your private keys into your account, if Web3 is instantiated with that account, all the transactions will be made using the
TransactionManager, Contract deployment or Functions will be signed offline using the latest nonce.
This example shows what happens to the
nonce value when we send a transaction with a Nethereum account:
We first need to create an instance of an account, then use it to instantiate a
Let’s first declare our new
var privateKey = "0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7"; var account = new Nethereum.Web3.Accounts.Account(privateKey);
web3is the Web3 instance using the new
var web3 = new Web3(account);
We can now create an instance of the NonceService that will help us keep track of transaction.\ Please note: when using the TransactionManager the NonceService is started automatically. The below is mostly for the sake of demontration.
account.NonceService = new InMemoryNonceService(account.Address, web3.Client);
Let’s now examine what happens to the
nonce value before and after we send a transaction:
Before a transaction is sent:
NonceService keeps track of all transactions, including the ones still pending, making it easy to assign the right nonce to a transaction about to be sent.
Here is how to return the current number of transaction for the
account we declared earlier:
var currentNonce = await web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(account.Address, BlockParameter.CreatePending());
actualNonce includes the total number of transactions including the pending transactions which have been submitted but are yet to be confirmed.
It is also possible to return the next nonce that needs to be assigned to a future transaction, this nonce will be determined by the `NonceService` using the current nonce plus the pending transactions sent by our account:
var futureNonce = await account.NonceService.GetNextNonceAsync();
Now, let’s send a simple transaction, the right nonce will be automatically assigned to it by the
var recipientAddress = "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae"; var transaction = await web3.TransactionManager.SendTransactionAsync(account.Address, recipientAddress, new HexBigInteger(1));
After a transaction has been sent
Finally, using the NonceService, we can check if our transaction count has changed:
currentNonce = await web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(account.Address, BlockParameter.CreatePending());
As the above code demonstrates, the
nonce was automatically incremented, thanks to the use of
Sending a transaction with an arbitrary nonce
There are scenarios where we might want to supply a Nonce manually, for example if we want to sign a transaction completely offline. Here is how to verify the number of transactions sent by an account:
Let’s first create an object instance of
var OfflineTransactionSigner = new TransactionSigner();
We can now declare a variable representing the next nonce for our upcoming transaction:
futureNonce = await account.NonceService.GetNextNonceAsync();
Finally, let’s sign our transaction offline:
var encoded = OfflineTransactionSigner.SignTransaction(privateKey, recipientAddress, 10,futureNonce);
And finally, send our transaction:
var txId = await web3.Eth.Transactions.SendRawTransaction.SendRequestAsync("0x" + encoded);