Técnico › ¿Cuánto?

Compromisos de Pedersen

Cómo sumar importes que nadie puede ver y comprobar que el balance cuadra

En resumen

Un importe v se oculta en el punto C = y·G + v·H, donde y es una «máscara» aleatoria. Los compromisos se pueden sumar: la suma de compromisos compromete a la suma de importes. La red comprueba que Σ inputs − Σ outputs − fee·H compromete a cero sin conocer ningún importe.

Qué es un compromiso

Un compromiso (commitment) es un «sobre cerrado con un número dentro». Tiene dos propiedades:

  • Ocultación (hiding): no se puede saber el número mirando el sobre.
  • Vinculación (binding): no se puede abrir el sobre como si contuviera otro número.

Un compromiso de Pedersen sobre una curva elíptica:

C = y·G + v·H v — importe en unidades atómicas (1 XMR = 10¹² unidades), y — factor de cegado aleatorio, G y H — dos puntos independientes

Sin la máscara y tendríamos C = v·H, y el importe sería fácil de averiguar por fuerza bruta (no hay tantos importes posibles). La máscara hace que C sea indistinguible de un punto aleatorio.

Por qué importa el segundo generador H

H se obtiene aplicando a G un hash a punto, así que nadie conoce un k tal que H = k·G. Si alguien conociera k, podría «reabrir» un compromiso: y·G + v·H = (y + k(v − v'))·G + v'·H, e imprimir dinero de la nada. La vinculación se basa precisamente en que nadie conoce k.

Homomorfismo: sumar a ciegas

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

La suma de dos sobres es un sobre con la suma. Así la red puede comprobar que nada se crea ni se destruye:

C_in (5 XMR)importe oculto = C_out1 (3 XMR)al destinatario + C_out2 (1.99)cambio + 0.01·Hcomisión C_in − C_out1 − C_out2 − fee·H = (y_in − y₁ − y₂)·G + 0·H → un compromiso a cero: un punto de la forma z·G

Solo queda demostrar que la diferencia tiene de verdad la forma z·G (y no z·G + algo·H). Basta con firmar usándola como clave pública: la firma solo funciona si conoces z y no hay componente H. La comisión es pública, así que su compromiso es simplemente fee·H, sin máscara.

Pseudo-salidas: cómo funciona esto con anillos

Hay una pega: la entrada es un anillo de 16 salidas, y no podemos decir cuál es el compromiso real. La solución de RingCT es el compromiso de pseudo-salida C̃:

  1. para cada entrada, el remitente crea un compromiso nuevo C̃ = y'·G + v·H con el mismo importe pero una máscara nueva;
  2. las máscaras se eligen de forma que Σ C̃ = Σ C_out + fee·H exactamente (el balance se comprueba con las pseudo-salidas);
  3. en CLSAG, para cada miembro del anillo, se demuestra que C_i − C̃ compromete a cero para el miembro real: C_π − C̃ = (y − y')·G.

Así la firma de anillo demuestra a la vez la propiedad de la salida y que el importe de la pseudo-salida es igual al de la entrada real.

Cómo conoce el destinatario el importe y la máscara

Remitente y destinatario comparten el secreto Hs(8rA ‖ i) (ver direcciones de un solo uso). A partir de él derivan:

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

El importe cifrado (8 bytes) va en la transacción. El destinatario descifra v, calcula y y comprueba que y·G + v·H coincide con C en la blockchain.

¿Qué impide un importe negativo?

Toda la aritmética es módulo l. «−1» es l − 1, un número enorme. Se podría crear una salida de 1000 XMR y otra de «−999 XMR»: el balance cuadraría y aparecerían 999 XMR de la nada. Los compromisos no pueden detectarlo. Por eso cada salida va acompañada de una prueba de rango.