En un capítulo anterior, mostramos que multiplicar las sumas de los elementos de los vectores y calcula la suma de los términos del producto exterior, es decir, . También mostramos que el producto exterior “contiene” el producto interior a lo largo de su diagonal principal.
Para “extraer” el producto interior , se deben restar del producto exterior todos los términos que no forman parte del producto interior, es decir, la región sombreada en color púrpura a continuación:

Hay de estos términos, por lo que hacer esto directamente no es eficiente.
Sin embargo, observe que podemos “llenar el producto exterior” de la manera que se muestra en la siguiente animación:
En la animación anterior, después de que el probador envía los términos fuera de la diagonal, el probador pliega tanto como , reduciendo su longitud a la mitad.
Al final, después de haber plegado y veces, el tamaño de los vectores es 1. Cuando , el producto exterior es igual al producto interior y simplemente revelamos ambos vectores, que serán de tamaño constante.
Resumen – y cómo se relaciona esto con el algoritmo del capítulo anterior
Anteriormente, mostramos cómo probar que conocemos la apertura de un compromiso enviando elementos en lugar de . A modo de resumen:
El probador compromete el vector en como . Luego, el probador envía los términos fuera de la diagonal y . El verificador responde con y el probador pliega el vector sobre como y envía al verificador. Dado que tiene una longitud de , hemos reducido a la mitad el tamaño de los datos que se transmitirán.
El verificador luego comprueba que
La intención original de este algoritmo era probar que conocemos el compromiso en demostrando que sabemos que la suma del producto interior y los términos fuera de la diagonal y es igual a la suma de los elementos de los productos exteriores de las particiones por pares de y .
Si aplicamos recursivamente este algoritmo, obtenemos el algoritmo descrito al principio de este capítulo.
Sin embargo, también podríamos interpretar este procedimiento como una prueba de que conocemos la apertura del compromiso donde con respecto al vector base de longitud . Podríamos probar de manera ingenua que conocemos la apertura de enviando y con el verificador comprobando que .
Pero en lugar de probar que conocemos la apertura de enviando , podemos aplicar recursivamente el algoritmo para probar que conocemos la apertura de enviando un vector de tamaño . De hecho, podemos seguir iterando recursivamente hasta que sea de tamaño .
La siguiente animación proporciona una intuición de lo que está sucediendo. La próxima sección describe la animación en detalle.

