1. Declaração do problema
1.1 Antecedentes
EIP-7503 (Zero-Knowledge Wormholes) propõe um design de privacidade on-chain construído em torno de endereços de gravação. ERC-8065 (Zero Knowledge Token Wrapper) define um wrapper que adiciona privacidade no estilo EIP-7503 aos tokens existentes. ZWToken é uma implementação completa do ERC-8065com suporte para ERC-20, ETH nativo, ERC-721 e ERC-1155.
Como uma implementação do ERC-8065, o ZWToken herda o principal problema de segurança levantado, mas não resolvido no EIP-7503 – o Ataque de aniversário de 160 bits. Este documento declara esse problema e a mitigação do ZWToken.
1.2 Fluxo de privacidade do endereço de gravação
O fluxo principal do EIP-7503 é:
- O usuário deriva um gravar endereço (endereço privado) de um
secret - Os tokens são enviados para esse endereço (depósito em compromisso)
- Através de uma prova ZK de que eles sabem
secreto usuário lembra uma quantidade igual de tokens em outro endereço
A privacidade neste fluxo depende de: ninguém pode vincular o endereço gravado ao endereço do destinatário.
1.3 Vetores de ataque
Os endereços Ethereum são de 160 bits. O endereço de gravação pode ser derivado como:
addrScalar = Poseidon(8065, tokenId, secret) // ~254 bits
addr20 = addrScalar mod 2^160 // truncated to 160 bits
UM ataque de aniversário pode encontrar uma colisão em 2 ^ 80 operações. O invasor procura dois espaços em paralelo; pelo paradoxo do aniversário, ~2 ^ 80 tentativas são suficientes para encontrar um par correspondente:
Vetor 1: Double-mint (dois segredos colidem no mesmo endereço)
Poseidon(8065, 0, secret_A) mod 2^160 == Poseidon(8065, 0, secret_B) mod 2^160
O invasor usa secret_A e secret_B produzir anuladores e remissões diferentes duas vezes contra o mesmo compromisso.
Vetor 2: colisão CREATE2
Poseidon(8065, 0, secret) mod 2^160 == CREATE2(deployer, salt, codehash)
O invasor usa CREATE2 para controlar o endereço de gravação: eles podem drenar tokens diretamente e também remintá-los por meio da prova ZK.
Vetor 3: Colisão de chave privada EOA
Poseidon(8065, 0, secret) mod 2^160 == keccak256(publicKey)(12:32)
O invasor pesquisa ambos secret e chaves ECDSA; em caso de colisão, eles detêm a chave privada do endereço de gravação e podem assinar as transferências.
1.4 Gravidade
| Métrica | Valor |
|---|---|
| Complexidade do ataque | 2 ^ 80 operações hash |
| Segurança de 128 bits | 2 ^ 128 operações |
| Brecha | Não atende à segurança de 128 bits |
| Estimativa aproximada de custo | Dezenas de bilhões de dólares em hardware (hoje) |
| Impacto | Inflação do protocolo; piscina pode ser drenada |
Embora hoje seja economicamente inviável, o risco aumenta com a otimização de hardware e ASIC.
2. Arte Anterior
2.1 Prova de Trabalho EIP-7503
Adicione uma restrição PoW no circuito ZK:
Poseidon(MAGIC_POW, secret) mod 2^POW_BITS == 0
secret deve satisfazer a condição PoW, aumentando o custo de encontrar segredos válidos.
Prós:
- Nenhuma alteração no fluxo voltado para o usuário
- Adapta-se naturalmente ao circuito ZK
Contras:
- Melhora apenas o fator constante, não a complexidade assintótica—o invasor paga uma
2^POW_BITSmultiplicador - O tempo de geração de provas aumenta significativamente (os usuários executam PoW no lado do cliente)
- Pior experiência no celular
POW_BITSé difícil de ajustar: muito baixo é ineficaz, muito alto é inaceitável para os usuários
2.2 Limite de Depósito
Limite o valor máximo por depósito/transferência/resgate.
Prós:
- Simples de implementar
- Limita o lucro por ataque abaixo do custo de um ataque de 2^80 aniversários
Contras:
- Quebra a compatibilidade e composição do token—depósito, transferência, etc. não pode exceder o limite, então ZWToken (por exemplo, Wrapped ERC-20 com privacidade) não pode se comportar como um ERC-20 padrão com DeFi (Uniswap, Aave, etc.)
- Restringe grandes transferências legítimas
3. Solução ZWToken: Remint de modo duplo
3.1 Ideia Central
Defina um anonymousCap abaixo do custo econômico de um ataque de 2^80 aniversários:
- Quantidade ≤ capital anônimo: modo totalmente anônimo; comportamento inalterado de antes
- Valor > capital anônimo: modo de revelação; o usuário deve publicar o endereço de gravação; o contrato queima nesse endereço e depois é cunhado
Na pior das hipóteses, após aproximadamente 2 ^ 80 de trabalho, o invasor só poderá roubar até anonymousCap. Se anonymousCap for inferior ao custo estimado de um ataque 2^80 (dezenas de milhares de milhões de dólares hoje), não há motivo económico racional. Grandes transferências são forçadas através do burn-from-address, então a inflação deste vetor é impossível.
Ao contrário de um limite de depósito global, o limite aplica-se apenas em lembrar; depósito, transferência e retirada padrão ERC-20 são irrestritospreservando total compatibilidade e capacidade de composição DeFi. A IU limita as transferências para gravar endereços em anonymousCap; as transferências entre endereços comuns são ilimitadas.
3.2 Projeto de Circuito
Adicione uma entrada pública revealedAddr e uma restrição quadrática:
signal input revealedAddr; // 0 = anonymous, addr20 = revealed
// If revealedAddr != 0, it must equal addr20
revealedAddr * (revealedAddr - addr20) === 0;
revealedAddr = 0: modo anônimo; restrição é válida trivialmenterevealedAddr = addr20: modo de revelação; provarevealedAddrcorresponde ao circuitoaddr20revealedAddr = anything else: a restrição falha; a prova não pode ser produzida
Custo: uma restrição extra (13084 → 13085); impacto insignificante no tempo de prova.
3.3 Desenho do Contrato
// BaseZWToken.sol
uint256 public immutable anonymousCap; // 0 = no cap
function _requireRevealIfNeeded(uint256 amount, address revealedAddr) internal view {
// Amount above cap requires reveal
if (anonymousCap > 0 && amount > anonymousCap && revealedAddr == address(0)) {
revert RevealRequired();
}
// Revealed address with code = CREATE2 collision attack
if (revealedAddr != address(0) && revealedAddr.code.length != 0) {
revert BurnAddressHasCode();
}
}
Fluxo de remint no modo de revelação (exemplo ZWERC20):
// 1. Burn from burn address first
if (revealedAddr != address(0)) {
_burn(revealedAddr, amount);
}
// 2. Then normal remint (mint to recipient)
_executeRemint(to, id, amount, redeem, relayerFee);
Efeito: queimar amount → hortelã amount (dividido entre destinatário + retransmissor + protocolo); A variação total líquida do fornecimento é 0.
3.4 Guarda EXTCODESIZE
Um endereço de gravação válido é um endereço aleatório de Poseidon; é sempre um EOA (sem bytecode). Se um revealedAddr tem código, alguém implantou um contrato lá por meio da colisão CREATE2 – evidência direta de ataque.
if (revealedAddr.code.length != 0) revert BurnAddressHasCode();
Custo: código de operação EXTCODESIZE, ~100 gás.
4. Referências
Fontesethresear



