evm2rv は、solc --bin-runtime の出力である EVM ランタイムバイトコードを受け取り、Rand プログラムに変換します。出力は C で、rand-guest build がその C を RV32IM イメージにコンパイルします。このイメージは、Rand の EVM インタープリタゲスト evm.bin と同じ入力ベクトルを受け取り、同じ 8 個の出力ワードを公開します。コントラクトをオペコードごとに解釈するのではなく、ネイティブに実行します。このページでは、ERC-20 を例に全体の流れを示します。
概要
| 入力 | EVM ランタイムバイトコード。16 進テキスト(.hex)または生のバイト列 |
| 出力 | C のシム crate。rand-guest build でイメージコンテナにします |
| 実行場所 | オフチェーン、開発者のマシン上。チェーンが見るのはイメージだけです |
| 一致が必要なもの | evm.bin のステータス、8 個の出力ワード、gas_used |
| リポジトリ | circuits リポジトリの evm2rv/ |
| 固定されたツールチェーン | Rust 1.98.1、clang 23.1.1、cc crate 1.4.6 |
トランスパイラが受け取るのは、作成コードではなくランタイムコードです。証明の対象はデプロイ済みコントラクトへの 1 回の呼び出しであり、コンストラクタは実行されません。コンストラクタが書き込むはずの状態は、代わりにストレージウィットネスとして事前状態に含まれます。
変換には 2 つのステージがあります。どちらも結果とガスは同じで、それぞれが独自の hc を持つ別のプログラムです。
| ステージ | フラグ | 内容 |
|---|---|---|
| 1 | --stage 1 | すべてのオペコードをメモリ上のスタックで処理します |
| 2 | --stage 2(デフォルト) | 各ブロックのスタックワードを C のローカル変数に置き、定数を変換時に畳み込みます |
入力ベクトルはインタープリタのものと同じです。コード、calldata、呼び出し元などの環境ワード、ガスリミット、事前状態ルート、そして呼び出しが触れるスロットごとに 1 つのストレージウィットネスで構成されます。出力は out[0] のステータス(1 が成功、0 がリバート、2 が例外的停止)と、out[1..8] の EVM_OUT ダイジェストです。このダイジェストは、コードハッシュ、両方の状態ルート、返却データ、ログにわたります。
信頼モデル
検証者は、変換済みコントラクトを 2 段階で確認します。
hc == translate(bytecode)。 公開されたバイトコードに対して、同じ--stageと--chain-idでevm2rvを再実行し、固定されたツールチェーンで再ビルドしてhcを比較します。生成される crate はccを正確なバージョンに固定し、独自のCargo.lockを持ちます。- コードガードが
hcをバイトコードに結び付けます。 変換されたロジックはイメージにコンパイルされますが、CODECOPYとCODESIZEは入力ベクトル内のコードを読みます。チェックがなければ、1 つのhcが、呼び出し側の選んだコードバイトで実行されうることになります。そのためcontract.cには元のバイトコード(その長さとバイト列)の Poseidon2 ダイジェストが含まれ、シムは変換済みコードを実行する前に、入力ベクトルのコードを同じ方法でハッシュします。それ以外のコードは拒否されます。ステータスは 2、gas_usedは 0、停止理由はOutOfBoundsです。
EVM_OUT ダイジェストは、インタープリタのものと同様に、コードの keccak256 にも結び付いています。チェーンは元のバイトコードもそのハッシュも記録しません。他者が hc を確認できるようにするには、バイトコード、または Solidity ソースと正確な solc ビルドを公開してください。
コードガードのコストは、transfer で 1,008 サイクルです。ソースと 1 バイトでも異なるコードは、ステージ 2 で 30,418 サイクルで拒否されます(ティア 16)。
対応範囲とトラップ
オペコード。 Shanghai のセットです。--evm-version shanghai でコンパイルしてください。より新しいターゲットは MCOPY などの Cancun のオペコードを出力し、これらはトラップします。
環境オペコード:
| オペコード | 変換 |
|---|---|
ADDRESS, CALLER, CALLVALUE, CALLDATASIZE, CODESIZE | インタープリタと同様に、入力ベクトルから読みます |
CHAINID | --chain-id の値を変換時に埋め込むため、hc がそれに結び付きます。コードに CHAINID が含まれる場合は必須です |
ORIGIN | CALLER と等しくなります(呼び出しは 1 回で、リレイヤーはありません) |
GASPRICE, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, SELFBALANCE, BASEFEE, BLOCKHASH | インタープリタと同じくトラップします(ステータス 2) |
9 個のブロックコンテキストオペコードは、ブロックに結び付ける設計ができるまでトラップします。非公開入力はブロックの事実を運べません。検証者が H_IN を開けないためです。この判断が下るまで、時刻やブロック番号を読むコントラクト(たとえば permit の期限)はトラップします。
呼び出し。 CALL、CALLCODE、DELEGATECALL、STATICCALL は、宛先がアドレス 1〜9 のときにプリコンパイルを実行します。それ以外の宛先はトラップします。CALL または CALLCODE で 0 でない value を指定するとトラップします。 残高モデルがないためです。
プリコンパイル。 9 つすべてがソフトウェアで動作します。1 つの証明が保持できる行数は最大 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 の base と modulus は、それぞれ 1,024 バイトが上限です。
許容される差異。 ステータス、8 個のワード、gas_used は一致しなければなりません。次の 3 つの差異は許容されます。停止の種類が異なる場合があること(静的ガスは各ブロックの先頭で課金されるため、OutOfGas と別の停止が入れ替わることがあります。どちらもステータスは 2 で、ガスはすべて消費されます)。変換が CHAINID、ORIGIN、プリコンパイル呼び出しを実装していること(インタープリタはこれらをトラップします)。そしてコードガードが、ソース以外のコードを拒否することです。
計測値
コードガードを含めた ERC-20 の呼び出しです。サイクルは rand-guest run の値で、括弧内はティアです。
| 呼び出し | インタープリタ(evm.bin) | ステージ 1 | ステージ 2(デフォルト) |
|---|---|---|---|
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、ステージ 1 | 15,804 | 9cdb79e7…2fd048c8 |
| ERC-20、ステージ 2 | 11,686 | a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6 |
ステージ 2 は、3 つの呼び出しをインタープリタのサイクル数のそれぞれ 54.5%、56.2%、55.0% で実行します。transfer はティア 18 のままです。66,235 サイクルに 3,155 行のダイジェストを加えると 69,390 行となり、ティア 16 の 65,535 を 3,855 行超えます。
ステージ 2 の transfer のおよそ 60%(約 39,928 サイクル)は、両方のイメージが共有するハーネスです。入力のデコード、ストレージウィットネスの検証、ABI の keccak256 呼び出し、出力ダイジェストがそれにあたります。どんな変換作業も、この下限より下には進めません。
ウォークスルー: ERC-20 を Solidity から一致する出力まで
すべてのコマンドは、7ef3220 の circuits チェックアウトのルートから実行します。ツールチェーンは Rust プログラムのデプロイのもの、clang 23.1.1 は RISC-V プログラムのデプロイのものを使います。長い出力は … の箇所で省略しています。
0. ツールをビルドする
(cd evm2rv && cargo +1.98.1 build --release)
(cd rand-guest && cargo +1.98.1 build --release)
1. Solidity をランタイムバイトコードにコンパイルする
コントラクトは、コミット済みの guests-compiled/evm/contracts/ERC20.sol です。OpenZeppelin v4 のストレージレイアウトを持つ自己完結型の ERC-20 で、pragma solidity 0.8.37 を使います。solc は必ず 0.8.37 を使ってください。別のバージョンでは最適化が異なり、バイトコードも変わります。
| リリース | github.com/ethereum/solidity/releases の v0.8.37 |
| 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
最後の行がランタイムバイトコードで、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: 作成コードではなく、デプロイされるコードを出力します。--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は 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
これが公開されているステージ 2 の hc です。crate 名はこれに影響しません。
4. 入力ベクトルを作成して transfer を実行する
erc20_vector の例は、パリティテストのベクトルを入力ワードとして出力します。transfer(ALICE が 1,000 のうち 250 を BOB に送る)、approve(ALICE が BOB に 5 の使用を許可する)、transferFrom(BOB が、許可額 300 のもとで ALICE の 1,000 のうち 100 を移す)です。ご自身のコントラクトでは、Rust で同じように EvmCall を組み立ててください。この例がテンプレートになります。
$ (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
チェックを1つのコマンドにまとめると次のとおりです。
$ 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 は、これらに加えて5つのベクターを両方のステージで実行し、gas_used も検証します(70件のテストと、無視される2件の証明)。
| 変換後(ステージ2) | インタープリタ(evm.bin) | |
|---|---|---|
| 8個の出力ワード | 同一 | 同一 |
| サイクル数(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 で動いていたため、そこではデプロイが拒否されました。v0.4 のチェーンであるチェーン13は max_program_words を 65,535 に設定し、以降のすべてのチェーンがこの値を維持しています。稼働中のチェーンであるチェーン20(ジェネシス 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135)も、チェーン13のブロック、コールエンベロープ、パブリック入力の上限を維持し、証明の上限を 4 MiB(4,194,304 バイト。チェーン17以降。チェーン13から16は 8 MiB)に半減しています。ワード数の上限だけが関門ではなくなりました。チェーン20のジェネシスは hardening_v6 を設定しており、これにより、ティア14のコール上限で1回のコールが保持できるプログラムワード数を超えるデプロイは無効になります(ProgramUncallable)。パブリック入力がない場合、上限は 8,180 ワードです。このイメージは 11,686 ワードであるため、チェーン20はデプロイを拒否し、コールも拒否します。ERC-20 のコールはいずれもティア16または18で証明され、ティア14のコール上限を超えるためです(手順7)。チェーン20ではブロックアグリゲーションは無効です(ジェネシスに aggregation セクションがありません)。ブートストラップノードは Running a validator に記載されています。デプロイ手数料は 0.001 + 0.0001 × 11,686 = 1.1696 RAND で、これは下記のチェーン13でのデプロイで支払われた手数料です。
このデプロイは、チェーン13が稼働中のチェーンであった 2026-09-19 に、チェーン13(本番の 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 ワード。rand-guest の表記では hc a0feae92…、ウォレットの表記では 92aefea0… |
| 手数料バンドル(チェーン13の 2-in-2-out バンドル) | ティア14、1,419,655 バイト、92.2 秒で証明 |
| デプロイ tx(チェーン13) | c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0 |
| コミット時の高さ(チェーン13) | 381 |
チェーン14以降、手数料を支払うバンドルは、すべてのトランザクションが持つ 4-in-4-out の隠蔽アセットバンドル1つであり、トランザクションのバインディングに対して証明されます。ティアは引き続き14です。チェーン14と15の本番プロファイルでは、約100秒、約 1.42 MB です。チェーン17以降はバンドルに ティア10の認可証明(分割認可)も付くため、チェーン18と19では、トランザクションの手数料バンドルはバンドル証明が約 1.49 MB、認可証明が約 1.37 MB になります。
7. コール
transfer のコールは、921ワードのベクターをプライベート入力として持ち、どのチェーンでも入力上限の範囲内に収まり(デフォルトでは 4,295 ワード、チェーン20では 16,071 ワード)、パブリックワードはありません。チェーン20には送信できません。 プログラムをそこにデプロイできず(手順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 秒(53.8分)、証明サイズは 811,600 バイトです。検証にはドロップレットで 101.4 秒、48 GB の Apple シリコン搭載ラップトップで 3.66 GB RSS、50.2 秒かかりました(証明のメモリ)。gas セクションのないチェーン(チェーン17まで)の規則であるレジャーの固定ティアスケジュールでは、ティア18のコール手数料は 0.0024 RAND でした。チェーン18と19は、コールを、宣言されたガス上限とそのバイト数で価格付けします。ジェネシス価格では、ガス1単位あたり 100、KiB あたり 800 です。また、ティア14を超えるコールは受け付けません。
チェーン13でのコール(2026-09-19)
approve(BOB, 5) のコール(649プライベートワード、ティア16)は、チェーン13が稼働中のチェーン(本番 FRI プロファイル)であった 2026-09-19 に、同じウォレットから実行されました。それ以降のチェーンではコールは行われておらず、プログラムもデプロイされていません。チェーン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 秒で証明 |
| 手数料バンドル(チェーン13の 2-in-2-out バンドル) | ティア14、1,420,423 バイト、96.0 秒で証明 |
| シールされたエンベロープ | 2,700 バイト |
| コール tx(チェーン13) | 279e1f62abf3f177ca71b08c633e051c37bc4720118c79ddb00936f036c4bfa7 |
| アンカー時 / コミット時 | 高さ 802 / 高さ 919 |
| 手数料 | 0.00357 RAND |
レシート h_pub | null |
| レシート出力 | 同じベクターに対するインタープリタの8ワードと等しい |
| 経過時間、ピーク RSS | 622.8 秒、22.9 GB、48 GB の M シリーズ搭載ラップトップ上 |
- 本番の証明は 3,412,405 バイトで、チェーン12の 2 MiB の証明上限を超えています。チェーン12はこのコールを受け付けられませんでしたが、チェーン13の 8 MiB の上限では受け付けられました。チェーン14から16も同じ上限を維持し、チェーン17から20は 4 MiB(4,194,304 バイト)に設定しましたが、この大きさの証明はそれでも収まります。v0.5.6 以降、ノードはサイズを確認する前に、ティアを理由にこのコールを拒否します。
- 手数料には、チェーン13のスケジュールのバイト項が含まれます。3.41 MB の証明は、2 MiB + 18,432 バイトの無料枠を超えています。チェーン18以降、すべてのコールは 0 バイト目から KiB 単位で支払います。
レシートの8ワードは手順4のものです。チェーンは EVM の状態を保持しません。呼び出し側が事前状態ルートとストレージウィットネスを提供し、事後状態ルートは EVM_OUT ダイジェストの中にあります。
Limits
- ブロックコンテキストなし。 ブロックコンテキストを扱う9つのオペコードは、ブロックへの結び付けの設計が定まるまでトラップします。
- 価値の移転なし。 値がゼロでない
CALLはトラップします。プリコンパイル以外のコントラクトへの呼び出しもトラップします。 - ecrecover、bn256 の乗算、ペアリング、大きな modexp は現時点では証明できません。 いずれも 2²⁰ 行のティア上限を超えるため、コプロセッサテーブルが必要です。
- チェーン上に EVM の状態はありません。 呼び出しは1回の実行に対する1つの証明であり、状態は呼び出し側が保持します。
- デプロイも ERC-20 の呼び出しも、chain 20 ではオンチェーンで受理されません。 chain 20 の
hardening_v6では、tier 14 の呼び出しが保持できる語数を超えるデプロイは無効であり(ProgramUncallable。最大 8,180 語)、イメージは 11,686 語です。fullnode v0.5.6 以降、呼び出しの証明は tier 14 以下でなければなりません(CallTierTooHigh)。approveは tier 16、transferとtransferFromは tier 18 で証明されます。同じチェックにより、呼び出しが宣言できるハッシュテーブルにも上限があります。Keccak は 2¹² 行(128 回の置換)、SHA-256 は 2¹³ 行(128 回の圧縮)です。呼び出し自体は、以下のとおりオフチェーンでなら引き続き証明できます。 - ストレージインデックスの深さは32ビットです。
evm-coreは、コントラクトのストレージのリーフをkeccak256(slot)の上位32ビットで配置します。そのため、選ばれたスロットと衝突するスロットを見つけるには約 2³² 回の Keccak ハッシュが必要で、衝突したスロットが非ゼロの値を保持している間は、選ばれたスロットが凍結されます(偽造も値の消失も起きません)。これは現在は休眠状態です。evm.binは 18,009 語であり、tier 14 の上限での呼び出しが保持できるのは最大 8,184 語のため、どのチェーンでも呼び出せる EVM ランタイムはありません。fullnode のdocs/confidential.mdには、この前提条件が呼び出し上限の隣に記録されています(監査 v6、ZKV-6。v0.6.7 タグの後、2026-09-30 からmainに反映)。チェーンが tier 14 の呼び出し上限を引き上げる前、または呼び出せるほど小さい EVM ランタイムを受け入れる前に、ストレージインデックスを深くし、evm.binを再固定しなければなりません。 - 実証済み。 ステージ1の transfer 証明(tier 18)は、48 GB のノート PC では 1,016 秒後、24.7 GB の時点で SIGKILL されました。一方、ステージ2の
transfer呼び出しは 128 GB の droplet(m-16vcpu-128gb)で証明に成功しました。ピーク RSS は 85.0 GB、所要時間は 3,230.5 秒(53.8 分)、証明は 811,600 バイトで、検証には 101.4 秒(droplet)、50.2 秒・RSS 3.66 GB(48 GB のノート PC)かかりました。インタプリタのevm.binによる transfer 証明もほぼ同じ結果でした。85.5 GB、3,143.3 秒(52.4 分)、805,108 バイトの証明です。両者の証明は公開出力ワードが同一であり、実際のプルーバーでの同等性が確認されました。approve(tier 16、変換済み)ははるかに低コストです。21.7 GB、786.7 秒(13.1 分)、798,930 バイトの証明です。変換によって証明コストが下がるのは、呼び出しがより小さいティアに収まる場合に限られます。 - チェーンはバイトコードを記録しません。
hcを他の人が再構築できるよう、バイトコードを公開してください。