FABRKNT
Consensus Engineering — Reth で L1 のコンセンサスを作る
実コンセンサスコードを読む
レッスン 5 / 12·CONTENT18 分45 XP
コース
Consensus Engineering — Reth で L1 のコンセンサスを作る
レッスンの役割
CONTENT
順序
5 / 12

レッスン4 — Reth の Consensus trait を読む

問い

Reth のソースを開く。「PoS」で検索する。ほとんど何も出てこない — なぜなら Reth は PoS も BFT もまったく実装していないから。その仕事は consensus client(Lighthouse、Prysm、自前エンジン)側にある。では「Hyperliquid は Reth で動く」「Berachain は PoL のために Reth を fork した」と言うとき、実際に触っているコンセンサスの接合面は何か?

原理(最小モデル)

  • 役割分担: head 選択(CL) vs ブロック検証(EL = Reth). Reth の Consensus trait は chain head を選ばない。受け取ったブロックを 検証する だけ。
  • 3 フェーズの検証. validate_block_pre_execution(安いチェック、EVM 不要)→ validate_body_against_header(暗号的整合性)→ validate_block_post_execution(state root、ガス、receipt — コンセンサスクリティカル)。
  • HeaderValidator が親 trait. Header の検証は body の検証から分離されている — header は先に到着、body なしで検証可能、ライトクライアントは header だけでよい。
  • BFT 固有のチェックは HeaderValidator に入る. 「この proposer はこの slot の選出リーダーか?」「proposer 署名は valid か?」など。

具体例

crates/consensus/consensus/src/lib.rs の中核 trait:

#[auto_impl::auto_impl(&, Arc)]
pub trait Consensus<B: Block>: HeaderValidator<B::Header> {
    type Error;

    fn validate_body_against_header(
        &self,
        body: &B::Body,
        header: &SealedHeader<B::Header>,
    ) -> Result<(), Self::Error>;

    fn validate_block_pre_execution(
        &self,
        block: &SealedBlock<B>,
    ) -> Result<(), Self::Error>;
}

pub trait FullConsensus<N: NodePrimitives>: Consensus<N::Block> {
    fn validate_block_post_execution(
        &self,
        block: &RecoveredBlock<N::Block>,
        result: &BlockExecutionResult<N::Receipt>,
    ) -> Result<(), ConsensusError>;
}

HeaderValidator:

pub trait HeaderValidator<H>: Send + Sync + Debug {
    fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError>;

    fn validate_header_against_parent(
        &self,
        header: &SealedHeader<H>,
        parent: &SealedHeader<H>,
    ) -> Result<(), ConsensusError>;

    fn validate_header_with_total_difficulty(
        &self,
        header: &H,
        total_difficulty: U256,
    ) -> Result<(), ConsensusError>;
}

3 フェーズ × 各フェーズで見るもの:

フェーズチェック内容コスト
pre_executiontx root / receipt root / block hash / size / timestamp<1ms
body_against_headertransactions root == merkle(tx)、withdrawals root暗号的、明確
post_executiongas used / state root / receipts root / logs bloomコンセンサスクリティカル

失敗例(誤解)

「Reth は PoS を実装している」— 間違い。Reth は EL(execution layer)。PoS(投票、attestation、proposer 選出)は CL(consensus client)の仕事。Reth は受け取ったブロックを検証するだけで、誰が head かを選ばない。

「state root チェックは pre-execution で十分」— 間違い。post-execution が知っている = pre-execution が知らない = 「実際に EVM を回した結果の post-state」。state root は post-execution でしか検証できない。

🛑 予測。 Reth はコンセンサスレイヤから来るブロックを検証する必要がある。EVM が tx を実行する前に、どんなチェックが走るべきか? 4 つ列挙。(答え: ① 暗号的 = tx root / receipt root が header と一致、② 構造的 = block hash が well-formed、③ 時間的 = timestamp が未来に行き過ぎていない、④ コンセンサス固有 = proposer がアクティブな validator set にいるか、署名は有効か、round 番号は正しいか。)

ステップで組み立てる

Step 1: 3 フェーズを実行順で覚える

pre_execution → body_against_header → post_execution。trait の宣言順ではなく ランタイムで走る順序。

Step 2: デフォルト実装 EthBeaconConsensus を読む

crates/ethereum/consensus を開く。チェック内容:

  • ブロックが正しい形であること
  • 実行後の state root が一致すること
  • Gas limit が parent の ±1/1024 以内(EIP-1559 弾性境界)
  • BaseFee が EIP-1559 公式に従うこと
  • Timestamp が単調増加すること

PoS の検証はしない — それは CL の仕事。

Step 3: BFT 用のカスタム override 箇所

Override 箇所理由
HeaderValidatorproposer 署名、validator set 包含、view/round 番号
validate_block_post_executionコンセンサス固有 post-state(HyperEVM の orderbook 状態など)
Slashing 関連フィールドheader に slashing 証拠が含まれる場合の暗号検証

trait を書き直すわけではない — 異なる実装を提供して NodeBuilder に配線する。

Step 4: リポで実物を読む

git clone https://github.com/paradigmxyz/reth
cd reth
  1. crates/consensus/consensus/src/lib.rs を開いて Consensus trait の全体を読む
  2. crates/ethereum/consensus/src/lib.rs を開いて実装を見つける
  3. 数える: EthBeaconConsensus は何個のメソッドを override し、いくつをデフォルトのまま使っているか?
  4. validate_block_post_execution が呼び出される唯一の場所を見つける(メソッド名で検索)

答え合わせ

  • head 選択 vs ブロック検証の境界: head 選択 = consensus client(Lighthouse / Prysm / 自前)、ブロック検証 = Reth の Consensus 実装。
  • state root を post に置く理由: state root は EVM 実行の結果。pre には実行結果がない(post になって初めて分かる)。
  • BFT カスタム L1 で override すべき 3 箇所: HeaderValidator(proposer 署名 + validator set)、validate_block_post_execution(コンセンサス固有 post-state)、slashing 関連フィールドの暗号検証。

合格基準

  • 3 検証フェーズを実行順で言える。
  • 「Reth は head を選ばない」「CL が head を選ぶ」を即答できる。
  • HeaderValidator がなぜ Consensus trait の親なのか言える。
  • リポを clone して ConsensusEthBeaconConsensus の関係を辿れる。

まとめ(3行)

  • Reth の Consensus trait は ブロック検証 のみを担う — head 選択は consensus client の仕事。
  • 検証は 3 フェーズ(pre / body↔header / post)、post-execution が state root / gas / receipt のコンセンサスクリティカルチェック。
  • BFT カスタム L1 は trait を書き直さず HeaderValidator + validate_block_post_execution に 異なる実装を提供 して NodeBuilder に配線する。