Em meu artigo anterior sobre os três turnos, descrevi algumas das principais razões pelas quais é valioso começar a pensar explicitamente em suporte L1 + cross-L2, segurança de carteira e privacidade como recursos fundamentais necessários da pilha do ecossistema, em vez de Coisas são construído como plugins que podem ser projetados individualmente por uma única carteira.
Este post focará mais diretamente nos aspectos técnicos de um subproblema específico, como: como ler mais facilmente L1 de L2, L2 de L1 ou L2 de outro L2. Resolver esse problema é fundamental para permitir arquiteturas de separação de ativos/keystore, mas também tem casos de uso valiosos em outras áreas, principalmente otimizando cadeias de chamadas cross-L2 confiáveis, incluindo casos de uso como movimentação de ativos entre L1 e L2 .
Este catálogo de artigos
Qual é o objetivo?
Como é a prova de cadeia cruzada?
Que tipo de esquema de prova podemos usar?
Merkle proof
ZK SNARKs
Certificado KZG dedicado
Verkle prova de árvore
polimerização
leitura direta de status
Como o L2 aprende a raiz do estado Ethereum mais recente?
Carteiras em cadeias não L2s
proteção de privacidade
Resumir
1. Qual é o objetivo?
Assim que o L2 se tornar popular, os usuários terão ativos em vários L2s e, possivelmente, também nos L1s. Depois que as carteiras de contratos inteligentes (multisig, recuperação social ou outras) se tornarem populares, as chaves necessárias para acessar determinadas contas mudarão com o tempo e as chaves mais antigas não precisarão mais ser válidas. Uma vez que essas duas coisas aconteçam, os usuários precisarão de uma maneira de alterar as chaves que têm acesso a muitas contas localizadas em muitos lugares diferentes sem fazer um número extremamente grande de transações.
Em particular, precisamos de uma maneira de lidar com endereços contrafactuais: endereços que não foram “registrados” na cadeia de forma alguma, mas ainda precisam receber e reter fundos com segurança. Todos nós confiamos em endereços contrafactuais: quando você usa o Ethereum pela primeira vez, pode gerar um endereço ETH que alguém pode usar para pagar sem "registrar" o endereço na cadeia (isso requer o pagamento de uma txfee, portanto, já tenha algum ETH).
Para EOA, todos os endereços começam com um endereço contrafactual. Com carteiras de contratos inteligentes, os endereços contrafactuais ainda são possíveis graças em grande parte ao CREATE2, que permite que você tenha um endereço ETH que só pode ser preenchido por um contrato inteligente com um código que corresponda a um hash específico.
Algoritmo de Cálculo de Endereço EIP-1014 (CREATE2)
No entanto, as carteiras de contrato inteligentes apresentam um novo desafio: a possibilidade de alterações de chaves de acesso. Este endereço é um hash do initcode e pode conter apenas a chave de verificação inicial da carteira. A chave de verificação atual será armazenada no armazenamento da carteira, mas esse registro de armazenamento não se propagará magicamente para outros L2s.
Se um usuário tiver muitos endereços em muitos L2s, incluindo endereços não conhecidos pelo L2 em que estão (porque são contrafactuais), parece haver apenas uma maneira de permitir que os usuários alterem suas chaves: uma arquitetura de separação de ativo/armazenamento de chaves. Cada usuário tem (i) um "contrato de armazenamento de chaves" (no L1 ou em um L2 específico) que armazena chaves de verificação para todas as carteiras e regras para troca de chaves, e (ii) "contratos de carteira" que lêem cadeias para chaves de verificação.
Existem duas maneiras de conseguir isso:
Versão leve (apenas verifica as chaves atualizadas): Cada carteira armazena a chave de verificação localmente e contém uma função que pode ser chamada para verificar a prova de cadeia cruzada do estado atual do armazenamento de chaves e atualizar sua chave de chave de verificação armazenada localmente para corresponder. Quando a carteira é usada pela primeira vez em um determinado L2, essa função deve ser chamada para obter a chave de autenticação atual do keystore.
-Prós: As provas de cadeia cruzada são usadas com moderação, portanto, não importa se as provas de cadeia cruzada são caras. Todos os fundos só podem ser usados com a chave atual, portanto, permanecem seguros.
Desvantagem: Para alterar a chave de verificação, você deve fazer uma alteração de chave on-chain no keystore e em cada carteira inicializada (embora não seja uma carteira contrafactual). Isso pode custar muito gás.
Versão pesada (verifique cada tx): Cada transação requer uma prova cross-chain mostrando a chave atual no keystore.
Prós: menos complexidade do sistema, atualizações de armazenamento de chaves baratas.
Contras: tx único é caro, portanto, mais engenharia é necessária para tornar as provas de cadeia cruzada muito mais baratas. Também não é facilmente compatível com ERC-4337, que atualmente não suporta a leitura de objetos mutáveis em contratos durante a verificação.
2. Como é a prova de cadeia cruzada?
Para demonstrar toda a complexidade, exploraremos o caso mais difícil: armazenamento de chaves em um L2, carteira em um L2 diferente. Apenas metade desse design é necessária se o keystore na carteira estiver em L1.
Suponha que o keystore esteja no Linea e a carteira esteja no Kakarot. Uma prova completa da chave da carteira inclui:
Prova da raiz atual do estado Linea, dada a raiz atual do estado Ethereum conhecida por Kakarot
Prova da chave atual no keystore, dada a raiz atual do estado Linea
Existem dois principais problemas complicados de implementação aqui:
Que tipo de evidência usamos? (É uma prova de Merkel? Outra coisa?
Como o L2 aprende primeiro a raiz do estado L1 (Ethereum) mais próximo (ou, como veremos, possivelmente o estado L1 completo)? Ou, como L1 aprende raízes de estado L2?
Em ambos os casos, quanto tempo demora entre o que acontece de um lado e o que é demonstrável do outro lado?
3. Que tipos de esquemas de prova podemos usar?
Existem cinco opções principais:
-Merkle proof
-General ZK-SNARKs
Prova dedicada (por exemplo, usando KZG)
-Verkle demonstra que eles estão entre KZG e ZK-SNARKs em termos de carga de trabalho e custo de infraestrutura.
Nenhuma prova necessária, depende da leitura direta do estado
Em termos de trabalho de infraestrutura necessário e custo do usuário, eu os classificaria aproximadamente da seguinte forma:
"Agregação" refere-se à agregação de todas as provas fornecidas pelos usuários dentro de cada bloco em uma grande meta-prova que combina todas as provas. Isso é possível com SNARK e KZG, mas não com garfos Merkle (você pode combinar um pouco os garfos Merkle, mas economiza apenas log(txs por bloco)/log(número total de keystores), na prática é provavelmente 15-30% no meio, então provavelmente não vale o preço).
A agregação só vale a pena quando o cenário possui um grande número de usuários, então na prática uma implementação da versão 1 poderia omitir a agregação e implementá-la na versão 2.
4. Como funcionam as provas de Merkle?
Este é fácil: basta seguir o diagrama da seção anterior. Mais precisamente, cada “prova” (assumindo o caso de provar a dificuldade máxima de uma L2 para outra L2) conterá:
Uma ramificação de Merkle atestando a raiz do estado de L2 segurando o keystore, dada a última raiz de estado do Ethereum conhecida por L2. O keystore mantém a raiz do estado de L2 armazenada em um slot de armazenamento conhecido em um endereço conhecido (o contrato em L1 representa L2), de modo que o caminho através da árvore pode ser codificado permanentemente.
Uma ramificação Merkle que atesta a chave de verificação atual, dada a raiz do estado de L2 que contém o armazenamento de chaves. Aqui, novamente, a chave de autenticação é armazenada em um slot de armazenamento conhecido em um endereço conhecido, para que o caminho possa ser codificado.
Infelizmente, as Provas de Estado do Ethereum são complexas, mas existem bibliotecas para validá-las e, se você usar essas bibliotecas, esse mecanismo não é muito complicado de implementar.
**O maior problema é o custo. **As provas Merkle são muito longas, infelizmente a árvore Patricia é ~3,9 vezes mais longa do que o necessário (para ser exato: uma prova Merkle ideal para uma árvore contendo N objetos tem 32 * log2(N) bytes de comprimento e porque as árvores Patricia do Ethereum tem 16 folhas por filho, e a prova dessas árvores é 32 * 15 * log16(N) ~= 125 * log2(N) bytes de comprimento). Em um estado com cerca de 250 milhões (~2²⁸) de contas, isso torna cada prova 125 * 28 = 3500 bytes, ou cerca de 56.000 gás, mais o custo adicional de decodificação e verificação do hash.
Juntas, as duas provas acabariam custando cerca de 100.000 a 150.000 gás (se usadas por transação, excluindo a verificação de assinatura) - muito mais do que o preço base atual de 21.000 gás por transação. Porém, se a prova for verificada em L2, a discrepância se agrava. A computação dentro de L2 é barata porque a computação é feita fora da cadeia e em um ecossistema com muito menos nós do que L1. Por outro lado, os dados devem ser publicados em L1. Portanto, a comparação não é 21k de gás versus 15k de gás; é 21k de gás L2 versus 100k de gás L1.
Podemos calcular o que isso significa observando a comparação entre o custo do gás L1 e o custo do gás L2:
Atualmente, o L1 é 15 a 25 vezes mais caro que o L2 para envio simples e 20 a 50 vezes mais caro para trocas de token. O envio simples é uma quantidade relativamente grande de dados, mas a quantidade de cálculo da troca é muito maior. Portanto, a troca é uma referência melhor para o custo aproximado de computação L1 versus computação L2. Levando tudo isso em consideração, se assumirmos uma relação de custo de 30x entre o custo computacional L1 e o custo computacional L2, isso parece implicar que o custo de colocar uma prova Merkle em L2 pode ser equivalente a 50 transações regulares.
Claro, usar árvores Merkle binárias reduz o custo por um fator de ~ 4, mas mesmo assim, na maioria dos casos, o custo é muito alto - se estivermos dispostos a sacrificar a compatibilidade com a atual árvore de estado hexagonal do Ethereum, procuramos melhores opções.
5. Como funcionará a prova ZK-SNARK?
O uso de ZK-SNARKs também é conceitualmente fácil de entender: basta substituir as provas de Merkle no diagrama acima por ZK-SNARKs provando a existência dessas provas de Merkle. Um ZK-SNARK requer ~400.000 GAS para computação, o que leva cerca de 400 bytes (em comparação com: uma transação básica requer 21.000 gas e 100 bytes, que podem ser reduzidos para ~25 palavras após a compactação no futuro Festival). Portanto, do ponto de vista computacional, o custo dos ZK-SNARKs é 19 vezes o custo das transações básicas atuais e, do ponto de vista dos dados, o custo dos ZK-SNARKs é 4 vezes maior que o das transações básicas atuais e 16 vezes o custo das transações básicas futuras.
Esses números são uma grande melhoria em relação à prova de Merkel, mas ainda são muito caros. Há duas maneiras de melhorar isso: (i) provas KZG para fins especiais ou (ii) agregação, semelhante à agregação ERC-4337, mas usando matemática mais sofisticada. Podemos estudar os dois ao mesmo tempo.
6. Como funcionam as provas KZG para fins especiais?
Atenção, esta seção é mais matemática do que as outras. Isso ocorre porque estamos indo além das ferramentas de uso geral e construindo algumas mais baratas para fins especiais, então temos que ir "sob o capô" mais. Se matemática profunda não é sua praia, pule direto para a próxima seção.
Primeiro, uma recapitulação de como os compromissos KZG funcionam:
Podemos representar um conjunto de dados [D_1...D_n] usando a prova KZG de um polinômio derivado dos dados: especificamente, o polinômio P, onde P(w) = D_1, P(w²) = D _2 ... P(wⁿ) = D_n. onde é a "raiz uniforme", o valor de wN = 1 para algum tamanho de domínio de avaliação N (tudo isso é feito em campos finitos).
Para "comprometer" com P, criamos um ponto de curva elíptica com(P) = P₀ * G + P₁ * S₁ + ... + Pk * Sk. aqui:
-G é o ponto gerador para a curva
-Pi é o coeficiente i do polinômio P
-Si é o ponto i na configuração confiável
Para provar que P(z) = a, criamos um quociente polinomial Q = (P - a) / (X - z) e criamos um compromisso com(Q). Só é possível criar tal polinômio se P(z) for realmente igual a a.
Para verificar a prova, verificamos a equação Q * (X - z) = P - a realizando uma verificação de curva elíptica na prova com(Q) e o compromisso polinomial com(P): verificamos e(com(Q ), com( X - z)) ? = e(com(P) - com(a), com(1))
Algumas propriedades importantes a serem conhecidas incluem:
a prova é apenas o valor com(Q), que é de 48 bytes
-com(P₁) + com(P₂) = com(P₁ + P₂)
Isso também significa que você pode "editar" o valor em um contrato existente. Suponha que saibamos que D_i é atualmente a, queremos defini-lo como b e o compromisso existente com D é com(P). promessa "P, mas P(wⁱ) = b, e nenhuma outra mudança de avaliação", então definimos com(new_P) = com(P) + (ba)*com(Li), onde Li é "lag Langeriano polinomial", igual a 1 em wⁱ e 0 em outros pontos wj.
Para realizar essas atualizações de forma eficiente, cada cliente pode pré-computar e armazenar todos os compromissos N para o polinômio de Lagrange (com(Li)). Em um contrato on-chain, pode ser muito armazenar todos os compromissos N, então você pode fazer um compromisso KZG para o conjunto de valores com(L_i) (ou hash(com(L_i)), então sempre que alguém precisa Ao atualizar a árvore on-chain, eles podem simplesmente fornecer prova de sua correção para o com(L_i) apropriado.
Portanto, temos uma estrutura onde podemos continuar adicionando valores ao final da lista crescente, embora com algum limite de tamanho (na verdade, centenas de milhões podem ser factíveis). Em seguida, usamos isso como nossa estrutura de dados para gerenciar (i) compromissos com a lista de chaves em cada L2, armazenados naquele L2 e espelhados em L1 e (ii) compromissos com a lista de compromissos de chaves L2, armazenados no Ethereum L1 e espelhado para cada L2.
Manter os compromissos atualizados pode fazer parte da lógica principal do L2 ou ser implementado por meio de uma ponte de depósito e retirada sem alterar o protocolo principal do L2.
Portanto, uma prova completa requer:
O keystore contém o último com (lista de chaves) em L2 (48 bytes)
-KZG prova que com(lista de chaves) é com(valor em mirror_list), compromisso com todas as listas de envio de listas de chaves (48 bytes)
-KZG prova que sua chave está em com (lista de chaves) (48 bytes, mais 4 bytes para o índice)
Na verdade, é possível combinar duas provas KZG em uma, obtendo um tamanho total de apenas 100 bytes.
Observe uma sutileza: como uma lista de chaves é uma lista, não um mapa de chave/valor como o estado é, as posições da lista de chaves devem ser atribuídas em ordem. O contrato de promessa de chave conterá seu próprio registro interno, mapeando cada keystore para um ID e, para cada chave, ele armazenará o hash (endereço do keystore) em vez de apenas a chave, a fim de se comunicar explicitamente com outros L2s que armazenam a chave uma entrada específica está falando.
A vantagem dessa técnica é que ela funciona muito bem em L2. Os dados têm 100 bytes, o que é aproximadamente 4 vezes menor que ZK-SNARKs e menor que Merkle proofs. O custo de cálculo é principalmente um cheque de par, ou cerca de 119.000 gás. Em L1, os dados são menos importantes que a computação, então, infelizmente, os KZGs são um pouco mais caros do que as provas de Merkle.
7. Como funcionará a árvore Verkle?
As árvores verkle envolvem essencialmente o empilhamento de compromissos KZG (ou compromissos IPA, que podem ser mais eficientes e usar criptografia mais simples): para armazenar 2⁴⁸ valores, você faz um compromisso KZG com uma lista de 2²⁴ valores, cada um dos quais é um compromisso KZG para 2²⁴ Valor. Árvores Verkle são fortemente consideradas para árvores de estado Ethereum, porque as árvores Verkle podem ser usadas para manter mapeamentos de valor-chave, não apenas listas (basicamente, você pode criar uma árvore de tamanho 2²⁵⁶, mas começar vazia, somente se você preencher apenas partes específicas do árvore quando você realmente precisa preenchê-los).
Como é uma árvore Vicker. Na verdade, você pode dar a cada nó uma largura de 256 == 2⁸ para árvores baseadas em IPA e 2²⁴ para árvores baseadas em KZG.
As provas em árvores Verkle são um pouco mais longas do que em KZG; elas podem ter centenas de bytes de comprimento. Eles também são difíceis de verificar, especialmente se você tentar agregar muitas provas em uma.
Na verdade, as árvores Verkle devem ser consideradas como as árvores Merkle, mas mais viáveis sem SNARKing (devido ao menor custo de dados), e o SNARKing é mais barato (devido ao menor custo de prova).
A maior vantagem das árvores Verkle é que elas podem coordenar estruturas de dados: as provas Verkle podem ser usadas diretamente para os estados L1 ou L2, não possuem estrutura de superposição e usam exatamente o mesmo mecanismo para L1 e L2. Uma vez que os computadores quânticos se tornem um problema, ou uma vez que a ramificação Merkle se mostre suficientemente eficiente, as árvores Verkle podem ser substituídas no local por árvores hash binárias com funções hash compatíveis com SNARK.
8. Agregados
Se N usuários fazendo N transações (ou mais realisticamente, N ERC-4337 UserOperations) precisam provar N reivindicações de cadeia cruzada, podemos economizar muito dinheiro agregando essas provas: combinando essas transações em um bloco ou O construtor do pacote que entra no bloco pode criar uma prova que comprove todas essas afirmações ao mesmo tempo.
Isso pode significar:
Provas ZK-SNARK para ramificações N Merkle
Uma prova múltipla KZG
Um Verkle multi-prova (ou multi-prova ZK-SNARK)
Em todos os três casos, cada prova requer apenas algumas centenas de milhares de gás. O construtor precisa fazer um desses em cada L2 para usuários naquele L2; portanto, para facilitar a construção, todo o esquema precisa ser suficientemente usado para que haja geralmente pelo menos algumas transações no mesmo bloco em vários comércios principais de L2s .
Se ZK-SNARKs forem usados, o principal custo marginal é apenas a "lógica de negócios" de passar números entre contratos, de modo que cada usuário pode precisar de vários milhares de gás L2. Se a prova múltipla KZG for usada, o provador precisa adicionar 48 gases para cada armazenamento de chaves L2 usado no bloco, portanto, o custo marginal do esquema por usuário será por L2 (não por usuário) e, em seguida, Adicionado ~ 800 gás L1 . Mas esses custos são muito menores do que sem agregação, que inevitavelmente envolve mais de 10.000 gás L1 e centenas de milhares de gás L2 por usuário. Para árvores Verkle, você pode usar multi-provas Verkle diretamente, adicionando cerca de 100-200 bytes por usuário, ou você pode fazer ZK-SNARKs de Verkle multi-provas, que custam similarmente aos ZK-SNARKs do ramo Merkle, mas a prova custa muito mais baixo.
Do ponto de vista da implementação, é melhor para os empacotadores agregar provas de cadeia cruzada por meio do padrão de abstração de conta ERC-4337. O ERC-4337 já possui um mecanismo para os construtores agregarem partes das ações do usuário de maneira personalizada. Existe ainda uma implementação para agregação de assinatura BLS, que pode reduzir os custos de gás em L2 por um fator de 1,5 a 3, dependendo das outras formas de compressão incluídas.
Diagrama da postagem de implementação da carteira BLS mostrando o fluxo de trabalho para assinaturas agregadas BLS em uma versão anterior do ERC-4337. O fluxo de trabalho para agregar provas de cadeia cruzada pode ser muito semelhante.
9. Leitura direta do status
Uma possibilidade final, também disponível apenas para L2 lendo L1 (e não L1 lendo L2), é modificar L2 para que eles façam chamadas estáticas diretamente para contratos em L1.
Isso pode ser feito com opcodes ou pré-compilação, que permite chamadas para o L1, onde você fornece o endereço de destino, gás e calldata, e retorna a saída, mas como essas chamadas são estáticas, elas não podem realmente alterar nenhum estado do L1. L2 precisa saber que L1 já processou o depósito, então não há nada que impeça que tal coisa seja implementada; isso é principalmente um desafio de implementação técnica (veja: isso de uma RFP otimista para suportar chamadas estáticas para L1).
Observe que, se o keystore estiver em L1 e L2 integrar chamadas estáticas L1, nenhum atestado será necessário! No entanto, se L2 não integrar chamadas estáticas L1, ou se o armazenamento de chaves estiver em L2 (o que pode eventualmente ter que ser feito uma vez que L1 se torne muito caro para os usuários usarem até mesmo um pouco), então a prova será necessária.
10. Como o L2 aprende a raiz do estado Ethereum mais recente?
Todos os esquemas acima requerem que o L2 acesse a raiz do estado L1 mais próximo ou todo o estado L1 mais próximo. Felizmente, todos os L2s já possuem alguma funcionalidade para acessar o estado L1 mais recente. Isso porque eles precisam dessa funcionalidade para lidar com mensagens de L1 para L2, principalmente depósitos.
De fato, se L2 tiver uma função de depósito, você poderá usar esse L2 como está para mover a raiz do estado L1 para um contrato em L2: basta fazer com que o contrato em L1 chame o opcode BLOCKHASH e passe-o para L2 como uma mensagem de depósito . O cabeçalho de bloco completo pode ser recebido no lado L2 e sua raiz de estado extraída. No entanto, cada L2 preferencialmente tem uma maneira explícita de acessar diretamente o último estado L1 completo ou a raiz do estado L1 mais próxima.
O principal desafio em otimizar a maneira como o L2 recebe a raiz do estado L1 mais recente é obter segurança e baixa latência ao mesmo tempo:
Se L2 implementa a funcionalidade "ler diretamente L1" de forma preguiçosa, apenas lendo a raiz do estado L1 final, então o atraso é normalmente de 15 minutos, mas em casos extremos de vazamentos de inatividade (que você deve tolerar), o atraso pode ser semanas.
L2 pode definitivamente ser projetado para ler raízes de estado L1 atualizadas, mas como L1 pode se recuperar (o que acontece durante vazamentos inativos mesmo com finalidade de soquete único), L2 também precisa ser capaz de se recuperar. Do ponto de vista da engenharia de software, isso é tecnicamente desafiador, mas pelo menos o Optimistic tem a capacidade.
Se você usar uma ponte de depósito para trazer as raízes do estado L1 para L2, a economia simples pode exigir muito tempo entre as atualizações de depósito: se o custo total de um depósito for de 100.000 gás, vamos supor $ 1.800 em ETH e uma taxa de 200 gwei, e raízes L1 entram em L2 uma vez por dia, isso custaria $ 236 por L por dia, ou $ 13.148 por L2 por ano para manter o sistema. Uma hora de atraso custa US$ 315.569 por L2 por ano. Na melhor das hipóteses, há um fluxo constante de usuários ricos impacientes pagando para atualizar e manter o sistema atualizado para todos os outros. Na pior das hipóteses, alguns atores altruístas terão que pagar por isso.
"Oráculos" (pelo menos a tecnologia que algumas pessoas DeFi chamam de "oráculos") não são uma solução aceitável aqui: o gerenciamento de chaves da carteira é uma função de baixo nível muito crítica para a segurança, portanto, deve depender no máximo de vários A muito simples, infraestrutura de baixo nível que não requer confiança criptográfica.
Além disso, na direção oposta (L1s é lido como L2):
Na Agregação Otimista, as raízes dos estados demoram uma semana para atingir o L1 devido a atrasos à prova de fraude. Em ZK rollups, agora leva horas devido a uma combinação de tempo de verificação e restrições econômicas, embora a tecnologia futura reduza isso.
A pré-confirmação (do sequenciador, certificador, etc.) não é uma solução aceitável para L1 lê L2. O gerenciamento de carteira é uma função de baixo nível muito crítica para a segurança, portanto, o nível de segurança da comunicação L2 - L1 deve ser absoluto: nem mesmo é possível enviar uma raiz de estado L1 errada assumindo o conjunto do validador L2. A única raiz de estado em que L1 deve confiar é aquela que foi aceita como a raiz de estado final pelo contrato de manutenção da raiz de estado de L2 em L1.
Para muitos casos de uso de DeFi, alguns deles são inaceitavelmente lentos para operações de cadeia cruzada sem confiança; para esses casos, você realmente precisa de pontes mais rápidas com modelos de segurança mais imperfeitos. No entanto, para o caso de uso de atualização de chaves de carteira, atrasos maiores são mais aceitáveis: em vez de atrasar as transações por horas, você atrasa as alterações de chave. Você só precisa manter a chave antiga por mais tempo. Se você alterar sua chave porque ela foi roubada, você terá uma vulnerabilidade por um longo tempo, mas pode ser mitigada, por exemplo. Por meio de uma carteira com funcionalidade de congelamento.
Em última análise, a melhor solução para minimizar a latência é fazer com que o L2 implemente leituras diretas otimizadas da raiz do estado L1, onde cada bloco L2 (ou log de computação da raiz do estado) contém um ponteiro para o bloco L1 mais recente, portanto, se L1 se recuperar e L2 pode se recuperar também. Os contratos de keystore devem ser colocados na rede principal ou no L2 do ZK Rollup para que possam ser rapidamente comprometidos com o L1.
Os blocos da cadeia L2 podem depender não apenas dos blocos L2 anteriores, mas também dos blocos L1. Se L1 se recuperar em tal link, L2 também se recuperará. Vale a pena notar que também é assim que as versões anteriores (pré-Dank) de sharding foram concebidas para funcionar; veja aqui o código.
11. Quanta conexão com Ethereum outra rede precisa para manter um keystore enraizado na carteira Ethereum ou L2?
Surpreendentemente, não muitos. Na verdade, nem precisa ser um rollup: se for L3 ou validação, não há problema em manter uma carteira lá, desde que você mantenha o keystore em um rollup L1 ou ZK. O que você realmente precisa é que a cadeia tenha acesso direto à raiz do estado do Ethereum e um compromisso técnico e social de estar disposto a se reorganizar se o Ethereum se reorganizar e a fazer um hard fork se o Ethereum for forcado.
Uma questão de pesquisa interessante é determinar até que ponto uma cadeia pode ter essa forma de conexão com várias outras cadeias (por exemplo, Ethereum e Zcash). Fazer isso ingenuamente é possível: se Ethereum ou Zcash se reorganizar, sua cadeia pode concordar em se reorganizar (e hard fork se for Ethereum ou Zcash forks), mas seus operadores de nó e sua comunidade geralmente têm duas vezes dependências tecnológicas e políticas. Portanto, esta técnica pode ser usada para conectar algumas outras cadeias, mas com um custo maior. Os esquemas baseados em ponte ZK têm propriedades técnicas atraentes, mas sua principal fraqueza é que eles não são robustos a ataques de 51% ou hard forks. Pode haver soluções mais inteligentes também.
12. Proteção de privacidade
Idealmente, também queremos preservar a privacidade. Se você tiver muitas carteiras gerenciadas pelo mesmo keystore, queremos garantir que:
Não está claro se essas carteiras estão todas conectadas umas às outras.
Os Responsáveis pela Reabilitação Social não sabem que morada estão a proteger.
Isso cria alguns problemas:
Não podemos usar as provas Merkle diretamente porque elas não protegem a privacidade.
Se usarmos KZG ou SNARK, a prova precisa fornecer uma versão oculta da chave de verificação sem revelar a localização da chave de verificação.
Se usarmos agregação, o agregador não deve aprender as posições no texto simples; em vez disso, o agregador deve receber provas cegas e ter uma maneira de agregar essas provas.
Não podemos usar uma "versão light" (renovação de chaves usando apenas provas cross-chain), porque isso cria um vazamento de privacidade: se muitas carteiras forem atualizadas ao mesmo tempo devido ao processo de atualização, o tempo vaza informações que podem ser relevantes para essas carteiras.
Portanto, temos que usar a "versão pesada" (prova cross-chain de cada transação).
Com SNARKs, a solução é conceitualmente simples: por padrão, as provas são ocultadas por informações e os agregadores precisam gerar SNARKs recursivos para provar SNARKs.
O principal desafio dessa abordagem atualmente é que a agregação exige que o agregador crie um SNARK recursivo, que atualmente é muito lento.
Com o KZG, podemos usar este trabalho (veja também: uma versão mais formal deste trabalho no artigo Caulk) em provas KZG reveladoras não indexadas como ponto de partida. No entanto, a agregação de provas cegas é um problema em aberto que requer mais atenção.
Infelizmente, ler L1 diretamente de dentro de L2 não preserva a privacidade, embora a implementação da funcionalidade de leitura direta ainda seja muito útil, tanto para minimizar a latência quanto por causa de sua utilidade para outros aplicativos.
13. Resumo
Para ter uma carteira de recuperação social cross-chain, o fluxo de trabalho mais realista é uma carteira que mantém um keystore em um local e uma carteira que mantém carteiras em vários locais, onde a carteira lê o keystore para (i) atualizar sua chave de autenticação localmente vista, ou (ii) no processo de validação de cada transação.
Um elemento-chave que torna isso possível são as provas de cadeia cruzada. Precisamos trabalhar para otimizar essas provas. ZK-SNARKs ou soluções KZG personalizadas aguardando a prova de Verkle parecem ser as melhores opções.
A longo prazo, é necessário um protocolo de agregação (onde os bundlers geram provas agregadas como parte da criação de um bundle de todas as ações do usuário enviadas pelos usuários) para minimizar os custos. Isso provavelmente deve ser integrado ao ecossistema ERC-4337, embora possam ser necessárias alterações no ERC-4337.
L2 deve ser otimizado para minimizar a latência de leitura do estado L1 (ou pelo menos a raiz do estado) de dentro de L2. É ideal para L2s ler o estado L1 diretamente, economizando espaço de prova.
As carteiras não podem estar apenas no L2; você também pode colocar carteiras em sistemas com um nível mais baixo de conectividade com o Ethereum (L3, ou até mesmo concordar em incluir a raiz do estado do Ethereum e a cadeia independente de bifurcações).
No entanto, o keystore deve estar no L1 ou no rollup ZK de alta segurança L2. O uso do L1 economiza muita complexidade, mas mesmo isso pode ser muito caro a longo prazo, portanto, um keystore precisa ser usado no L2.
Preservar a privacidade exigirá trabalho extra e tornará certas escolhas mais difíceis. Mas provavelmente devemos recorrer a soluções de preservação da privacidade de qualquer maneira, pelo menos nos certificando de que tudo o que criamos seja compatível com a preservação da privacidade.
Ver original
O conteúdo é apenas para referência, não uma solicitação ou oferta. Nenhum aconselhamento fiscal, de investimento ou jurídico é fornecido. Consulte a isenção de responsabilidade para obter mais informações sobre riscos.
Buterin: insights sobre leituras cross-L2 para carteiras e outros casos de uso
Autor: Vitalik Buterin Compilação: Vernacular Blockchain
Em meu artigo anterior sobre os três turnos, descrevi algumas das principais razões pelas quais é valioso começar a pensar explicitamente em suporte L1 + cross-L2, segurança de carteira e privacidade como recursos fundamentais necessários da pilha do ecossistema, em vez de Coisas são construído como plugins que podem ser projetados individualmente por uma única carteira.
Este post focará mais diretamente nos aspectos técnicos de um subproblema específico, como: como ler mais facilmente L1 de L2, L2 de L1 ou L2 de outro L2. Resolver esse problema é fundamental para permitir arquiteturas de separação de ativos/keystore, mas também tem casos de uso valiosos em outras áreas, principalmente otimizando cadeias de chamadas cross-L2 confiáveis, incluindo casos de uso como movimentação de ativos entre L1 e L2 . Este catálogo de artigos Qual é o objetivo? Como é a prova de cadeia cruzada? Que tipo de esquema de prova podemos usar? Merkle proof ZK SNARKs Certificado KZG dedicado Verkle prova de árvore polimerização leitura direta de status Como o L2 aprende a raiz do estado Ethereum mais recente? Carteiras em cadeias não L2s proteção de privacidade Resumir
1. Qual é o objetivo?
Assim que o L2 se tornar popular, os usuários terão ativos em vários L2s e, possivelmente, também nos L1s. Depois que as carteiras de contratos inteligentes (multisig, recuperação social ou outras) se tornarem populares, as chaves necessárias para acessar determinadas contas mudarão com o tempo e as chaves mais antigas não precisarão mais ser válidas. Uma vez que essas duas coisas aconteçam, os usuários precisarão de uma maneira de alterar as chaves que têm acesso a muitas contas localizadas em muitos lugares diferentes sem fazer um número extremamente grande de transações. Em particular, precisamos de uma maneira de lidar com endereços contrafactuais: endereços que não foram “registrados” na cadeia de forma alguma, mas ainda precisam receber e reter fundos com segurança. Todos nós confiamos em endereços contrafactuais: quando você usa o Ethereum pela primeira vez, pode gerar um endereço ETH que alguém pode usar para pagar sem "registrar" o endereço na cadeia (isso requer o pagamento de uma txfee, portanto, já tenha algum ETH). Para EOA, todos os endereços começam com um endereço contrafactual. Com carteiras de contratos inteligentes, os endereços contrafactuais ainda são possíveis graças em grande parte ao CREATE2, que permite que você tenha um endereço ETH que só pode ser preenchido por um contrato inteligente com um código que corresponda a um hash específico.
Algoritmo de Cálculo de Endereço EIP-1014 (CREATE2)
No entanto, as carteiras de contrato inteligentes apresentam um novo desafio: a possibilidade de alterações de chaves de acesso. Este endereço é um hash do initcode e pode conter apenas a chave de verificação inicial da carteira. A chave de verificação atual será armazenada no armazenamento da carteira, mas esse registro de armazenamento não se propagará magicamente para outros L2s. Se um usuário tiver muitos endereços em muitos L2s, incluindo endereços não conhecidos pelo L2 em que estão (porque são contrafactuais), parece haver apenas uma maneira de permitir que os usuários alterem suas chaves: uma arquitetura de separação de ativo/armazenamento de chaves. Cada usuário tem (i) um "contrato de armazenamento de chaves" (no L1 ou em um L2 específico) que armazena chaves de verificação para todas as carteiras e regras para troca de chaves, e (ii) "contratos de carteira" que lêem cadeias para chaves de verificação.
Existem duas maneiras de conseguir isso:
Versão leve (apenas verifica as chaves atualizadas): Cada carteira armazena a chave de verificação localmente e contém uma função que pode ser chamada para verificar a prova de cadeia cruzada do estado atual do armazenamento de chaves e atualizar sua chave de chave de verificação armazenada localmente para corresponder. Quando a carteira é usada pela primeira vez em um determinado L2, essa função deve ser chamada para obter a chave de autenticação atual do keystore. -Prós: As provas de cadeia cruzada são usadas com moderação, portanto, não importa se as provas de cadeia cruzada são caras. Todos os fundos só podem ser usados com a chave atual, portanto, permanecem seguros.
2. Como é a prova de cadeia cruzada?
Para demonstrar toda a complexidade, exploraremos o caso mais difícil: armazenamento de chaves em um L2, carteira em um L2 diferente. Apenas metade desse design é necessária se o keystore na carteira estiver em L1.
Suponha que o keystore esteja no Linea e a carteira esteja no Kakarot. Uma prova completa da chave da carteira inclui:
3. Que tipos de esquemas de prova podemos usar?
Existem cinco opções principais: -Merkle proof -General ZK-SNARKs
"Agregação" refere-se à agregação de todas as provas fornecidas pelos usuários dentro de cada bloco em uma grande meta-prova que combina todas as provas. Isso é possível com SNARK e KZG, mas não com garfos Merkle (você pode combinar um pouco os garfos Merkle, mas economiza apenas log(txs por bloco)/log(número total de keystores), na prática é provavelmente 15-30% no meio, então provavelmente não vale o preço). A agregação só vale a pena quando o cenário possui um grande número de usuários, então na prática uma implementação da versão 1 poderia omitir a agregação e implementá-la na versão 2.
4. Como funcionam as provas de Merkle?
Este é fácil: basta seguir o diagrama da seção anterior. Mais precisamente, cada “prova” (assumindo o caso de provar a dificuldade máxima de uma L2 para outra L2) conterá: Uma ramificação de Merkle atestando a raiz do estado de L2 segurando o keystore, dada a última raiz de estado do Ethereum conhecida por L2. O keystore mantém a raiz do estado de L2 armazenada em um slot de armazenamento conhecido em um endereço conhecido (o contrato em L1 representa L2), de modo que o caminho através da árvore pode ser codificado permanentemente. Uma ramificação Merkle que atesta a chave de verificação atual, dada a raiz do estado de L2 que contém o armazenamento de chaves. Aqui, novamente, a chave de autenticação é armazenada em um slot de armazenamento conhecido em um endereço conhecido, para que o caminho possa ser codificado. Infelizmente, as Provas de Estado do Ethereum são complexas, mas existem bibliotecas para validá-las e, se você usar essas bibliotecas, esse mecanismo não é muito complicado de implementar. **O maior problema é o custo. **As provas Merkle são muito longas, infelizmente a árvore Patricia é ~3,9 vezes mais longa do que o necessário (para ser exato: uma prova Merkle ideal para uma árvore contendo N objetos tem 32 * log2(N) bytes de comprimento e porque as árvores Patricia do Ethereum tem 16 folhas por filho, e a prova dessas árvores é 32 * 15 * log16(N) ~= 125 * log2(N) bytes de comprimento). Em um estado com cerca de 250 milhões (~2²⁸) de contas, isso torna cada prova 125 * 28 = 3500 bytes, ou cerca de 56.000 gás, mais o custo adicional de decodificação e verificação do hash. Juntas, as duas provas acabariam custando cerca de 100.000 a 150.000 gás (se usadas por transação, excluindo a verificação de assinatura) - muito mais do que o preço base atual de 21.000 gás por transação. Porém, se a prova for verificada em L2, a discrepância se agrava. A computação dentro de L2 é barata porque a computação é feita fora da cadeia e em um ecossistema com muito menos nós do que L1. Por outro lado, os dados devem ser publicados em L1. Portanto, a comparação não é 21k de gás versus 15k de gás; é 21k de gás L2 versus 100k de gás L1. Podemos calcular o que isso significa observando a comparação entre o custo do gás L1 e o custo do gás L2:
Atualmente, o L1 é 15 a 25 vezes mais caro que o L2 para envio simples e 20 a 50 vezes mais caro para trocas de token. O envio simples é uma quantidade relativamente grande de dados, mas a quantidade de cálculo da troca é muito maior. Portanto, a troca é uma referência melhor para o custo aproximado de computação L1 versus computação L2. Levando tudo isso em consideração, se assumirmos uma relação de custo de 30x entre o custo computacional L1 e o custo computacional L2, isso parece implicar que o custo de colocar uma prova Merkle em L2 pode ser equivalente a 50 transações regulares. Claro, usar árvores Merkle binárias reduz o custo por um fator de ~ 4, mas mesmo assim, na maioria dos casos, o custo é muito alto - se estivermos dispostos a sacrificar a compatibilidade com a atual árvore de estado hexagonal do Ethereum, procuramos melhores opções.
5. Como funcionará a prova ZK-SNARK?
O uso de ZK-SNARKs também é conceitualmente fácil de entender: basta substituir as provas de Merkle no diagrama acima por ZK-SNARKs provando a existência dessas provas de Merkle. Um ZK-SNARK requer ~400.000 GAS para computação, o que leva cerca de 400 bytes (em comparação com: uma transação básica requer 21.000 gas e 100 bytes, que podem ser reduzidos para ~25 palavras após a compactação no futuro Festival). Portanto, do ponto de vista computacional, o custo dos ZK-SNARKs é 19 vezes o custo das transações básicas atuais e, do ponto de vista dos dados, o custo dos ZK-SNARKs é 4 vezes maior que o das transações básicas atuais e 16 vezes o custo das transações básicas futuras. Esses números são uma grande melhoria em relação à prova de Merkel, mas ainda são muito caros. Há duas maneiras de melhorar isso: (i) provas KZG para fins especiais ou (ii) agregação, semelhante à agregação ERC-4337, mas usando matemática mais sofisticada. Podemos estudar os dois ao mesmo tempo.
6. Como funcionam as provas KZG para fins especiais?
Atenção, esta seção é mais matemática do que as outras. Isso ocorre porque estamos indo além das ferramentas de uso geral e construindo algumas mais baratas para fins especiais, então temos que ir "sob o capô" mais. Se matemática profunda não é sua praia, pule direto para a próxima seção. Primeiro, uma recapitulação de como os compromissos KZG funcionam: Podemos representar um conjunto de dados [D_1...D_n] usando a prova KZG de um polinômio derivado dos dados: especificamente, o polinômio P, onde P(w) = D_1, P(w²) = D _2 ... P(wⁿ) = D_n. onde é a "raiz uniforme", o valor de wN = 1 para algum tamanho de domínio de avaliação N (tudo isso é feito em campos finitos). Para "comprometer" com P, criamos um ponto de curva elíptica com(P) = P₀ * G + P₁ * S₁ + ... + Pk * Sk. aqui: -G é o ponto gerador para a curva -Pi é o coeficiente i do polinômio P -Si é o ponto i na configuração confiável Para provar que P(z) = a, criamos um quociente polinomial Q = (P - a) / (X - z) e criamos um compromisso com(Q). Só é possível criar tal polinômio se P(z) for realmente igual a a. Para verificar a prova, verificamos a equação Q * (X - z) = P - a realizando uma verificação de curva elíptica na prova com(Q) e o compromisso polinomial com(P): verificamos e(com(Q ), com( X - z)) ? = e(com(P) - com(a), com(1)) Algumas propriedades importantes a serem conhecidas incluem:
Portanto, uma prova completa requer:
7. Como funcionará a árvore Verkle?
As árvores verkle envolvem essencialmente o empilhamento de compromissos KZG (ou compromissos IPA, que podem ser mais eficientes e usar criptografia mais simples): para armazenar 2⁴⁸ valores, você faz um compromisso KZG com uma lista de 2²⁴ valores, cada um dos quais é um compromisso KZG para 2²⁴ Valor. Árvores Verkle são fortemente consideradas para árvores de estado Ethereum, porque as árvores Verkle podem ser usadas para manter mapeamentos de valor-chave, não apenas listas (basicamente, você pode criar uma árvore de tamanho 2²⁵⁶, mas começar vazia, somente se você preencher apenas partes específicas do árvore quando você realmente precisa preenchê-los).
Como é uma árvore Vicker. Na verdade, você pode dar a cada nó uma largura de 256 == 2⁸ para árvores baseadas em IPA e 2²⁴ para árvores baseadas em KZG. As provas em árvores Verkle são um pouco mais longas do que em KZG; elas podem ter centenas de bytes de comprimento. Eles também são difíceis de verificar, especialmente se você tentar agregar muitas provas em uma. Na verdade, as árvores Verkle devem ser consideradas como as árvores Merkle, mas mais viáveis sem SNARKing (devido ao menor custo de dados), e o SNARKing é mais barato (devido ao menor custo de prova). A maior vantagem das árvores Verkle é que elas podem coordenar estruturas de dados: as provas Verkle podem ser usadas diretamente para os estados L1 ou L2, não possuem estrutura de superposição e usam exatamente o mesmo mecanismo para L1 e L2. Uma vez que os computadores quânticos se tornem um problema, ou uma vez que a ramificação Merkle se mostre suficientemente eficiente, as árvores Verkle podem ser substituídas no local por árvores hash binárias com funções hash compatíveis com SNARK.
8. Agregados
Se N usuários fazendo N transações (ou mais realisticamente, N ERC-4337 UserOperations) precisam provar N reivindicações de cadeia cruzada, podemos economizar muito dinheiro agregando essas provas: combinando essas transações em um bloco ou O construtor do pacote que entra no bloco pode criar uma prova que comprove todas essas afirmações ao mesmo tempo. Isso pode significar:
Diagrama da postagem de implementação da carteira BLS mostrando o fluxo de trabalho para assinaturas agregadas BLS em uma versão anterior do ERC-4337. O fluxo de trabalho para agregar provas de cadeia cruzada pode ser muito semelhante.
9. Leitura direta do status
Uma possibilidade final, também disponível apenas para L2 lendo L1 (e não L1 lendo L2), é modificar L2 para que eles façam chamadas estáticas diretamente para contratos em L1. Isso pode ser feito com opcodes ou pré-compilação, que permite chamadas para o L1, onde você fornece o endereço de destino, gás e calldata, e retorna a saída, mas como essas chamadas são estáticas, elas não podem realmente alterar nenhum estado do L1. L2 precisa saber que L1 já processou o depósito, então não há nada que impeça que tal coisa seja implementada; isso é principalmente um desafio de implementação técnica (veja: isso de uma RFP otimista para suportar chamadas estáticas para L1). Observe que, se o keystore estiver em L1 e L2 integrar chamadas estáticas L1, nenhum atestado será necessário! No entanto, se L2 não integrar chamadas estáticas L1, ou se o armazenamento de chaves estiver em L2 (o que pode eventualmente ter que ser feito uma vez que L1 se torne muito caro para os usuários usarem até mesmo um pouco), então a prova será necessária.
10. Como o L2 aprende a raiz do estado Ethereum mais recente?
Todos os esquemas acima requerem que o L2 acesse a raiz do estado L1 mais próximo ou todo o estado L1 mais próximo. Felizmente, todos os L2s já possuem alguma funcionalidade para acessar o estado L1 mais recente. Isso porque eles precisam dessa funcionalidade para lidar com mensagens de L1 para L2, principalmente depósitos. De fato, se L2 tiver uma função de depósito, você poderá usar esse L2 como está para mover a raiz do estado L1 para um contrato em L2: basta fazer com que o contrato em L1 chame o opcode BLOCKHASH e passe-o para L2 como uma mensagem de depósito . O cabeçalho de bloco completo pode ser recebido no lado L2 e sua raiz de estado extraída. No entanto, cada L2 preferencialmente tem uma maneira explícita de acessar diretamente o último estado L1 completo ou a raiz do estado L1 mais próxima. O principal desafio em otimizar a maneira como o L2 recebe a raiz do estado L1 mais recente é obter segurança e baixa latência ao mesmo tempo:
Os blocos da cadeia L2 podem depender não apenas dos blocos L2 anteriores, mas também dos blocos L1. Se L1 se recuperar em tal link, L2 também se recuperará. Vale a pena notar que também é assim que as versões anteriores (pré-Dank) de sharding foram concebidas para funcionar; veja aqui o código.
11. Quanta conexão com Ethereum outra rede precisa para manter um keystore enraizado na carteira Ethereum ou L2?
Surpreendentemente, não muitos. Na verdade, nem precisa ser um rollup: se for L3 ou validação, não há problema em manter uma carteira lá, desde que você mantenha o keystore em um rollup L1 ou ZK. O que você realmente precisa é que a cadeia tenha acesso direto à raiz do estado do Ethereum e um compromisso técnico e social de estar disposto a se reorganizar se o Ethereum se reorganizar e a fazer um hard fork se o Ethereum for forcado. Uma questão de pesquisa interessante é determinar até que ponto uma cadeia pode ter essa forma de conexão com várias outras cadeias (por exemplo, Ethereum e Zcash). Fazer isso ingenuamente é possível: se Ethereum ou Zcash se reorganizar, sua cadeia pode concordar em se reorganizar (e hard fork se for Ethereum ou Zcash forks), mas seus operadores de nó e sua comunidade geralmente têm duas vezes dependências tecnológicas e políticas. Portanto, esta técnica pode ser usada para conectar algumas outras cadeias, mas com um custo maior. Os esquemas baseados em ponte ZK têm propriedades técnicas atraentes, mas sua principal fraqueza é que eles não são robustos a ataques de 51% ou hard forks. Pode haver soluções mais inteligentes também.
12. Proteção de privacidade
Idealmente, também queremos preservar a privacidade. Se você tiver muitas carteiras gerenciadas pelo mesmo keystore, queremos garantir que:
O principal desafio dessa abordagem atualmente é que a agregação exige que o agregador crie um SNARK recursivo, que atualmente é muito lento. Com o KZG, podemos usar este trabalho (veja também: uma versão mais formal deste trabalho no artigo Caulk) em provas KZG reveladoras não indexadas como ponto de partida. No entanto, a agregação de provas cegas é um problema em aberto que requer mais atenção. Infelizmente, ler L1 diretamente de dentro de L2 não preserva a privacidade, embora a implementação da funcionalidade de leitura direta ainda seja muito útil, tanto para minimizar a latência quanto por causa de sua utilidade para outros aplicativos.
13. Resumo
Para ter uma carteira de recuperação social cross-chain, o fluxo de trabalho mais realista é uma carteira que mantém um keystore em um local e uma carteira que mantém carteiras em vários locais, onde a carteira lê o keystore para (i) atualizar sua chave de autenticação localmente vista, ou (ii) no processo de validação de cada transação. Um elemento-chave que torna isso possível são as provas de cadeia cruzada. Precisamos trabalhar para otimizar essas provas. ZK-SNARKs ou soluções KZG personalizadas aguardando a prova de Verkle parecem ser as melhores opções. A longo prazo, é necessário um protocolo de agregação (onde os bundlers geram provas agregadas como parte da criação de um bundle de todas as ações do usuário enviadas pelos usuários) para minimizar os custos. Isso provavelmente deve ser integrado ao ecossistema ERC-4337, embora possam ser necessárias alterações no ERC-4337. L2 deve ser otimizado para minimizar a latência de leitura do estado L1 (ou pelo menos a raiz do estado) de dentro de L2. É ideal para L2s ler o estado L1 diretamente, economizando espaço de prova. As carteiras não podem estar apenas no L2; você também pode colocar carteiras em sistemas com um nível mais baixo de conectividade com o Ethereum (L3, ou até mesmo concordar em incluir a raiz do estado do Ethereum e a cadeia independente de bifurcações). No entanto, o keystore deve estar no L1 ou no rollup ZK de alta segurança L2. O uso do L1 economiza muita complexidade, mas mesmo isso pode ser muito caro a longo prazo, portanto, um keystore precisa ser usado no L2. Preservar a privacidade exigirá trabalho extra e tornará certas escolhas mais difíceis. Mas provavelmente devemos recorrer a soluções de preservação da privacidade de qualquer maneira, pelo menos nos certificando de que tudo o que criamos seja compatível com a preservação da privacidade.