Rand Protocol
Documentação
Programas RISC-V

O transpilador de Ethereum

O evm2rv traduz o bytecode de runtime da EVM para a ISA RISC-V da Rand: o que ele é, o modelo de confiança e sua proteção de código, os opcodes e precompiles que cobre e onde gera trap, os números medidos e um passo a passo completo de um ERC-20, desde o Solidity, passando por solc --bin-runtime, até saídas que coincidem palavra por palavra com as do interpretador da EVM.

evm2rv recebe bytecode de runtime da EVM, a saída de solc --bin-runtime, e o transforma em um programa Rand. Ele gera C, e rand-guest build compila esse C em uma imagem RV32IM. A imagem recebe o mesmo vetor de entrada que o guest interpretador de EVM da Rand, evm.bin, e publica as mesmas oito palavras de saída. Ela executa o contrato nativamente, em vez de interpretá-lo opcode por opcode. Esta página mostra o caminho completo com um ERC-20.

O que é

entradabytecode de runtime da EVM, como texto hexadecimal (.hex) ou bytes brutos
saídaum crate C shim; rand-guest build o transforma em um contêiner de imagem
onde executafora da cadeia, na máquina do desenvolvedor; a cadeia só vê a imagem
o que deve coincidiro status, as oito palavras de saída e o gas_used de evm.bin
repositórioevm2rv/ no repositório de circuitos
toolchain fixadaRust 1.98.1, clang 23.1.1, o crate cc 1.4.6

O tradutor recebe código de runtime, não código de criação. Uma prova cobre uma chamada a um contrato já implantado, então o construtor nunca é executado. O estado que o construtor escreveria faz parte, em vez disso, do pré-estado, como testemunhas de armazenamento.

Há dois estágios de tradução. Ambos dão os mesmos resultados e o mesmo gas; cada um é seu próprio programa, com seu próprio hc.

estágioflago que faz
um--stage 1cada opcode sobre uma pilha em memória
dois--stage 2, o padrãoas palavras da pilha de cada bloco em variáveis locais de C, constantes resolvidas na tradução

O vetor de entrada é o do interpretador: o código, o calldata, o chamador e demais palavras de ambiente, o limite de gas, a raiz do pré-estado e uma testemunha de armazenamento por slot tocado pela chamada. As saídas são out[0], o status (1 sucesso, 0 revert, 2 parada excepcional), e out[1..8], o digest EVM_OUT sobre o hash do código, as duas raízes de estado, os dados de retorno e os logs.

Modelo de confiança

Um verificador checa um contrato traduzido em duas etapas.

  1. hc == translate(bytecode). Execute evm2rv novamente sobre o bytecode publicado, com o mesmo --stage e o mesmo --chain-id, recompile com a toolchain fixada e compare hc. O crate gerado fixa cc com exatidão e traz seu próprio Cargo.lock.
  2. A guarda de código liga hc ao bytecode. A lógica traduzida é compilada dentro da imagem, mas CODECOPY e CODESIZE leem o código do vetor de entrada. Sem uma verificação, um mesmo hc poderia rodar com bytes de código escolhidos pelo chamador. Por isso contract.c traz um digest Poseidon2 do bytecode de origem (seu comprimento e seus bytes), e o shim calcula o hash do código do vetor de entrada da mesma forma antes de executar qualquer código traduzido. Qualquer outro código é recusado: status 2, gas_used 0, parada OutOfBounds.

O digest EVM_OUT também liga o keccak256 do código, como o do interpretador. A cadeia não registra o bytecode de origem nem o seu hash. Para que outros possam verificar hc, publique o bytecode, ou o código-fonte Solidity e o build exato do solc.

A guarda custa 1.008 ciclos na transferência. Um código que difere em um único byte da origem é recusado em 30.418 ciclos no estágio dois (tier 16).

Cobertura e o que gera trap

Opcodes. O conjunto Shanghai. Compile com --evm-version shanghai: alvos mais novos emitem MCOPY e outros opcodes do Cancun, que geram trap.

Opcodes de ambiente:

opcodetradução
ADDRESS, CALLER, CALLVALUE, CALLDATASIZE, CODESIZEdo vetor de entrada, como o interpretador os lê
CHAINIDo valor de --chain-id, embutido na tradução, de modo que hc o vincula; obrigatório quando o código contém CHAINID
ORIGINigual a CALLER (uma chamada, sem relayer)
GASPRICE, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, SELFBALANCE, BASEFEE, BLOCKHASHtrap (status 2), como no interpretador

