Página Inicial
Artigos
Melhorando a transparência cripto com Zero-Knowledge Proof (ZKP)

Melhorando a transparência cripto com Zero-Knowledge Proof (ZKP)

Intermediário
Publicado em Feb 10, 2023Atualizado em Jan 5, 2024
10m

TL;DR

Uma zero-knowledge proof ou ZKP (prova de conhecimento zero) permite que uma parte (um verificador) determine a validade de uma declaração dada por outra parte (o provador) sem qualquer conhecimento do conteúdo da declaração. Por exemplo, a Binance pode querer provar que os fundos de seus usuários estão totalmente garantidos em reservas sem revelar todos os saldos de usuários individuais.

Uma Proof of Reserves ou “Prova de Reservas” (PoR) pode ser construída com uma Merkle Tree (árvore de Merkle) que oferece proteção contra a falsificação de dados internos, neste caso, dos saldos líquidos totais dos usuários, que estão sob custódia da corretora. Isso pode então ser combinado com um zk-SNARK (um protocolo de prova de conhecimento zero) que garante que os usuários possam verificar que seus saldos fazem parte do saldo total líquido de ativos, sem ter conhecimento dos saldos individuais de cada usuário.

Introdução

Diante dos eventos do mercado, a segurança dos criptoativos em custódia se tornou um tema muito importante. Os usuários da tecnologia Blockchain valorizam a transparência e a abertura, mas também defendem a privacidade e a confidencialidade. Isso cria um dilema no que diz respeito à comprovação das reservas de fundos mantidos pelos custodiantes. Muitas vezes, há um desequilíbrio entre transparência, confiança e confidencialidade dos dados.

No entanto, isso não é necessariamente o que ocorre. Ao combinar protocolos zero-knowledge proof como zk-SNARKs com Merkle Trees, é possível encontrar uma solução eficaz para todas as partes.

O que é zero-knowledge proof?

Uma zero-knowledge proof ou ZKP (prova de conhecimento zero) permite que uma parte (um verificador) determine a validade de uma declaração dada por outra parte (o provador) sem qualquer conhecimento do conteúdo da declaração. Vamos ver um exemplo simples.

Digamos que você tem um cofre trancado e que só você sabe a senha. Para o nosso exemplo, vamos considerar que esse cofre não pode ser arrombado, violado ou aberto de qualquer outra forma que não seja através da combinação correta. Este fato também é conhecido e verificado por seu amigo que está participando do experimento.

Você afirma ao seu amigo que sabe a combinação, mas não quer entregá-la ou abrir a caixa na frente dele. No topo da caixa há um buraco pelo qual seu amigo pode colocar uma nota. Para que isso seja uma prova de conhecimento zero, seu amigo não deve ter nenhuma informação extra sobre o processo além das informações fornecidas anteriormente.

Você pode provar ao seu amigo que sabe a combinação. Basta abrir a caixa, contar a ele o que estava escrito no bilhete e fechá-la novamente. Nesse processo, no entanto, você não revelou a combinação em nenhum momento.

Para um exemplo mais avançado, consulte o nosso artigo O que é zero-knowledge proof e como isso afeta a tecnologia blockchain?.

Por que usamos zero-knowledge proofs?

As provas de conhecimento zero são adequadas para provar algo sem revelar informações ou detalhes confidenciais. Por exemplo, você pode não querer compartilhar informações financeiras ou pessoais que possam ser usadas de forma inadequada.

Através da criptografia, você pode provar que possui uma chave privada sem revelá-la ou assinar algo digitalmente. Uma corretora de criptomoedas também pode querer comprovar o status de suas reservas sem revelar informações confidenciais de seus usuários, incluindo os saldos individuais de suas contas. 

Para esses exemplos (e muitos outros), uma zero-knowledge proof usaria algoritmos que recebem um input (entrada) de dados e retornam “verdadeiro” ou “falso” como output (saída). 

Definindo zero-knowledge proofs em termos técnicos

Em termos técnicos, uma prova de conhecimento zero segue uma estrutura específica com determinados critérios. Já mencionamos as funções de provador e verificador, mas também há três critérios que uma ZKP deve cobrir:

  1. Integridade. Critério também referido como completude. Se a afirmação for verdadeira, o verificador será convencido pela prova fornecida, sem a necessidade de qualquer outra informação ou verificação.

  2. Solidez. Se a declaração for falsa, o verificador não será convencido da veracidade de uma declaração pela prova fornecida.

  3. Conhecimento zero. Se a afirmação for verdadeira, o verificador não terá conhecimento de nenhuma informação além do fato de que a afirmação é verdadeira.

O que é zk-SNARK?

Zk-SNARK (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) é um protocolo de prova que segue os princípios de conhecimento zero descritos anteriormente. Com um zk-SNARK, você pode provar que conhece o valor original submetido ao hashing (discutiremos mais adiante) sem revelar qual é esse valor. Você também pode provar a validade de uma transação sem revelar nenhuma informação sobre os valores, quantidade ou endereços específicos relativos a ela.

