
Solana Builders: ZK Compression
Tabla de contenido
- Propiedades
- Si el tamaño de la transacción es de 1232 bytes, ¿cómo envías todos los datos como parte de ella?
- ¿Las pruebas de Merkle son la única forma de hacerlo?
- Dices que los datos de la cuenta que normalmente se almacenan en un nodo completo como parte del estado deben proporcionarse junto con la transacción. ¿Dónde se almacenan esos datos?
- ¿Pueden hacer esto otras cadenas?
- ¿Qué es ZK Compression?
- Componibilidad atómica síncrona
- Paralelismo
En los días posteriores al anuncio de Helius y Light Protocol sobre su proyecto de compresión de conocimiento cero (ZK) en Solana, se ha hablado mucho sobre ZK Compression. Una parte importante de la conversación se ha centrado en la nomenclatura. ¿Es un rollup ZK? ¿Una L2? ¿O algo completamente distinto?
¿Por qué es importante? Algunas personas del ecosistema de Solana creen que las discusiones sobre la nomenclatura son innecesarias. Estoy parcialmente de acuerdo en que el nombre que usamos no es tan importante como lo que hace, pero sigue siendo relevante porque esos nombres hacen referencia a estructuras con propiedades específicas y las agrupan. Por lo tanto, llamarlo XYZ puede informarnos sobre sus propiedades y supuestos de confianza. ¡Y eso sí debería importarnos!
Propiedades
Antes de evaluarlas en el contexto de ZK Compression, enumeremos algunas propiedades de Solana que nos interesan:
- Componibilidad atómica síncrona
- Concurrencia
- Seguridad
- Disponibilidad
- Resistencia a la censura
Supuestos de confianza iniciales
Usaremos el término "sin necesidad de confianza" para referirnos a los supuestos de seguridad de un nodo completo. Esta definición será nuestra base. Todo lo que un nodo completo no pueda hacer por sí solo implica supuestos de confianza adicionales.
Un nodo completo es la única forma de interactuar con un protocolo sin necesidad de confianza. Si alguien introduce supuestos de confianza adicionales —un puente, un comité, una multifirma, ZK, fraude o cualquier otra cosa— y afirma que no requieren confianza, se equivoca.
Contexto
Intentaré ofrecer brevemente el contexto necesario sobre Solana para entender ZK Compression mediante una lista rápida de puntos.
- El estado de Solana se almacena en los discos de los nodos completos, en “AccountsDB”
- La unidad de almacenamiento se llama "cuenta"
- Las cuentas tienen direcciones (32 bytes cada una)
- La cantidad de datos que puede almacenar una cuenta varía entre 0 y 10 MB (máximo)
- Almacenar 10 MB en Solana cuesta aproximadamente 70 SOL, que paga el creador de la cuenta. Este costo está vinculado al almacenamiento y no al número de cuentas: puede ser 1 cuenta de 10 MB o 1000 cuentas de 10 KB.
- El tamaño actual de todas las cuentas en Solana es de 76 GB (comprimidos)
- Cada transacción de Solana debe especificar todas las cuentas que lee y en las que escribe
- Actualmente, las transacciones de Solana tienen un límite de 1232 bytes (existe una propuesta para aumentarlo)
- Cada transacción de Solana debe especificar ciertos datos
- Firmas (64 bytes cada una)
- Cuentas (32 bytes cada una)
- Datos de instrucciones (longitud arbitraria)
- Hash de bloque reciente (32 bytes)
- Direcciones de programas (32 bytes cada una) (CPI: invocaciones entre programas)
- Las transacciones de Solana incluyen un “hash de bloque reciente” de 32 bytes que debe incluirse dentro de los 150 bloques más recientes; de lo contrario, se considera inválido y debe volver a firmarse y enviarse
Ciclo de vida de una transacción normal
Cuando se ejecuta una transacción típica, su ciclo de vida es el siguiente:
- Primero se comprueba la antigüedad (solo las transacciones recientes son válidas), la deduplicación, la estructura, las comisiones (gas) y las firmas de una transacción
- El bytecode del programa se carga según la dirección del programa y se crea una instancia de la máquina virtual de Solana (SVM)
- Se comprueban todas las cuentas referenciadas por la transacción, se cargan del almacenamiento en la memoria y se pasan a la SVM
- Se ejecuta el bytecode del programa
- Todas las cuentas modificadas se sincronizan de nuevo con el almacenamiento en su estado actualizado
Las principales motivaciones de ZK Compression son:
- El estado on-chain es costoso. Por ejemplo, mil cuentas cuestan 70 SOL, por lo que productos como Drip Haus se encarecen rápidamente
- Incluso sin una merkelización completa del estado, más cuentas almacenadas en disco implican snapshots, índices y otros elementos de mayor tamaño
- No se accede con frecuencia a todas las cuentas, por lo que es innecesario asumir un costo continuo de recursos
Entonces, ¿cuál es la forma más sencilla de lograr la compresión?
En lugar de almacenar las cuentas en disco y leerlas cuando sea necesario (paso 3 del ciclo de vida de ejecución de una transacción), una transacción puede incluir los datos de la cuenta como parte del payload. Esto ahorra los costos asociados con el almacenamiento on-chain. Sin embargo, genera un nuevo problema: ¿cómo puedes garantizar que los usuarios no mientan sobre el estado?
Por ejemplo, supongamos que el valor off-chain de una cuenta que almacena el saldo de un token es 1200 y que su campo de propietario contiene “BYixJwV32DjeuyRww72PwZMyKcaedN533GBrv7CDh4n9”.
Si envías una transacción con esos datos a la cadena, ¿cómo sabría la cadena que no mentiste sobre cuántos tokens tiene la dirección “BYixJwV32DjeuyRww72PwZMyKcaedN533GBrv7CDh4n9”?
Después de todo, el nodo completo que procesa la transacción no tiene acceso a los datos off-chain; espera que tú proporciones los datos junto con la transacción.
Puedes usar pruebas de Merkle para esto. Sin entrar en detalles, considera que las pruebas de Merkle permiten "comprometerse" con ciertos datos de forma verificable y ocupan poco espacio de almacenamiento on-chain. Todos los nodos completos sincronizados con la cadena almacenan ese pequeño "compromiso". Cuando alguien proporciona los datos en una transacción, también puede incluir en la misma transacción una "prueba" que se verifica con respecto al compromiso. Esta prueba es criptográficamente segura.
¿Esto presenta algún problema?
El problema es que las pruebas de Merkle pueden ser grandes. Si un árbol contiene 100 000 cuentas, el tamaño de la prueba para una de ellas es 17 * 32 = 544 bytes. Si quieres proporcionar pruebas para varias cuentas, en el peor de los casos el tamaño se multiplica por el de la prueba. Así, diez cuentas ocuparían 10 * 544 = 5440 bytes en el peor de los casos. Este problema de espacio es específico de Solana porque, actualmente, una transacción de Solana tiene un límite de 1232 bytes, mientras que otras cadenas suelen ser menos restrictivas. Consulta la sección anterior, donde enumeramos el tamaño de cada componente: programas, firmas, hash de bloque reciente, etc. Por eso, incluso en el mejor de los casos, usas la mitad del tamaño de la transacción solo para la prueba de Merkle.
Esto plantea algunas preguntas.
Si el tamaño de la transacción es de 1232 bytes, ¿cómo envías todos los datos como parte de ella?
Es una excelente pregunta. ZK Compression resulta útil para una cantidad muy grande de cuentas si contienen pocos datos. Los saldos de tokens (8 bytes por token), pequeñas cantidades de metadatos para NFT, etc. —100 bytes de datos caben fácilmente en una transacción—. Incluir 1000 bytes es difícil si consideramos todos los demás elementos que debe contener. Si necesitas que tus cuentas almacenen cantidades mayores de datos, este método (y ZK Compression) no funcionará.
Hay un matiz en esta afirmación: la misma lógica que usa ZK Compression puede aplicarse a partes del estado de una cuenta. Esto significa que, aunque no sea posible incluir todos los datos de una cuenta en el payload de una sola transacción, existen formas de evitarlo, en concreto, comprometer y proporcionar pruebas de datos parciales.
¿Las pruebas de Merkle son la única forma de hacerlo?
No. Una prueba de Merkle es solo un tipo de compromiso vectorial. Tiene un tamaño de compromiso de 32 bytes y un tamaño de prueba de Log2(N) * 32 bytes, donde N es el tamaño del vector con el que te comprometes. De ahí obtuvimos el 17, ya que Log2(100000) es 17. Sin embargo, también existen compromisos con un tamaño de prueba constante (KZG, Pedersen). De hecho, ¡ZK Compression usa uno de esos métodos!
Dices que los datos de la cuenta que normalmente se almacenan en un nodo completo como parte del estado deben proporcionarse junto con la transacción. ¿Dónde se almacenan esos datos?
Muy buena pregunta, y afecta nuestros supuestos de confianza y propiedades. La respuesta es: ¡en cualquier lugar! Los servidores RPC especializados pueden almacenar estos datos; pueden formar parte de Filecoin o IPFS, o incluso el usuario puede almacenarlos en su propia máquina. Lo importante es que, mientras todos los elementos del vector estén almacenados en algún lugar, las pruebas pueden calcularse sobre la marcha. Abordaremos las implicaciones del lugar de almacenamiento en la sección sobre propiedades.
¿Pueden hacer esto otras cadenas?
ZK Compression requiere específicamente que la verificación de zk-SNARK sea económica. Por eso funciona bien en Solana, donde el cómputo es más barato que el almacenamiento. El concepto general de usar compromisos vectoriales y pruebas para los datos proporcionados en cada transacción es viable en otras cadenas, pero el alto costo del cómputo hace que la ventaja sea menos notable que en Solana. De hecho, un costo de gas persistente por la verificación, comparado con una operación única de SSTORE, resulta más caro en las cadenas basadas en EVM.
¿Qué es ZK Compression?
- Si no sabes qué es ZK Compression, esta sección lo explica
- Si tienes una idea vaga de qué es, te recomiendo leer esta sección porque existen algunos conceptos erróneos comunes
- Si sabes exactamente qué es, te agradecería que la leyeras para que puedas corregirme si me equivoqué en algo :)
Si entendiste la sección anterior, ya entendiste el 90 % de ZK Compression. El principal problema era el tamaño de la prueba de Merkle, así que ZK Compression simplemente usa un esquema para demostrar un cálculo.
Si no sabes qué es ZK, no importa demasiado. Solo necesitas saber que permite demostrar que realizaste un cálculo "correctamente". Un ejemplo sencillo: quieres demostrar que multiplicaste dos números para obtener un tercero. Es decir, 4*3 = 12
La “forma ZK” de demostrarlo consiste en tener la siguiente función:
f(x,y) = x*y
Si generas un circuito para el código anterior, el probador genera una prueba de que el cálculo es correcto. El circuito es el compromiso, por lo que todos saben cuál es el "cálculo" que ejecutas. Pero lo mejor es que no necesitan conocer las entradas. Cuando ejecutas f(3,4), devuelve 12 y la "prueba". Ahora cualquiera puede tomar 12, "prueba," y verificar que multiplicaste DOS números cualesquiera para obtener 12. No saben si usaste 4,3, 6,2 o incluso 12,1. El hecho de que puedas ocultar esos números y que otra persona aun así pueda verificar el resultado es de donde proviene la parte de “conocimiento cero”.
¿Por qué explico esto?
Este concepto general es increíblemente potente para demostrar que realizaste un cálculo determinado y obtuviste cierto resultado. Cuando alguien tiene el resultado y la prueba, puede verificar si lo hiciste correctamente sin ejecutar realmente el cálculo. Y esto se aplica a CUALQUIER cálculo arbitrario. Yo solo multipliqué dos números, pero también puedes usarlo para afirmar: "Verifiqué estas diez firmas y todas son válidas". Este es el segundo beneficio y una de las principales razones por las que se usan pruebas de conocimiento cero incluso cuando no necesitas “ocultar” algo. Conviertes un problema que requiere ejecutar 1000 pasos de cómputo —o incluso un millón— en uno que solo requiere verificar una prueba para saber que el cálculo se realizó correctamente. La salvedad es que generar la prueba toma cierto tiempo.
ZK Compression usa la misma tecnología para ejecutar la lógica de pertenencia al árbol de Merkle. Por lo tanto, cuenta con un circuito que puede tomar los datos de la cuenta y una prueba (128 bytes), y verificar que los datos realmente forman parte del "compromiso" en la cadena. (La prueba real ocupa 256 bytes, pero una ventaja de las curvas elípticas y los puntos es que, si conoces la curva, solo necesitas un punto para obtener el segundo).
Esto se hace principalmente para reducir el tamaño de la prueba a una constante de 128 bytes, lo que deja bastante espacio —en términos relativos— para los datos de cuentas pequeñas. Mientras una prueba de Merkle normal ocupa Log2(N), ZK Compression siempre tiene un tamaño constante, por lo que puedes tener una cantidad muy grande de cuentas bajo un solo compromiso. (Como referencia, una prueba de Merkle para 100 000 cuentas ocuparía alrededor de 550 bytes, la mitad del payload de la transacción).
Esta prueba puede generarse off-chain, pero debe verificarse on-chain porque un programa necesita saber que proporcionaste los datos correctos de una cuenta antes de permitir que continúe la ejecución. Para ello, debe existir el mecanismo básico que verifica las pruebas ZK. El sistema de prueba específico que usa ZK Compression se llama Groth16 y, a su vez, depende de la syscall alt_bn128, que actualmente está restringida mediante una feature gate en mainnet y se encuentra en fase de pruebas.
Lo interesante es que el mecanismo que usa ZK Compression puede verificar cálculos arbitrarios, no solo "¿pertenece esta hoja a un árbol que tiene esta raíz?".
Una ventaja principal de ZK Compression es que ofrece toda la infraestructura para que un desarrollador no tenga que ocuparse en absoluto de la parte "ZK". Desde la perspectiva del desarrollador, se trata como cualquier otra cuenta, con los mismos campos, etc. Por eso, dentro del programa puede manejarse como una cuenta normal. Abstraer la mayor parte de la "magia de ZK" y evitar que los desarrolladores tengan que ocuparse de ella aporta valor.
Rollups ZK
Sin entrar en demasiados detalles, los rollups ZK usan en gran medida los mismos conceptos que ZK Compression. La principal similitud es que todo el estado del rollup se representa como una única raíz en la capa base (Ethereum), razón por la cual algunas personas afirman que ZK Compression es un rollup. Sin embargo, existen diferencias cruciales.
Consideremos 100 transacciones de un rollup.
Todo el rollup ZK se trata como un circuito (como el programa de multiplicación que usamos de ejemplo). Se verifican las 100 transacciones (firma, lógica del contrato, comprobación de deduplicación, etc.) y se genera una sola prueba para
"Después de aplicar 100 transacciones, la raíz del estado cambia de A a B". Cuando se verifica la prueba, el contrato inteligente actualiza la raíz del estado de A a B.
Sin embargo, en ZK Compression, cada una de las 100 transacciones contiene una prueba que simplemente indica que los datos de la cuenta son correctos, pero las transiciones de estado (generadas por las transacciones) se ejecutan realmente on-chain como parte de la propia SVM. Una vez validada la prueba, se trata como una cuenta normal. Esto es fundamental para la propiedad de componibilidad que veremos a continuación.
Revisión de las propiedades
Ahora llegamos a la parte interesante. ¿Qué propiedades de Solana conserva ZK Compression?
Componibilidad atómica síncrona
Si tengo una transacción que hace referencia a 2 cuentas comprimidas con ZK y 10 cuentas "normales", la función de componibilidad no se rompe. Una instrucción que hace referencia a una cuenta comprimida con ZK puede llamar a otra instrucción o programa que haga referencia a una cuenta "normal" sin comprimir. Esta función se conserva por completo incluso si dos cuentas están comprimidas en árboles diferentes. Si una instrucción falla, se revierte toda la transacción (atómica), y los cambios de una instrucción llamada en la línea 1 son visibles en la línea 2 (síncrona).
Esto no se cumple en los rollups, ya que los rollups ZK no pueden llamarse entre sí de forma síncrona ni atómica, a menos que usen bloqueos globales y permitan reversiones entre rollups.
Paralelismo
Esta función tiene cierto impacto en el paralelismo y vale la pena considerar cada caso:
Escrituras en varias cuentas comprimidas dentro del mismo árbol
Cada árbol es concurrente por sí mismo. Esto significa que, si los usuarios leen o escriben en dos cuentas comprimidas bajo la misma raíz de estado, pueden ejecutarse de forma concurrente y la raíz del estado puede actualizarse de igual manera. La lógica sería la misma que usa Solana para las actualizaciones concurrentes de árboles de Merkle en los cNFT.
Escrituras en la misma cuenta comprimida
Cada cuenta comprimida no es concurrente. Si dos usuarios intentan escribir en la misma cuenta comprimida, una de las transacciones fallará sin importar el orden. Durante una ejecución normal, las escrituras en una cuenta de la instrucción anterior están disponibles para la siguiente instrucción. Sin embargo, con las cuentas comprimidas con ZK, la prueba de los datos de la cuenta no sería válida porque demuestra el estado anterior.
Otro aspecto que debes tener en cuenta es que el alto uso de unidades de cómputo (CU) de la compresión reduce la concurrencia máxima por árbol. Esto se debe a que cada cuenta solo puede consumir 12 millones de unidades de cómputo por bloque, dado el límite de CU por cuenta.
Aunque la componibilidad atómica síncrona presenta un problema en la mayoría de los rollups, la propiedad de paralelismo se menciona principalmente para destacar que ZK Compression no requiere componentes adicionales, como secuenciadores. Al no haber un secuenciador, la cadena base determina el orden. Podríamos analizar cómo esta misma propiedad se cumple en un rollup basado, pero no en la mayoría de los rollups, ya que estos usan secuenciadores centralizados para definir el orden.
Supuestos de confianza
Aunque cualquiera puede almacenar todos los datos sin procesar necesarios para generar las pruebas y enviar transacciones, esto introduce un supuesto de confianza adicional que afecta la disponibilidad del estado comprimido. Si por algún motivo los datos se "pierden" o se retrasan, no podrás enviar una transacción a menos que tú mismo los hayas almacenado. Por suerte, esto se conoce como un problema f+1 y no como un problema 3f+1, que requiere tolerancia a fallas bizantinas. Un problema f+1 solo necesita un nodo honesto que proporcione los datos y, como las pruebas son "autoverificables", no existe un problema de "seguridad". Principalmente, existe un problema de "disponibilidad" y un vector de censura.
Tanto los rollups normales como ZK Compression requieren que se proporcione una prueba de validez. Sin embargo, mientras los rollups codifican la función completa de transición de estado dentro de la prueba de validez, ZK Compression solo codifica "¿son correctos los datos de la cuenta?". Por eso, los supuestos de confianza son ligeramente distintos. En el caso de la compresión, los supuestos de confianza se aplican principalmente al acceso al estado, mientras que la transición de estado se ejecuta por completo. En el caso del rollup, se aplican a toda la función de transición de estado desde la perspectiva de la capa base. Los supuestos de seguridad relativos al número de bits o a la dificultad/intratabilidad del problema subyacente son los mismos (supuesto bilineal de Diffie-Hellman), pero lo que cambia es *para qué* confías en ese modelo de seguridad: acceso al estado frente a ejecución. Solo lo menciono porque es importante saber dónde se agregan supuestos de confianza adicionales y dónde no.
Actualmente, el programa que verifica las cuentas comprimidas con ZK puede actualizarse, pero en el futuro podría hacerse inmutable o congelarse, ya que solo realiza una operación muy específica (abrir pruebas de Merkle) que realmente no requiere actualizaciones constantes.
Además, la compresión de estado puede lograrse de otras dos maneras.
- Se está desarrollando una propuesta para aumentar el tamaño de las transacciones (canal proj-3x-tx en el Discord de Solana). Cuando se implemente, podrás usar pruebas de Merkle normales si el tamaño lo permite
- Cuando la syscall alt_bn128 esté disponible, también podrá usarse para un compromiso vectorial normal con un tamaño de prueba constante (KZG funciona con cualquier curva apta para emparejamientos, incluida alt_bn128). Esto no requiere un circuito de prueba ZK
¿Cómo llamamos a esto?
Por desgracia, términos como rollup, L2 y Validium se han aplicado con tanta laxitud que algunos rollups ni siquiera lo son. No heredan la disponibilidad, la seguridad ni la resistencia a la censura de la capa base. Aunque se ha acusado a Helius de usar un "término de marketing", esas mismas personas han usado "rollup" de forma muy imprecisa para referirse a proyectos en los que invirtieron por la misma razón: marketing. De hecho, se asignan diferentes etapas a proyectos que no son rollups solo para que puedan seguir llamándose rollups.
No todos son culpables de esto. Algunas personas han sido totalmente honestas sobre el uso de terminología precisa y han dedicado meses-persona a discutir con quienes usan términos imprecisos para engañar a los usuarios (reconocimiento a Toghrul, quien siempre ha exigido terminología precisa a *todos*).
Dado que presenta propiedades y supuestos de confianza distintos a los de un rollup, llamarlo rollup podría confundir a los usuarios. Llamarlo Validium también es demasiado amplio porque ignora que la componibilidad atómica síncrona y el paralelismo no se rompen, que la disponibilidad de datos (DA) está on-chain y, además, que la propia función de transición de estado no requiere confianza (porque los nodos completos ejecutan los programas reales por completo, en vez de limitarse a verificar una prueba de validez de la ejecución). Algunas personas podrían argumentar que las pruebas ZK no requieren confianza, pero eso simplemente no es cierto. Aunque minimizan considerablemente la confianza, matemáticamente los supuestos de seguridad no son los mismos. Pueden ser suficientes para el 99 % de los casos de uso, pero imponen un supuesto de confianza adicional respecto a un nodo completo; por ejemplo, el supuesto bilineal de Diffie-Hellman en el caso de una zk-SNARK basada en una curva de emparejamiento. Sin embargo, como ZK Compression usa la snark para comprobar la validez de la propia cuenta, es justo decir que ZK Compression no carece de supuestos de confianza: las cuentas comprimidas con ZK tienen supuestos de confianza que no existen para las cuentas "normales". Por lo tanto, se encuentra en algún punto entre un sistema sin necesidad de confianza y un rollup ZK completo.
Si usamos los nombres para deducir propiedades, yo diría que llamarlo "rollup" no comunica la presencia de esas propiedades o supuestos de confianza. ¿Quizá deberíamos crear un nombre nuevo? ZK Compression suena perfectamente bien siempre que los supuestos de confianza estén claros.
Artículos relacionados
Suscríbete a Helius
Mantente al día con las novedades del desarrollo en Solana y recibe actualizaciones cuando publiquemos