Os nove opcodes de contexto de bloco geram trap até que exista um desenho para vinculá-los a um bloco. Uma entrada privada não pode carregar um fato de bloco, porque um verificador não consegue abrir H_IN. Até que essa decisão seja tomada, um contrato que lê a hora ou o número do bloco (um prazo de permit, por exemplo) gera trap.

Chamadas. CALL, CALLCODE, DELEGATECALL e STATICCALL executam um precompile quando o destino é um endereço de 1 a 9. Qualquer outro destino gera trap. Um valor diferente de zero em CALL ou CALLCODE gera trap: não há modelo de saldo.

Precompiles. Os nove rodam em software. Alguns são lentos demais para serem provados hoje, porque uma prova comporta no máximo 2²⁰ linhas:

precompilevetorcicloscabe em 2²⁰
1 ecrecoverValidKey14.510.525não
2 sha256”abc” / 200 bytes2.791 / 5.289sim
3 ripemd160”abc”12.195sim
4 identity100 bytes4.498sim
5 modexpnagydani-1-square / -1-pow0x10001122.332 / 874.548sim
5 modexpeip_example1 (256 bits)11.170.118não
5 modexpnagydani-5-pow0x10001 (1.024 bytes)55.076.950não
6 bn256 addchfast1978.262sim, por pouco
7 bn256 mulchfast13.060.669não
8 bn256 pairing1 par / 2 pares1.853.656.212 / 2.019.000.469não
9 blake2f12 rodadas14.493sim, até cerca de 1.800 rodadas

A base e o módulo de modexp são limitados a 1.024 bytes cada.

Divergências aceitas. O status, as oito palavras e o gas_used devem coincidir. Três diferenças são aceitas: o tipo de parada pode diferir (o gas estático é cobrado no início de cada bloco, de modo que OutOfGas e outra parada podem se trocar; ambos são status 2, com todo o gas consumido); a tradução implementa CHAINID, ORIGIN e as chamadas a precompiles, que o interpretador trata como trap; e a guarda de código recusa qualquer código diferente de sua origem.

Os números

Chamadas ERC-20, com a guarda de código incluída. Ciclos obtidos de rand-guest run; tier entre parênteses.

chamadainterpretador (evm.bin)estágio umestágio dois (padrão)
transfer(BOB, 250)121.638 (18)80.211 (18)66.235 (18)
approve(BOB, 5)85.645 (18)56.261 (16)48.119 (16)
transferFrom(ALICE, BOB, 100)161.434 (18)110.074 (18)88.824 (18)
programapalavrashc
evm.bin, o interpretador18.0097e1aea2b…854c08
ERC-20, estágio um15.8049cdb79e7…2fd048c8
ERC-20, estágio dois11.686a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6

O estágio dois executa as três chamadas em 54,5%, 56,2% e 55,0% dos ciclos do interpretador. A transferência permanece no tier 18: seus 66.235 ciclos mais 3.155 linhas de digest somam 69.390 linhas, 3.855 acima das 65.535 do tier 16.

Cerca de 60% de uma transferência no estágio dois (cerca de 39.928 ciclos) é o harness que as duas imagens compartilham: decodificar a entrada, verificar as testemunhas de armazenamento, as chamadas keccak256 da ABI e o digest de saída. Nenhum trabalho de tradução desce abaixo desse piso.

Passo a passo: um ERC-20, do Solidity a saídas coincidentes

Todos os comandos são executados a partir da raiz de um checkout do repositório de circuitos em 7ef3220, com a toolchain de Deploy a Rust program e o clang 23.1.1 de Deploy a RISC-V program. Saídas longas são cortadas onde indicado por ….

0. Compile as ferramentas

(cd evm2rv && cargo +1.98.1 build --release)
(cd rand-guest && cargo +1.98.1 build --release)

1. Compile o Solidity para bytecode de runtime

O contrato é o guests-compiled/evm/contracts/ERC20.sol versionado: um ERC-20 autocontido com o layout de armazenamento do OpenZeppelin v4, pragma solidity 0.8.37. Use exatamente o solc 0.8.37; outra versão otimiza de forma diferente e gera bytecode diferente.

