Technique › Combien ?

Engagements de Pedersen

Comment additionner des montants que personne ne voit et vérifier que le solde est équilibré

En bref

Un montant v est caché dans le point C = y·G + v·H, où y est un « masque » aléatoire. Les engagements s’additionnent : la somme des engagements engage la somme des montants. Le réseau vérifie que Σ inputs − Σ outputs − fee·H est un engagement à zéro sans apprendre aucun montant.

Ce qu’est un engagement

Un engagement (commitment), c’est une « enveloppe scellée contenant un nombre ». Il a deux propriétés :

  • Dissimulation (hiding) : on ne peut pas connaître le nombre à partir de l’enveloppe.
  • Contrainte (binding) : on ne peut pas ouvrir l’enveloppe en prétendant qu’elle contient un autre nombre.

Un engagement de Pedersen sur une courbe elliptique :

C = y·G + v·H v — montant en unités atomiques (1 XMR = 10¹² unités), y — facteur d’aveuglement aléatoire, G et H — deux points indépendants

Sans le masque y, on aurait C = v·H, et le montant serait facile à retrouver par force brute (il n’y a pas tant de montants possibles). Le masque rend C indiscernable d’un point aléatoire.

Pourquoi le second générateur H est important

H est obtenu en hachant G vers un point, si bien que personne ne connaît de k tel que H = k·G. Si quelqu’un connaissait k, il pourrait « rouvrir » un engagement : y·G + v·H = (y + k(v − v'))·G + v'·H — et créer de l’argent à partir de rien. C’est précisément l’ignorance de k qui garantit la contrainte.

Homomorphisme : additionner à l’aveugle

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

La somme de deux enveloppes est une enveloppe contenant la somme. Le réseau peut donc vérifier la conservation :

C_in (5 XMR)montant caché = C_out1 (3 XMR)au destinataire + C_out2 (1,99)monnaie rendue + 0.01·Hfrais C_in − C_out1 − C_out2 − fee·H = (y_in − y₁ − y₂)·G + 0·H → un engagement à zéro : un point de la forme z·G

Il reste à prouver que la différence est réellement de la forme z·G (et non z·G + quelque chose·H). Il suffit de signer en l’utilisant comme clé publique : la signature ne fonctionne que si l’on connaît z et qu’il n’y a pas de composante H. Les frais sont publics, leur engagement est donc simplement fee·H, sans masque.

Pseudo-sorties : comment cela fonctionne avec les anneaux

Il y a un hic : l’entrée est un anneau de 16 sorties, et on ne peut pas dire quel engagement est le vrai. La solution de RingCT est l’engagement de pseudo-sortie C̃ :

  1. pour chaque entrée, l’expéditeur crée un nouvel engagement C̃ = y'·G + v·H avec le même montant mais un nouveau masque ;
  2. les masques sont choisis de sorte que Σ C̃ = Σ C_out + fee·H exactement (l’équilibre est vérifié à l’aide des pseudo-sorties) ;
  3. dans CLSAG, pour chaque membre de l’anneau, on prouve que C_i − C̃ est un engagement à zéro pour le membre réel : C_π − C̃ = (y − y')·G.

Ainsi, la signature en anneau prouve à la fois la propriété de la sortie et l’égalité entre le montant de la pseudo-sortie et celui de l’entrée réelle.

Comment le destinataire apprend le montant et le masque

L’expéditeur et le destinataire partagent le secret Hs(8rA ‖ i) (voir adresses à usage unique). Ils en dérivent :

y = Hs( "commitment_mask" ‖ Hs(8rA ‖ i) )
amount_enc = v ⊕ Keccak( "amount" ‖ Hs(8rA ‖ i) )[0..8]

Le montant chiffré (8 octets) se trouve dans la transaction. Le destinataire déchiffre v, calcule y et vérifie que y·G + v·H correspond à C sur la blockchain.

Qu’est-ce qui empêche un montant négatif ?

Tous les calculs se font modulo l. « −1 », c’est l − 1, un nombre énorme. On pourrait créer une sortie de 1000 XMR et une sortie de « −999 XMR », l’équilibre tiendrait, et 999 XMR apparaîtraient de nulle part. Les engagements ne le voient pas. C’est pourquoi chaque sortie est accompagnée d’une preuve d’intervalle.