ZkDai: transacciones DAI privadas en Ethereum usando Zk-SNARKs

En esta publicación, mi objetivo es familiarizar al lector con algunas herramientas para implementar Zk-SNARKs en ethereum. Por el bien de la ilustración, estaría recorriendo nuestro proyecto en el que mi equipo trabajó durante ETHSingapore; ZkDai: realice ZCash como transacciones DAI privadas en Ethereum.

Usando ZkDai uno puede proteger al remitente de la transacción, el destinatario y el monto. ¡Obtuvimos el premio de patrocinador de la API MakerDAO para el proyecto! Nuestro envío, junto con el video de demostración, se puede encontrar aquí y el repositorio de github aquí.

Recientemente, se ha escrito mucho h sobre pruebas de conocimiento cero, sin embargo, a pesar de eso, las matemáticas detrás de las pruebas de conocimiento cero siguen siendo “crípticas” y difíciles de comprender. Para un lector no iniciado, Introducción a zk-SNARKs y Programas de aritmética cuadrática sirven como excelentes recursos para comenzar a leer sobre Zk-SNARKs y esta publicación ayudará al lector a ponerlos en práctica mientras trata las matemáticas subyacentes como una caja negra.

¿Qué hace una prueba sucinta de conocimiento cero de propósito general?
Suponga que tiene una función (pública) C, una entrada (privada) w y una salida (pública) x . Quiere demostrar que conoce una w tal que C (w) = x , sin revelar qué es w. Además, para que la prueba sea concisa, desea que sea verificable mucho más rápido que calcular C en sí. Ahora, cuando un probador desea demostrar que conoce una respuesta al cálculo C, generará una prueba utilizando la clave de prueba , entradas públicas (x) y entradas secretas (w). Cuando el verificador quiere verificar esa respuesta que el cálculo es realmente correcto, verificaría la prueba usando la clave de verificador, entradas públicas (x) y prueba.

Un cómputo Zk-SNARK debe ser inicializado por una parte confiable. Esto implica modificar el cálculo C en un formato de circuito y usar un parámetro secreto lambda para generar las claves de prueba y verificación que se usan más adelante. Después de esto, es necesario destruir lambda – también llamado el “desperdicio tóxico” – de lo contrario, permitirá que un adversario genere pruebas falsas del conocimiento. Por esta razón, el equipo de Zcash, una bifurcación de bitcoin que se basa en zK-SNARK y permite realizar transacciones “protegidas”, llevó a cabo una ceremonia elaborada para generar estas claves. Zcash tiene transacciones protegidas opcionales que le permiten transferir montos ocultos a direcciones ocultas.

Implementación de ZkDai

Los detalles de implementación detrás de ZkDai se inspiran directamente en el papel zerocash. Tenga en cuenta que esta es una implementación muy hacky de 36 horas (privado de sueño y infusión de red bull), y es posible que el enfoque tenga fallas y (o) sea altamente ineficiente en la práctica. Planeo solucionar esos problemas mientras sigo trabajando en el proyecto.

ZkDai tiene el concepto de una nota secreta . Una nota se indica mediante una tupla que es una combinación de 2 elementos: la clave pública del propietario de la nota (pk) y el valor de la nota en Dai ( v ).

Una nota reside en la cadena como (Hash (Nota), Encriptar (Nota)) . El segundo elemento Encrypt (Note) es la nota encriptada con la clave pública del propietario de la nota (en la práctica, la clave pública de encriptación probablemente será diferente de pk usada anteriormente) . La versión encriptada de la nota es para que el propietario identifique que una nota en particular le pertenece.
Llamemos a Hash (Nota) como la nota secreta. Dado que solo la nota secreta con hash se guarda en la cadena, es imposible saber la imagen previa que constituye el hash y, por lo tanto, nadie más que el propietario de la nota está al tanto de la tupla (pk, v) que constituye la nota.

Gastar una nota ZkDai

Las notas de ZkDai se gastan como UTXO. Para transferir un valor particular a un receptor, seleccione algunas notas secretas cuyo valor neto sea al menos el valor con el que desea realizar la transacción. Este valor se propagará al receptor en forma de una nueva nota ZkDai y el valor sobrante se convertirá en una nueva nota secreta (como UTXO) asignada a su clave.

Ahora, aquí es donde entran en escena las pruebas de conocimiento cero. Para poder gastar una nota en particular, el propietario debe demostrar el conocimiento de:

El cálculo público C luego devuelve el hash de la nota, la que también está presente en la cadena. Poniéndolo en la forma C (w) = x de cálculo de conocimiento cero anterior, se convierte en

Si tiene una prueba válida de este cálculo, ¡de hecho es el orgulloso propietario de la nota secreta!

Un caso más específico (pero sin la pérdida de generalidad) sería enviar un valor a un receptor usando una sola nota secreta. Esto implicaría generar 2 notas nuevas:

