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 по хэшу кода, обоим корням состояния, возвращаемым данным и логам.
Модель доверия
Верификатор проверяет транслированный контракт в два шага.
hc == translate(bytecode). Заново запуститеevm2rvна опубликованном байткоде с теми же--stageи--chain-id, пересоберите на зафиксированном наборе инструментов и сравнитеhc. Сгенерированный crate жёстко фиксируетccи содержит собственныйCargo.lock.- Охранная проверка кода связывает
hcс байткодом. Транслированная логика компилируется в образ, ноCODECOPYиCODESIZEчитают код из входного вектора. Без проверки один и тот жеhcмог бы исполняться с байтами кода, выбранными вызывающим. Поэтомуcontract.cсодержит дайджест Poseidon2 исходного байткода (его длины и байтов), а прослойка хэширует код из входного вектора тем же способом до запуска любого транслированного кода. Другой код отклоняется: статус 2,gas_used0, остановка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 ecrecover | ValidKey | 14 510 525 | нет |
| 2 sha256 | ”abc” / 200 байт | 2 791 / 5 289 | да |
| 3 ripemd160 | ”abc” | 12 195 | да |
| 4 identity | 100 байт | 4 498 | да |
| 5 modexp | nagydani-1-square / -1-pow0x10001 | 122 332 / 874 548 | да |
| 5 modexp | eip_example1 (256 бит) | 11 170 118 | нет |
| 5 modexp | nagydani-5-pow0x10001 (1 024 байта) | 55 076 950 | нет |
| 6 bn256 add | chfast1 | 978 262 | да, едва |
| 7 bn256 mul | chfast1 | 3 060 669 | нет |
| 8 bn256 pairing | 1 пара / 2 пары | 1 853 656 212 / 2 019 000 469 | нет |
| 9 blake2f | 12 раундов | 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 009 | 7e1aea2b…854c08 |
| ERC-20, первая стадия | 15 804 | 9cdb79e7…2fd048c8 |
| ERC-20, вторая стадия | 11 686 | a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6 |
Вторая стадия выполняет три вызова за 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 |
| ресурс для macOS | solc-macos, sha256 a27396e7732aa52e80ff89ad7bd8a2e46fec2a6dcc4ef20cd16e5e0c502d6821 |
| ресурс для Linux | solc-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) | |
|---|---|---|
| восемь выходных слов | идентичны | идентичны |
| циклы, transfer | 66 235 | 121 638 |
| уровень | 18 | 18 |
| слова программы | 11 686 | 18 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_pub | null |
| выходы квитанции | равны восьми словам интерпретатора для того же вектора |
| время работы, пиковый RSS | 622,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.