Pular para o conteúdo
0%

Account abstraction parte 4: assinaturas agregadas

David Philipson headshot

Escrito por David Philipson

Publicado em 14 de fevereiro de 20235 min de leitura

Aggregate signatures

Nossa implementação atual valida cada user op do bundle separadamente. Essa é uma forma bem direta de pensar a validação, mas potencialmente custosa. Verificar assinaturas pode acabar ficando caro em termos de gas, porque isso exige bastante aritmética criptográfica.

Não seria bom se pudéssemos validar vários ops ao mesmo tempo com apenas uma assinatura em vez de várias?

Fazer isso depende de um conceito da criptografia, assinaturas agregadas.

Um esquema de assinatura que suporta agregação oferece uma forma, dadas várias mensagens assinadas com chaves diferentes, de gerar uma única assinatura combinada tal que verificar a assinatura combinada implica que todas as assinaturas que a compõem também são válidas.

Um exemplo comum de esquema de assinatura que suporta agregação é BLS.

Essa otimização é particularmente útil para implementar rollups, já que o objetivo principal de um rollup é a compressão de dados, e a agregação de assinaturas nos permite comprimir a parte referente às assinaturas.

Para saber mais sobre a economia de espaço obtida com a agregação de assinaturas, veja o tweet do Vitalik sobre o assunto.

Apresentando os aggregators

De imediato, percebemos que nem todos os user ops de um bundle podem ter suas assinaturas agregadas juntas. Lembre-se de que uma wallet tem permissão para usar qualquer lógica arbitrária que quiser para validar a assinatura que recebe, então pode haver vários esquemas de assinatura presentes no mesmo bundle.

Como provavelmente não podemos agregar assinaturas de esquemas diferentes, nosso bundle acabará com grupos de ops, cada grupo usando um esquema de agregação distinto ou nenhum esquema de agregação.

Como precisamos ter vários esquemas de agregação representados on chain, cada um com sua própria lógica, cada esquema de agregação será representado por um contrato que chamaremos de aggregator.

Um esquema de agregação é definido por como ele combina várias assinaturas em uma só e por como ele valida a assinatura combinada, então um aggregator expõe essas duas funções como métodos:

Um contrato agregador combina ops de múltiplos usuários em um grupo com uma única assinatura.
Um contrato agregador combina ops de múltiplos usuários em um grupo com uma única assinatura.

Como cada wallet define seu próprio esquema de assinatura, cabe a cada wallet decidir com qual aggregator ela é compatível, se houver algum.

Se uma wallet quiser participar da agregação, ela expõe um método para escolher seu aggregator:

Usando esse novo método getAggregator, o bundler pode agrupar ops que têm o mesmo aggregator e usar o método aggregateSignatures desse aggregator para calcular uma assinatura combinada para eles.

Um grupo poderia se parecer com isto:

💡 Se um bundler tiver conhecimento off-chain sobre um aggregator em particular, ele pode otimizar codificando de forma nativa uma versão do algoritmo de agregação de assinaturas, em vez de executar aggregateSignatures como código EVM.

Em seguida, precisamos atualizar o contrato entry point para usar os novos aggregators.

Lembre-se de que o entry point tem um método handleOps que recebe uma lista de ops.

Vamos dar a ele um novo método, handleAggregatedOps, que faz a mesma coisa, mas recebe os ops agrupados por aggregator:

O novo método, handleAggregatedOps, funciona basicamente da mesma forma que handleOps. A única diferença está na etapa de validação.

Enquanto handleOps realiza a validação chamando o método validateOp de cada wallet, handleAggregatedOps vai, em vez disso, chamar o método validateSignatures do aggregator sobre a assinatura combinada de cada grupo, usando o aggregator daquele grupo.

Diagrama: o executor agrupa user ops com o agregador antes de enviá-las ao entry point para validação em lote
O executor usa o agregador para agrupar ops antes de enviá-las ao entry point, para que todas possam ser validadas ao mesmo tempo.

Estamos quase terminando!

Mas há um problema aqui que já deve ser bem familiar a essa altura.

O bundler quer simular a validação e verificar que o aggregator vai validar um grupo de ops antes de incluí-los no bundle, porque, se a validação falhar, o bundler é obrigado a pagar pelo gas. Mas um aggregator com lógica arbitrária pode facilmente ter sucesso durante a simulação e falhar durante a execução.

