Rand Protocol
Документация
Программы RISC-V

Транспилятор Ethereum

evm2rv транслирует байт-код среды выполнения EVM в RISC-V ISA сети Rand: что это такое, модель доверия и защита кода, какие опкоды и прекомпайлы он покрывает и где завершается ловушкой, измеренные показатели, а также полный разбор ERC-20 от Solidity через solc --bin-runtime до результатов, которые слово в слово совпадают с выводом интерпретатора EVM.

evm2rv принимает runtime-байткод EVM (вывод solc --bin-runtime) и превращает его в программу Rand. Он пишет C, а rand-guest build компилирует этот C в образ RV32IM. Образ принимает тот же входной вектор, что и гостевой интерпретатор EVM в Rand, evm.bin, и публикует те же восемь выходных слов. Он исполняет контракт нативно, а не интерпретирует его опкод за опкодом. На этой странице весь путь показан на примере ERC-20.

Что это такое

входruntime-байткод EVM в виде hex-текста (.hex) или сырых байтов
выходcrate-прослойка на C; rand-guest build превращает её в контейнер образа
где работаетвне цепочки, на машине разработчика; цепочка видит только образ
чему должен соответствоватьстатусу, восьми выходным словам и gas_used у evm.bin
репозиторийevm2rv/ в репозитории circuits
зафиксированный набор инструментовRust 1.98.1, clang 23.1.1, crate cc 1.4.6

Транслятор принимает runtime-код, а не creation-код. Доказательство покрывает один вызов развёрнутого контракта, поэтому конструктор не исполняется. Состояние, которое записал бы конструктор, входит в предсостояние в виде свидетелей хранилища.

Трансляция выполняется в две стадии. Обе дают одинаковые результаты и расход газа; каждая является отдельной программой со своим hc.

стадияфлагчто делает
первая--stage 1каждый опкод поверх стека в памяти
вторая--stage 2, по умолчаниюслова стека каждого блока в локальных переменных C, константы свёрнуты при трансляции

Входной вектор тот же, что у интерпретатора: код, calldata, вызывающий и другие слова окружения, лимит газа, корень предсостояния и по одному свидетелю хранилища на каждый слот, которого касается вызов. Выходы: out[0] — статус (1 — успех, 0 — откат, 2 — аварийная остановка) и out[1..8] — дайджест EVM_OUT по хэшу кода, обоим корням состояния, возвращаемым данным и логам.

Модель доверия

Верификатор проверяет транслированный контракт в два шага.

  1. hc == translate(bytecode). Заново запустите evm2rv на опубликованном байткоде с теми же --stage и --chain-id, пересоберите на зафиксированном наборе инструментов и сравните hc. Сгенерированный crate жёстко фиксирует cc и содержит собственный Cargo.lock.
  2. Охранная проверка кода связывает hc с байткодом. Транслированная логика компилируется в образ, но CODECOPY и CODESIZE читают код из входного вектора. Без проверки один и тот же hc мог бы исполняться с байтами кода, выбранными вызывающим. Поэтому contract.c содержит дайджест Poseidon2 исходного байткода (его длины и байтов), а прослойка хэширует код из входного вектора тем же способом до запуска любого транслированного кода. Другой код отклоняется: статус 2, gas_used 0, остановка OutOfBounds.

Дайджест EVM_OUT также связывает keccak256 кода, как и у интерпретатора. Цепочка не хранит исходный байткод и его хэш. Чтобы другие могли проверить hc, опубликуйте байткод либо исходный код Solidity и точную сборку solc.

Охранная проверка стоит 1 008 циклов на transfer. Код, отличающийся от исходного на один байт, отклоняется за 30 418 циклов на второй стадии (ярус 16).

Покрытие и что вызывает ловушку

Опкоды. Набор Shanghai. Компилируйте с --evm-version shanghai: более новые цели порождают MCOPY и другие опкоды Cancun, которые вызывают ловушку.

Опкоды окружения:

опкодтрансляция
ADDRESS, CALLER, CALLVALUE, CALLDATASIZE, CODESIZEиз входного вектора, как их читает интерпретатор
CHAINIDзначение --chain-id, зашитое при трансляции, поэтому hc его связывает; обязательно, если код содержит CHAINID
ORIGINравен CALLER (один вызов, без релеера)
GASPRICE, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, SELFBALANCE, BASEFEE, BLOCKHASHловушка (статус 2), как у интерпретатора

