The Nonce: What a Failed Transaction Does to It, and How This Deployment Had to Be Defended From Itself
What it is for
A signed transaction is a bearer object. Anybody who sees it can send it again.
The nonce stops that. Each account carries a counter; a transaction names the value it expects, and the chain refuses it otherwise. Once used, that value never works again.
On this chain it is the whole of the replay protection, because the signature binds no chain identifier and no expiry.
The pipeline, in the order the executor runs it
one: verify the signature two: load the sender's account three: compare the counter four: work out the fee, and check the balance covers it five: deduct the fee and increment the counter six: run the operation
The boundary is between four and five, and it is the only thing a sender really needs to know.
So here is the rule, exactly
A transaction that fails at steps one to four costs nothing. No fee, no counter movement, no state written. The account is exactly as it was.
A transaction that gets past step four consumes the counter and the fee even if the operation then fails. A transfer for more than you hold is rejected by the handler at step six, and by then you have already paid.
The receipts bear this out. Transactions measured earlier in this estate that failed for want of the fee itself report a fee of zero, and one that could cover the fee but not the amount reports the fee charged. The ordering in the source and the receipts on the chain agree.
Which means "failed" is not one thing
Two transactions can both say failed and mean opposite things about your account.
A reader building against this should treat the fee field, not the status string, as the answer to "did this cost me anything". And a wallet that retries a failed transaction with the same counter value will succeed only in the first case, because in the second the counter has moved.
And the deployment had to be defended from itself
All four validators here share one database. Each engine executes the same proposed block against it.
So the first engine bumps the counter, and the second through fourth then hit the counter check and fail, producing a bogus rejection for a transaction that had already succeeded, and overwriting the good receipt with it.
The fix is a short-circuit: a transaction already carrying a receipt is not re-executed, and the existing receipt is returned. The comment states the reasoning, including why it is sound: the transaction identifier commits to the sender, the counter, the payload and the signature, so two transactions with the same identifier must have the same effect.
And the same comment says the check is a no-op in production, where each validator would have its own store. So the execution path exercised on this network is not quite the one production would use, which is a fair thing to know about any development deployment and is rarely written down.
The counter can also lag its own receipt
The client library retries once when a transaction is rejected for a stale counter, and the reason is recorded in the code: a create followed immediately by another operation from the same signer failed with a counter mismatch, because the lookup had not caught up with the previous receipt.
So reading the counter, then signing, then sending is a race, and the library papers over it with one retry rather than pretending it does not exist.
And a correction this page owes
We published that seven operation types are fee-exempt. It is eight, and the eighth is registering a compute node rather than an identifier operation.
The cause is worth more than the count: we read the exemption list in a second, newer copy of the executor whose own comment says it was ported verbatim from the live one. It was not. A comment claiming a verbatim port is not a verbatim port, and the page has been corrected.
What you can check yourself
fetch any failed transaction's receipt and read
fee_paidrather thanstatus
Zero means it never reached the fee step and your counter did not move. Non-zero means it did both, and the next transaction from that account needs the next value.
Keep reading
- Fork Choice: The Rule Is the Highest Certificate, and On This Deployment There Is One Database Behind All Four Validators
- The Genesis Block: You Cannot Obtain Ours, and the Hash That Claims to Identify It Is a Hash of Its Name
- The State Root: It Moves When State Moves and Holds When It Does Not, and You Cannot Open It
- Gas, Fixed Fees, and the 70/20/10 Split