La criptografía ZK es la familia de protocolos que permite a un probador convencer a un verificador de que una afirmación es verdadera sin revelar el testigo que la respalda. Este artículo explica el modelo mental probador-verificador, contrasta las decisiones de diseño de SNARK y STARK, y ofrece un flujo de trabajo ejecutable para verificar pruebas y medir los costos de prueba y verificación. También cubre los modos de fallo que rompen implementaciones reales, como claves de verificación desajustadas, entradas públicas incorrectas y suposiciones de configuración de confianza. El objetivo es un método reproducible que pueda aplicar a cualquier stack ZK en lugar de un tutorial específico de un proveedor.
Qué garantiza la criptografía ZK y qué no
Una prueba de conocimiento cero (ZKP) es un protocolo en el que un probador convence a un verificador de que una afirmación es verdadera sin transmitir nada más allá de la verdad de esa afirmación. En criptografía ZK, la afirmación generalmente se expresa como una relación sobre una instancia pública y un testigo privado: el probador conoce un testigo w tal que R(instancia, w) se cumple, y el verificador solo aprende que tal w existe. Un ejemplo canónico es probar el conocimiento de una clave privada correspondiente a una clave pública sin revelar la clave.
Tres propiedades definen el contrato de seguridad. Completitud significa que un probador honesto con un testigo válido siempre convence al verificador. Solidez significa que un probador deshonesto no puede convencer al verificador de una afirmación falsa excepto con probabilidad insignificante. Conocimiento cero significa que el verificador no aprende nada sobre el testigo más allá de la verdad de la afirmación. Una cuarta propiedad, la sucintitud, es lo que hace prácticos a los SNARK y STARK: el tamaño de la prueba y el tiempo de verificación se mantienen pequeños en relación con el tamaño de la computación.
Lo que la criptografía ZK no le da: no oculta el hecho de que se hizo una prueba, no protege el testigo si su entorno de probador está comprometido, y no elimina la necesidad de vincular las pruebas a las entradas públicas correctas. La mayoría de los incidentes en producción no son fallos de las matemáticas subyacentes; son desajustes entre el circuito, la clave de verificación y las entradas públicas.
- Completitud: un testigo válido siempre verifica.
- Solidez: las afirmaciones falsas no pueden probarse (excepto con probabilidad insignificante).
- Conocimiento cero: el verificador no aprende nada sobre el testigo.
- Sucintitud: el tamaño de la prueba y el costo de verificación son sublineales en la computación.
Suposiciones y requisitos previos antes de construir
Antes de escribir un circuito o llamar a un verificador, fije las siguientes suposiciones para que sus mediciones sean significativas. Necesita una afirmación y un testigo definidos, un sistema de pruebas elegido (familia SNARK o STARK), una decisión de configuración confiable o transparente, y un entorno de verificador que pueda reproducir las mismas entradas públicas. Para la verificación en cadena también necesita un endpoint RPC de confianza para leer contratos de verificación y registros de eventos; la guía de endpoints RPC multicadena cubre cómo seleccionar uno, y el servicio API de OnFinality puede servir como ruta de lectura para transacciones de verificación.
Las versiones del conjunto de herramientas importan más que en la mayoría del software. Las versiones de Circom, snarkjs, Halo2, Plonky y Noir cambian los sistemas de restricciones y las claves de verificación entre versiones. Fije cada dependencia y registre las versiones exactas junto a cualquier benchmark que publique. Si verifica en cadena, también fije la cadena y la dirección del contrato, porque un verificador desplegado en una red no existe en otra; consulte las redes compatibles antes de asumir que un verificador es accesible.
- Afirmación y testigo definidos por escrito.
- Sistema de pruebas y modelo de configuración elegidos (confiable vs transparente).
- Versiones del conjunto de herramientas fijadas y hash de commit del circuito.
- Entorno del verificador y codificación de entradas públicas documentados.
- Ruta de lectura RPC seleccionada para comprobaciones de verificación en cadena.
El modelo mental probador-verificador
Piense en un protocolo ZK como una tubería de compilador. La lógica de su aplicación se compila en un circuito aritmético o un sistema de restricciones. El probador toma el testigo y produce un objeto de prueba. El verificador toma la prueba, la clave de verificación y las entradas públicas, y devuelve aceptar o rechazar. La clave de verificación se deriva del circuito y, para SNARK con configuración confiable, de los parámetros de la ceremonia. Si cualquiera de estas tres entradas cambia, la verificación falla incluso cuando la prueba en sí es válida.
Las pruebas interactivas requieren mensajes de ida y vuelta entre probador y verificador. Las pruebas no interactivas colapsan esto en un solo mensaje, típicamente mediante la transformación Fiat-Shamir, que hashea la transcripción para derivar valores de desafío. Esa transformación es donde viven muchos errores de implementación: si el probador y el verificador hashean campos de transcripción diferentes, o hashean en un orden diferente, los desafíos derivados divergen y la verificación falla. Al depurar, siempre compare la codificación exacta en bytes de las entradas públicas y el separador de dominio de la transcripción en ambos lados.
- El circuito define la relación.
- El probador consume el testigo y emite la prueba.
- El verificador consume la prueba, la clave de verificación y las entradas públicas.
- La transcripción Fiat-Shamir debe coincidir byte a byte en ambos lados.
SNARK versus STARK: elegir un sistema de pruebas
Los SNARK (argumentos de conocimiento sucintos no interactivos) generalmente producen pruebas pequeñas y verificación rápida, por lo que dominan la verificación en cadena. Muchas construcciones SNARK dependen de emparejamientos de curvas elípticas y una configuración confiable, por lo que la ceremonia y el manejo de sus residuos tóxicos se convierten en parte de su modelo de seguridad. Los STARK (argumentos de conocimiento escalables transparentes) usan compromisos basados en hash, evitan la configuración confiable y escalan a computaciones muy grandes, pero las pruebas son más grandes y la verificación es más pesada.
La elección es una compensación, no una clasificación. Si su verificador se ejecuta dentro de un contrato inteligente con presupuestos de gas ajustados, las pruebas SNARK pequeñas suelen ser la opción práctica. Si necesita transparencia y suposiciones con inclinación post-cuántica, los sistemas de la familia STARK son atractivos. Las pruebas recursivas, donde una prueba verifica otras pruebas, le permiten amortizar el costo de verificación entre lotes y son comunes en diseños de rollup. Cualquiera que elija, documente explícitamente las suposiciones de configuración y el nivel de seguridad, porque los auditores lo preguntarán.
- SNARK: pruebas pequeñas, verificación rápida, a menudo configuración confiable.
- STARK: configuración transparente, pruebas más grandes, verificación más pesada.
- Pruebas recursivas: amortizan la verificación entre lotes.
- Documente las suposiciones de configuración y el nivel de seguridad para los auditores.
Flujo de trabajo de verificación ejecutable con snarkjs
El siguiente procedimiento verifica una prueba Groth16 usando snarkjs, un conjunto de herramientas JavaScript ampliamente utilizado. Asume que ya tiene proof.json, public.json y verification_key.json de una ejecución de prueba previa. El comando es determinista: sale con 0 e imprime OK! en caso de éxito, y sale con un valor distinto de cero en caso de fallo. Ejecútelo en CI para que cada cambio de circuito esté condicionado a una comprobación de verificación.
El detalle crítico es que public.json debe contener las entradas públicas en el orden exacto en que el circuito las declara. Reordenar señales, o codificar un elemento de campo como una cadena decimal en un lugar y una cadena hexadecimal en otro, es la causa más común de que una prueba válida falle la verificación.
npm install -g snarkjs@latest
# Verify a Groth16 proof against the verification key and public inputs
snarkjs groth16 verify verification_key.json public.json proof.json
# Expected output on success:
# [INFO] snarkJS: OK!Verificación de pruebas en cadena a través de un endpoint RPC
Para verificadores en cadena, la prueba se envía en una transacción y la función verifyProof del contrato devuelve un booleano. Puede reproducir el resultado de la verificación fuera de la cadena llamando a la función de vista con los mismos datos de llamada. El siguiente script lee el contrato verificador a través de un endpoint RPC e imprime el resultado. Reemplace RPC_URL, VERIFIER_ADDRESS y los argumentos de la prueba con sus propios valores; nunca confirme claves reales.
Use un endpoint dedicado para comprobaciones de verificación repetidas para no ser limitado a mitad de ejecución. La oferta de nodos dedicados de OnFinality proporciona capacidad aislada, y los precios de RPC explican cómo el volumen de solicitudes se asigna al costo. Para una comparación rápida de proveedores antes de comprometerse, la nota del Asistente RPC sobre la mejor API RPC de Ethereum es un punto de partida útil.
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const verifier = new ethers.Contract(
process.env.VERIFIER_ADDRESS,
["function verifyProof(uint256[2] a, uint256[2][2] b, uint256[2] c, uint256[] publicInputs) view returns (bool)"],
provider
);
async function main() {
const ok = await verifier.verifyProof(
["0x...", "0x..."],
[["0x...", "0x..."], ["0x...", "0x..."]],
["0x...", "0x..."],
["1", "2"]
);
console.log("verified:", ok);
}
main().catch((e) => { console.error(e); process.exit(1); });Medición reproducible del costo de prueba y verificación
Las afirmaciones de rendimiento ZK solo son útiles si el método de medición es reproducible. Registre el commit del circuito, las versiones del conjunto de herramientas, el hardware (modelo de CPU, número de núcleos, RAM) y el comando exacto utilizado. Ejecute cada medición al menos cinco veces y reporte la mediana y la dispersión, no una sola ejecución. Separe el tiempo de prueba del tiempo de verificación, porque escalan de manera diferente: la prueba crece con el tamaño del circuito, mientras que la verificación se mantiene aproximadamente constante para sistemas sucintos.
Use la siguiente tabla como plantilla de resultados. Complétela con sus propias ejecuciones; no copie números de la documentación, porque el tiempo de prueba depende en gran medida del hardware y la versión. El procedimiento es: (1) congelar el circuito y el testigo, (2) ejecutar el probador cinco veces, (3) ejecutar el verificador cinco veces, (4) registrar la mediana y el mínimo/máximo, (5) repetir después de cualquier actualización del conjunto de herramientas para detectar regresiones.
- | Métrica | Ejecución 1 | Ejecución 2 | Ejecución 3 | Mediana | Dispersión |
- | Tiempo de prueba (s) | | | | | |
- | Tiempo de verificación (ms) | | | | | |
- | Tamaño de la prueba (bytes) | | | | | |
- | RAM máxima (MB) | | | | | |
- | Gas en cadena (verificación) | | | | | |
Resultado esperado de la verificación y cómo interpretarlo
Una verificación fuera de cadena exitosa imprime OK! y sale con 0. Una verificación en cadena exitosa devuelve true de la llamada de vista y no emite reversión. Trate cualquier otro resultado como un fallo y capture el error completo, porque el mensaje de error generalmente identifica qué entrada divergió. Si el verificador revierte con InvalidProof, la prueba o las entradas públicas son incorrectas. Si revierte con un error de gas, la codificación de la prueba puede estar malformada o el contrato verificador puede estar en una red diferente a la que espera.
Registre el hash de la clave de verificación y el hash de las entradas públicas junto a cada resultado de verificación. Cuando una prueba que pasó en staging falla en producción, comparar esos dos hashes le dice de inmediato si el circuito cambió o si las entradas cambiaron. Esta única práctica resuelve la mayoría de los fallos de verificación entre entornos.
- Código de salida 0 y
OK!significan que la verificación fuera de cadena tuvo éxito. - El valor de retorno
truesignifica que la verificación en cadena tuvo éxito. - La reversión
InvalidProofapunta a un desajuste de prueba o entradas públicas. - Registre el hash de la clave de verificación y el hash de las entradas públicas en cada ejecución.
Solución de fallos comunes de verificación ZK
Clave de verificación desajustada. La prueba se generó contra una versión del circuito diferente a la que espera el verificador. Regenerar la clave de verificación a partir del commit exacto del circuito y volver a desplegar el verificador. Orden incorrecto de entradas públicas. El circuito declara las señales en un orden y el llamador las proporciona en otro. Compare la lista de señales públicas del circuito con el arreglo que pasa. Desajuste de codificación. Los elementos de campo codificados como cadenas decimales en un componente y hexadecimal en otro no coincidirán. Estandarice una codificación en el límite.
Divergencia de la transcripción Fiat-Shamir. El probador y el verificador hashean campos diferentes o usan separadores de dominio diferentes. Compare la construcción de la transcripción en ambas implementaciones. Desajuste de configuración confiable. La prueba se generó con parámetros de ceremonia que no coinciden con la clave de verificación. Vuelva a ejecutar la prueba con los parámetros correctos. Estado RPC obsoleto. La verificación en cadena lee un contrato verificador que fue actualizado o redesplegado; confirme la dirección y el ID de cadena antes de depurar la prueba en sí.
- Regenere la clave de verificación a partir del commit exacto del circuito.
- Haga coincidir el orden de las entradas públicas con las señales declaradas del circuito.
- Estandarice la codificación de elementos de campo en cada límite.
- Alinee los campos de la transcripción Fiat-Shamir y los separadores de dominio.
- Confirme la dirección del verificador y el ID de cadena antes de depurar las matemáticas.
Limitaciones, compensaciones y riesgos operativos
La criptografía ZK desplaza la confianza en lugar de eliminarla. Las configuraciones confiables concentran el riesgo en la ceremonia; una ceremonia comprometida puede falsificar pruebas. Las configuraciones transparentes eliminan ese riesgo pero aumentan el tamaño de la prueba y el costo de verificación. Las pruebas recursivas añaden complejidad y nuevos modos de fallo. Ninguno de estos sistemas protege un testigo que se filtra a través de canales laterales en el entorno de ejecución del probador, así que aísle los entornos de prueba y trate el material del testigo como secretos de producción.
Operativamente, la prueba es costosa y a ráfagas. Un probador que se ejecuta cómodamente en desarrollo puede agotar la memoria con un testigo grande en producción. Presupueste hardware dedicado, monitoree la RAM máxima y ponga en cola los trabajos de prueba en lugar de ejecutarlos concurrentemente en máquinas compartidas. La verificación en cadena cuesta gas que varía con la codificación de la prueba y la implementación del verificador, así que mida el gas en la red objetivo en lugar de estimarlo desde otra cadena.
- La configuración confiable concentra el riesgo de la ceremonia.
- Las configuraciones transparentes intercambian tamaño de prueba por eliminación de confianza.
- Los canales laterales del probador pueden filtrar testigos.
- La prueba consume mucha memoria; aísle y ponga en cola los trabajos.
- Mida el gas en la red objetivo, no en otra cadena.
Próximos pasos para una integración ZK en producción
Comience escribiendo formalmente la afirmación y el testigo, luego construya el circuito más pequeño que exprese la relación y verifíquelo de extremo a extremo antes de agregar características. Agregue el comando de verificación de snarkjs a CI para que cada cambio de circuito esté condicionado. Para verificadores en cadena, agregue una comprobación de llamada de vista contra una dirección de contrato y un ID de cadena fijados. Registre la tabla de resultados de la sección de medición y vuelva a ejecutarla después de cada actualización del conjunto de herramientas.
Cuando esté listo para conectar la verificación a redes en vivo, elija una ruta de lectura RPC que coincida con sus requisitos de confiabilidad. Revise la guía de endpoints RPC para los criterios de selección, compare los precios de RPC para la planificación de costos, y use nodos dedicados cuando el tráfico de verificación necesite aislamiento. Para recomendaciones concretas de endpoints y proveedores, consulte al Asistente RPC en lugar de adivinar solo con la documentación.
- Formalice la afirmación y el testigo antes de codificar.
- Condicione los cambios de circuito con verificación en CI.
- Fije la dirección del verificador y el ID de cadena para comprobaciones en cadena.
- Vuelva a ejecutar la tabla de medición después de cada actualización.
- Seleccione una ruta de lectura RPC que coincida con las necesidades de confiabilidad.