Девять опкодов контекста блока вызывают ловушку, пока нет схемы их привязки к блоку. Приватный вход не может нести факт о блоке, потому что верификатор не может раскрыть H_IN. Пока это решение не принято, контракт, который читает время или номер блока (например, срок действия permit), завершается ловушкой.

Вызовы. CALL, CALLCODE, DELEGATECALL и STATICCALL запускают прекомпайл, если цель — адрес от 1 до 9. Любая другая цель вызывает ловушку. Ненулевое значение в CALL или CALLCODE вызывает ловушку: модели баланса нет.

Прекомпайлы. Все девять работают программно. Некоторые сегодня слишком медленны для доказательства, потому что доказательство вмещает не более 2²⁰ строк:

прекомпайлвекторциклыпомещается в 2²⁰
1 ecrecoverValidKey14 510 525нет
2 sha256”abc” / 200 байт2 791 / 5 289да
3 ripemd160”abc”12 195да
4 identity100 байт4 498да
5 modexpnagydani-1-square / -1-pow0x10001122 332 / 874 548да
5 modexpeip_example1 (256 бит)11 170 118нет
5 modexpnagydani-5-pow0x10001 (1 024 байта)55 076 950нет
6 bn256 addchfast1978 262да, едва
7 bn256 mulchfast13 060 669нет
8 bn256 pairing1 пара / 2 пары1 853 656 212 / 2 019 000 469нет
9 blake2f12 раундов14 493да, примерно до 1 800 раундов

Основание и модуль modexp ограничены 1 024 байтами каждое.

Допустимые расхождения. Статус, восемь слов и gas_used должны совпадать. Допускаются три отличия: вид остановки может различаться (статический газ списывается в начале каждого блока, поэтому OutOfGas и другая остановка могут поменяться местами; обе дают статус 2 с полным расходом газа); трансляция реализует CHAINID, ORIGIN и вызовы прекомпайлов, которые интерпретатор отвергает ловушкой; охранная проверка кода отклоняет любой код, кроме исходного.

Цифры

Вызовы ERC-20 с включённой охранной проверкой кода. Циклы получены от rand-guest run; в скобках указан ярус.

вызовинтерпретатор (evm.bin)первая стадиявторая стадия (по умолчанию)
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)
программасловаhc
evm.bin, интерпретатор18 0097e1aea2b…854c08
ERC-20, первая стадия15 8049cdb79e7…2fd048c8
ERC-20, вторая стадия11 686a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6

Вторая стадия выполняет три вызова за 54,5 %, 56,2 % и 55,0 % циклов интерпретатора. Transfer остаётся на ярусе 18: его 66 235 циклов плюс 3 155 строк дайджеста дают 69 390 строк, что на 3 855 больше 65 535 яруса 16.

Около 60 % transfer на второй стадии (около 39 928 циклов) приходится на обвязку, общую для обоих образов: декодирование входа, проверку свидетелей хранилища, вызовы keccak256 из ABI и дайджест выхода. Никакая работа трансляции не опускается ниже этой границы.

Пошагово: ERC-20 от Solidity до совпадающих выходов

Каждая команда выполняется из корня checkout circuits на 7ef3220, с набором инструментов из Deploy a Rust program и clang 23.1.1 из Deploy a RISC-V program. Длинный вывод обрезан там, где стоит ….

0. Соберите инструменты

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

1. Скомпилируйте Solidity в runtime-байткод

Контракт — закоммиченный guests-compiled/evm/contracts/ERC20.sol: самодостаточный ERC-20 с раскладкой хранилища OpenZeppelin v4, pragma solidity 0.8.37. Используйте строго solc 0.8.37: другая версия оптимизирует иначе и даёт другой байткод.

релизv0.8.37 с github.com/ethereum/solidity/releases
ресурс для macOSsolc-macos, sha256 a27396e7732aa52e80ff89ad7bd8a2e46fec2a6dcc4ef20cd16e5e0c502d6821
ресурс для Linuxsolc-static-linux, из того же релиза
$ 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

Последняя строка — runtime-байткод, 1 296 байт. Он побайтово совпадает с закоммиченным guests-compiled/evm/contracts/erc20.runtime.hex; SOLC=$PWD/solc bash guests-compiled/evm/contracts/build.sh проверяет это и печатает contracts: erc20.runtime.hex reproduces.