As discussões e o uso de zk-SNARKs são muito comuns no mundo da tecnologia blockchain e das criptomoedas. Você pode estar se perguntando por que alguém se incomodaria em usar um zk-SNARK quando poderia usar um método simples de par de chaves pública e privada para garantir a segurança das informações. No entanto, não seríamos capazes de implementar a prova matemática para garantir que não sejam incluídos saldos negativos e que a soma da Merkle Tree esteja correta. 

No caso das reservas de uma corretora, a intenção é comprovar a proporção direta de 1:1 dos saldos dos clientes, sem que os identificadores e saldos de cada conta sejam divulgados publicamente. Além disso, a tecnologia zk-SNARK torna a falsificação de dados ainda mais improvável.

O que é uma Merkle Tree?

Para apresentar a soma dos fundos das contas dos usuários da Binance, é preciso lidar com um grande volume de dados. Uma maneira de apresentar essa grande quantidade de dados criptograficamente é usando uma Merkle Tree (árvore de Merkle). Uma quantidade enorme de informações pode ser armazenada de forma eficiente dentro dela, e sua natureza criptográfica facilita a verificação de integridade.

Funções Hash

Para codificar sucintamente um input (entrada), uma Merkle Tree depende do uso de funções de hashing. Resumidamente, hashing refere-se ao processo de geração de um output (saída) de tamanho fixo a partir de um input (entrada) de tamanho variável. Em outras palavras, quando um input de qualquer comprimento é submetido ao hashing por meio de um algoritmo, ele produzirá um output criptografado de comprimento fixo.

Sendo assim, enquanto o input permanecer o mesmo, o output também será sempre o mesmo. Isso significa que podemos submeter grandes quantidades de dados de transações a funções de hashing e convertê-los em um output gerenciável. Se alguma informação do input for alterada, o output será totalmente diferente.

Por exemplo, poderíamos submeter o conteúdo de 100 livros à função de hashing SHA-256. O resultado seria um output como o do exemplo abaixo:

801a9be154c78caa032a37b4a4f0747f1e1addb397b64fa8581d749d704c12ea

Se alterássemos um único caractere do input (os 100 livros), o hash resultante seria completamente diferente, como por exemplo:

abc5d230121d93a93a25bf7cf54ab71e8617114ccb57385a87ff12872bfda410

Essa é uma propriedade importante das funções de hashing, pois ela permite a fácil verificação da precisão dos dados. Se alguém replicar o processo de hashing desses mesmos 100 livros usando o algoritmo SHA-256, obterá exatamente o mesmo hash de saída (output). Se o output for diferente, podemos afirmar com certeza que o input foi alterado. Isso significa que não há necessidade de verificar individualmente ou manualmente as diferenças entre os inputs, o que pode ser trabalhoso.

Merkle Trees no mundo das criptomoedas

Ao armazenar dados de transação em uma blockchain, cada nova transação é enviada por meio de uma função de hashing, que gera valores de hash exclusivos. Imagine que temos oito transações (A a H) e as submetemos ao hashing, individualmente, para obter seus hashes resultantes (hashed outputs). Estes são o que chamamos de Merkle leaf nodes ("nodes de folhas" da Merkle Tree). Na imagem abaixo, podemos ver o valor de hash exclusivo de cada letra: hA para A, hB para B, hC para C, etc.

Podemos então combinar pares de hashed outputs para gerar uma novo hashed output. A combinação dos hashes hA e hB, por exemplo, nos dariam o novo hashed output hAB, conhecido como Merkle branch ("ramificação de Merkle"). Note que cada vez que um novo output é gerado, ele tem comprimento e tamanho fixos, de acordo com a função de hashing utilizada.

Agora, temos os dados de duas transações (por exemplo, A e B) combinadas em um hash (hAB). Observe que, se alterarmos qualquer informação de A ou B e repetirmos o processo, nosso hashed output hAB será completamente diferente.

O processo continua enquanto combinamos novos pares de hashes para submetê-los ao hashing novamente (veja a imagem abaixo). Fazemos o hashing de hAB com hCD para obter um único hash hABCD e fazemos o mesmo com hEF e hGH para obter hEFGH. No final, recebemos um único hash representando os hashed outputs de todos os hashes das transações anteriores. Em outras palavras, o hashed output hABCDEFGH representa todas as informações que vieram antes dele.

O gráfico exibido acima é chamado de Merkle Tree e o hashed output hABCDEFGH é a Merkle root ("raiz de Merkle"). Usamos Merkle roots em cabeçalhos de bloco, pois eles resumem criptograficamente todos os dados da transação em um bloco de maneira sucinta. Também é possível verificar rapidamente se algum dado foi adulterado ou alterado dentro do bloco.

Limitações das Merkle Trees

Voltemos ao nosso exemplo de reservas de corretoras centralizadas (CEX). Uma CEX quer provar o valor atrelado em proporção de 1:1 de todos os ativos de seus clientes e cria uma Merkle Tree que agrupa (hashing) os UIDs dos clientes e seus holdings líquidos (considerando ativos e passivos) a nível de tokens. Após a liberação (e assinatura para provar a propriedade sobre a Merkle root fornecida), um usuário individual não teria como verificar se a Merkle Tree é válida sem ter acesso a todos os seus inputs.

