Autor: ChiHaoLu (chihaolu.eth) Fonte: médio Tradução: Shanoba, Golden Finance
Este artigo se concentra no desenvolvimento de Account Abstraction (AA) na solução Aztec Layer 2 e conteúdos relacionados. Cito uma série de recursos oficiais da Aztec, incluindo documentação oficial, blogs e tutoriais. Por favor, encontre estes excelentes recursos na seção de referências no final do artigo!
Devido à maior complexidade do Aztec em comparação com outras implementações AA nativas no ZK-Rollups, os leitores podem se beneficiar de ter algum contexto para entender melhor este artigo.
Aztec é uma rede de camada 2 de código aberto projetada para fornecer escalabilidade e proteção de privacidade para Ethereum. A Aztec aproveita as provas zkSNARK para fornecer proteção de privacidade e escalabilidade através do serviço ZK-Rollup. Os usuários do Aztec não precisam de terceiros confiáveis ou mecanismos de consenso adicionais para acessar transações privadas.
Todos sabemos que nos ZK-Rollups tradicionais, “ZK” não significa necessariamente privacidade; Isso significa usar provas de conhecimento zero (ZKPs) para provar que certos cálculos foram realizados corretamente fora da cadeia. No entanto, em Aztec, a privacidade é implementada no ZK-Rollup, além da escalabilidade. Indo mais fundo, no passado, os detalhes de cada transação eram publicamente visíveis on-chain, mas na Aztec, a entrada e saída de cada transação são criptografadas. Essas transações são verificadas pelo ZKP para provar que as informações criptografadas são precisas e originadas em texto simples. Somente o usuário que constrói essas transações privadas conhece as informações reais de texto sem formatação.
Mesmo personagens importantes no ZK-Rollup, como Sequencer e Prover, não conseguem determinar o que é o texto simples. Todas as informações sobre a transação, incluindo o remetente, o destinatário, os dados da transação e o valor da transferência, estão ocultas. Embora apenas os próprios usuários saibam os detalhes da transação, eles ainda podem confiar na correção da transação. Essa confiança decorre do fato de que apenas transações legítimas podem gerar provas válidas de conhecimento zero para provar sua precisão.
Como implementar transações privadas e como verificar seus fundamentos é um grande tópico que está além do escopo deste artigo. Em termos simples, o que precisamos é de uma “camada adicional para verificação de prova de conhecimento zero” para validar listas ZKP, cada uma das quais valida uma transação privada. É por isso que eles são chamados de “ZK-ZK-Rollups”.
Em asteca, uma abstração de conta nativa é usada, o que significa que não há diferença entre uma conta de propriedade externa (EOA) e uma conta de contrato. Todas as contas são contratos inteligentes. Portanto, daremos uma breve visão geral do ecossistema de desenvolvimento de contratos da Aztec, pois é crucial entender o desenvolvimento de contratos. No entanto, se você não planeja desenvolver o contrato de conta por conta própria, ler e entender este artigo não exige que você ligue o computador e escreva o contrato. Você só precisa entender a lógica no código do contrato de conta. Você pode explorar a profundidade do tópico a seu próprio critério!
Noir é uma linguagem para escrever programas SNARK, semelhante ao Circcom e ZoKrates. Ele não só permite que você gere automaticamente contratos do Verificador de Solidez depois que o circuito é criado, mas também pode ser usado para escrever seus próprios protocolos ou até mesmo blockchains. Como o Noir não depende inteiramente do Aztec (ele não compila para um sistema de prova específico), você pode alcançar seus objetivos implementando um servidor de back-end e interface de contrato inteligente para o sistema de prova.
Na Aztec, o Noir é usado para escrever contratos inteligentes onde variáveis (estados) e funções podem ser protegidas pela privacidade.
De acordo com nosso entendimento de blockchains públicas, geralmente todos os estados são públicos. Em asteca, é importante compreender o conceito de Estado privado e como gerenciá-lo (adicionar, modificar, excluir). O Estado privado é encriptado e propriedade do seu detentor. Por exemplo, se eu for o proprietário de um contrato, uma variável específica nesse contrato pode ser criptografada e oculta estado privado. Apenas eu, como proprietário deste estado privado, posso desencriptar o texto cifrado para obter o texto simples.
O estado privado é armazenado anexando apenas a árvore do banco de dados. Isso é feito porque alterar o valor do estado diretamente pode vazar muitas informações do diagrama de transações. No entanto, o banco de dados não armazena diretamente o valor do estado privado. Em vez disso, ele os registra como notas privadas em forma criptografada (por exemplo, x=0 -> x=1) addr=0x00 -> addr=0x01. Então, na realidade, essas variáveis privadas, embora pareçam ser variáveis, são na verdade compostas por um monte de notas privadas imutáveis. Esta é a abstração de variáveis. Se não estiver claro, vamos em frente.
Quando precisar excluir um estado privado, você poderá adicionar os caracteres inválidos relacionados a esse estado privado a outro banco de dados inválido para invalidá-lo. Quando precisar alterar o estado privado, primeiro invalide-o e, em seguida, adicione um novo comentário privado a ele.
Abordaremos o conceito de anuladores em breve. Você pode pensar nisso como a chave que você precisa para vincular uma nota privada ao seu caráter inválido. Apenas o proprietário de uma chave inválida pode identificar e usar a nota privada associada a ela.
Neste ponto, leitores experientes podem ter notado que essa estrutura é muito semelhante às UTXOs (saídas de transação não gastas), e podemos iterar sobre UTXOs para determinar o estado atual do estado privado (embora seja importante lembrar que é o usuário que assina a transação, não a UTXO; Explicaremos isso mais adiante).
! [TEg3TOpeZXF82AgjnmG7in3uwiZp3ZtIpGsNQvxc.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-2ad04801d9-dd1a6f-cd5cc0.webp “7128680”)
O que é uma Nota Privada? Um UTXO é chamado de Nota, e nós percorremos esta Árvore de Notas para obter informações sobre o estado privado. Quando queremos mudar o Estado privado, os passos são os seguintes:
Como todos sabemos, o EIP-4337 visa mover todo o processo de transação para a camada de aplicação, implementar um sistema de relé aberto e abstrair a assinatura (mecanismo de verificação) e o modelo de pagamento através da natureza programável dos contratos inteligentes. No entanto, para a Native Account Abstraction, seja na era StarkNet, zkSync ou Aztec, que é o foco deste artigo, certos elementos precisam ser gravados no protocolo da Camada 2 para funcionar corretamente. Por exemplo, em Aztec, chaves de criptografia e chaves inválidas precisam ser implementadas no nível de protocolo.
Compreender a abstração de conta nativa requer uma compreensão mais profunda de como toda a cadeia funciona (assumindo que é chamada de “nativa”, com a lógica de execução do AA naturalmente ligada a um protocolo Layer2 específico). Por exemplo, na era zkSync, há uma necessidade de entender o contrato do sistema, na StarkNet, há uma necessidade de entender como o sequenciador funciona, e em Aztec, é crucial entender o papel dessas “chaves e seu estado privado subjacente”.
Em Aztec, ao contrário de outras implementações de abstração de conta, não há um nome de função estritamente definido (assinatura de função) como um ponto de entrada para o contrato de conta (por exemplo, validateUserOp em EIP-4337, validateTransactionzkSync Era e __validate__StarkNet). O usuário é livre para escolher qualquer função no contrato de conta como ponto de entrada e enviar uma transação com os parâmetros relevantes.
A função escolhida pelo usuário (chamamos de entrypoint()) deve ser privada (executada no ambiente de execução privada do usuário). Ele só pode ser chamado a partir do cliente do proprietário do contrato de conta (a carteira do usuário). Quando o entrypoint() da carteira do usuário é executado localmente, ele também gera uma prova de conhecimento zero. Este atestado informa o protocolo asteca da fase de verificação de que a execução off-chain ocorreu e foi bem-sucedida.
Isto também se estende à questão de impor ou não restrições ao que pode ser feito durante a fase de validação. É bem sabido que, como não há limite de custo para validar transações (essencialmente, validar transações é uma exibição de função), um invasor pode executar um ataque de negação de serviço (DOS) no mempool para comprometer o bundler (EIP-4337) ou o operador/sequenciador (AA nativo). O EIP-4337 define quais opcodes são proibidos e como o acesso ao armazenamento é restrito. O zkSync Era relaxa algum uso do OpCode, enquanto o StarkNet não permite chamadas de contrato externas.
Como o protocolo Aztec envolve o cliente validando uma prova de conhecimento zero anexada, em vez de realmente chamar uma função de validação para determinar se o resultado é ou , trueAztecfalse, ao contrário de outros protocolos, não impõe nenhuma restrição durante a fase de validação. O ponto de entrada do contrato na conta pode ligar livremente para outros contratos, acessar qualquer armazenamento e realizar quaisquer cálculos.
Mais detalhadamente, no zkSync Era e StarkNet, apenas o “contrato de conta” pode iniciar uma transação, porque o protocolo chama uma função especificada como ponto de entrada, impondo essa restrição. No entanto, em asteca, “todos os contratos” podem iniciar transações, pois não há limite para qual função o protocolo chama como ponto de entrada. Isso significa que, no Aztec, não é mais um fluxo de interação fixo onde um usuário envia uma transação para uma função específica (como o contrato EntryPoint no EIP-4337 ou o sequenciador/operador no AA nativo) e, em seguida, invoca o contrato de destino. Os usuários podem enviar transações através da carteira e deixar diretamente o contrato alvo concluir as interações relevantes, o que aumenta muito a flexibilidade.
Se você estiver familiarizado com o EIP-2938, outra implementação do AA, descobrirá que o Aztec é mais parecido com a abordagem multilocatário. No entanto, este é um tópico mais profundo que você pode explorar por conta própria.
Em asteca, cada conta normalmente tem duas chaves mestras: a chave de assinatura e a chave mestra de privacidade.
A chave de assinatura é usada para representar a autorização do usuário para executar uma ação específica usando a chave privada. Um exemplo simples é quando um usuário registra uma chave pública derivada da chave de assinatura no contrato de conta. A assinatura gerada pode então ser restaurada dentro do contrato assinando a transação ou mensagem usando essa chave de assinatura para verificar se ela corresponde à chave pública registrada no contrato (também conhecida como chave controlada pelo proprietário, que é armazenada na forma de uma chave). endereçoContrato de carteira Solidity).
A escolha do algoritmo para a curva elíptica das assinaturas digitais cabe ao utilizador. Por exemplo, Ethereum usa secp256k1, enquanto Aztec fornece um exemplo schnorr para usar. No contrato de conta, a função entrypoint() atua como um ponto de entrada (a origem da chamada) e é usada pela lógica de validação (is_valid_impl()) para verificar se a assinatura Schnorr corresponde à chave pública do registro std::schnorr::verify_signature(…).
! [eWwFvxmMF7pNt0axLcucSvjcig6QHMXl2HKH3luz.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-3454e57f80-dd1a6f-cd5cc0.webp “7128683”)
A chave de assinatura é essencialmente a mesma que a chave controlada pelo proprietário na carteira de contrato inteligente com a qual estamos familiarizados, por isso deve ser relativamente fácil de entender. Na verdade, as chaves de assinatura não são absolutamente necessárias. Se o desenvolvedor da conta tiver implementado um mecanismo de verificação diferente, a conta pode não ter uma chave de assinatura.
A chave mestra de privacidade na sua conta Aztec é intransmissível; Cada conta asteca está vinculada a uma chave mestra privada. A chave mestra privada deriva a chave mestra pública, que é então colocada em hash com o código do contrato para gerar o endereço do contrato de conta.
! [9WujRrvfd8YccfQkh9kbCtz0O1GVAKvNVJI7kXtm.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-709665cdb6-dd1a6f-cd5cc0.webp “7128684”)
Nós public_master_key o account_address, partial_address e coletivamente do usuário como o endereço completo da conta. Ao lidar com o estado privado, o usuário precisa fornecer essas três informações para que qualquer pessoa possa verificar se a chave pública corresponde ao endereço pretendido.
No entanto, se se tratar de uma candidatura public_master_key que não pretende tratar de um Estado privado e está em falta (por exemplo, DeFi), pode simplesmente preencher esse public_master_key campo 0 para indicar que não deseja receber notas privadas.
Assim, enquanto a Aztec nos permite implementar um mecanismo de verificação e até mesmo algum mecanismo de recuperação no contrato da conta para aumentar a segurança da conta, o mecanismo relacionado à chave mestra de privacidade é impresso no protocolo e vinculado ao endereço. Portanto, não é intercambiável.
A implicação aqui é que essa chave é tão importante quanto a chave privada de uma EOA (conta de propriedade externa) no Ethereum, tornando-a um único ponto de falha (SPoF) para uma conta. Se um usuário perder ou a chave mestra de privacidade de uma conta for roubada, não há dúvida de que a conta não será recuperável.
A chave mestra de privacidade também usa um processo semelhante ao BIP-32 para exportar chaves de criptografia e chaves inválidas. Os usuários podem usar diferentes chaves de criptografia e chaves inválidas em diferentes aplicativos ou ações para garantir privacidade e segurança.
A chave pública da chave de encriptação é usada para encriptar a nota privada, enquanto a chave privada é usada para a desencriptar. Por exemplo, em um cenário de transferência de token, se eu (o remetente do token) quiser transferir tokens para meu amigo (destinatário do token), preciso criptografar a nota privada (a transferência de tokens envolve a mudança de variáveis, essencialmente o equilíbrio é alterar a variável de estado privado UTXO usando a chave pública criptográfica do meu amigo) e enviá-la.
Do ponto de vista de um estranho, sem conhecer a chave privada criptográfica do destinatário do token, ele não pode decifrar essa nota privada ou saber quem é o destinatário do token.
Cada vez que uma nota privada é usada, um caractere inválido derivado desse comentário privado é gerado (criptografado com uma chave inválida). Este mecanismo é utilizado para evitar gastos duplos (para evitar que outros utilizem o mesmo método para determinar a localização de uma nota ou para deduzir fundos duas vezes) porque o protocolo asteca verifica se os caracteres inválidos são únicos. Para corresponder esse caractere inválido a uma nota privada, uma chave privada inválida é necessária para descriptografá-la, para que apenas seu proprietário possa estabelecer uma relação entre os dois.
Descrever o conceito de uma transação em Aztec pode ser desafiador porque pode ser facilmente confundido com um UTXO (Private Notes) e o modo de execução de uma transação em EVM e Aztec é completamente diferente.
Em Aztec, cada transação é transmitida na forma de uma prova de conhecimento zero (obviamente por razões de privacidade). Os usuários devem realizar cálculos localmente em seus nós (aplicativos de carteira ou clientes) para gerar provas correspondentes às transações, em vez de simplesmente enviar objetos de transação para o mempool do minerador ou qualquer operador de Camada 2 via API RPC, como costumávamos fazer.
Quando um usuário cria uma transação localmente, há dois elementos importantes:
! [xReWU4p6VrxP45q5EYNXZGAziaZhIG1DTrjeO4yD.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-c9db8c15b3-dd1a6f-cd5cc0.webp “7128688”)
No nível de protocolo da Aztec, o hash de cada transação válida é usado como um meio para impedir que a mesma transação seja executada várias vezes. O desenvolvedor do contrato de conta pode decidir se deve haver um nonce no contrato de conta e na lógica associada. Por exemplo, eles podem definir requisitos como garantir que o campo nonce na transação seja estritamente aumentado ou que a transação possa ser processada em qualquer ordem.
Uma vez que não há um requisito estrito de nonce no nível do protocolo, a Aztec não pode cancelar uma transação pendente enviando uma nova transação com as mesmas taxas de nonce e gás mais altas.
Como mencionado anteriormente, o usuário pode especificar qualquer função no contrato de conta como um ponto de entrada, dependendo da situação, e a operação em Aztec não é controlada por um simples objeto de negociação. Na verdade, o que diz à conta do contrato o que fazer é um objeto chamado “pedido de execução”. O objeto representa o comportamento do usuário, como “Chamar a função de transferência no contrato com 0x1234 dos seguintes parâmetros”.
O usuário inicia uma transação localmente na carteira, onde o sender_address é o endereço do contrato de conta, contendo os dados de codificação relevantes da função na transferência de contrato de destino de chamada de carga útil(), e a assinatura que pode ser verificada pelo contrato de conta. A carteira converte esses dois elementos em uma solicitação de execução.
Em seguida, a carteira insere uma nota privada, uma chave de criptografia ou um segredo inválido em uma máquina virtual local para simular essa solicitação de execução. Durante o processo de simulação, será gerado um traço de execução, que será entregue ao provador para gerar uma prova de conhecimento zero. Esta prova mostra que estes cálculos (a execução de funções privadas) são de facto feitos localmente pelo utilizador.
Através deste processo, obtemos duas informações: prova e private_data (a saída do circuito kernel privado para esta transação). A carteira então envia o objeto de transação contendo essas duas informações para o mempool do Sequencer da Aztec e conclui a transação.
Em asteca, não simplesmente inserimos todas as informações em uma máquina de Turing como o EVM para gerar o estado atualizado. Em vez disso, ele depende do circuito dentro de cada Dapp para determinar como as informações de privacidade devem funcionar. Isso significa que os desenvolvedores de Dapp precisam ter uma maneira de provar o estado das variáveis de contrato. Por exemplo, vamos considerar o saldo do usuário em um contrato de token ERC-20. Se quisermos transferir 10 tokens DAI, o contrato pode ter a seguinte lógica:
Do ponto de vista de um estranho, eles só podem ver novas nulidades e comentários aparecendo, e todos eles são criptografados. Assim, todos sabem que houve um novo acordo, mas o que exatamente está acontecendo lá dentro é conhecido apenas pelos participantes envolvidos.
! [B2NwqMIhntBOllrlchIWeaetEbkSSdoTARJiM27A.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-e6bc34e04b-dd1a6f-cd5cc0.webp “7128690”)
As carteiras em Aztec são uma parte importante do gerenciamento das interações dos usuários com o blockchain e seus dados privados. Aqui está um resumo das tarefas que a carteira asteca tem que lidar:
Um ponto-chave a ser observado é que a carteira precisa digitalizar todos os blocos começando com o bloco de gênese, usar a chave do usuário para descobrir e descriptografar as notas privadas relevantes e, em seguida, armazená-las em um banco de dados local para uso futuro. Isso é fundamental para facilitar a interação do usuário e garantir que os dados do estado privado possam ser gerenciados de forma eficaz.
Você mencionou outro aspeto importante do Aztec, que é a necessidade de os usuários transmitirem o endereço completo. Quando uma carteira cria uma nota de criptografia para o destinatário de uma transação de destino, ela também precisa ser capaz de obter o endereço completo do destinatário. Isto pode ser conseguido introduzindo manualmente ou mantendo uma base de dados local do endereço do destinatário. Para obter mais detalhes sobre isso, consulte a documentação oficial asteca.
Em Aztec, as principais tarefas de um contrato de conta são verificar assinaturas (confirmando que as transações são autorizadas pelo proprietário da conta e, portanto, autorizadas de forma mais ampla, em vez de necessariamente “verificadas por um algoritmo de assinatura digital específico”), gerenciar o consumo de gás e invocar outros contratos.
Este é um exemplo oficial de um contrato de conta asteca usando o algoritmo de assinatura Schnorr. O ponto de entrada para todas as transações é a função entrypoint() (você é livre para escolher a função ou nome como o ponto de partida, mas neste caso entrypoint() é usado).
! [LahY9kfNGKkYkSm5ULPlReKATiTA3K5bjFGIClh0.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-be4c89f0e2-dd1a6f-cd5cc0.webp “7128692”)
Quando chamamos a conta entrypoint() com a carga útil do anexo, o entrypoint() do contrato de conta chamará o entrypoint() da biblioteca de contas AA asteca().
! [OskBKScb0LqWpcTMIuhBMjeSB151sFvSWbwXl.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-c46b7b5d32-dd1a6f-cd5cc0.webp “7128697”)
A Aztec não exige que nossos contratos de conta importem para as bibliotecas de contas EntrypointPayload e Aztec AA; Você é livre para projetar sua própria lógica de contrato de conta.
! [HNskT1CkOOPiZZaAVvqlJNf7L5VxU39Fz9ir2Ica.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-dbf6df09b4-dd1a6f-cd5cc0.webp “7128698”)
é o contexto, um objeto que está disponível em todas as funções em Aztec.nr. Contém todas as informações do kernel necessárias para a execução do aplicativo de contexto. Citado da documentação oficial asteca. - Qual é o pano de fundo
O acima é o código entrypoint() da biblioteca de contas Aztec AA. Ele é responsável por determinar se a operação é autorizada pelo proprietário da conta com base na função de validação () definida na conta is_valid_impl contrato, e faz as chamadas necessárias para implementar a operação de _calls necessária para a transação.
Isso significa que, se você quiser fazer referência a essa biblioteca Aztec AA e seu contrato de conta não tiver a implementação is_valid_impl com a mesma assinatura de função, esta etapa falhará.
! [QZuhkG4BTiKMQF4hFZKGw0gi9MBlU5VGcDjyQyU9.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-92b47183b4-dd1a6f-cd5cc0.webp “7128699”)
Outro detalhe importante da implementação é usar get_auth_witness() para recuperar assinaturas. Você pode consultar as referências abaixo para saber mais sobre como as Testemunhas funcionam. Em termos simples, uma testemunha é “uma autorização para o usuário fazer o que ele quer fazer”.
Uma testemunha de autenticação é um esquema para verificar operações em Aztec, para que os usuários possam permitir que terceiros, como protocolos ou outros usuários, executem ações em seu nome. Citado da documentação oficial asteca. — Testemunhas certificadas
A razão pela qual é chamado de “testemunha” em vez de simplesmente “assinatura” é porque a forma como o contrato de conta é verificado não envolve necessariamente a verificação da assinatura.
Por exemplo, digamos que há uma operação que transfere 1000 tokens da conta de Alice para uma plataforma DeFi. Neste caso, o contrato de token precisa consultar o contrato de conta de Alice para ver se ela aprova a “ação”. Esta “ação” requer que uma testemunha de autenticação seja gerada na carteira de Alice (localmente), que pode ser verificada através do contrato de sua conta. Se o contrato de conta for verificado com sucesso, o contrato de token saberá que a “ação” foi autorizada.
! [WJkuu8NWicgcHvCyH08YpHhjYtTtYiP8GbRxwwKs.png] (https://img-cdn.gateio.im/webp-social/moments-40baef27dd-a797a836e2-dd1a6f-cd5cc0.webp “7128700”)
Até agora, a Aztec não implementou um mecanismo de taxas, e seu objetivo também é abstrair o pagamento de taxas. Isso significa que, para uma transação ser considerada válida, ela deve provar que bloqueou fundos suficientes para cobrir suas próprias taxas. No entanto, não especifica de onde esses fundos devem vir, tornando os pagamentos ou pagamentos em espécie facilmente possíveis através de câmbio imediato.