FABRKNT
Reth Expert — 本番エンジニアリング
本番エンジニアリング
レッスン 12 / 25·CONTENT26 分60 XP
コース
Reth Expert — 本番エンジニアリング
レッスンの役割
CONTENT
順序
12 / 25

レッスン11 — Differential fuzzing と execution-spec-tests

問い

フォークを ship した。カスタム precompile、独自 payload builder、いじったガススケジュール。ユニットテストは通っている、バニラ Reth diff テストも通る。だが、誰もテストを書かなかったパスに潜むバグはどう見つけるか?

原理(最小モデル)

  • 2 方向の自動圧力. EEST(execution-spec-tests)が 仕様に対する準拠 を構造的に保証 + Differential fuzzing が 誰も探さなかったバグ を浮かび上がらせる。
  • EEST はフォーク CI の毎 push. 昨日からの fail デルタが非ゼロなら build break。Triage: 意図的(document)/ 非意図的(バグ)。
  • Differential fuzz 4 リファレンス. vanilla Revm(Revm セマンティクス)/ Geth debug_traceTransaction(mainnet コンセンサス)/ Erigon(系譜逸脱検知)/ 形式仕様(Python EELS)。
  • 24-48 時間走らせる + 縮約. Foundry スタイルの shrinking で 50-200 byte の最小再現に → 人間が repro を読んで根本原因特定。
  • 両規律は補完であり代替ではない. EEST = 仕様明示範囲、fuzz = 仕様未定義パス含む。両方が production L1 チーム(Hyperliquid / Tempo / Berachain)の標準。
  • Fault injection(chaos の前駆体). 入力ではなく 環境(DB read 失敗、ネットワーク分断、部分書き込み、OOM)を fuzz → 「書き込み中 crash でチェーン壊れる」クラス。

具体例

EEST を自分のフォークに走らせる:

# spec-tests フレームワークを clone
git clone https://github.com/ethereum/execution-spec-tests
cd execution-spec-tests
uv sync

# フォークの spec-test runner バイナリをビルド(各フォークが自前同梱; revm は `revme`)
cargo build --release -p revme

# スイートをバイナリに対して実行
uv run consume direct \
  --bin /path/to/your/fork/target/release/revme \
  -- ./tests/cancun/      # または任意のサブセット

Differential fuzz パターン:

ランダム tx → [自前フォーク] → state_root_A
                       ↓
              [リファレンス実装] → state_root_B

assert(state_root_A == state_root_B)

Fuzz harness 例:

// tests/differential_fuzz.rs
use libafl::prelude::*;       // または proptest、arbitrary、独自 harness

fn fuzz_target(input: &[u8]) -> Result<()> {
    let tx = arbitrary_tx_from_bytes(input)?;
    let pre = arbitrary_pre_state_from_bytes(input)?;

    let your_root = your_fork_execute(pre.clone(), tx.clone())?.state_root;
    let ref_root  = reference_execute(pre, tx)?.state_root;

    if your_root != ref_root {
        return Err(format!("DIFFERENTIAL: your={your_root}, ref={ref_root}").into());
    }
    Ok(())
}

3 ツール役割表:

ツール捕まえる捕まえない
EEST仕様が明示的カバーするケースの回帰仕様未定義挙動のバグ、フォーク固有エッジケース
Differential fuzzingリファレンスからの乖離(仕様未定義パス含む)リファレンスとフォーク両方間違っている仕様違反
両方併用コンセンサスクリティカルバグの広範囲仕様黙 + リファレンスなし + fuzzer 未到達の稀バグ

失敗例(誤解)

「EEST が通れば fuzz 不要」— 間違い。EEST は仕様明示範囲だけ。仕様が黙っているパス(カスタム precompile の境界条件、新 opcode 組み合わせ)は fuzz が浮かび上がらせる。

「Differential fuzz は vanilla Revm に対してだけ」— 不十分。Revm vs Geth の対比で Revm 系全体のバグ(系譜共有バグ)を炙れる。Erigon や EELS(Python 仕様)との対比で更に層を厚く。

「fuzz 24 時間で出ないなら問題なし」— 間違い。Fuzzer の入力空間カバレッジ依存。crash 0 = バグなしではない。フォーク誕生から半年が hit 率最高期 = この期間は重点的に。

🛑 予測。 vanilla Revm は何年も fuzz されている、なぜフォークで fuzz が特に有用?(答え: バニラ EVM のバグはほぼ新機能の未テスト組み合わせに残る。あなたのフォークは 新機能そのもの → fuzz hit 率が最も高い時期。同じ予算でも、vanilla を 1 ヶ月 fuzz する vs フォーク半年目を 1 週間 fuzz する → 後者の発見密度が桁違いに高い。)

ステップで組み立てる

Step 1: EEST を 1 回走らせる

uv run consume direct --bin revme -- ./tests/cancun/ → pass/fail/skip 数 → 昨日との delta → 非ゼロは build break。

Step 2: 簡単な fuzz harness を書く

proptestlibafl で「ランダム tx + pre-state → 両実装で実行 → state_root assert」の 50 行。1 時間走らせて乖離をログ。

Step 3: 4 リファレンスから選ぶ

  • vanilla Revm: Revm 系譜のバグ検知
  • Geth: mainnet コンセンサス検知
  • Erigon: 系譜逸脱検知
  • 形式仕様(Python EELS): 仕様直接対比

Step 4: 縮約規律

shrinking で 50-200 byte の最小 repro に → 人間が読んで根本原因特定。24-48 時間 fuzz + shrink + 人間 triage が標準。

Step 5: フォーク CI マトリクス

毎 push: EEST サブセット(5 分以内)+ 短い fuzz(5 分)+ diff against vanilla Reth(1000 ブロック)。週次: 24h fuzz + 全 EEST + マルチリファレンス diff。

Step 6: Fault injection で chaos の前段

入力ではなく環境 fuzz: kill -9 実行中プロセス、MDBX random page 破壊、ネットワーク分断、OOM 状況。Reth 自身の CI はここまでやらない、production フォークチームが追加。

答え合わせ

  • EEST と fuzz の冗長性ではない理由: EEST は仕様に対してチェック、fuzz は別実装に対してチェック — 仕様が制約しないパスも含めて。仕様が「opcode X の入力 A での挙動は未定義」だと EEST はテストを書けない。fuzz は無作為に A を生成して別実装と比較する → 仕様未定義領域で乖離があれば検知。
  • fuzz hit 率が「フォーク誕生から半年」で最高な理由: 新機能は未テスト組み合わせの空間が広い + 設計者の頭の中だけのテスト依存 → 想像外の入力で破綻する。時間が経つほどユーザが trigger する → 公開バグになる → 残りは fuzz が掘る相手が少なくなる。
  • Fault injection が fuzzing と区別される領域: fuzzing = 入力空間探索(「どの tx で壊れるか」)、fault injection = 環境探索(「どの障害で安全性プロパティが破られるか」)。「書き込み中に crash してチェーンが壊れた」クラスは fuzzing には現れない(入力は正常)+ 環境が異常。

合格基準

  • EEST と differential fuzzing が補完である理由を 1 文で言える。
  • 4 リファレンス実装の選び分けを言える。
  • 簡単な fuzz harness(proptest で 50 行)を書ける。
  • フォーク CI マトリクス(毎 push / 週次)を設計できる。
  • Fault injection が fuzz と違う領域を捕まえる理由を言える。

まとめ(3行)

  • EEST(仕様準拠)+ Differential fuzzing(リファレンス対比)が production フォークの正しさ保証 2 本柱、両方が毎 CI 必須。
  • Fuzz は 24-48h 走らせて shrink、フォーク誕生から半年が hit 率最高、4 リファレンスで層を厚く。
  • Fault injection(環境 fuzz)が chaos engineering の前段、「正常入力 + 環境異常」クラスのバグを捕まえる。