releasev0.8.37 de github.com/ethereum/solidity/releases
asset para macOSsolc-macos, sha256 a27396e7732aa52e80ff89ad7bd8a2e46fec2a6dcc4ef20cd16e5e0c502d6821
asset para Linuxsolc-static-linux, da mesma release
$ curl -sSL -o solc https://github.com/ethereum/solidity/releases/download/v0.8.37/solc-macos
$ shasum -a 256 solc
a27396e7732aa52e80ff89ad7bd8a2e46fec2a6dcc4ef20cd16e5e0c502d6821  solc
$ chmod +x solc && ./solc --version
solc, the solidity compiler commandline interface
Version: 0.8.37+commit.f401782d.Darwin.appleclang

$ (cd guests-compiled/evm && ../../solc --optimize --optimize-runs 200 --evm-version shanghai \
      --bin-runtime contracts/ERC20.sol) | tee solc.out
======= contracts/ERC20.sol:ERC20 =======
Binary of the runtime part:
608060405234801561000f575f5ffd5b5060043610610060575f3560e01c8063095ea7b31461006457806318160ddd1461008c57806323b872dd1461…
$ tail -1 solc.out > evm2rv/target/mytoken.hex

A última linha é o bytecode de runtime, com 1.296 bytes. Ele é idêntico, byte a byte, ao guests-compiled/evm/contracts/erc20.runtime.hex versionado; SOLC=$PWD/solc bash guests-compiled/evm/contracts/build.sh verifica isso e imprime contracts: erc20.runtime.hex reproduces.

As flags importam:

  • --bin-runtime: o código implantado, não o código de criação.
  • --evm-version shanghai: o alvo padrão do 0.8.37 emite MCOPY, que gera trap.
  • --optimize --optimize-runs 200: um perfil para contratos implantados.

2. Traduza

$ evm2rv/target/release/evm2rv evm2rv/target/mytoken.hex --out evm2rv/target/mytoken --chain-id 12
1296 code bytes: 74 blocks, 798 opcodes, 51 jumpdests (stage 2)
CHAINID is the constant 12
no trapping opcodes present
wrote evm2rv/target/mytoken (crate mytoken): contract.c, Cargo.toml, Cargo.lock, build.rs, src/main.rs, shim.ld
  • --out deve estar dentro de um checkout do repositório de circuitos. Sem ele, evm2rv apenas analisa o código.
  • Qualquer opcode que gerasse trap seria listado aqui como aviso, com seu pc.
  • Esta sessão foi capturada com --chain-id 12. Este ERC-20 não tem CHAINID, então --chain-id não altera seu hc. Um contrato que lê CHAINID exige a flag; defina-a como a cadeia em que você vai implantar (--chain-id 19 para a cadeia em produção).

contract.c termina com o digest da guarda de código:

const uint32_t *evm_code_digest(void) {
    static const uint32_t d[8] = {0xf679280fu, 0x86c10e83u, 0x7e8f1f39u, 0xa2377de3u, 0x8a2e282fu, 0xc54cad42u, 0xddfaf009u, 0x7d28a8afu};
    return d;
}

3. Construa a imagem

O limite padrão é de 4.096 palavras, então passe --max-words 65535.

$ rand-guest/target/release/rand-guest build evm2rv/target/mytoken --max-words 65535
…
11686 words against a cap of 65535 (fits); 0 ecall(s) with a non-static a7
OK
wrote evm2rv/target/mytoken/image.bin and its .sha256 (11686 words, hc a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6, program id f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d)

$ rand-guest/target/release/rand-guest info evm2rv/target/mytoken/image.bin
form: image container
text 11147 words at 0x10000; data 408 words (205 non-zero) at 0x1ae2c; prologue 539 words; program 11686 words from base_pc 0xf794
hc a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6
program id f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d
11686 words against a cap of 4096: does not fit

Este é o hc publicado do estágio dois. O nome do crate não o afeta.

4. Monte um vetor de entrada e execute uma transferência

O exemplo erc20_vector imprime os vetores do teste de paridade como palavras de entrada: transfer (ALICE envia 250 a BOB, de seus 1.000), approve (ALICE permite que BOB gaste 5) e transferFrom (BOB move 100 dos 1.000 de ALICE, contra uma allowance de 300). Para o seu próprio contrato, monte um EvmCall da mesma forma em Rust; o exemplo é o modelo.

$ (cd evm2rv && cargo +1.98.1 run -q --release --example erc20_vector -- transfer > target/transfer.words)
921 input words

$ rand-guest/target/release/rand-guest run evm2rv/target/mytoken/image.bin --input $(cat evm2rv/target/transfer.words)
out[0] = 1
out[1] = 513227413
out[2] = 3087537901
out[3] = 995619457
out[4] = 4004100029
out[5] = 234390638
out[6] = 3262201797
out[7] = 3842361427
cycles 66235
tier 18