Vamos resolver isso exatamente da mesma forma que fizemos para paymasters e factories: restringimos a quais storage o aggregator pode acessar e quais opcodes ele pode usar, e exigimos que ele faça stake de ETH no entry point, a menos que não acesse storage.

E é isso para assinaturas agregadas!

Encerramento

O que criamos aqui é mais ou menos a arquitetura completa do ERC-4337! Existem algumas diferenças nos detalhes, como os nomes e argumentos de alguns dos métodos, mas não sobra nada que eu consideraria uma diferença arquitetural. Se eu fiz um bom trabalho, você agora deve conseguir ler o ERC-4337 de verdade e entender o que está acontecendo.

Se você chegou até aqui, muito obrigado por ler minha explicação! Espero que ela tenha te ajudado tanto quanto me ajudou escrevê-la.

Adendo: diferenças em relação ao ERC-4337

Embora já tenhamos a arquitetura geral da account abstraction definida, as pessoas inteligentes por trás do ERC-4337 pensaram em algumas coisas ligeiramente diferentes do que descrevemos acima.

Vamos ver algumas delas!

1. Intervalos de tempo de validação

Acima, fui bem vago sobre o tipo de retorno do validateOp da wallet e do validatePaymasterOp do paymaster. O ERC-4337 encontra uma boa forma de aproveitar isso.

Algo que uma wallet gostaria muito de fazer é permitir que um user op seja válido apenas por um certo período de tempo. Caso contrário, um bundler mal-intencionado poderia segurar essa operação por muito tempo e depois incluí-la em um bundle bem mais tarde, num momento vantajoso para o bundler.

A wallet poderia querer se proteger disso verificando o TIMESTAMP durante a validação, para garantir que ele não esteja muito distante no futuro, mas não pode, porque proibimos o TIMESTAMP durante a validação para evitar que as simulações fiquem imprecisas. Isso significa que a wallet precisa de outra forma de indicar em quais momentos a operação é válida.

Assim, o ERC-4337 dá a validateOp** um valor de retorno que a wallet pode usar para escolher um intervalo de tempo:**

Esse valor de retorno representa o intervalo de tempo em que a operação é válida como dois inteiros de 8 bytes, um após o outro.

Outra observação do ERC-4337: as wallets devem retornar um valor sentinela do validateOp em vez de reverter em caso de falha na validação, o que ajuda na estimativa de gas, já que o eth_estimateGas não informa quanto gas foi usado em uma transação que reverte.

2. Call data arbitrário para wallets e factories

Dissemos que a interface da nossa wallet era:

No ERC-4337, as wallets não têm de fato um método chamado executeOp.

Em vez disso, a user operation tem um campo callData:

Isso é passado para a wallet como call data.

Para um contrato inteligente típico, os quatro primeiros bytes desses dados serão interpretados como um function selector e o restante como argumentos da função.

Isso significa que, além do método validateOp obrigatório, as wallets podem definir sua própria interface, e as user operations podem ser usadas para chamar métodos arbitrários na wallet.

Na mesma linha, no ERC-4337 os contratos factory não têm de fato um método deployContract. Eles também recebem call data arbitrário, nesse caso a partir do campo initCode do op.

3. Dados compactos para paymasters e factories

Acima, dissemos que a user operation continha campos para especificar um paymaster, assim como quais dados passar para ele:

No ERC-4337, esses campos são combinados em um só como uma otimização, em que os primeiros 20 bytes do campo são o endereço do paymaster e o restante são os dados:

O mesmo vale para as factories e os dados enviados a elas: enquanto usamos dois campos, factory e factoryData, o ERC-4337 combina esses em um único campo, initCode.

Pronto, você conseguiu!

Esperamos que você tenha aprendido bastante sobre Account Abstraction.

Você poderia ter inventado a account abstraction

Perdeu o início dessa série de 4 partes? Volte e leia desde o começo!

  1. Account Abstraction Parte 1: Protegendo Nossos Ativos
  2. Account Abstraction Parte 2: Patrocinando Transações com Paymasters
  3. Account Abstraction Parte 3: Criação de Wallets
Background gradient

Construa magia blockchain

A Alchemy combina os produtos e ferramentas de desenvolvimento Web3 mais poderosos com recursos, comunidade e suporte lendário.