A ideia por trás do Relic é simples: os contratos inteligentes devem ser capazes de acessar com segurança qualquer dado enviado para a Ethereum. Afinal, podemos acessar facilmente o histórico no Etherscan ou com nosso próprio nó-arquivo, então por que não nos dApps?
Embora a ideia básica seja simples, há muito que precisa acontecer para fazer isso de uma forma confiável e segura.
Hashes de Blocos
O primeiro passo é obter acesso aos hashes de cada bloco da cadeia Ethereum. Por um lado, isso é fácil: com cerca de 15,6 milhões de blocos e 32 bytes de hash Keccak por bloco, isso é menos de 500 MB de dados. Embora isso não pareça muitos dados para 2022, infelizmente o armazenamento Ethereum se parece e custa muito menos que o armazenamento de 2022 e muito mais que o armazenamento de 1956.
Disco rígido IBM 350 de 3,75 MB de 1956. Crédito de History in Pictures
Com os dados de armazenamento na Ethereum custando cerca de 20.0000 de gás por palavra de 32 bytes, custaria cerca de 6.240 ETH a um preço de gás de 20gwei para armazenar tantos dados. Isso provavelmente equivale a armazenar 500 MB de dados em 1956…
Claro, temos maneiras mais eficientes de armazenar os hashes de blocos. Em vez de armazená-los todos individualmente, podemos usar a onipresente Árvore de Merkle (Merkle Tree). Essa estrutura de dados usa funções hash criptográficas — funções que recebem dados como entrada e geram um blob de dados de tamanho fixo chamado hash. As funções hash criptográficas possuem algumas (1) propriedades especiais:
- primeira resistência de pré-imagem: dado um hash, é difícil encontrar dados que resultem nesse hash específico
- resistência à colisão: é difícil criar dois dados diferentes que resultem no mesmo hash
- segunda resistência de pré-imagem: dado um pedaço de dado, é difícil encontrar outros dados que tenham o mesmo valor (esta é uma propriedade mais fraca do que a resistência à colisão)
Podemos combinar esses hashes em uma árvore: um arranjo de dados onde cada nó tem dois nós filhos associados a ele, e o valor de um nó é o hash de seus dois filhos combinados. Construindo isso, cada nível da árvore tem metade dos nós do nível abaixo dele, até chegarmos a um único nó raiz (root) no topo.
Isso nos permite usar apenas um único hash criptográfico para confirmar quantos dados quisermos. Uma vez que a raiz superior é fixada, as folhas de dados inferiores não podem ser alteradas. (Porque se pudessem, isso significaria que alguém poderia contornar uma das propriedades criptográficas declaradas das funções de hash.)
Um exemplo simples de uma Árvore de Merkle
Para provar que um determinado dado está associado a uma Árvore de Merkle, os usuários podem apresentar os dados originais, bem como a sequência de parceiros que foram hashes ao lado desses dados enquanto subimos na árvore como um testemunho. Um trecho de código como um contrato inteligente pode verificar se esse processo resulta na raiz Merkle correta no final, o que significa que os dados apresentados devem estar presentes quando a Árvore de Merkle foi originalmente criada. Existem algumas restrições práticas (2), mas isso efetivamente resolve a questão do armazenamento: podemos pegar cada um de nossos cabeçalhos de bloco e colocá-los em uma Árvore de Merkle. Ao armazenar raízes de Merkle em vez de toda a sequência de dados, o armazenamento cai significativamente.
Confiando em Hashes de Blocos
Claro, é fácil passar um hash na cadeia (on-chain) e reivindicar que está correto, mas isso depende da confiança. Como os usuários saberão que o Relic passou a Árvore de Merkle correta do hashes de blocos?
Felizmente, todo o objetivo das blockchains é possibilitar a verificação da autenticidade dos dados anteriores. Os dados do cabeçalho de cada bloco são incluídos no cálculo de hash Keccak que descreve o próximo bloco. Semelhante a uma Árvore de Merkle, isso significa que o hash do bloco atual é um compromisso com todos os blocos anteriores. Portanto, desde que possamos vincular todos os nossos blocos históricos usando hashing sucessivo e fazer com que o bloco final corresponda ao estado atual da Ethereum, sabemos que todos os dados históricos estão corretos.
Exemplo básico de uma blockchain
Tudo isso parece ótimo, mas como podemos pegar nossa Árvore de Merkle e verificar o hashing sucessivo de todos os dados incluídos? Infelizmente a resposta é: não podemos.
zk-SNARKs
Bem, isso cheira. Mas e se, ao criarmos nossa Árvore de Merkle, armazenarmos algumas informações extras que provam que os dados originais seguem a propriedade blockchain? Acontece que isso é possível usando zk-SNARKs. Em um nível básico, um SNARK (3) é uma maneira de usar alguns dados para produzir um pequeno testemunho associado (semelhante a uma prova matemática) que pode ser usado para convencer um verificador de que os dados possuem certas propriedades. Intuitivamente: um SNARK é uma maneira de provar rapidamente a terceiros que um cálculo (potencialmente difícil) foi feito corretamente. A mágica é que o processo de verificação é muito rápido e o testemunho associado é muito pequeno — perfeito para os recursos limitados da blockchain.
Com isso, temos todas as peças básicas que precisamos para criar nossos dados históricos. Podemos “SNARKify” a construção de nossa árvore de Merkle para garantir que os dados originais tenham a propriedade blockchain e resultem na raiz Merkle declarada. Então, podemos “conectar” o último bloco de nossa prova a um bloco recente que ainda é acessível de dentro da Ethereum Virtual Machine (EVM), mostrando que o bloco recente foi criado por hash dos dados de nosso bloco. Provando assim que a Árvore de Merkle foi criada a partir de nossos dados de bloco anteriores.
Por razões práticas, não criamos uma única Árvore de Merkle para toda a história da Ethereum. Em vez disso, fazemos várias árvores de tamanho fixo. Isso torna as provas de Merkle mais curtas e facilita o trabalho como um zk-SNARK (4), ambos mantendo os custos de gás sob controle para os usuários.
Falaremos mais sobre zk-SNARKs e como o Relic os usa mais em uma postagem futura, mas por enquanto esta é uma visão geral boa o suficiente.
Dados recentes
Ufa. Então, agora lidamos com os hashes de blocos históricos, mas e se alguém precisar de um hash de bloco de algumas horas atrás?
Em primeiro lugar, o Relic mantém um servidor rodando 24 horas por dia, 7 dias por semana, construindo provas zk-SNARK e enviando-as na cadeia uma vez a cada 8192 blocos (um pouco menos de 1 por dia).
Como mencionamos anteriormente, a EVM pode acessar os últimos 256 hashes de blocos de dados em apenas uma hora. Mas e as coisas com mais de 1 hora, mas mais recentes que 1 dia?
Felizmente, este problema é muito semelhante ao que resolvemos antes com nossos blocos históricos: conceitualmente, gostaríamos de fazer uma cadeia de hash do bloco desejado para um bloco recente o suficiente para ser acessível a partir da EVM. Como antes, em vez de tentar enviar centenas ou milhares de blocos e fazer hash de todos eles, a maneira mais fácil de fazer isso é usar um SNARK.
Portanto, concomitantemente com a construção de provas zk-SNARK, 24 horas por dia, 7 dias por semana, Árvores de Merkle para os hashes de blocos, o Relic também separa alguns que não são enviados automaticamente para a cadeia. Se alguém quiser usar o Relic para provar um fato de 2.000 blocos atrás, a API web2 Relic emitirá sua prova como um zk-SNARK de uma pequena Árvore de Merkle de cabeçalhos de bloco e sua inclusão nessa árvore, em vez de simplesmente uma inclusão em um das já publicadas Árvores de Merkle.
Estado da Ethereum
Neste ponto, discutimos (em alto nível) como provar qualquer hash de bloco histórico na Ethereum. Isso já é útil por si só para desenvolvedores (5), mas a maioria das pessoas se preocupa menos com o hash Keccak de um bloco e muito mais com o que aconteceu dentro do bloco… Felizmente, esta última etapa é bastante direta.
Diagrama de bloco Ethereum de Weber, et.al
Se você prestou atenção até agora, alguns desses nomes devem chamar sua atenção. Essas coisas rotuladas stateRoot, transactionsRoot, receiptsRoot, storageRoot
? Essas são todas as raízes das Árvores de Merkle - bem, tecnicamente, Merkle Patricia Tries, mas é a mesma ideia.
Se você quiser provar que uma transação ocorreu em um bloco Ethereum: pegue o cabeçalho do bloco, extraia a transactionsRoot
, e então crie uma Prova de Merkle da transação. Da mesma forma para outros fundamentos da Ethereum (armazenamento, recibos, etc.).
Amarrando tudo junto
Esta é apenas uma introdução, mas agora você tem a ideia básica de como é possível acessar dados históricos de qualquer ponto da história da Ethereum na cadeia e de maneira comprovadamente segura.
Vamos ver um exemplo de como um usuário pode usar o Relic para provar um fato básico sobre dados históricos, como “minha conta Ethereum existe desde 2017” (6).
Arquitetura de alto nível do Relic
- Contratos inteligentes que verificam os zk-SNARKs para o histórico de blocos são implantados pelo Relic
- A equipe do Relic executa o Relic Prover fora da cadeia (off-chain) para gerar provas de cabeçalhos de bloco. Para garantir que os cabeçalhos dos blocos sejam precisos, todos eles são verificados 100% na cadeia pelos circuitos zk-SNARK.
- Os usuários que desejam provar seu fato (a conta existia em 2017) podem falar com o Relic Prover (7) fora da cadeia para ajudar a construir duas provas: uma provando que seu bloco de nascimento existia na cadeia Ethereum (mostrando que existe na Árvore de Merkle, comprovado hashes de blocos, ou mostrando que está nos últimos 256 blocos, ou usando um SNARK), e outra prova mostrando que a Ethereum stateRoot Merkle Patricia Tree tem uma entrada para sua conta neste bloco.
- Essas duas provas são então submetidas ao Verificador de Provas de Estado (State Proof Verifier), que garante que ambas as provas estejam corretas.
- O Verificador de Provas de Estado então armazena o fato comprovado no Relicário (8), para confirmar o fato na blockchain para uso futuro (sem exigir provas a cada vez).
- Para coisas como Certidões de Nascimento, um Token Soul Bound (9) pode ser emitido, para que os usuários possam mostrar seus fatos comprovados na OpenSea ou em locais semelhantes.
Depois que um fato é comprovado, ele pode ser facilmente consultado totalmente na cadeia por qualquer dApp, interagindo com o Relicário. Qualquer contrato inteligente agora pode verificar facilmente os fatos sobre o estado da Ethereum em qualquer ponto da história de toda a cadeia!
Fique ligado
Esperamos que você tenha gostado de aprender como o Relic funciona! Acompanhe nosso projeto no Twitter e no Discord enquanto publicamos mais informações. Leia nosso código em profundidade em nosso Github e experimente o Relic você mesmo em https://app.relicprotocol.com/.
Volte para postagens mais detalhadas sobre como usamos zk-SNARKs, como o Relic pode ser usado por dApps e muito mais!
(1) Existem outras propriedades importantes para uso geral, mas menos importantes aqui.
(2) O tamanho dos dados de testemunho é logarítmico no comprimento dos dados armazenados. Para uma árvore de cerca de 16 milhões de itens, seriam 24 hashes que precisam ser apresentados em dados de chamada como testemunho. Isso também não permite uma atualização fácil da Árvore de Merkle à medida que novos blocos são produzidos.
(3) Embora os zk-SNARKs sejam um tipo especial de SNARK que fornece garantias adicionais de privacidade chamadas de “conhecimento zero”, isso não é importante para nosso caso de uso. Os SNARKs modernos também são zk-SNARKs, mas esse não precisa ser o caso.
(4) O tempo (e, portanto, o gás) para verificar um SNARK ainda cresce com a quantidade de “material” (chamado tamanho do circuito) que você deseja verificar. Além disso, embora seja rápido verificar um SNARK, é bastante difícil criá-los - e novamente o custo para construir escalas SNARKs com seu tamanho.
(5) Você pode acessar esses dados hoje de nossos contratos! Embora ainda estejamos trabalhando para melhorar a documentação desses recursos.
(6) Chamamos isso de “certidão de nascimento”, você pode fazer a sua própria em https://app.relicprotocol.com/
(7) Hoje é mais fácil usar o Relic Prover para criar isso, mas não é obrigatório!
(8) Um relicário é um recipiente para relíquias.
(9) Por que não um NFT transferível? Se as certidões de nascimento (ou outros fatos) fossem transferíveis, eles não teriam nenhum significado!
Este artigo foi escrito por Relic Protocol. Traduzido por Marcelo Panegali.
Latest comments (0)