Флаги важны:

  • --bin-runtime: развёрнутый код, а не creation-код.
  • --evm-version shanghai: цель по умолчанию для 0.8.37 порождает MCOPY, который вызывает ловушку.
  • --optimize --optimize-runs 200: профиль для развёрнутого контракта.

2. Транслируйте

$ 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 должен указывать внутрь checkout circuits. Без него evm2rv только анализирует код.
  • Любой опкод, вызывающий ловушку, был бы перечислен здесь как предупреждение с его pc.
  • Этот сеанс записан с --chain-id 12. В этом ERC-20 нет CHAINID, поэтому --chain-id не меняет его hc. Контракт, читающий CHAINID, требует этот флаг; задайте в нём цепочку, в которой будете развёртывать (--chain-id 19 для действующей цепочки).

contract.c заканчивается дайджестом охранной проверки кода:

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

3. Соберите образ

Лимит по умолчанию — 4 096 слов, поэтому передайте --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

Это опубликованный hc второй стадии. Имя crate на него не влияет.

4. Постройте входной вектор и запустите transfer

Пример erc20_vector печатает векторы теста на паритет в виде входных слов: transfer (ALICE отправляет BOB 250 из своих 1 000), approve (ALICE разрешает BOB потратить 5) и transferFrom (BOB переводит 100 из 1 000 ALICE при разрешении 300). Для собственного контракта постройте EvmCall на Rust тем же способом; пример служит шаблоном.