Uma corretora pode ter deixado de incluir alguns inputs. Ela também poderia criar contas falsas com saldos negativos para alterar o passivo total. Por exemplo, embora os ativos dos clientes possam totalizar $1.000.000, uma conta falsa pode ser adicionada com um saldo de -$500.000. Isso resultaria em um valor de reservas de apenas $500.000.

O caso para a comprovação de reservas é diferente da Merkle root de um bloco, pois os usuários podem ver todas as transações de um bloco em um blockchain explorer. No entanto, por motivos de segurança e privacidade de dados, uma CEX não tem interesse em divulgar o saldo de cada conta. Os clientes também não ficariam satisfeitos com a divulgação pública de seus saldos de conta. Nesse caso, a CEX não pode provar que os saldos dos usuários somam o total correto sem tornar visíveis os outros saldos dos usuários.

Uma solução que as corretoras podem considerar é o uso de um auditor terceirizado confiável. O auditor pode verificar as contas e reservas individuais antes de finalmente atestar a validade da Merkle root fornecida. No entanto, para os usuários, esse método exige confiança no auditor e nos dados usados para a auditoria. Você não precisa depender de terceiros quando pode confiar em dados.

Combinando zk-SNARKs com Merkle Trees

O problema acima é um caso de uso perfeito para zk-SNARKs. Queremos provar que as reservas cobrem totalmente o valor de passivos dos usuários e que não são falsificadas. No entanto, por motivos de privacidade e segurança, não queremos mostrar ao verificador a composição exata dos saldos e reservas dos usuários. 

Usando um zk-SNARK, uma corretora cripto pode provar que todos os conjuntos de saldos dos leaf nodes da Merkle Tree (ou seja, os saldos das contas dos usuários) contribuem para o saldo total de ativos de usuários, conforme alegação da corretora. Cada usuário pode acessar facilmente seu leaf node para verificar se o mesmo foi incluído no processo. O zk-SNARK também garante que qualquer Merkle Tree gerada não inclua usuários com um saldo negativo de ativos líquidos totais (o que implicaria falsificação de dados, pois todos os empréstimos são sobrecolateralizados). Também se utiliza um cálculo do estado global da Binance, ou seja, uma lista do saldo líquido total de cada ativo que cada cliente da Binance possui.

Vamos dar uma olhada na forma como a Binance aborda a situação. Para começar, a Binance define as restrições (constraints) computacionais que deseja provar e as define como um circuito programável. Abaixo, temos o conjunto de três restrições que a Binance usa em seu modelo. 

Para o conjunto de saldos de cada usuário (Merkle tree leaf node), nosso circuito garante que:

  1. Os saldos de ativos de um usuário são incluídos no cálculo da soma dos saldos líquidos totais dos usuários da Binance.

  2. O saldo líquido total do usuário é maior ou igual a zero.

  3. A alteração da Merkle root é válida (ou seja, não usa informações falsificadas) após atualizar as informações de um usuário para o hash do leaf node.

A Binance pode então gerar uma prova zk-SNARK para a construção da Merkle Tree de acordo com esse circuito programável. Isso significa que a corretora deve executar o pesado processo computacional que envolve o hashing dos IDs e saldos dos usuários, ao mesmo tempo em que garante que a prova atenda às restrições.

Um verificador examinará a prova (e o código aberto divulgado publicamente) para se convencer de que o cálculo foi executado corretamente e que todas as restrições foram respeitadas. A verificação computacional é concluída em um tempo extremamente curto em comparação ao tempo de prova.

A cada lançamento de provas (Proof of Reserves), a corretora publicará:

1. A prova da Merkle Tree para cada usuário.

2. A prova zk-SNARK e o input público (um hash da lista do saldo líquido total de cada ativo e a Merkle root) do circuito para todos os usuários.

Os interessados podem verificar a prova da Merkle Tree, garantindo que seus saldos individuais foram considerados para a Merkle root. Eles também podem verificar a prova zk-SNARK para garantir que a construção da Merkle Tree atenda às restrições definidas no circuito. Para uma explicação mais detalhada da solução zk-SNARK e seu desempenho, consulte nosso blog Como zk-SNARKs melhoram o sistema de Proof of Reserves da Binance.

Considerações finais

Os zk-SNARKs fornecem a tecnologia necessária para garantir, simultaneamente, a integridade e a privacidade dos dados. Seu uso para a comprovação de reservas e aumento da transparência de CEXs deve ajudar a aumentar confiança na indústria blockchain. Para muitos, um avanço como este tem sido aguardado há muito tempo e chega em um momento crucial para as CEXs.

Esta é a primeira versão do nosso zk-SNARK e estamos ansiosos para receber o feedback da comunidade, para que possamos continuar aprimorando o sistema.

Leituras adicionais