Dado un circuito aritmético codificado como un Rank 1 Constraint System, es posible crear una prueba ZK de que se tiene un testigo (witness), aunque no una que sea sucinta. Este artículo describe cómo lograrlo.
Una prueba de conocimiento cero para un R1CS se logra convirtiendo el vector testigo en puntos de curva elíptica sobre campos finitos y reemplazando el producto de Hadamard con un emparejamiento bilineal para cada fila.
Dado un Rank 1 Constraint System donde cada matriz tiene filas y columnas, lo escribimos como
Donde , , son matrices con filas y columnas, y es el vector testigo (que contiene una asignación que satisface cada una de las señales en el circuito aritmético). El vector tiene 1 columna y filas, y es la multiplicación elemento por elemento (producto de Hadamard).
En forma expandida, se ve así:
En esta configuración, podemos demostrar a un verificador que tenemos un vector testigo que satisface el R1CS simplemente dándole el vector , ¡pero con la obvia desventaja de que esto no es una prueba de conocimiento cero!
Algoritmo de prueba de conocimiento cero para un R1CS.
Si “encriptamos” el vector testigo multiplicando cada entrada por o , ¡las matemáticas seguirán funcionando correctamente!
Para entender esto, considera que si llevamos a cabo la multiplicación de matrices
y también
Los logaritmos discretos de los dos puntos de la curva elíptica en la segunda multiplicación de matrices tienen el mismo valor que los elementos de la primera multiplicación de matrices.
En otras palabras, cada vez que multiplicamos el vector columna por una fila en la matriz cuadrada, llevamos a cabo dos multiplicaciones de puntos de curva elíptica y una suma de curva elíptica.
Notación para curvas elípticas
Decimos que es un punto de curva elíptica en creado al multiplicar el elemento del campo por . Decimos que es un punto de curva elíptica en generado al multiplicar con el generador . Debido al problema del logaritmo discreto, no podemos extraer dado o . Dado un punto y , decimos que el emparejamiento (pairing) de los dos puntos es .
Pasos del probador
Encriptemos nuestro vector multiplicando cada entrada con el punto generador para producir el punto de curva elíptica .
Para la matriz , estamos haciendo lo siguiente:
Anticipando que el producto de Hadamard se convierta en una lista de emparejamientos de curvas elípticas, podemos usar puntos para encriptar también el vector de modo que el verificador pueda hacer el emparejamiento.
Después de esta operación, tenemos una sola columna de puntos de curva elíptica en originada por la multiplicación y una sola columna de puntos de .
El siguiente paso ingenuo sería encriptar el vector con puntos para que el verificador pueda emparejar el resultado de con para ver si es igual a , pero los puntos son masivos, por lo que preferiríamos que el verificador empareje los puntos de curva elíptica de en y luego empareje cada entrada con un punto . Emparejar con un punto es, en cierto sentido, “multiplicar por uno” pero convirtiendo el punto en un punto .
El probador luego entrega el vector y el vector al verificador.
Paso de verificación
Por lo tanto, el paso de verificación se convierte en
Los vectores anteriores de elementos serán iguales elemento por elemento si y solo si el probador ha proporcionado un testigo válido.
Bueno, casi. Llegaremos a eso en una sección posterior.
Primero debemos mencionar un detalle de implementación importante
Entradas públicas
Si nuestra afirmación de conocimiento es “Conozco tal que donde ”, entonces nuestro vector testigo probablemente se verá de la siguiente manera:
donde . En este caso, necesitamos que sean públicos. Para lograrlo, simplemente no encriptamos los dos primeros elementos del testigo. El verificador comprobará las salidas públicas, luego encriptará las entradas públicas multiplicándolas con un punto o para que la fórmula de verificación no cambie.
Lidiando con un probador malicioso.
Debido a que los vectores están encriptados, el verificador no puede saber de inmediato si el vector de puntos encripta los mismos valores que el vector de puntos .
Es decir, el probador está suministrando y . Dado que el verificador no conoce los logaritmos discretos del vector de puntos, ¿cómo sabe el verificador que el vector de puntos tiene los mismos logaritmos discretos que el vector de puntos ?
El verificador puede comprobar la igualdad de los logaritmos discretos (sin conocerlos) emparejando ambos vectores de puntos con un vector del generador opuesto y comprobando que los puntos resultantes sean iguales. Específicamente,
Este algoritmo es mayormente académico
Este algoritmo es muy ineficiente para el verificador. Si las matrices en el R1CS son grandes (y para algoritmos interesantes, lo serán), entonces el verificador tiene que hacer muchos emparejamientos y sumas de curvas elípticas. La suma de curvas elípticas es bastante rápida, pero los emparejamientos de curvas elípticas son lentos (y cuestan mucho gas en Ethereum).
Sin embargo, es bueno ver que en esta etapa, las pruebas de conocimiento cero son posibles, y si tienes una buena comprensión de las operaciones de curvas elípticas (y no has olvidado tu aritmética de matrices), no son difíciles de entender.
Haciendo que esta técnica sea verdaderamente de conocimiento cero
Tal como está ahora, nuestro vector testigo no puede ser desencriptado, sin embargo, puede ser adivinado. Si un atacante (alguien que intenta descubrir el testigo sin encriptar) usa alguna información auxiliar para hacer una suposición fundamentada sobre el testigo, puede comprobar su trabajo multiplicando su vector testigo adivinado por los generadores de puntos de la curva elíptica y viendo si el resultado es el mismo que los vectores testigo del probador.
Aprenderemos cómo defendernos de la adivinación de testigos en nuestra cobertura de Groth16.
Además, ten en cuenta que nadie usa el algoritmo descrito en el mundo real, ya que es demasiado ineficiente. Sin embargo, si lo implementas, te ayudará a practicar la implementación de aritmética de curvas elípticas significativa y a construir una prueba de conocimiento cero funcional de extremo a extremo (casi).
Puedes ver un ejemplo de implementación del algoritmo descrito aquí por Obront en este repositorio.
Aprende más con RareSkills
Este material es de nuestro Zero Knowledge Course.
Publicado originalmente el 26 de agosto de 2023