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 é:

  1. O usuário deriva um gravar endereço (endereço privado) de um secret
  2. Os tokens são enviados para esse endereço (depósito em compromisso)
  3. 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_BITS multiplicador
  • 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 trivialmente
  • revealedAddr = addr20: modo de revelação; prova revealedAddr corresponde ao circuito addr20
  • revealedAddr = 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

By victor

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *