FABRKNT
Reth Expert — 本番エンジニアリング
Reth ベースのチェーン — 拡張パターンを読む
レッスン 21 / 25·CONTENT18 分45 XP
コース
Reth Expert — 本番エンジニアリング
レッスンの役割
CONTENT
順序
21 / 25

レッスン20 — Custom executor(execution layer を差し替える)

問い

Executor は「tx を実行して post-state を生成するもの」。Ethereum mainnet では vanilla revm、Optimism では revm + deposit-tx 処理 + L1 cost 計算 + 異なる precompile。Reth がこの実行レイヤーをどう差し替えさせるか?

原理(最小モデル)

  • 3 trait の境界. ConfigureEvm(block context → 適切な precompile / gas schedule で revm 構成)+ BlockExecutionStrategy(block から tx 取り出し → revm 流し → receipt + state change 蓄積)+ ExecutorBuilder(NodeBuilder スロット)。
  • Optimism が override する 4 つ. Custom precompile リスト(L1 block hash アクセスなど)+ Deposit transaction 処理(署名検証スキップ)+ L1 cost 計算(calldata の L1 投稿コスト)+ Pre-execution hook(L1 block oracle slot 更新)。
  • Precompile は ConfigureEvm、L1 cost は実行戦略. Custom precompile = config、L1 cost = executor メインループ(precompile に収まらない consensus-critical ロジック)。
  • 配線. ChainSpec → [どの fork アクティブ?] → EVM config → [アクティブ precompile set] → revm。
  • Execution loop は mainnet + 2 行差. Deposit tx 判定 + L1 cost apply 以外は mainnet と同じ。

具体例

3 trait:

  • ConfigureEvm — block コンテキスト → revm インスタンス構成
  • BlockExecutionStrategy(または類似)— block から tx 取り出し → revm 流し → receipt + state change 蓄積
  • ExecutorBuilder — NodeBuilder スロット

Optimism の override:

Override理由
Custom precompile リストOP は L1 block hash アクセスなど追加
Deposit transaction 処理Deposit tx は署名検証スキップ(L1 で認証済み)
L1 cost 計算OP tx は L2 gas に加え L1 data cost 支払い
Pre-execution hookblock 内最初の tx 実行前、L1 block oracle slot 更新

配線:

ChainSpec  ──[どの fork がアクティブ?]──▶  EVM config  ──[アクティブな precompile set]──▶  revm

Execution loop 擬似コード:

for tx in block.body:
    if is_deposit_tx(tx) and current_fork.allows_deposits():
        skip_signature_verify()
    else:
        verify_signature(tx)?

    db = state_provider.load_relevant_accounts(tx)
    cfg = configure_evm(chainspec, block, db)   // precompile、gas schedule をセット
    result = revm.transact(cfg, tx)
    apply_l1_cost(tx, result, db)               // L2 固有
    state.commit(result.state_changes)
    receipts.push(result.receipt)
return post_state_root(state), receipts

Mainnet では 3 行目(deposit 判定)と 9 行目(L1 cost)が消えるだけ。他はまったく同じ

L1 cost 計算ステップ:

  1. 各 tx 実行前に既知の storage slot から L1 base fee + blob gas price を読む
  2. l1_cost = calldata_gas × l1_base_fee + blob_overhead を計算
  3. L2 gas 課金 に加えて 送信者残高から控除
  4. Fee vault に入金

Tempo(L1)で予測される差分:

  • "deposit tx" 概念なし(親 chain なし)
  • L1 cost 課金なし
  • ただし: 決済 primitives 用 custom precompile(FX、settlement attestation)+ Pre-execution hook(FX rate oracle slot)+ 異なる fee 市場構造(stablecoin-native)

失敗例(誤解)

「L1 cost charging を precompile で実装すれば speed が出る」— 間違い。precompile は「tx 実行中の特定 CALL に応じる」もの → tx 実行 に送信者残高から控除できない。L1 cost は実行戦略レベル → executor メインループ。

「Custom precompile は executor に直書きすれば良い」— 間違いConfigureEvm impl が revm に precompile セットを手渡す → ChainSpec の hardfork schedule で gate → 各 fork で正しい precompile set。直書きすると hardfork transition で壊れる。

「Deposit tx は通常 tx と同じ実行で良い」— 間違い。Deposit tx は L1 で既に認証済み(L1 コントラクトが認証)→ L2 側で署名検証する署名がない / 検証する必要なし。OP の deposit tx は署名フィールドが空または特殊値、署名検証スキップが必須。

🛑 予測。 OP の L1 cost charging はなぜ precompile として実装できないのか? 「performance のため」だけなら掘り下げが足りない — precompile が tx 実行前に任意のアカウントから控除できない consensus 上の理由 は?(答え: precompile は tx 実行中の特定 CALL に応じる純粋関数 → 任意のアカウント残高を tx 実行 に控除する権限がない(既存の precompile アドレス 0x01-0x0a も全部入出力ベースで、ホスト state を勝手に書き換えない)。L1 cost は ① tx 実行前、② 送信者残高から、③ block 内全 tx に適用 → これは executor の責務、Yellow Paper の framework 外。consensus-critical ロジックは executor に住む — precompile では tx-scoped、executor は block-scoped。)

ステップで組み立てる

Step 1: 3 trait 境界を即答

ConfigureEvm / BlockExecutionStrategy / ExecutorBuilder

Step 2: Optimism の 4 override

Custom precompile / Deposit tx / L1 cost / Pre-execution hook。

Step 3: Precompile vs Executor の判断軸

「tx-scoped 入出力か」= precompile / 「block-scoped、tx 実行前後、ホスト state 任意書き換え」= executor。

Step 4: Execution loop 擬似コード暗唱

mainnet との差分 2 行(deposit 判定 + L1 cost apply)= extension model の最小差分

Step 5: Tempo の予測差分

L1 なので deposit / L1 cost / L1 block oracle なし、代わりに決済 primitives + FX oracle + stablecoin fee 市場。

答え合わせ

  • L1 cost が executor のみに住む理由: precompile = tx 実行中の特定 CALL に応じる純粋関数(入出力)、executor = block 全体の制御(tx 実行前後、ホスト state 書き換え)。L1 cost は ① 全 tx に適用 + ② tx 実行前に控除 + ③ ホスト state 書き換え → 3 つとも precompile の責務外。
  • Custom precompile が ChainSpec を経由する理由: 各 fork で異なる precompile set が必要(hardfork で precompile 追加 / 削除)→ EVM config が単独で hardfork 状態を知らないと正しい precompile set を渡せない → ChainSpec が「現 block での fork 状態」を提供 → EVM config が「fork に対応する precompile set」を選ぶ → revm が受け取る。
  • mainnet と OP のメインループ差分: 3 行目(deposit tx 判定 + 署名検証スキップ)+ 9 行目(apply_l1_cost)の 2 行だけ。他のすべて(state load / configure_evm / transact / state.commit / receipts.push)は mainnet と同じ。extension model の最小差分 = consensus 互換性 + コード共有率最大

合格基準

  • 3 trait(ConfigureEvm / BlockExecutionStrategy / ExecutorBuilder)を即答できる。
  • Optimism の 4 override を即答できる。
  • Precompile vs Executor の判断軸を言える。
  • Execution loop の mainnet との 2 行差分を言える。
  • L1 cost が executor のみに住む理由を 3 つ言える。

まとheme(3行)

  • 3 trait(ConfigureEvm / BlockExecutionStrategy / ExecutorBuilder)が execution layer の差し替え API、ChainSpec → EVM config → revm の配線。
  • Optimism の 4 override(Custom precompile / Deposit tx / L1 cost / Pre-execution hook)= mainnet との最小差分(2 行)。
  • Precompile = tx-scoped 純粋関数、Executor = block-scoped、ホスト state 任意書き換え可 — L1 cost は executor のみ。