5. Compare com o interpretador

A mesma entrada no guest interpretador:

$ rand-guest/target/release/rand-guest run guests-compiled/bin/evm.bin --input $(cat evm2rv/target/transfer.words)
out[0] = 1
out[1] = 513227413
out[2] = 3087537901
out[3] = 995619457
out[4] = 4004100029
out[5] = 234390638
out[6] = 3262201797
out[7] = 3842361427
cycles 121638
tier 18

E o interpretador executado nativamente no host, o oráculo dos testes:

$ (cd evm2rv && cargo +1.98.1 run -q --release --example erc20_vector -- transfer --expected)
out[0] = 1
out[1] = 513227413
…
out[7] = 3842361427
interpreter (native): status 1, Return, gas_used 29956

A verificação em um único comando:

$ diff <(rand-guest/target/release/rand-guest run evm2rv/target/mytoken/image.bin --input $(cat evm2rv/target/transfer.words) | grep '^out') \
       <(rand-guest/target/release/rand-guest run guests-compiled/bin/evm.bin --input $(cat evm2rv/target/transfer.words) | grep '^out') \
  && echo "the eight words are identical"
the eight words are identical

Repita os passos 4 e 5 com approve e transferFrom; ambos produzem palavras idênticas. cd evm2rv && cargo +1.98.1 test --release executa estes e mais cinco vetores nos dois estágios, e também verifica gas_used (70 testes, mais 2 provas ignoradas).

traduzido (segundo estágio)interpretador (evm.bin)
oito palavras de saídaidênticasidênticas
ciclos, transfer66,235121,638
tier1818
palavras do programa11,68618,009

6. Deploy

rand program deploy evm2rv/target/mytoken/image.bin

A carteira imprime o id do programa, o número de palavras e hc, e então verifica o limite da chain antes de provar qualquer coisa. A imagem tem 11,686 palavras, então exige uma chain cujo genesis defina max_program_words em pelo menos 11,686. A chain 12 usava o padrão de 4,096, e por isso o deploy foi recusado ali. A chain 13, a chain da v0.4, definiu max_program_words como 65,535, e todas as chains desde então mantêm esse valor. A chain 20, a chain em operação (genesis 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135), mantém também os limites de bloco, de envelope de chamada e de entrada pública da chain 13 e reduz pela metade o limite de prova para 4 MiB (4,194,304 bytes, desde a chain 17; as chains 13 a 16 definiam 8 MiB). O limite de palavras deixou de ser a única barreira. O genesis da chain 20 define hardening_v6, e, sob essa regra, um deploy com mais palavras de programa do que uma chamada comporta no limite de chamada do tier 14 é inválido (ProgramUncallable): no máximo 8,180 palavras, sem entrada pública. Esta imagem tem 11,686 palavras, então a chain 20 recusa o deploy, e recusaria também as chamadas: toda chamada ERC-20 é provada no tier 16 ou 18, acima do limite de chamada do tier 14 (passo 7). A agregação de blocos está desativada na chain 20 (seu genesis não tem seção aggregation); seus nós de bootstrap estão listados em Running a validator. A taxa de deploy é 0.001 + 0.0001 × 11,686 = 1.1696 RAND, a mesma que o deploy na chain 13 abaixo pagou.

Este deploy foi executado na chain 13 em 2026-09-19, quando a chain 13 era a chain em operação (perfil FRI de produção, genesis 8123ccac1883a45750e4df6964fb7cd3f0b321798cde4c0ef406a0293939ece3), a partir da carteira genesis shielded-1. Todas as chains seguintes (14 a 19) foram criadas como chains novas, sem os programas da chain 13, e a imagem não foi reimplantada em nenhuma delas; assim, a transação e a altura abaixo pertencem à chain 13 e já não são resolvidas no explorador. O id do programa é um hash apenas de base_pc e das palavras, de modo que a mesma imagem recebe o mesmo id em qualquer chain que admita o deploy. A saída da carteira, com as linhas de progresso omitidas (…):

$ rand program deploy <image.bin>
program id: f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d (11686 words, hc 92aefea0951c31a717574962aeb70e10a37012a7d035d48e8713c36df68feae0)
…
proved in 92.2s: tier 14, 1419655 bytes
submitted deploy c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0
…
id do programaf074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d
imagem11,686 palavras; hc a0feae92… na grafia do rand-guest, 92aefea0… na da carteira
pacote de taxa (pacote 2-in-2-out da chain 13)tier 14, 1,419,655 bytes, provado em 92.2 s
tx de deploy (chain 13)c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0
confirmada na altura (chain 13)381