Nota N2 es el cambio sobrante de la nota original de valor v, por lo que, al igual que los UTXO, se asignará al remitente. Entonces, los 2 nuevos parámetros privados para la generación de zkp son receiverPk y v ’. Además, modificando ligeramente el cálculo C de modo que devuelva 1 cuando C (w) == x ; la prueba es válida si C (x, w) = 1 donde x es la entrada pública. Entonces, para ZkDai, el cálculo se convierte en

El cálculo de conocimiento cero C se ve así

Una vez que se genera la prueba de conocimiento cero, se puede enviar junto con las versiones encriptadas de newNote1 y newNote2 en una transacción.

Tenga en cuenta que esta transacción oculta el gráfico de transacciones. El remitente está oculto en el sentido de que puede usar una nueva dirección eth cada vez para realizar una transacción que envía el zkp en cadena. Solo necesita demostrar el conocimiento de “sk”, que es la clave secreta correspondiente a la clave pública que posee la nota, es decir, la prueba de conocimiento cero es lo que prueba la propiedad de una nota, no el remitente de la transacción. El destinatario siempre está oculto, ya que esa información está codificada en la nota hash.

Implementación de zkSNARK en Ethereum

Zokrates es una caja de herramientas para zkSNARKs en Ethereum. Le ayuda a crear programas fuera de la cadena (pruebas de conocimiento cero) y vincularlos a la cadena de bloques Ethereum. Primero, configuraremos zokrates en nuestra máquina local y lo ejecutaremos como un contenedor de ventana acoplable.

En el momento de escribir este artículo, zokrates se encuentra en la versión 0.3.1

Ahora, escribiremos la función de cálculo que un comprobador necesita probar (y la que el verificador verificará) en el archivo zk-circuit.code . Algunas notas de los documentos de ZoKrates antes de continuar:

Tenemos que pasar 512 bits de entrada a sha256. Como consecuencia, usamos cuatro elementos de campo, cada uno codificando 128 bits, para representar nuestra entrada. Luego, los cuatro elementos se concatenan en ZoKrates y se pasan a SHA256. Dado que el hash resultante tiene una longitud de 256 bits, lo dividimos en dos y devolvemos cada valor como un número de 128 bits. El circuito zk se verá así:

Copie el zk-circuit.code dentro del contenedor de zokrates.
docker cp zk-circuit.code zokrates: / home / zokrates /

Vaya al contenedor de zokrates y compile el circuito. zokrates-compile compila el circuito en condiciones planas y produce dos archivos: archivo “.code” legible por humanos y archivo binario.

Ahora, generamos las claves de comprobación y verificación para el cálculo.

zokrates setup realiza una configuración confiable para un sistema de restricciones dado. Utiliza una función de generador para generar estas claves a partir del circuito aritmético y el parámetro lambda de “residuos tóxicos”. (pk, vk) = G (λ, C) .
./zokrates setup

Puede comprobar que se han generado 2 archivos nuevos, proving.key y verify.key . La clave de prueba puede hacerse pública para que cualquier persona pueda generar pruebas del conocimiento. A continuación, generaremos un contrato de verificador que se implementará en la cadena de bloques ethereum. Afortunadamente, zokrates proporciona esta funcionalidad lista para usar.
./zokrates export-verifier

Usando verifying.key , genera un contrato de solidez que contiene la clave de verificación generada y una función pública verifyTx para verificar una solución al programa compilado.

Ahora generaremos la prueba zk. Escribí un script que imprime el comando compute testimonial que debería ser enviado al zokrates cli para generar la prueba requerida.

La función getTransferZkParams (remitente, noteValue, receptor, valor) imprime el comando compute-testimonial zokrates cli que debe ejecutarse dentro del contenedor docker zokrates. Para mi ejemplo de

Al ejecutar el script zokcmd.js , se obtiene el comando

Aquí cada entrada es un número de 128 bits y está representada en base 10. Ahora, ejecutando el comando en el contenedor zokrates para generar el testigo; Obtengo
Testigo: ~ out_0 1

Ahora, para generar la prueba que realmente funcionará como una transacción;
./zokrates generate-proof
La prueba se escribe en proof.json .

El siguiente paso es enviar esta prueba como una transacción junto con las versiones encriptadas de las 2 nuevas notas. El script para hacer lo mismo se puede encontrar aquí.

Finalmente, hacia el final del hackathon, tenía 2 opciones: dormir un par de horas antes de que comenzara la evaluación o escribir en la funcionalidad para poder convertir su ZkDai secreto (lo llamábamos liquidar) en vainilla Token DAI visible públicamente para que el propietario gaste libremente. No hace falta decir que elegí el último: D El código de solidez para el mismo se puede encontrar aquí. Los detalles sobre el gas, la transacción de muestra, etc. se pueden encontrar en el siguiente tweet.

Sé que he pasado por alto algunos detalles. Si tiene alguna idea, pregunta o comentario, diríjase a la sección de comentarios. Si desea ayudarme a desarrollar más ZkDai, envíeme un mensaje de texto en Twitter. Me gustaría saber de ti.