Technical › How much?

Pedersen commitments

How to add up amounts nobody can see and check that the balance holds

In short

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:

C = y·G + v·H v — amount in atomic units (1 XMR = 10¹² units), y — random blinding factor, G and H — two independent points

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

C₁ + C₂ = (y₁ + y₂)·G + (v₁ + v₂)·H

The sum of two envelopes is an envelope with the sum. So the network can check conservation:

C_in (5 XMR)amount hidden = C_out1 (3 XMR)to recipient + C_out2 (1.99)change + 0.01·Hfee C_in − C_out1 − C_out2 − fee·H = (y_in − y₁ − y₂)·G + 0·H → a commitment to zero: a point of the form z·G

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̃:

  1. for each input the sender makes a new commitment C̃ = y'·G + v·H with the same amount but a new mask;
  2. masks are chosen so that Σ C̃ = Σ C_out + fee·H exactly (the balance is checked using pseudo-outputs);
  3. 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:

y = Hs( "commitment_mask" ‖ Hs(8rA ‖ i) )
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.