$ (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. Сравните с интерпретатором

Тот же вход на гостевом интерпретаторе:

$ 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

И интерпретатор, запущенный нативно на хосте, — оракул тестов:

$ (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

Проверка одной командой:

$ 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

Повторите шаги 4 и 5 для approve и transferFrom; оба дают идентичные слова. cd evm2rv && cargo +1.98.1 test --release запускает их и ещё пять векторов на обеих стадиях, а также проверяет gas_used (70 тестов и 2 проигнорированных доказательства).

транслированная (вторая стадия)интерпретатор (evm.bin)
восемь выходных словидентичныидентичны
циклы, transfer66 235121 638
уровень1818
слова программы11 68618 009

6. Развёртывание

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

Кошелёк выводит id программы, число слов и hc, затем проверяет лимит цепочки, прежде чем что-либо доказывать. Образ занимает 11 686 слов, поэтому нужна цепочка, в генезисе которой max_program_words не меньше 11 686. Цепочка 12 работала со значением по умолчанию 4 096, поэтому развёртывание там было отклонено. Цепочка 13, цепочка v0.4, задала max_program_words равным 65 535, и каждая последующая цепочка сохраняет это значение. Цепочка 21, действующая с 2026-10-12 (fullnode v0.8.0, генезис ca76c2766ed6090ac6ba5dad86b724dd5d2b80c0edfaed287cd8b129e663ca9a), также сохраняет лимиты конверта вызова и публичных входов цепочки 13, задаёт лимит доказательства 6 MiB (6 291 456 байт; цепочки с 17 по 20 задают 4 MiB, цепочки с 13 по 16 — 8 MiB) и лимит блока 19 MiB. Лимит на число слов — уже не единственный барьер. Генезис цепочки 21, как и цепочек 20 и 19, задаёт hardening_v6, и при нём развёртывание программы с большим числом слов, чем способен вместить вызов при лимите вызова уровня 14, недействительно (ProgramUncallable): не более 8 180 слов без публичного входа. Этот образ занимает 11 686 слов, поэтому цепочка 21 отклоняет развёртывание, а отклонила бы и вызовы: каждый вызов ERC-20 доказывается на уровне 16 или 18, выше лимита вызова уровня 14 (шаг 7). Агрегация блоков на цепочке 21 отключена (в её генезисе нет секции aggregation); её загрузочные узлы перечислены в Запуск валидатора. Комиссия за развёртывание составляет 0.001 + 0.0001 × 11,686 = 1,1696 RAND — именно такую комиссию заплатило развёртывание на цепочке 13 ниже.

Это развёртывание выполнено на цепочке 13 2026-09-19, пока она была действующей (боевой профиль FRI, генезис 8123ccac1883a45750e4df6964fb7cd3f0b321798cde4c0ef406a0293939ece3), с генезис-кошелька shielded-1. Каждая последующая цепочка (с 14 по 19) создавалась заново, без программ цепочки 13, и образ ни на одной из них не развёртывался повторно, поэтому приведённые ниже транзакция и высота относятся к цепочке 13 и в обозревателе больше не находятся. Id программы — хеш одних лишь base_pc и слов, поэтому один и тот же образ получает один и тот же id на любой цепочке, допускающей развёртывание. Вывод кошелька, строки прогресса опущены (…):

$ rand program deploy <image.bin>
program id: f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d (11686 words, hc 92aefea0951c31a717574962aeb70e10a37012a7d035d48e8713c36df68feae0)
…
proved in 92.2s: tier 14, 1419655 bytes
submitted deploy c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0
…
id программыf074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d
образ11 686 слов; hc a0feae92… в записи rand-guest, 92aefea0… в записи кошелька
пакет комиссии (пакет цепочки 13 с 2 входами и 2 выходами)уровень 14, 1 419 655 байт, доказан за 92,2 s
транзакция развёртывания (цепочка 13)c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0
зафиксирована на высоте (цепочка 13)381

Начиная с цепочки 14 оплачивающий пакет — это единый пакет со скрытыми активами с 4 входами и 4 выходами, который несёт каждая транзакция, доказанный относительно привязки транзакции. Это по-прежнему уровень 14: около 100 s и около 1,42 MB при боевом профиле на цепочках 14 и 15. Начиная с цепочки 17 пакет также несёт доказательство авторизации уровня 10 (разделённая авторизация), поэтому на цепочках 18 и 19 пакет комиссии транзакции — это около 1,49 MB доказательства пакета и около 1,37 MB доказательства авторизации.

7. Вызов

Вызов transfer несёт вектор из 921 слова как приватные входы, в пределах лимита входов любой цепочки (по умолчанию 4 295 слов, 16 071 на цепочках 20 и 21), и ни одного публичного слова. Его нельзя отправить в цепочку 21: программу там нельзя развернуть (шаг 6), transfer доказывается на уровне 18, а начиная с fullnode v0.5.6 (2026-09-25) узел отклоняет любое доказательство вызова выше уровня 14 с CallTierTooHigh ещё до построения ключа верификатора. Команда ниже доказывает локально, а цепочка отклоняет результат; измерения — это доказательства вне цепочки. rand call принимает каждое слово как повторяемый --input:

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

Теперь измерено. Доказательство уровня 18 было прервано на ноутбуке с 48 GB при 24,7 GB, но на дроплете со 128 GB (m-16vcpu-128gb) вызов транслированного transfer был доказан: пиковый RSS 85,0 GB, 3 230,5 s (53,8 min), доказательство 811 600 байт. Оно было верифицировано за 101,4 s на дроплете и за 50,2 s при 3,66 GB RSS на ноутбуке Apple silicon с 48 GB (память для доказательства). По плоской шкале уровней реестра — правилу цепочек без секции gas (до цепочки 17) — комиссия за вызов на уровне 18 составляла 0,0024 RAND. Цепочки 18 и 19 оценивают вызов по объявленному лимиту газа и по его размеру в байтах — 100 единиц за газ и 800 за KiB при генезисных ценах — и не допускают вызовов выше уровня 14.

Вызов на цепочке 13, 2026-09-19

Вызов approve(BOB, 5) (649 приватных слов, уровень 16) был выполнен на цепочке 13 2026-09-19, пока она была действующей, с боевым профилем FRI, из того же кошелька. На более поздних цепочках вызовов не делалось, поскольку программа там не развёрнута, а цепочка 21, как и цепочка 20, отклоняет это доказательство вызова уровня 16 (CallTierTooHigh). Вывод кошелька, строки прогресса опущены (…):

$ 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
…
доказательство вызовауровень 16, 3 412 405 байт, доказано за 388,7 s
пакет комиссии (пакет цепочки 13 с 2 входами и 2 выходами)уровень 14, 1 420 423 байт, доказан за 96,0 s
запечатанный конверт2 700 байт
транзакция вызова (цепочка 13)279e1f62abf3f177ca71b08c633e051c37bc4720118c79ddb00936f036c4bfa7
привязана к высоте / зафиксирована на высоте802 / 919
комиссия0,00357 RAND
квитанция h_pubnull
выходы квитанцииравны восьми словам интерпретатора для того же вектора
время работы, пиковый RSS622,8 s, 22,9 GB, на ноутбуке серии M с 48 GB
  • Боевое доказательство занимает 3 412 405 байт, что больше лимита доказательства цепочки 12 в 2 MiB. Цепочка 12 не могла принять этот вызов; лимит цепочки 13 в 8 MiB — смог. Цепочки с 14 по 16 сохранили тот же лимит, цепочки с 17 по 20 задали 4 MiB (4 194 304 байт), цепочка 21 задаёт 6 MiB — доказательство такого размера укладывается под каждый из них. Начиная с v0.5.6 узел отклоняет вызов по его уровню, не глядя на размер.
  • Комиссия включает байтовое слагаемое шкалы цепочки 13: доказательство в 3,41 MB превышает бесплатную квоту 2 MiB + 18 432 байт. Начиная с цепочки 18 каждый вызов оплачивается за KiB с нулевого байта.

Восемь слов квитанции — те же, что на шаге 4. Цепочка не хранит состояние EVM: вызывающий передаёт корень состояния до вызова и свидетельства хранилища, а корень состояния после вызова входит в дайджест EVM_OUT.

Лимиты

  • Нет контекста блока. Девять опкодов контекста блока завершаются трапом, пока не появится проект привязки к блоку.
  • Нет переводов value. CALL с ненулевым value завершается трапом. Вызовы контрактов, не являющихся прекомпайлами, также завершаются трапом.
  • ecrecover, умножение bn256, спаривание и большой modexp сегодня невозможно доказать. Каждая из этих операций превышает предел яруса в 2²⁰ строк; для них нужны таблицы сопроцессора.
  • Состояния EVM в цепочке нет. Вызов — это одно доказательство одного выполнения; состояние несёт вызывающая сторона.
  • Ни развёртывание, ни какой-либо вызов ERC-20 не допускаются в chain 21. При hardening_v6 в chain 21 развёртывание программы, содержащей больше слов, чем вмещает вызов яруса 14, недопустимо (ProgramUncallable; не более 8 180 слов), а образ занимает 11 686. Начиная с fullnode v0.5.6 доказательство вызова должно относиться к ярусу 14 или ниже (CallTierTooHigh); approve доказывается на ярусе 16, transfer и transferFrom — на ярусе 18. Та же проверка ограничивает таблицы хешей, которые может объявить вызов: 2¹² строк Keccak (128 перестановок) и 2¹³ строк SHA-256 (128 сжатий). Вызовы по-прежнему доказываются вне цепочки, как описано ниже.
  • Индекс хранилища имеет глубину 32 бита. evm-core помещает лист хранилища контракта по старшим 32 битам keccak256(slot), поэтому слот, коллидирующий с выбранным, обходится примерно в 2³² хешей Keccak, и пока он хранит ненулевое значение, выбранный слот заморожен (ни подделки, ни потери значения). Это не проявляется: evm.bin занимает 18 009 слов, а вызов на пределе яруса 14 вмещает не более 8 184, поэтому ни один рантайм EVM не может быть вызван ни в одной цепочке. Файл docs/confidential.md в fullnode фиксирует это условие рядом с пределом вызова (аудит v6, ZKV-6; в main с 2026-09-30, после тега v0.6.7): прежде чем цепочка снимет предел вызова яруса 14 или допустит рантайм EVM, достаточно малый для вызова, индекс хранилища необходимо углубить, а evm.bin перезакрепить.
  • Доказано. Доказательство перевода первой стадии (ярус 18) было завершено сигналом SIGKILL на ноутбуке с 48 ГБ при 24,7 ГБ через 1 016 с, но вызов transfer второй стадии был доказан на дроплете со 128 ГБ (m-16vcpu-128gb): пиковый RSS 85,0 ГБ, 3 230,5 с (53,8 мин), доказательство размером 811 600 байт, проверено за 101,4 с (дроплет) и за 50,2 с при 3,66 ГБ RSS (ноутбук с 48 ГБ). Доказательство перевода интерпретатора evm.bin дало близкий результат: 85,5 ГБ, 3 143,3 с (52,4 мин), доказательство размером 805 108 байт — и оба доказательства содержат идентичные публичные выходные слова, что подтверждает паритет на реальном доказывающем устройстве. approve (ярус 16, транслированный) обходится значительно дешевле: 21,7 ГБ, 786,7 с (13,1 мин), доказательство размером 798 930 байт. Трансляция снижает стоимость доказывания только тогда, когда переводит вызов в меньший ярус.
  • Цепочка не записывает байт-код. Опубликуйте его, чтобы другие могли пересобрать hc.

Последняя проверка по коммиту fullnode c0878c3f (chain 21) от 2026-10-12, а также 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. Если код и эта страница расходятся, прав код; сообщите нам.