Desde a chain 14, o pacote de pagamento é o único pacote de ativo oculto 4-in-4-out que toda transação carrega, provado contra o binding da transação. Continua no tier 14: cerca de 100 s e cerca de 1.42 MB no perfil de produção nas chains 14 e 15. Desde a chain 17, um pacote também carrega uma prova de autenticação do tier 10 (autorização dividida), de modo que, nas chains 18 e 19, o pacote de taxa de uma transação tem cerca de 1.49 MB de prova do pacote e cerca de 1.37 MB de prova de autenticação.

7. Call

Uma chamada de transferência carrega o vetor de 921 palavras como entradas privadas, dentro do limite de entrada de qualquer chain (4,295 palavras por padrão, 16,071 na chain 20), e nenhuma palavra pública. Ela não pode ser enviada à chain 20: o programa não pode ser implantado ali (passo 6), a transferência é provada no tier 18 e, desde o fullnode v0.5.6 (2026-09-25), um nó recusa qualquer prova de chamada acima do tier 14 com CallTierTooHigh, antes de construir uma chave do verificador. O comando abaixo prova localmente e a chain recusa o resultado; as medições são de provas fora da chain. rand call recebe cada palavra como um --input repetido:

rand call f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d \
    $(for w in $(cat evm2rv/target/transfer.words); do printf -- '--input %s ' "$w"; done)

Agora medido. Uma prova do tier 18 foi interrompida em um laptop de 48 GB a 24.7 GB, mas em um droplet de 128 GB (m-16vcpu-128gb) a chamada transfer traduzida foi provada: 85.0 GB de RSS de pico, 3,230.5 s (53.8 min), uma prova de 811,600 bytes. Ela foi verificada em 101.4 s no droplet e em 50.2 s, com 3.66 GB de RSS, em um laptop Apple-silicon de 48 GB (memória de prova). Sob o cronograma fixo de tiers do ledger, a regra nas chains sem seção gas (até a chain 17), a taxa de chamada no tier 18 era 0.0024 RAND. As chains 18 e 19 precificam uma chamada pelo limite de gas declarado e pelos seus bytes, 100 unidades por gas e 800 por KiB nos preços do genesis, e não admitem nenhuma chamada acima do tier 14.

The call on chain 13, 2026-09-19

A chamada approve(BOB, 5) (649 palavras privadas, tier 16) foi executada na chain 13 em 2026-09-19, quando ela era a chain em operação, com o perfil FRI de produção, a partir da mesma carteira. Nenhuma chamada foi feita em chains posteriores, nas quais o programa não está implantado, e a chain 20 recusa esta prova de chamada do tier 16 (CallTierTooHigh). A saída da carteira, com as linhas de progresso omitidas (…):

$ rand call f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d --input <w0> --input <w1> …   # the 649 words of approve(BOB, 5)
…
proved in 388.7s: tier 16, 3412405 bytes, outputs [1, 942495465, 790002515, 1351749335, 1059083501, 2923046783, 2814575942, 696258848]
…
proved in 96.0s: tier 14, 1420423 bytes
submitted call 279e1f62abf3f177ca71b08c633e051c37bc4720118c79ddb00936f036c4bfa7
…
prova da chamadatier 16, 3,412,405 bytes, provada em 388.7 s
pacote de taxa (pacote 2-in-2-out da chain 13)tier 14, 1,420,423 bytes, provado em 96.0 s
envelope selado2,700 bytes
tx de chamada (chain 13)279e1f62abf3f177ca71b08c633e051c37bc4720118c79ddb00936f036c4bfa7
ancorada em / confirmada emaltura 802 / altura 919
taxa0.00357 RAND
recibo h_pubnull
saídas do reciboiguais às oito palavras do interpretador para o mesmo vetor
tempo total, RSS de pico622.8 s, 22.9 GB, em um laptop M-series de 48 GB
  • A prova de produção tem 3,412,405 bytes, acima do limite de prova de 2 MiB da chain 12. A chain 12 não poderia aceitar esta chamada; o limite de 8 MiB da chain 13 aceitou. As chains 14 a 16 mantiveram o mesmo limite, e as chains 17 a 20 definem 4 MiB (4,194,304 bytes), dentro do qual uma prova deste tamanho ainda cabe. Desde a v0.5.6, um nó recusa a chamada por causa do tier antes de examinar o tamanho.
  • A taxa inclui o termo de bytes do cronograma da chain 13: a prova de 3.41 MB está acima da franquia gratuita de 2 MiB + 18,432 bytes. Desde a chain 18, toda chamada paga por KiB desde o byte 0.