Para que este algoritmo funcione, la longitud de los vectores debe ser una potencia de dos. Sin embargo, si la longitud no es una potencia de dos, podemos rellenar los vectores con ceros hasta que la longitud sea una potencia de dos.
Probando que conocemos la apertura de con de datos
El algoritmo
El probador y el verificador acuerdan un vector base de longitud . El probador envía al verificador , que es . El probador desea convencer al verificador de que conoce la apertura de enviando únicamente datos de tamaño logarítmico.
El probador y el verificador luego participan en el siguiente algoritmo a continuación. Los argumentos después de | significan que solo son conocidos por el probador.
En la descripción del algoritmo a continuación, es la longitud de los vectores en la entrada, los cuales tienen todos la misma longitud.
Caso 1:
- El probador envía y el verificador comprueba que y el algoritmo termina.
Caso 2:
- El probador calcula y envía al verificador donde
- El verificador envía la aleatoriedad
- Tanto el probador como el verificador calculan:
- El probador calcula
Comentarios sobre el algoritmo
El probador está probando de forma recursiva que, dados los valores y , conoce el tal que . Ambas partes pliegan recursivamente hasta que es un solo punto, y el probador pliega recursivamente hasta que es un solo punto.
El probador transmite una cantidad constante de datos en cada iteración, y la recursión se ejecutará como máximo veces, por lo que el probador envía de datos.
Enfatizamos que este algoritmo no es de conocimiento cero porque en el caso , el verificador aprende todo el vector. El verificador también podría enviar valores no aleatorios para para intentar aprender algo sobre .
Sin embargo, recuerde que nuestra motivación para este algoritmo es reducir el tamaño de la comprobación , y y no eran secretos desde el principio.
De hecho, no hemos mostrado cómo probar que conocemos el producto interior con datos de tamaño logarítmico, solo hemos mostrado que conocemos la apertura de un compromiso con datos de tamaño logarítmico. Sin embargo, es sencillo actualizar nuestro algoritmo para mostrar que conocemos el producto interior de dos vectores, como haremos más adelante en este artículo.
Tiempo de ejecución
El verificador lleva a cabo el cálculo veces, y el primer toma un tiempo de . A primera vista, parece que el tiempo de ejecución del verificador es . Sin embargo, note que con cada iteración, se divide a la mitad, resultando en un tiempo de ejecución de , lo que lleva a un tiempo de ejecución general para el verificador de .
Probando que conocemos el producto interior
Ahora ajustamos el algoritmo anterior para probar que realizamos el producto interior entre dos vectores escalares, en lugar de un vector de elementos del campo y un vector de puntos de curva elíptica.
Específicamente, debemos probar que contiene un compromiso del producto interior . Este producto interior es un escalar, por lo que hacemos un compromiso de Pedersen normal en lugar de un compromiso de vector. Para esto utilizamos un punto aleatorio de curva elíptica (con logaritmo discreto desconocido) . Por lo tanto, .
Sin embargo, no podemos simplemente reutilizar nuestro algoritmo anterior porque el probador puede proporcionar múltiples aperturas para . Por ejemplo, si y , el probador también puede abrir con los vectores y .
Para crear una prueba segura de conocimiento de un producto interior, el probador también debe calcular y enviar un compromiso para y .
La solución ingenua es ejecutar el algoritmo de compromiso dos veces. Las dos primeras veces son para probar que y están comprometidos correctamente en y y la tercera vez es para mostrar que se calculó correctamente. En la siguiente sección, mostramos cómo modificar nuestro algoritmo para calcular el producto interior cuando ambos vectores son elementos del campo.
Convirtiendo un producto interior escalar a un producto interior escalar-punto
Sea el vector que consiste en copias del punto , es decir,
Por lo tanto,
Note que es igual a .
Es decir, podemos multiplicar cada entrada de por y tomar el producto interior de ese vector con y el resultado será el mismo que . Por ejemplo, si y , entonces .
A partir de ahí, podríamos probar lo siguiente:
Una prueba en lugar de tres
Podemos hacerlo mejor que enviar tres compromisos y ejecutar el algoritmo tres veces.
Debido a que los puntos en , y tienen una relación de logaritmo discreto desconocida, pueden combinarse como un único compromiso .
Reorganizaremos ligeramente el compromiso como para que el siguiente truco sea más evidente.
Para probar todo este compromiso a la vez, en lugar de tres productos interiores, observe que
donde significa concatenación de vectores.
Efectivamente, estamos probando que comprometimos el vector en la base vectorial de curva elíptica .
En la práctica, no concatenamos realmente los vectores porque la longitud total generalmente no sería una potencia de dos. Más bien, calculamos los componentes , y por separado, pero calculamos y como si estuvieran concatenados.
Mostramos el algoritmo en la siguiente animación:
El algoritmo
Dado y el compromiso deseamos probar que está comprometido como se afirma. Es decir, , y están comprometidos en y .
Caso 1:
- El probador envía y el verificador comprueba que . El algoritmo termina.
Caso 2:
- El probador calcula y envía al verificador que son simplemente los términos fuera de la diagonal de todos los vectores concatenados juntos (vea la animación de arriba):
- El verificador envía la aleatoriedad .
- Tanto el probador como el verificador calculan:
- El probador calcula:
Los siguientes ejercicios se pueden encontrar en nuestro ZK Bulletproofs GitHub Repo:
Ejercicio 1: Complete el código que falta a continuación para implementar el algoritmo para probar que está comprometido en para producir el punto :
from py_ecc.bn128 import G1, multiply, add, FQ, eq, Z1
from py_ecc.bn128 import curve_order as p
import numpy as np
from functools import reduce
import random
def random_element():
return random.randint(0, p)
def add_points(*points):
return reduce(add, points, Z1)
# if points = G1, G2, G3, G4 and scalars = a,b,c,d vector_commit returns
# aG1 + bG2 + cG3 + dG4
def vector_commit(points, scalars):
return reduce(add, [multiply(P, i) for P, i in zip(points, scalars)], Z1)
# these EC points have unknown discrete logs:
G_vec = [(FQ(6286155310766333871795042970372566906087502116590250812133967451320632869759), FQ(2167390362195738854837661032213065766665495464946848931705307210578191331138)),
(FQ(6981010364086016896956769942642952706715308592529989685498391604818592148727), FQ(8391728260743032188974275148610213338920590040698592463908691408719331517047)),
(FQ(15884001095869889564203381122824453959747209506336645297496580404216889561240), FQ(14397810633193722880623034635043699457129665948506123809325193598213289127838)),
(FQ(6756792584920245352684519836070422133746350830019496743562729072905353421352), FQ(3439606165356845334365677247963536173939840949797525638557303009070611741415))]
# return a folded vector of length n/2 for scalars
def fold(scalar_vec, u):
pass
# return a folded vector of length n/2 for points
def fold_points(point_vec, u):
pass
# return (L, R)
def compute_secondary_diagonal(G_vec, a):
pass
a = [4,2,42,420]
P = vector_commit(G_vec, a)
L1, R1 = compute_secondary_diagonal(G_vec, a)
u1 = random_element()
aprime = fold(a, u1)
Gprime = fold_points(G_vec, pow(u1, -1, p))
L2, R2 = compute_secondary_diagonal(Gprime, aprime)
u2 = random_element()
aprimeprime = fold(aprime, u2)
Gprimeprime = fold_points(Gprime, pow(u2, -1, p))
assert len(Gprimeprime) == 1 and len(aprimeprime) == 1, "final vector must be len 1"
assert eq(vector_commit(Gprimeprime, aprimeprime), add_points(multiply(L2, pow(u2, 2, p)), multiply(L1, pow(u1, 2, p)), P, multiply(R1, pow(u1, -2, p)), multiply(R2, pow(u2, -2, p)))), "invalid proof"
Ejercicio 2: Modifique el código anterior para implementar el algoritmo que prueba que contiene un compromiso para , y , y que . Utilice el siguiente vector base para y el punto de curva elíptica :
H = [(FQ(13728162449721098615672844430261112538072166300311022796820929618959450231493), FQ(12153831869428634344429877091952509453770659237731690203490954547715195222919)),
(FQ(17471368056527239558513938898018115153923978020864896155502359766132274520000), FQ(4119036649831316606545646423655922855925839689145200049841234351186746829602)),
(FQ(8730867317615040501447514540731627986093652356953339319572790273814347116534), FQ(14893717982647482203420298569283769907955720318948910457352917488298566832491)),
(FQ(419294495583131907906527833396935901898733653748716080944177732964425683442), FQ(14467906227467164575975695599962977164932514254303603096093942297417329342836))]
Q = (FQ(11573005146564785208103371178835230411907837176583832948426162169859927052980), FQ(895714868375763218941449355207566659176623507506487912740163487331762446439))
Este tutorial es parte de una serie sobre Bulletproof ZKP.