How a Transaction Is Signed: Four Fields, a Strict Verifier That Closes a Real Attack, and a Signature Over JSON
The envelope, from a real transaction
Fetch any transaction and you get four fields.
the sender's public key, thirty-two bytes a nonce, the sender's own counter a payload, naming the operation a signature, sixty-four bytes
that is exactly what the type declares and exactly what came back, with nothing else in the response.
What actually gets signed
Not the transaction. A hash of three things joined together: the public key, the nonce written out as eight little-endian bytes, and the payload rendered as JSON.
The signature is an ordinary Ed25519 signature over that hash.
And the transaction's identifier is a separate hash, over the whole signed object, so the identifier only exists once the signature does.
Your address is derived, not assigned
Take the public key, hash it, keep the first twenty bytes, encode in base58.
Nobody issues you an address. Anybody holding your public key can compute it, and nobody can go the other way.
Note that it is a truncation. Twenty bytes of a thirty-two-byte hash is a deliberate size trade-off, and it is the same choice most chains make.
The verifier is the strict one, and this is the best thing on the page
Ed25519 libraries usually offer two verification functions. This code uses the strict one, and the comment beside it says exactly why.
Under non-strict verification, an all-zeros public key together with an all-zeros signature verifies against anything. The equation is vacuously satisfied. On a chain, that means somebody submits a transaction claiming a sender key of all zeros and it passes.
Strict verification rejects it, along with other small-order keys and with malleable signatures in general.
Somebody thought about this, chose correctly, and wrote down the reason for the next reader. That is worth more than the choice on its own, and this estate reports it as loudly as it reports gaps.
It has a second benefit: because malleable signatures are refused, a given intent from an honest signer has one signature and therefore one transaction identifier.
And the pre-image is a JSON rendering
The payload is serialized to JSON, and the bytes of that JSON are what get hashed.
So a signature verifies only if two independent JSON encoders produce identical bytes. The chain uses one language's serializer. The client library uses another language's.
This is not hypothetical, and our own code shows it. The client carries a test that pins the field ORDER of one payload type, and a separate guard that re-checks a key in the envelope against the same key inside the payload. Both exist because the exact bytes matter.
The practical trap is large integers. A whole-number amount beyond the range a browser number represents exactly renders differently, and the signature silently stops matching. A canonical byte encoding rather than a JSON rendering removes the whole class, and that is a signing-format change rather than a fix.
Replay protection is the nonce, and only the nonce
Every account carries a counter. A transaction names the counter value it expects, and the chain refuses it otherwise.
The check happens before anything is taken. A transaction with the wrong counter produces a failed receipt without the fee being charged, which is the right order.
And the counter is the whole of the protection. The signed message names no chain, so it is the per-chain counter, and nothing in the signature itself, that keeps a transaction where it was meant to go.
What you can check yourself
solidus_getBlockat a height with a transaction, thensolidus_getTransactionon the hash
Count the fields. Then look for one naming the chain, or a timestamp, or an expiry. There are none, and the counter is doing all of that work alone.
Keep reading
- Anchor Transactions: The Word Names Three Different Operations, and Only One of Them Puts Anything Readable On This Chain
- Two Chains, Not Two Copies: Their Public Interfaces Do Not Overlap At All, and a Signature Is Not Bound To Either
- BLS Aggregation Behind Finality: Real Cryptography, a Certificate Nobody Outside Can See, and a Staking Path That Cannot Admit You

