Pedersen commitments
How to add up amounts nobody can see and check that the balance holds
An amount v is hidden in the point C = y·G + v·H, where y is a random "mask". Commitments can be added: the sum of commitments commits to the sum of amounts. The network checks that Σ inputs − Σ outputs − fee·H commits to zero without learning any amount.
What a commitment is
A commitment is a "sealed envelope with a number". It has two properties:
- Hiding: you can't learn the number from the envelope.
- Binding: you can't open the envelope as a different number.
A Pedersen commitment on an elliptic curve:
Without the mask y we'd have C = v·H, and the amount is easy to brute-force (there aren't that many amounts). The mask makes C indistinguishable from a random point.
Why the second generator H matters
H is obtained by hashing G to a point, so nobody knows a k with H = k·G. If someone knew k, they could "re-open" a commitment: y·G + v·H = (y + k(v − v'))·G + v'·H — and print money from nothing. Not knowing k is exactly what binding rests on.
Homomorphism: adding blindly
The sum of two envelopes is an envelope with the sum. So the network can check conservation:
What's left is to prove the difference really has the form z·G (and not z·G + something·H). It's enough to sign with it as a public key: the signature works only if you know z and there's no H component. The fee is public, so its commitment is just fee·H with no mask.
Pseudo-outputs: how this works with rings
There's a catch: the input is a ring of 16 outputs, and we can't say whose commitment is real. RingCT's solution is the pseudo-output commitment C̃:
- for each input the sender makes a new commitment
C̃ = y'·G + v·Hwith the same amount but a new mask; - masks are chosen so that
Σ C̃ = Σ C_out + fee·Hexactly (the balance is checked using pseudo-outputs); - in CLSAG, for each ring member, it's proven that
C_i − C̃commits to zero for the real member:C_π − C̃ = (y − y')·G.
So the ring signature simultaneously proves ownership of the output and that the pseudo-output's amount equals the real input's.
How the recipient learns the amount and mask
Sender and recipient share the secret Hs(8rA ‖ i) (see one-time addresses). From it they derive:
amount_enc = v ⊕ Keccak( "amount" ‖ Hs(8rA ‖ i) )[0..8]
The encrypted amount (8 bytes) sits in the transaction. The recipient decrypts v, computes y and checks that y·G + v·H matches C on the blockchain.
What stops a negative amount?
All arithmetic is modulo l. "−1" is l − 1, a huge number. You could make an output of 1000 XMR and an output of "−999 XMR", the balance would hold, and 999 XMR would appear from nowhere. Commitments can't see this. That's why every output comes with a range proof.