
Solana Builders: ZK Compression
Índice
- Propriedades
- Se o tamanho da transação é de 1.232 bytes, como você envia todos os dados como parte dela?
- As provas de Merkle são a única maneira de fazer isso?
- Você afirma que os dados da conta normalmente armazenados em um nó completo como parte do estado devem ser fornecidos com a transação. Onde esses dados são armazenados?
- Outras redes podem fazer isso?
- O que é ZK Compression?
- Componibilidade atômica síncrona
- Paralelismo
Nos dias seguintes ao anúncio do projeto Zero-Knowledge (ZK) Compression na Solana pela Helius e pela Light Protocol, houve muita discussão sobre o ZK Compression. Uma parte significativa da conversa se concentrou na nomenclatura. É um rollup ZK? Uma L2? Ou algo totalmente diferente?
Por que isso é importante? Algumas pessoas no ecossistema da Solana acreditam que as discussões sobre nomenclatura são desnecessárias. Concordo em parte que o nome usado não é tão importante quanto o que a tecnologia faz — mas ele ainda importa, pois esses nomes se referem a estruturas com propriedades específicas e as agrupam. Portanto, chamá-la de XYZ pode revelar suas propriedades e premissas de confiança — e devemos nos importar com isso!
Propriedades
Antes de avaliá-las no contexto do ZK Compression, vamos listar algumas propriedades da Solana que nos interessam:
- Componibilidade atômica síncrona
- Concorrência
- Segurança
- Vivacidade
- Resistência à censura
Premissas iniciais de confiança
Usaremos o termo "trustless" para as premissas de segurança de um nó completo. Essa definição é nossa referência. Tudo o que um nó completo não consegue fazer sozinho envolve premissas adicionais de confiança.
Um nó completo é a única maneira trustless de interagir com um protocolo. Se alguém introduzir premissas adicionais de confiança — bridge, comitê, multi-sig, ZK, fraude ou qualquer outra coisa — e afirmar que isso é trustless, essa pessoa está errada.
Contexto
Vou tentar apresentar brevemente o contexto necessário sobre a Solana para entender o ZK Compression usando tópicos rápidos.
- O estado da Solana é armazenado nos discos dos nós completos no “AccountsDB”
- A unidade de armazenamento é chamada de "conta"
- As contas têm endereços (32 bytes cada)
- A quantidade de dados que uma conta pode armazenar varia de 0 a 10 MB (no máximo)
- Armazenar 10 MB na Solana custa aproximadamente 70 SOL, pagos pelo criador da conta. Esse custo está vinculado ao armazenamento, não ao número de contas — podem ser 1 conta de 10 MB ou 1.000 contas de 10 KB.
- O tamanho atual de todas as contas na Solana é de 76 GB (compactados)
- Toda transação da Solana precisa especificar todas as contas que lê e nas quais grava
- Atualmente, as transações da Solana são limitadas a 1.232 bytes (há uma proposta para aumentar esse limite)
- Cada transação da Solana precisa especificar alguns dados
- Assinaturas (64 bytes cada)
- Contas (32 bytes cada)
- Dados de instrução (tamanho arbitrário)
- Hash de bloco recente (32 bytes)
- Endereços de programas (32 bytes cada) (CPI: invocações entre programas)
- As transações da Solana incluem um “hash de bloco recente” de 32 bytes que deve ser incluído nos 150 blocos mais recentes; caso contrário, a transação é considerada inválida e precisa ser assinada e enviada novamente
Ciclo de vida de uma transação normal
Quando uma transação é executada normalmente, seu ciclo de vida é o seguinte:
- Primeiro, são realizadas a verificação de idade (apenas transações recentes são válidas), a desduplicação, a verificação estrutural, a cobrança de taxas (gas) e a verificação de assinaturas da transação
- O bytecode do programa é carregado com base no endereço do programa, e a Solana Virtual Machine (SVM) é instanciada
- Todas as contas referenciadas pela transação são verificadas, carregadas do armazenamento para a memória e repassadas à SVM
- O bytecode do programa é executado
- Todas as contas modificadas são sincronizadas de volta com o armazenamento em seu estado atualizado
As principais motivações para o ZK Compression são:
- O estado on-chain é caro. Por exemplo, mil contas custam 70 SOL, então produtos como o Drip Haus ficam caros rapidamente
- Mesmo sem a merkelização completa do estado, mais contas armazenadas em disco significam snapshots, índices e outros elementos maiores
- Nem todas as contas são acessadas com frequência, portanto, não é necessário arcar continuamente com o custo de recursos
Então, qual é a maneira mais simples de realizar a compressão?
Em vez de armazenar contas em disco e lê-las quando necessário (etapa 3 do ciclo de vida da execução da transação), uma transação pode enviar os dados da conta como parte do payload, economizando os custos associados ao armazenamento on-chain. Mas isso cria um novo problema: como impor a regra de que os usuários não podem mentir sobre o estado?
Por exemplo, digamos que o valor off-chain de uma conta que armazena o saldo de um token seja 1.200 e que seu campo de proprietário contenha “BYixJwV32DjeuyRww72PwZMyKcaedN533GBrv7CDh4n9”.
Se você enviar uma transação com esses dados para a rede, como ela saberá que você não mentiu sobre quantos tokens o endereço “BYixJwV32DjeuyRww72PwZMyKcaedN533GBrv7CDh4n9” possui?
Afinal, o nó completo que processa a transação não tem acesso aos dados off-chain — ele espera que você forneça os dados junto à transação.
Você pode usar provas de Merkle para isso. Sem entrar em detalhes, considere que elas são uma forma de criar um "compromisso" verificável com determinados dados, ocupando pouco espaço de armazenamento on-chain. Todos os nós completos sincronizados com a rede armazenam esse pequeno "compromisso" e, quando alguém fornece os dados em uma transação, também pode fornecer uma "prova" na mesma transação, que pode ser verificada em relação ao compromisso. Essa prova é criptograficamente segura.
Há algum problema nisso?
O problema é que as provas de Merkle podem ser grandes. Se uma árvore contiver 100.000 contas, o tamanho da prova para uma dessas contas será 17 * 32 = 544 bytes. Se você quiser fornecer provas para várias contas, no pior caso, o tamanho será multiplicado pelo número de provas. Assim, dez contas no pior caso ocupariam 10 * 544 = 5.440 bytes. Esse problema de espaço é específico da Solana porque, atualmente, uma transação da Solana é limitada a 1.232 bytes, enquanto outras redes costumam ser menos restritivas. Consulte a seção acima, onde listamos o tamanho de cada componente — programas, assinaturas, hash de bloco recente etc. Portanto, mesmo no melhor cenário, você usa metade do tamanho da transação apenas para a prova de Merkle.
Isso levanta algumas questões.
Se o tamanho da transação é de 1.232 bytes, como você envia todos os dados como parte dela?
Essa é uma excelente pergunta. O ZK Compression é útil para um número muito grande de contas quando elas contêm pequenas quantidades de dados. Saldos de tokens (8 bytes por token), pequenas quantidades de metadados de NFTs etc. — 100 bytes de dados cabem facilmente em uma transação. Já 1.000 bytes é difícil, considerando que outros elementos também precisam ser incluídos. Se suas contas precisarem armazenar quantidades maiores de dados, esse método (e o ZK Compression) não funcionará.
Uma nuance dessa afirmação é que a mesma lógica usada no ZK Compression pode ser aplicada a partes do estado da conta. Isso significa que, embora não seja possível incluir todos os dados de uma conta no payload de uma única transação, ainda há alternativas — especificamente, criar um compromisso e fornecer uma prova para dados parciais.
As provas de Merkle são a única maneira de fazer isso?
Não. Uma prova de Merkle é apenas um tipo de compromisso vetorial. Ela tem um compromisso de 32 bytes e uma prova de Log2(N) * 32 bytes, em que N é o tamanho do vetor com o qual você está se comprometendo. Foi daí que obtivemos 17, já que Log2(100000) é 17. Mas também existem compromissos com tamanho de prova constante (KZG, Pedersen); na verdade, o ZK Compression usa um desses métodos!
Você afirma que os dados da conta normalmente armazenados em um nó completo como parte do estado devem ser fornecidos com a transação. Onde esses dados são armazenados?
Ótima pergunta, e ela afeta nossas premissas de confiança e propriedades. A resposta é: em qualquer lugar! Servidores RPC especializados podem armazenar esses dados; eles podem fazer parte do Filecoin ou do IPFS, ou o usuário pode até armazená-los em sua própria máquina. O importante é que, desde que todos os elementos do vetor estejam armazenados em algum lugar, as provas possam ser calculadas dinamicamente. Abordaremos as implicações do local de armazenamento na seção sobre propriedades.
Outras redes podem fazer isso?
O ZK Compression exige especificamente que a verificação de zk-SNARK seja barata. Isso funciona bem na Solana, pois a computação é mais barata que o armazenamento. O conceito generalizado de usar compromissos vetoriais e provas para os dados fornecidos a cada transação é possível em outras redes, mas o alto custo computacional torna essa relação de custo-benefício menos significativa do que na Solana. Na verdade, um custo persistente de gas para verificação, em comparação com um SSTORE executado uma única vez, acaba sendo mais caro em redes baseadas em EVM.
O que é ZK Compression?
- Se você não sabe o que é ZK Compression, esta seção explica
- Se você tem uma ideia vaga do que é, ainda recomendo ler esta seção, pois existem alguns equívocos comuns
- Se você sabe exatamente o que é, peço que leia para poder me corrigir caso eu tenha entendido algo errado :)
Se você entendeu a seção acima, já entendeu 90% do que é o ZK Compression. O principal problema era o tamanho da prova de Merkle. Portanto, o ZK Compression consiste apenas em usar um esquema para provar uma computação.
Se você não sabe o que é ZK, isso não importa muito. Tudo o que precisa saber é que se trata de uma forma de provar que você realizou uma computação "corretamente". Um exemplo simples: você quer provar que multiplicou dois números para obter um terceiro. Ou seja, 4*3 = 12
A “maneira ZK” de provar isso é ter a seguinte função:
f(x,y) = x*y
Se você gerar um circuito para o código acima, o provador gera uma prova de computação correta. O próprio circuito é o compromisso, então todos sabem qual é a "computação" executada. O interessante é que ninguém precisa conhecer as entradas. Quando você executa f(3,4), a função retorna 12 e a "prova". Agora, qualquer pessoa pode usar 12, "prova," e verificar que você multiplicou DOIS números quaisquer para obter 12. Ela não sabe se você usou 4,3, 6,2 ou até 12,1. O fato de você ocultar esses números e ainda permitir que alguém verifique é o que dá origem à parte de “conhecimento zero”.
Por que estou explicando isso?
Esse conceito generalizado é extremamente poderoso para provar que você realizou determinada computação e obteve determinado resultado. Quando alguém tem o resultado e a prova, pode verificar se você fez tudo corretamente sem realmente executar a computação. E isso vale para QUALQUER computação arbitrária. Eu apenas multipliquei dois números, mas você pode até usá-lo para dizer: "Verifiquei estas dez assinaturas, e todas são válidas". Esse é o segundo benefício e um dos principais motivos pelos quais as provas de conhecimento zero são usadas mesmo quando não é necessário “ocultar” algo. Isso porque você transforma um problema que exige a execução de 1.000 etapas computacionais (ou até um milhão) em um problema que exige apenas a verificação de uma prova para confirmar que a computação foi feita corretamente. A ressalva é que a geração da prova leva algum tempo.
O ZK Compression usa a mesma tecnologia para executar a lógica real de associação à árvore de Merkle. Portanto, ele tem um circuito que pode receber os dados da conta e uma prova (128 bytes), além de verificar se os dados realmente fazem parte do "compromisso" on-chain. (A prova real tem 256 bytes, mas uma propriedade conveniente das curvas elípticas e dos pontos é que, se você conhece a curva, precisa de apenas um ponto para obter o segundo).
Isso é feito principalmente para reduzir o tamanho da prova a 128 bytes constantes, ainda deixando bastante espaço, relativamente falando, para os dados de contas pequenas. Enquanto uma prova de Merkle normal tem tamanho Log2(N), o ZK Compression sempre tem tamanho constante. Assim, é possível ter um número muito grande de contas sob um único compromisso. (Como referência, uma prova de Merkle para 100.000 contas teria cerca de 550 bytes, o equivalente à metade do payload da transação)
Essa prova pode ser gerada off-chain, mas precisa ser verificada on-chain, pois um programa precisa saber que você forneceu os dados corretos de uma conta antes de permitir que a execução prossiga. O mecanismo básico para verificar provas ZK precisa estar disponível para isso. O sistema de prova específico usado pelo ZK Compression é chamado de Groth16 e, por sua vez, depende da syscall alt_bn128, que atualmente está protegida por feature gate na mainnet e em fase de testes.
O interessante é que o mecanismo usado pelo ZK Compression pode verificar computações arbitrárias (não apenas "esta folha pertence a uma árvore que tem esta raiz?").
Um dos principais benefícios do ZK Compression é fornecer toda a infraestrutura necessária para que o desenvolvedor não precise lidar com a parte de "ZK". Na perspectiva do desenvolvedor, ela é tratada como qualquer outra conta, com os mesmos campos etc. Portanto, dentro do programa, pode ser tratada como uma conta comum. Há valor em abstrair a maior parte da "mágica ZK" e evitar que os desenvolvedores precisem lidar com ela.
Rollups ZK
Sem entrar em muitos detalhes, os rollups ZK usam, em grande parte, os mesmos conceitos empregados pelo ZK Compression. A principal semelhança é que todo o estado do rollup é representado como uma única raiz na camada base (Ethereum), motivo pelo qual há afirmações de que o ZK Compression é um rollup. No entanto, existem diferenças fundamentais.
Vamos considerar 100 transações de rollup.
Todo o rollup ZK é tratado como um circuito (como o programa de multiplicação que usamos como exemplo). As 100 transações são verificadas (assinatura, lógica de contrato, verificação de desduplicação etc.), e uma única prova é gerada para
"Após aplicar 100 transações, a raiz do estado muda de A para B". Depois que a prova é verificada, o contrato inteligente atualiza a raiz do estado de A para B.
No ZK Compression, porém, cada uma das 100 transações contém uma prova que simplesmente informa que os dados da conta estão corretos, mas as transições de estado (geradas pelas transações) são realmente executadas on-chain como parte da própria SVM. Depois que a prova é validada, ela é tratada como uma conta comum. Isso é fundamental para a propriedade de componibilidade que discutiremos a seguir.
Revisão das propriedades
Agora chegamos à parte interessante. Quais propriedades da Solana o ZK Compression preserva?
Componibilidade atômica síncrona
Se eu tiver uma transação que referencia 2 contas com ZK Compression e 10 contas "normais", isso não compromete o recurso de componibilidade. Uma instrução que referencia uma conta com ZK Compression pode chamar outra instrução ou outro programa que referencia uma conta "normal" não compactada. Esse recurso é totalmente preservado mesmo quando duas contas são compactadas em árvores diferentes. Se uma instrução falhar, toda a transação será revertida (atômica), e as alterações de uma instrução chamada na linha 1 estarão visíveis para a linha 2 (síncrona).
Isso não acontece com rollups, pois rollups ZK não podem chamar uns aos outros de forma síncrona nem atômica (a menos que usem bloqueios globais e permitam reversões entre rollups).
Paralelismo
Esse recurso tem algum impacto sobre o paralelismo, e vale a pena considerar cada caso:
Gravações em várias contas compactadas na mesma árvore
Cada árvore é concorrente por si só. Isso significa que, se os usuários estiverem lendo ou gravando em duas contas compactadas sob a mesma raiz de estado, as operações poderão ser executadas simultaneamente, e a raiz do estado poderá ser atualizada de forma concorrente. A lógica aqui seria a mesma usada pela Solana para atualizações concorrentes de árvores de Merkle em cNFTs
Gravações na mesma conta compactada
Cada conta compactada não é concorrente. Se dois usuários tentarem gravar na mesma conta compactada, uma das transações falhará, independentemente da ordem. Durante a execução normal, as gravações feitas em uma conta pela instrução anterior ficam disponíveis para a instrução seguinte. Porém, com contas compactadas por ZK, a prova dos dados da conta seria inválida, pois é uma prova do estado anterior
Outro ponto importante é que o alto uso de unidades de computação (CU) da compressão reduz a concorrência máxima por árvore, pois cada conta só pode consumir 12 milhões de unidades de computação por bloco, devido ao limite de CU por conta.
Embora a componibilidade atômica síncrona seja um problema na maioria dos rollups, a propriedade de paralelismo é mencionada principalmente para destacar que nenhum componente adicional, como sequenciadores, é necessário para habilitar o ZK Compression. A ausência de um sequenciador significa que a rede base realiza a ordenação. Poderíamos discutir que essa propriedade também se aplica a um rollup baseado, mas ela não vale para a maioria dos rollups, pois eles usam sequenciadores centralizados para a ordenação.
Premissas de confiança
Embora qualquer pessoa possa armazenar todos os dados brutos necessários para gerar as provas e enviar transações, isso representa uma premissa adicional de confiança que afeta a vivacidade do estado compactado. Se, por algum motivo, os dados forem "perdidos" ou houver um atraso, você não poderá enviar uma transação, a menos que tenha armazenado os dados por conta própria. Felizmente, isso é conhecido como um problema f+1, e não como um problema 3f+1, que exige tolerância a falhas bizantinas. Um problema f+1 exige apenas que um nó honesto forneça os dados e, como as provas são "autoverificáveis", não há um problema de "segurança". Há principalmente um problema de "vivacidade" e um vetor de censura.
Tanto os rollups comuns quanto o ZK Compression exigem o fornecimento de uma prova de validade. Porém, enquanto os rollups codificam toda a função de transição de estado na prova de validade, o ZK Compression codifica apenas "Os dados da conta estão corretos?". Portanto, as premissas de confiança são ligeiramente diferentes. No caso da compressão, as premissas de confiança se referem principalmente ao acesso ao estado (enquanto a transição de estado é realizada integralmente). No caso do rollup, as premissas de confiança se referem à função completa de transição de estado (do ponto de vista da camada base). As premissas de segurança relativas ao número de bits ou à dificuldade/intratabilidade do problema subjacente são as mesmas (premissa bilinear de Diffie-Hellman), mas o que muda é *para que* você confia nesse modelo de segurança — acesso ao estado ou execução. Só menciono isso porque é importante saber onde premissas adicionais de confiança estão sendo adicionadas e onde não estão.
Atualmente, o programa que verifica as contas com ZK Compression pode ser atualizado, mas no futuro pode se tornar imutável ou ser congelado, pois executa apenas uma operação altamente específica (abrir provas de Merkle), que não exige atualizações constantes.
Além disso, a compressão de estado também pode ser realizada de outras duas maneiras.
- Uma proposta para aumentar o tamanho das transações (canal proj-3x-tx no Discord da Solana) está em desenvolvimento. Quando entrar em operação, você poderá usar provas de Merkle comuns se o tamanho for viável
- Quando a syscall alt_bn128 estiver ativa, ela também poderá ser usada para um compromisso vetorial comum com tamanho de prova constante (KZG funciona com qualquer curva compatível com emparelhamento, incluindo alt_bn128). Isso não exige um circuito provador ZK
Como devemos chamar isso?
Infelizmente, termos como rollup, L2 e Validium têm sido aplicados de maneira tão vaga que alguns rollups nem sequer são rollups. Eles não herdam a vivacidade, a segurança nem a resistência à censura da camada base. Embora a Helius tenha sido acusada de usar um "termo de marketing", as mesmas pessoas usaram "rollup" de forma muito vaga para se referir a projetos nos quais investiram, pelo mesmo motivo: marketing. Na verdade, estruturas que não são rollups são classificadas em diferentes estágios apenas para que possam continuar se chamando de rollups.
Nem todos são culpados disso — algumas pessoas têm sido extremamente honestas sobre o uso de terminologia precisa e passaram meses de trabalho discutindo com quem usa terminologia imprecisa para enganar os usuários (um reconhecimento ao Toghrul, que sempre exigiu terminologia precisa de *todos*).
Como ele tem propriedades e premissas de confiança diferentes das de um rollup, chamá-lo de rollup poderia confundir os usuários. Chamá-lo de Validium também é abrangente demais, pois ignora o fato de que a componibilidade atômica síncrona e o paralelismo não são comprometidos, que a disponibilidade de dados (DA) é on-chain e, ainda, que a própria função de transição de estado é trustless (pois os nós completos executam integralmente os programas reais, em vez de simplesmente verificar uma prova de validade da própria execução). Algumas pessoas podem argumentar que provas ZK são trustless, mas isso simplesmente não é verdade — embora minimizem bastante a necessidade de confiança, matematicamente, as premissas de segurança não são as mesmas (elas podem ser suficientes para 99% dos casos de uso, mas impõem uma premissa adicional de confiança em relação à de um nó completo — por exemplo, a premissa bilinear de Diffie-Hellman no caso de um zk-SNARK baseado em curva de emparelhamento). É claro que, como o ZK Compression usa o snark para verificar a validade da própria conta, é justo dizer que o ZK Compression não é trustless — existem premissas de confiança para contas com ZK Compression que não existem para contas "normais". Portanto, ele está em algum ponto entre um sistema trustless e um rollup ZK completo.
Se usamos nomes para inferir propriedades, eu diria que chamá-lo de "rollup" não comunica a presença dessas propriedades nem dessas premissas de confiança. Talvez seja melhor criar um novo nome? ZK Compression parece adequado, desde que as premissas de confiança estejam claras.
Artigos relacionados
Assine a Helius
Acompanhe as novidades mais recentes do desenvolvimento Solana e receba atualizações quando publicarmos


