Una prueba de rango en el contexto de los argumentos de producto interno es una prueba de que el escalar v ha sido comprometido en V y v es menor que 2n para algún número entero no negativo n.
Este artículo muestra cómo el documento de Bulletproofs construye dicha prueba. La idea a alto nivel es que si podemos demostrar que un vector aL consiste solo de unos y ceros y que aL es la representación binaria de v, entonces v debe ser menor que 2n. Esto es análogo a decir que un número que cabe en un entero sin signo de 8 bits debe ser menor que 256.
La ventaja de usar Bulletproofs para pruebas de rango es que la prueba de rango se puede construir directamente sin la necesidad de un circuito aritmético.
Monero utiliza las Pruebas de Rango de Bulletproofs (el algoritmo presentado aquí) para garantizar que la suma de las transacciones no sea negativa (en un campo finito, los números negativos son los elementos mayores que p/2, ya que son los inversos aditivos de los elementos menores o iguales a p/2, donde p es el orden del campo).
yn es un vector de dimensión n[1,y,y2,y3,...,yn−1]
y−n es un vector de dimensión n[1,y−1,y−2,...,y−(n−1)]
Nota que yn∘y−n=1n.
Descripción general de la prueba de rango
Demostrar que V es un compromiso a un escalar con un valor menor que 2n requiere demostrar lo siguiente:
aL es binario (solo contiene los valores 0 y 1).
El producto interno ⟨aL,2n⟩=v.
El segundo punto es fácil de demostrar, hacemos una prueba de producto interno normal y luego revelamos que 2n es uno de los vectores en el compromiso, o hacemos que el verificador construya el compromiso de 2n por sí mismo. Sin embargo, demostrar que aL es binario sin un circuito aritmético requiere un par de trucos algebraicos.
Cuatro trucos útiles
El documento de Bulletproofs utiliza implícitamente cuatro trucos algebraicos que es mejor enseñar explícitamente antes de ver directamente el algoritmo de la prueba de rango.
1. Demostrar que aL es binario
La afirmación de que aL es binario es equivalente a las siguientes dos aseveraciones:
aR=aL−1n
aL∘aR=0n
Por ejemplo, si aL=[1,0,0,1] entonces aR=[0,−1,−1,0].
En este caso, aL∘aR=0n porque
[1,0,0,1]∘[0,−1,−1,0]=[0,0,0,0]=0n
Ahora considera un caso donde aL no es binario, por ejemplo [2,1,0,0]. aR será [1,0,−1,−1]. El producto de Hadamard de aL y aR será [2,0,0,0]=0n.
De manera más general, si aL tiene una entrada no binaria, a esa entrada se le restará 1, y la entrada resultante en aR será distinta de cero. Cuando se calcula el producto de Hadamard, en ese índice particular, tanto aL como aR serán distintos de cero y el producto será distinto de cero, lo que significa que aL∘aR=0n.
Sin embargo, si una entrada particular en aL es 1, entonces aR será 0 en ese índice, por lo que el producto de Hadamard en ese índice también será cero.
Finalmente, si una entrada particular en aL es 0, entonces aR será −1 en ese índice y su producto elemento por elemento seguirá siendo cero en ese índice.
Por lo tanto, si aL es binario y aR se calcula como aR=aL−1, entonces aL∘aR=0n.
2. Demostrar que un vector es todo ceros
Supongamos que deseamos demostrar que el compromiso de Pedersen A contiene un vector cero. Creamos el compromiso de Pedersen A=⟨a,G⟩+αB y deseamos demostrarle a un verificador que a=0n.
Podría parecer suficiente enviar simplemente el término de cegado α, pero para que nuestra solución sea más componible, no queremos revelar el término de cegado porque eso podría afectar otros compromisos que hemos creado.
En su lugar, el probador envía A al verificador, y el verificador responde con un vector lleno de valores aleatorios r. El probador ahora debe demostrar que
⟨a,r⟩=0
Nota que esta es una prueba probabilística. Es posible, con probabilidad insignificante, que ⟨a,r⟩=0 para a=0n, pero no es posible que el probador falsifique tal a porque no sabe de antemano cuál será r.
Sin embargo, transmitir r requiere una sobrecarga de comunicación O(n), por lo que, en su lugar, el verificador solo envía un único elemento aleatorio y y el probador calcula yn y utiliza yn como el vector aleatorio.
Luego, el probador demuestra que ⟨a,yn⟩=0.
3. Demostrar que un producto interno tiene la forma ⟨aL,aR∘yn⟩ donde el verificador elige y y el probador calcula yn
Aún no tenemos un mecanismo para demostrar que aL∘aR=0n, ya que es un producto de Hadamard, no un producto interno. Sin embargo, afirmar que el vector aL∘aR es idénticamente 0n es lo mismo que afirmar que ⟨aL∘aR,yn⟩=0. Según las reglas del producto interno, podemos mover aR al otro lado del producto interno y ahora tenemos ⟨aL,aR∘yn⟩=0.
El verificador recibirá compromisos para aL y aR, no para aR∘yn. Dependerá del verificador construir un compromiso para aR∘yn de modo que esté convencido de que el probador usó aR∘yn como el segundo vector en el producto interno.
El truco clave en el que confiamos es que el probador utiliza los vectores base G y H para comprometer sus vectores, pero el verificador usa G y H∘y−n.
Cuando el probador envía la evaluación ru, el probador debe asegurarse de que los términos yn se cancelarán con y−n en el vector base del verificador y−n∘H.
Específicamente, el probador construye los compromisos
AS=⟨aL,G⟩+⟨aR,H⟩+αB=⟨sL,G⟩+⟨sR,H⟩+βB
Y envía (A,S) al verificador. No es necesario comprometer y enviar V porque en este caso es cero.
Los polinomios del probador serán
l(x)r(x)=aL+sLx=aR∘yn+sR∘ynx
Crucialmente, el probador ha multiplicado con el producto de Hadamard a sR por yn. Anteriormente, r(x) se calculaba como r(x)=aR∘yn+sRx (sin el yn∘sR). Esto permitirá más adelante que todos los términos yn se cancelen cuando el verificador calcule el compromiso ⟨ru,y−n∘H⟩. Internamente, ru es (aR+sRu)∘yn, por lo que el yn se cancelará cuando el verificador calcule ⟨(aR+sRu)∘yn,y−n∘H⟩, es decir:
Sin embargo, el probador aún no puede calcular l(x) ni r(x) porque el verificador todavía no ha enviado y. Por lo tanto, después de recibir (A,S), el verificador envía y y el probador calcula yn y calcula el polinomio t(x):
t(x)=⟨l(x),r(x)⟩=⟨aL,aR∘yn⟩+t1x+t2x2
donde
t1t2=⟨aL,sR∘yn⟩+⟨sL,aR∘yn⟩=⟨sL,sR∘yn⟩
El probador se compromete con los coeficientes t1 y t2 como
T1T2=t1G+τ1B=t2G+τ2B
y envía (T1,T2) al verificador. El verificador responde con u y el probador evalúa los polinomios vectoriales l(x) y r(x):
Nota que πt solo incluye los términos de cegado para t1 y t2. En la implementación anterior, πt se calculaba como γ+τ1u+τ2u2, donde γ es el término de cegado para V, que también es el coeficiente constante del polinomio t(x).
No hay término de cegado γ porque no hay compromiso con V, es decir, v no es secreto (es 0). El probador envía (lu,ru,tu,πlr,πt) y el verificador comprueba que:
La primera diferencia crucial es que el compromiso con ru se hace con respecto al vector base y−n∘H en lugar de H por las razones discutidas anteriormente.
Segundo, tuG=?T1u+T2u2−πtB no tiene un compromiso constante. Normalmente, la ecuación es tuG=?V+T1u+T2u2−πtB, pero V es un compromiso con 0 en este caso.
En general, si V contiene valores conocidos por el verificador, el verificador puede construir el compromiso con V como mostramos en la siguiente sección.
4. Demostrar un producto interno cuando interviene una constante pública aditiva
Como se aludió en la sección anterior, el verificador puede reconstruir compromisos si conoce el vector subyacente.
Por ejemplo, supongamos que estamos demostrando que
⟨aL+j,aR∘yn+k⟩=vz
donde j y k son vectores conocidos por el verificador y z es un escalar conocido por el verificador de antemano. A diferencia de yn, estos vectores y escalar se conocen antes de que comience la prueba. Nota que en este ejemplo k no está multiplicado por Hadamard con yn.
El probador aún se compromete solo a los valores secretos aL, aR y v como de costumbre:
ASV=⟨aL,G⟩+⟨aR,H⟩+αB=⟨sL,G⟩+⟨sR,H⟩+βB=vG+γB
Como de costumbre, los polinomios l(x) y r(x) son tales que el término constante es el vector del producto interno original y los términos lineales son sL y sR. Al recibir y del verificador, el probador calcula yn y crea, pero no evalúa, l(x) y r(x):
Nota que el término constante en πt es γz. El probador envía (lu,ru,tu,πlr,πt). Finalmente, el verificador calcula:
tuA+Su+compromiso de j en l(x)⟨j,G⟩+compromiso de k en r(x)⟨k,y−n∘H⟩tuG=?⟨lu,ru⟩=?⟨lu,G⟩+⟨ru,y−n∘H⟩+πlrB=?Vz+T1u+T2u2−πtB
lu y ru contienen j y k respectivamente, pero A y S no. Por lo tanto, el verificador calcula compromisos para esos vectores y los suma a los compromisos A y S. En el caso de k, el vector base y−n∘H hará que k se convierta en k∘y−n, por lo que el compromiso debe calcularse con respecto a y−n∘H. Finalmente, el término de cegado πt contiene vz, pero V no contiene z. Por lo tanto, el probador debe multiplicar V por z.
Al calcular ⟨j,G⟩, ⟨k,yn∘H⟩ y Vz, el verificador puede estar seguro de que el cálculo del producto interno realmente incluyó esos términos.
Prueba de rango
Para demostrar que V es un valor menor que 2n tenemos tres cosas que probar:
el producto interno ⟨aL,2n⟩=V, es decir, aL es la representación binaria de v
aR=aL−1
aL∘aR=0
Las dos últimas afirmaciones no están directamente en la forma de un producto interno. Sin embargo, podemos modificarlas ligeramente para lograr esto. Lo que realmente estamos diciendo es que los vectores
aR−aL+1
aL∘aR
son ambos 0n. Podemos usar el truco de una sección anterior para demostrar que son cero. Es decir, el probador necesita establecer que
⟨aL∘aR,yn⟩=0
y
⟨aR−aL+1,yn⟩=0
donde yn es el vector aleatorio derivado del valor y enviado por el verificador.
El documento original de Bulletproofs modifica ligeramente la primera afirmación de la siguiente manera para que podamos usar el tercer truco de la sección anterior:
⟨aL,aR∘yn⟩=0
Por lo tanto, el probador tiene tres productos internos que establecer:
⟨aL,2n⟩=v
⟨aL,aR∘yn⟩=0n
⟨aL−1n−aR,yn⟩=0n
Combinar tres productos internos en uno
Los tres productos internos se pueden combinar en uno solo usando una combinación lineal aleatoria con la aleatoriedad z proporcionada por el verificador.
Con un poco de álgebra de producto interno muy pesada, podemos combinar todos los productos internos de la siguiente manera. Mostramos la derivación en el apéndice.
Los términos en los recuadros de abajo contienen valores conocidos por el verificador, por lo que construiremos nuestro algoritmo de verificación para comprobar explícitamente esos valores. Es decir, el verificador calculará los compromisos para los valores en los términos recuadrados, no el probador:
Para ahorrar espacio, el documento de Bulletproofs se refiere al término (z−z2)⋅⟨1n,yn⟩−z3⟨1n,2n⟩ como δ(y,z), por lo que el producto interno se puede escribir como
⟨aL−z⋅1n,yn∘aR+yn⋅z+z2⋅2n⟩=z2⋅v+δ(y,z)
Nota que δ(y,z) es un valor que el verificador puede calcular.
Algoritmo de prueba de rango
El probador elige v y su representación binaria aL y calcula aR=aL−1.
Luego, el probador elige aleatoriamente el término de cegado α y calcula el compromiso combinado de aL y aR usando los vectores base G y H como
A=⟨aL,G⟩+⟨aR,H⟩+αB
Luego, el probador elige los términos lineales de los polinomios vectoriales que están por crearse, l(x) y r(x), como sL y sR, y se compromete a ellos
S=⟨sL,G⟩+⟨sR,H⟩+βB
El probador compromete el producto interno en V con respecto a un G de logaritmo discreto desconocido (no relacionado con G):
V=vG+γB
El probador envía (A,S,V) al verificador.
El verificador responde con valores aleatorios (y,z) que el probador utilizará para combinar los tres productos internos en uno solo.
⟨aL−z⋅1n,yn∘aR+yn⋅z+z2⋅2n⟩=z2⋅v+δ(y,z)
La parte izquierda del producto interno aL−z⋅1 será el término constante de l(x) y yn∘aR+yn⋅z+z2⋅2n será el término constante de r(x).
El probador envía los compromisos de t1 y t2 como
T1T2=t1G+τ1B=t2G+τ2B
No hay necesidad de comprometerse con t0; observa que es exactamente el producto interno que estamos tratando de probar, por lo que el verificador ya tiene el compromiso como V.
El verificador envía la aleatoriedad u y el probador calcula
Recuerda que el probador no comprometió los vectores completos que usó para el lado izquierdo y derecho del producto interno, sino solo aL y bL. El resto de los vectores eran vectores públicos aditivos conocidos por el verificador, por lo que el verificador reconstruyó los compromisos con los vectores al construir compromisos para los términos constantes y sumándolos al compromiso de los vectores secretos suministrados por el probador.
A modo de recordatorio, aquí está el producto interno original con los valores conocidos por el verificador recuadrados:
⟨aL+−z⋅1n,yn∘aR+yn⋅z+z2⋅2n⟩=z2⋅v+δ(y,z)
Se anima al lector a verificar que los términos recuadrados (valores conocidos por el verificador) en el producto original fueron reconstruidos por el verificador en los términos recuadrados en el conjunto de comprobaciones de igualdad anteriores.
Al replicar una parte del cálculo del probador, el verificador asegura que el probador realmente llevó a cabo el cálculo tal como afirma.
Corrección del algoritmo de verificación
Ahora mostramos que las comprobaciones de verificación finales son idénticamente correctas si el probador fue honesto.
A continuación mostramos el álgebra exacta, pero intuitivamente el verificador está “reconstruyendo” el vector izquierdo en el producto interno aL−z⋅1n, el vector derecho en el producto interno yn∘aR+yn⋅z+z2⋅2n y la salida z2v+δ(y,z).
Al verificador no se le dan compromisos para aL−z⋅1n ni para yn∘aR+yn⋅z+z2⋅2n, sino para aL y aR. De manera similar, al verificador no se le da un compromiso para la salida z2v+δ(y,z), sino solo para v.
Los términos aditivos y los términos multiplicados elemento por elemento con yn deben ser reconstruidos por el verificador.
Corrección de tu=t(u)
Para la comprobación tu=?⟨lu,ru⟩, esto es verdadero por definición, ya que así es como el probador calculó tu.
Corrección de los comprometidos l(x) y r(x) con respecto a A y S
Para A+Su+⟨−z⋅1n,G⟩+⟨z⋅yn+z2⋅2n,Hy−1⟩=?⟨lu,G⟩+⟨ru,Hy−1⟩+πlrB
Sin embargo, tal álgebra sería extremadamente desordenada. En su lugar, observamos que z2v+δ(y,z)G es el término constante del producto interno del polinomio vectorial de ⟨l(x),r(x)⟩. Para cancelar el término de cegado γB en V, observa que πt contiene z2γ, por lo que esto se cancelará con el término gamma en Vz2=(vG+γB)z2.
Dado que los compromisos de Pedersen son aditivamente homomórficos, el verificador simplemente puede calcular y sumar δ(y,z)G a Vz2 para calcular el compromiso al término constante del polinomio t(x).
Prueba de rango de tamaño logarítmico
Podemos reducir el tamaño de la transmisión de datos enviando un compromiso C a lu y ru y demostrando que los vectores comprometidos tienen un producto interno tu usando la prueba de tamaño logarítmico, y luego verificando que
A+Su+⟨−z⋅1n,G⟩+⟨z⋅yn+z2⋅2n,Hy−1⟩−πlrB=?C
y
tu=?⟨lu,ru⟩
con respecto a los vectores base G y Hy−1.
Uso del algoritmo de la prueba de rango para la suma de subconjuntos
El problema de la suma de subconjuntos pregunta, "dado un conjunto de números, ¿un subconjunto (posiblemente incluyendo todo el conjunto) suma k? Por ejemplo, si k=16 y el conjunto es {3,5,7,11} la respuesta es sí porque 5+11=16. Sin embargo, si k=13, entonces la respuesta es no.
El problema de la suma de subconjuntos es NP-Complete, lo que significa que, de manera similar a un circuito booleano o circuito aritmético, puede representar cualquier problema en NP. Es decir, cualquier problema en NP puede reescribirse (la palabra técnica es “reducirse”) a una instancia de suma de subconjuntos.
Al reemplazar 2n por [3,5,7,11], podemos demostrar que conocemos una solución a una suma de subconjuntos sin revelar la respuesta. Específicamente, el probador sabría que aL=[0,1,0,1] si k=16. En general, una entrada de uno en aL significa que incluimos ese elemento en el subconjunto y un cero significa que no está incluido en el subconjunto.
Por lo tanto, Bulletproofs son capaces de demostrar el conocimiento de cualquier testigo para cualquier problema en NP.
Apéndice: Derivación de la combinación de tres productos internos en uno
Podemos usar la regla ⟨x,b+c⟩+⟨b,y⟩=v→⟨x+y,b+c⟩=v+⟨y,c⟩ para combinar los términos que contienen aR∘yn. Aquí b es aR∘yn, c es z⋅yn+z2⋅2n, y y es −z⋅1n.