As oito palavras do recibo são as do passo 4. A chain não guarda estado da EVM: o chamador fornece a raiz do estado anterior e as testemunhas de armazenamento, e a raiz do estado posterior está dentro do digest EVM_OUT.

Limits

  • Sem contexto de bloco. Os nove opcodes de contexto de bloco geram trap até que exista um desenho de vinculação a blocos.
  • Sem transferências de valor. Um CALL com valor diferente de zero gera trap. Chamadas a contratos que não sejam precompiles geram trap.
  • ecrecover, bn256 mul, o pairing e modexp de grande porte não podem ser provados hoje. Cada um excede o limite de 2²⁰ linhas do tier; precisam de tabelas de coprocessador.
  • Sem estado da EVM na chain. Uma chamada é uma única prova sobre uma única execução; o chamador carrega o estado.
  • Nem o deploy nem qualquer chamada ERC-20 é admissível na chain 20. Sob o hardening_v6 da chain 20, um deploy com mais palavras do que uma chamada de tier 14 comporta é inválido (ProgramUncallable; no máximo 8.180 palavras), e a imagem tem 11.686. Desde o fullnode v0.5.6, a prova de uma chamada deve ser de tier 14 ou inferior (CallTierTooHigh); approve é provado no tier 16, transfer e transferFrom no tier 18. A mesma verificação limita as tabelas de hash que uma chamada pode declarar: 2¹² linhas de Keccak (128 permutações) e 2¹³ linhas de SHA-256 (128 compressões). As chamadas ainda podem ser provadas fora da chain, como abaixo.
  • O índice de armazenamento tem 32 bits de profundidade. O evm-core posiciona uma folha de armazenamento de contrato pelos 32 bits mais significativos de keccak256(slot); assim, encontrar um slot que colida com um slot escolhido custa cerca de 2³² hashes Keccak, e, enquanto ele guarda um valor diferente de zero, o slot escolhido fica congelado (sem falsificação e sem perda de valor). Isto está dormente: o evm.bin tem 18.009 palavras, e uma chamada no limite do tier 14 comporta no máximo 8.184, de modo que nenhum runtime da EVM pode ser chamado em nenhuma chain. O docs/confidential.md do fullnode registra a precondição junto ao limite de chamadas (auditoria v6, ZKV-6; em main desde 2026-09-30, após a tag v0.6.7): antes que uma chain suspenda o limite de chamadas do tier 14 ou admita um runtime da EVM pequeno o bastante para ser chamado, o índice de armazenamento deve ser aprofundado e o evm.bin fixado novamente.
  • Provado. Uma prova de transferência de primeira etapa (tier 18) foi encerrada com SIGKILL em um laptop de 48 GB, a 24,7 GB, após 1.016 s, mas a chamada transfer de segunda etapa foi provada em um droplet de 128 GB (m-16vcpu-128gb): 85,0 GB de RSS de pico, 3.230,5 s (53,8 min), uma prova de 811.600 bytes, verificada em 101,4 s (droplet) e em 50,2 s com 3,66 GB de RSS (laptop de 48 GB). A prova de transferência do evm.bin do interpretador ficou muito próxima: 85,5 GB, 3.143,3 s (52,4 min), uma prova de 805.108 bytes — e as duas provas carregam palavras de saída públicas idênticas, o que confirma a paridade no prover real. approve (tier 16, traduzido) é muito mais barato: 21,7 GB, 786,7 s (13,1 min), uma prova de 798.930 bytes. A tradução só reduz o custo de prova quando leva uma chamada a um tier menor.
  • A chain não registra o bytecode. Publique-o para que outros possam reconstruir o hc.

Última verificação em relação ao commit c0878c3f do fullnode (chain 21) em 2026-10-12, e circuits `7ef3220` (`evm2rv/README.md`, `guests-compiled/evm/contracts/SOLC.md`, `docs/superpowers/specs/2026-09-18-evm-to-rv32-translator-design.md`); the walkthrough re-run on 2026-09-19 with solc 0.8.37. Onde o código e esta página divergirem, o código está certo; avise-nos.