Resumen
Una zero-Knowledge Proof permite a una parte (el verificador) determinar la validez de una declaración dada por otra parte (el probador) sin ningún conocimiento del contenido de la declaración. Por ejemplo, es posible que Binance quiera probar que respaldó completamente los fondos de sus usuarios con reservas sin revelar los saldos de cada usuario.
Se podría construir una "Prueba de reservas" con un árbol de Merkle que protege contra la falsificación de los datos internos, en este caso, los saldos netos totales de los clientes, que son pasivos del exchange para sus usuarios. Esto se puede combinar con un zk-SNARK (un protocolo zero-Knowledge Proof) que garantiza que los usuarios puedan verificar que sus saldos están dentro del balance neto total de activos de todos los usuarios, sin conocer los saldos individuales.
Introducción
A la luz de los eventos del mercado, la seguridad de los criptoactivos en custodia se ha vuelto un tema muy discutido. Los usuarios de blockchain valoran mucho la transparencia y la apertura, pero también apoyan la privacidad y confidencialidad. De esta manera, surge el dilema para comprobar las reservas de los fondos mantenidos en custodia. A menudo, se debe compensar entre la transparencia, la confianza y la confidencialidad de los datos.
Sin embargo, este no tiene por qué ser el caso. Al combinar los protocolos Zero-Knowledge Proof, como los zk-SNARK, con árboles de Merkle, podemos encontrar una solución efectiva para todas las partes.
¿Qué es una Zero-Knowledge Proof?
Una Zero-Knowledge Proof permite a una parte (el verificador) determinar la validez de una declaración dada por otra parte (el probador) sin ningún conocimiento del contenido de la declaración. Veamos un ejemplo simple.
Supongamos que tienes una caja fuerte de la que solo tú conoces la combinación. En este ejemplo, esta caja fuerte no se puede tomar, forzar o abrir de ninguna otra manera que no sea usando la combinación. Tu amigo, que también participa en el experimento, conoce este hecho. Es algo establecido, verificado y conocido por él.
Afirmas que conoces la combinación, pero que no quieres compartirla ni abrir la caja fuerte delante de tu amigo. En la parte superior de la caja hay un agujero a través del cual tu amigo puede introducir un papel con una nota. Para que todo esto sea una Zero-Knowledge Proof, tu amigo no debe conocer ninguna información adicional sobre el proceso, excepto lo que tú afirmaste.
Puedes demostrarle a tu amigo que conoces la combinación de la caja abriéndola, diciéndole lo que estaba escrito en la nota y volviéndola a cerrar. De esta forma, en ningún momento revelarías la combinación.
Para ver un ejemplo más avanzado, consulta nuestro artículo ¿Qué es Zero-Knowledge Proof y cómo afecta a la blockchain?
¿Por qué usamos una Zero-Knowledge Proof?
Las Zero-Knowledge Proofs son adecuadas para demostrar algo sin revelar información o detalles sensibles. Este podría ser el caso si no quisieras entregar tu información financiera o personal, que podría ser utilizada de manera inapropiada.
En el ámbito cripto, podrías probar que tienes una clave privada sin la necesidad de revelarla ni de firmar algo digitalmente. Un exchange de criptomonedas también puede querer demostrar el estado de sus reservas sin revelar información confidencial sobre sus usuarios, incluidos sus saldos de cuenta individuales.
Para estos ejemplos (y muchos otros), una Zero-Knowledge Proof usaría un algoritmo que toma un input de datos y devuelve un output como "verdadero" o "falso".
Cómo definir las Zero-Knowledge Proofs en términos técnicos
Una Zero-Knowledge Proof, en términos técnicos, sigue una estructura específica con ciertos criterios. Ya vimos los roles de probador y verificador, pero la Zero-Knowledge Proof también debe cubrir tres criterios:
Completitud. Si la declaración es verdadera, el verificador quedará convencido por la prueba proporcionada, sin la necesidad de recibir ninguna otra información o verificación.
Validez. Si la declaración es falsa, un verificador no se convencerá de la veracidad de dicha afirmación por la prueba proporcionada.
Zero-knowledge. Si la declaración es verdadera, el verificador no obtiene ninguna información más allá de que la afirmación es verdadera.
¿Qué es un zk-SNARK?
Un zk-SNARK (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge, o sea, argumento sucinto no interactivo de conocimiento cero) es un protocolo de prueba que sigue los principios de zero-knowledge ya definidos. Con un zk-SNARK, podrías probar que conoces el valor de hash original (lo explicaremos más adelante) sin revelarlo. También podrías probar la validez de una transacción sin revelar ninguna información sobre cantidades, valores o direcciones específicas que estén involucradas.
Los zk-SNARK se usan y discuten comúnmente dentro del mundo de blockchain y criptomonedas. Pero te puedes preguntar por qué alguien se molestaría en usar un zk-SNARK cuando podría usar un método simple de par de claves públicas y privadas para proteger la información. Sin embargo, no podríamos implementar la prueba matemática para garantizar que no se incluyan balances negativos ni la suma del árbol de Merkle.
En el caso de las reservas de un exchange, queremos demostrar el respaldo en una proporción de 1:1 de los balances de los clientes sin que se hagan públicos los identificadores y los balances de cada cuenta. Además, la tecnología zk-SNARK hace que falsificar datos sea aún menos probable.
¿Qué es un árbol de Merkle?
Presentar la suma de los fondos de las cuentas de los usuarios de Binance requiere trabajar con un gran conjunto de datos. Una forma de presentar esta gran cantidad de datos de manera criptográfica es usando un árbol de Merkle. En dicho árbol se puede almacenar de manera eficiente una gran cantidad de información, y su naturaleza criptográfica hace que la integridad de los datos sea fácilmente verificable.
Funciones Hash
Para cifrar de forma sucinta un input, el árbol de Merkle depende del uso de funciones hash. En resumen, el hashing es el proceso de generar un output de extensión fija a partir de un input de extensión variable. En otras palabras, cuando se hace el hash de un input de cualquier longitud mediante un algoritmo, producirá un output de una longitud fija cifrada.
Siempre que el input siga siendo el mismo, el output también lo será. Esto significa que podemos tomar grandes cantidades de datos de transacciones y aplicarle la función de hash para obtener un output manejable. Si se cambia alguna información del input, el output será radicalmente diferente.
Por ejemplo, podríamos tomar el contenido de 100 libros y pasarlo por la función hash SHA-256. Como resultado, obtendríamos un output de este estilo:
801a9be154c78caa032a37b4a4f0747f1e1addb397b64fa8581d749d704c12ea
Si luego cambiamos un solo carácter del input (de alguno de esos 100 libros), el hash sería completamente diferente, como:
abc5d230121d93a93a25bf7cf54ab71e8617114ccb57385a87ff12872bfda410
Esta es una cualidad importante de las funciones de hash porque hace que la verificación de la precisión de los datos sea fácil. Si alguien replica el proceso de hashing de esos mismos 100 libros usando el algoritmo SHA-256, obtendría exactamente el mismo hash que el del output. Si el output es diferente, podemos afirmar con certeza que el input cambió. Esto significa que no hay necesidad de verificar individual y manualmente las diferencias entre los inputs, lo cual puede requerir mucho esfuerzo.
Los árboles de Merkle en el criptomundo
Cuando se almacenan datos de transacciones en una blockchain, cada nueva transacción se envía a través de una función de hash, que genera valores de hash únicos. Supongamos que tenemos ocho transacciones (de la A a la H) y que pasamos cada una de ellas por la función de hash para obtener los outputs de hash. Estos son lo que llamamos nodos de hoja de Merkle. En la imagen incluida a continuación, puedes ver el valor de hash único de cada letra: hA por A, hB por B, hC por C, etc.
Entonces, podemos tomar pares de outputs sometidos a hashing, combinarlos y recibir un nuevo output de hash. Por ejemplo, los hashes de hA y hB pasados juntos por la función de hash nos darían un nuevo output de hash de hAB conocido como rama de Merkle. Ten en cuenta que cada vez que se genera un nuevo output, viene con una longitud y tamaño fijos, según la función de hash usada.
Ahora tenemos los datos de dos transacciones (A y B) combinados en un hash (hAB). Ten en cuenta que si cambiamos cualquier información de A o B y repetimos el proceso, nuestro output de hash hAB sería completamente diferente.
El proceso continúa a medida que combinamos nuevos pares de hashes para pasarlos nuevamente por la función de hash (ver la imagen a continuación). Pasamos hAB por la función de hash junto con hCD y obtenemos un hash único hABCD, y hacemos lo mismo con hEF y hGH para obtener hEFGH. Al final, recibimos un único hash que representa los outputs de hash de todos los hashes de las transacciones previas. En otras palabras, el output de hash hABCDEFGH representa toda la información que le precedió.
El gráfico incluido arriba se llama árbol de Merkle, y el output de hash hABCDEFGH es la raíz de Merkle. Usamos las raíces de Merkle en encabezados de bloque, ya que resumen criptográficamente todos los datos de las transacciones de un bloque de manera resumida. También podemos verificar rápidamente si algún dato fue manipulado o cambiado dentro del bloque.
Las limitaciones del árbol de Merkle
Volvamos a nuestro ejemplo de las reservas de un CEX. Un CEX quiere demostrar el respaldo en una proporción de 1:1 de todos los activos de sus clientes y construye un árbol de Merkle que pasa todos los UID de sus clientes por la función de hash junto con sus holdings de activos neto (compensaciones de activos y pasivos) a nivel de tokens. Una vez liberado (y firmado para probar la propiedad sobre la raíz de Merkle proporcionada), un usuario individual no tendría cómo verificar si el árbol de Merkle es válido sin acceder a todos sus inputs.
El exchange puede haber omitido incluir algunos inputs. También podría crear cuentas falsas con balances negativos para alterar el pasivo total. Por ejemplo, aunque los activos de los clientes pueden sumar 1,000,000 USD, se podría agregar una cuenta falsa con un balance de -500,000 USD, lo que crearía un objetivo de reservas de solo 500,000 USD.
El caso de la prueba de reservas es diferente de la raíz de Merkle de un bloque, ya que los usuarios pueden ver todas las transacciones que contiene un bloque en un explorador de blockchain. Un CEX, sin embargo, no querrá revelar el balance de cada cuenta por motivos de seguridad y privacidad de datos. Los clientes tampoco estarían felices de que los balances de sus cuentas se hicieran públicos. En este caso, el CEX no puede probar que los balances de los usuarios suman el total correcto sin revelar los balances de los usuarios.
Una solución de la que pueden valerse los exchanges es usar un auditor externo confiable. El auditor puede verificar las cuentas individuales y las reservas antes de dar fe de la validez de la raíz de Merkle proporcionada. Sin embargo, para los usuarios, este método requiere confianza en el auditor y en los datos usados para la auditoría. No tienes que depender de un tercero cuando puedes confiar en los datos.
zk-SNARK y árboles de Merkle combinados
El problema que presentamos antes es un caso perfecto para usar zk-SNARK. Queremos probar que las reservas cubren por entero los pasivos de los usuarios y que no son falsificadas. Sin embargo, por motivos de privacidad y seguridad, no queremos mostrar al verificador la composición exacta de los balances y reservas de los usuarios.
Al usar un zk-SNARK, el exchange de criptomonedas puede probar que todos los conjuntos de nodos hoja del árbol de Merkle (es decir, los balances de las cuentas de los usuarios) están incluidos en el balance total de los activos de los usuarios indicado por el exchange. Cada usuario puede acceder fácilmente a su nodo hoja y verificar que su balance se incluyó en el proceso. El zk-SNARK también garantiza que cualquier árbol de Merkle generado no contenga usuarios con un balance total neto de activos en negativo (lo que implicaría la falsificación de datos, ya que todos los préstamos están sobregarantizados). También se usa un cálculo del estado global de Binance, es decir, una lista del balance total neto de cada activo que tiene cada cliente de Binance.
Veamos cómo funciona y cómo aborda Binance esta situación. Para empezar, Binance define las limitaciones del cómputo que desea probar y las define como un circuito programable. A continuación, incluimos un conjunto de tres limitaciones que usa Binance en su modelo.
Para el conjunto de balances de cada usuario (nodo de hoja del árbol de Merkle), nuestro circuito asegura que:
Los balances de activos de un usuario están incluidos en el cálculo de la suma de los balances totales netos que hay en Binance.
El balance total neto del usuario es mayor o igual a cero.
El cambio en la raíz del árbol de Merkle es válido (es decir, no hay uso de información falsa) luego de actualizar la información de un usuario al hash del nodo de hoja.
Binance puede entonces generar una prueba zk-SNARK para la construcción del árbol de Merkle de acuerdo con el circuito. Esto conlleva a que el exchange ejecute el cómputo pesado de pasar los ID y balances de los usuarios por la función de hash mientras garantiza que la prueba pase las limitaciones.
Un verificador examinará la prueba (y el código de código abierto de acceso público) para convencerse de que el cómputo se ejecutó bajo el cumplimiento de las limitaciones. El cómputo de verificación lleva un tiempo sumamente corto en comparación con el tiempo de prueba.
Con cada Prueba de reservas, el exchange publicará:
1. La prueba de Merkle para cada usuario.
2. La prueba zk-SNARK y el input público (un hash de la lista del balance total neto de cada activo y la raíz de Merkle) del circuito para todos los usuarios.
Las partes interesadas pueden verificar la prueba de Merkle y asegurarse de que sus balances individuales formen parte de la raíz del árbol de Merkle. También pueden verificar la prueba zk-SNARK para cerciorarse de que la construcción del árbol de Merkle cumpla con las limitaciones definidas en el circuito. Para obtener una explicación más detallada de la solución zk-SNARK y su rendimiento, consulta nuestro blog Cómo mejoran los zk-SNARK el sistema de Prueba de Reservas de Binance.
Conclusiones
Los zk-SNARK proporcionan la tecnología necesaria para garantizar tanto la integridad de los datos como su privacidad. Su aplicación para comprobar la existencia de las reservas y aumentar la transparencia de un CEX debería ayudar a aumentar la confianza en la industria blockchain. Para muchas personas, un desarrollo como este ha sido largamente esperado y llega en un momento fundamental para los CEX.
Esta es la primera versión de nuestro zk-SNARK, y esperamos recibir comentarios de la comunidad para poder continuar mejorando el sistema.