A transaction byte by byte
A full breakdown of a RingCT transaction's hex: every field, every function — on real and random data
A Monero transaction is three consecutive chunks of bytes: the prefix (inputs, outputs, extra), the RingCT base (fee, encrypted amounts, commitments) and the prunable part (Bulletproofs+, CLSAG, pseudo-outputs). Below is a tool that parses any v2 transaction byte by byte, computes the txid, checks the balance and — given a view key — finds "your" outputs and decrypts their amounts. For a random transaction it shows all the secrets, so you can see how every function is applied to actual numbers.
How to read the bytes
Monero's serialization is simple — no headers or field names, just values one after another. Two kinds of building blocks:
- varint — a variable-length integer: 7 bits per byte, the top bit means "another byte follows".
0x02= 2,0x80 0x01= 128,0x80 0xb5 0x18= 400000. - key / point / scalar — exactly 32 bytes, no length. Arrays of points whose length is known from context (e.g. CLSAG's
s, by ring size) also come without a length.
So the parser must know the format in advance: the RingCT type, read in the middle, decides how to read everything after it.
Which functions are applied where
| Field | Computed by | Formula | More |
|---|---|---|---|
| extra: R | sender | R = r·G | stealth addresses |
| output: P | sender | P = Hs(8·r·A ‖ i)·G + B | stealth addresses |
| view tag | sender | Keccak("view_tag" ‖ 8rA ‖ i)[0] | view tags |
| ecdhInfo | sender | v ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8] | Pedersen |
| outPk | sender | C = y·G + v·H, y = Hs("commitment_mask" ‖ Hs(8rA ‖ i)) | Pedersen |
| ring | sender's wallet | 16 global indices, gamma distribution over age | decoys |
| key image | input owner | I = x·Hp(P) | key image |
| pseudoOuts | sender | C̃ = y'·G + v·H, masks chosen so Σ C̃ = Σ C + fee·H | RingCT |
| CLSAG | input owner | a ring of challenges over P_i and C_i − C̃ | ring signatures |
| Bulletproofs+ | sender | proof that 0 ≤ v < 2⁶⁴ for all outputs | range proofs |
What a node does with these bytes
- Parses them; any extra or missing byte means rejection.
- Checks the key images are new and in the correct subgroup.
- Rebuilds the 16 global indices from the offsets and loads those outputs' keys and commitments from its database.
- Checks the balance
Σ C̃ = Σ outPk + fee·H— the tool below does this too. - Verifies Bulletproofs+ and each input's CLSAG.
What the recipient's wallet does
- Takes
Rfrom extra and computes the shared secret8·a·R(one operation per transaction). - For each output: a hash → compare the view tag; no match — stop.
- Match — compare
Hs(8aR ‖ i)·G + Bwith the output key. - The output is ours — decrypt the amount, rebuild the mask and check
y·G + v·HagainstoutPk.
The tool shows all these steps with concrete numbers.
The parser was checked against monerod's JSON decoding of real type 5 and type 6 transactions and a coinbase transaction: every field and the txid match. The "real tx from mainnet" button loads one of them — compare the txid with any block explorer.
What will an FCMP++ transaction look like?
The FCMP++ transaction format may still change before the hard fork, so there's no byte parser for it here. But from the current implementation the overall structure is clear — and it's surprisingly similar to today's:
| In RingCT | In FCMP++ | Functions |
|---|---|---|
| 16 offsets per input | a reference to the block whose tree the proof is built against | Curve Tree root over all outputs |
key image I = x·Hp(P) | the same L = x·Hp(O) | double spends caught as before |
pseudo-output C̃ | C̃ = C + r_c·G — part of the rerandomized tuple | rerandomization |
| CLSAG per input | SA+L per input (a few points and scalars) | generalized Schnorr |
| — | one membership proof for the whole transaction | Generalized Bulletproofs + divisors |
| 1-byte view tag, ECDH on Ed25519 | 3-byte view tag, X25519, Janus anchor | Carrot |
See rerandomization on actual numbers in the tool. Once the format is final, a byte-level breakdown of FCMP++ transactions will be added here too.