FABRKNT
Inside Reth — シンク・拡張・SDK
Reth スタック — シンク・拡張・SDK
レッスン 14 / 17·CONTENT12 分25 XP
コース
Inside Reth — シンク・拡張・SDK
レッスンの役割
CONTENT
順序
14 / 17

レッスン10 — ドリル: カスタム pool ビルダーを出荷する

問い

読むのはリハーサル、実装するのが記憶。カスタム pool ビルダーを書いて差し替え、ノードバイナリの中で自分のコードが動くのを観察。CustomPoolBuilder::build_pool の 3 要素 + LoggingValidator のラップパターン + Dev チェーンで確認 — どこにあるか?

注: 以下のコード断片は拡張ポイント理解のための概念スニペットです(... は省略箇所)。そのまま実行する用途ではありません。

原理(最小モデル)

  • CustomPoolBuilder::build_pool の 3 要素. Validators(署名 / nonce チェック)+ Ordering(デフォルト CoinbaseTipOrdering)+ Construction(InMemoryBlobStore + 上記合成 → EthTransactionPool)。
  • LoggingValidator ラップパターン. inner: V フィールド + validate_transaction で log + 委譲 = pool に副作用差し込む clean な方法。
  • Dev チェーンで確認. cargo run -- --dev で 1 ノードエフェメラル、tx 送信で log 発火確認。
  • 配線ミスの典型. LoggingValidator 構築したが Builder が内側 validator を pool に渡したまま → log 発火せず。
  • カスタム precompile の差し替え. executor を差し替える、同パターン転用(ExecutorBuilder 実装、CustomPoolBuilder と同形)。
  • 1 コンポーネント差し替えたら他は機械的. SDK の要点 = パターンが転用可能。

具体例

セットアップ:

git clone https://github.com/paradigmxyz/reth
cd reth/examples/custom-node-components

ビルド不要(Reth 全体ビルドは不要)。

CustomPoolBuilder::build_pool の 3 セクション:

  1. Validators — 署名 / nonce / その他のチェック
  2. Ordering — tx 順序付け戦略選択(デフォルト CoinbaseTipOrdering
  3. ConstructionInMemoryBlobStore + 上記選択を合成して EthTransactionPool 構築

Pool は単体機能ではなく (validator、ordering、blob store) の合成

LoggingValidator ラップパターン:

struct LoggingValidator<V> {
    inner: V,
}

impl<V: TransactionValidator> TransactionValidator for LoggingValidator<V> {
    type Transaction = V::Transaction;

    async fn validate_transaction(
        &self,
        origin: TransactionOrigin,
        transaction: Self::Transaction,
    ) -> TransactionValidationOutcome<Self::Transaction> {
        info!(
            tx_hash = %transaction.hash(),
            gas_price = ?transaction.gas_price(),
            "Pool: validating transaction"
        );
        self.inner.validate_transaction(origin, transaction).await
    }
}

(API 名は手元 reth の TransactionValidator トレイトの正確な形に合わせて調整。重要なのは 構造 であって正確な識別子ではない。)

CustomPoolBuilder::build_pool 内で wrap:

let inner_validator = TransactionValidationTaskExecutor::eth_builder(...)
    /* ...既存セットアップ... */
    .build_with_tasks(...);

let validator = LoggingValidator { inner: inner_validator };

// inner_validator の代わりに validator を pool に渡す

Dev チェーンで確認:

cargo run -- --dev

--dev = 1 ノードエフェメラル + 高速マイン。tx 送信例:

cast send \
  --rpc-url http://localhost:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  --value 1ether \
  0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045

ターミナル出力:

Pool: validating transaction tx_hash=0x... gas_price=...

出ない場合: 配線ミス。よくある原因 = LoggingValidator 構築したが Builder が 内側 validator を pool に渡したまま。配線を直して再実行。

カスタム precompile を追加したい場合:

.with_components(
    EthereumNode::components()
        .executor(CustomExecutorBuilder { extra_precompiles: vec![ed25519_verify] })
)

CustomPoolBuilderPoolBuilder 実装と同じやり方で ExecutorBuilder を実装。パターンがそのまま転用 — これが SDK の要点。1 コンポーネント差し替えたら、別のは機械的作業。

失敗例(誤解)

「pool は validator だけ」— 間違い。Pool は (validator、ordering、blob store) の 合成。3 要素どれも独立に差し替え可能。

LoggingValidator を構築しただけで log 発火」— 間違い。Builder で 内側 validator を渡してしまっている ことが典型ミス。validator 変数を pool に渡す必要、構築だけでは効果なし。

「executor 差し替えは pool 差し替えと別の作法」— 間違いExecutorBuilderPoolBuilder と同形(build_executor メソッド + コンテキスト引数)。1 コンポーネント差し替え方を学んだら他は機械的

ステップで組み立てる

Step 1: CustomPoolBuilder を読む

src/main.rsCustomPoolBuilder 構造体 + PoolBuilder 実装。

Step 2: 3 要素を特定

Validators + Ordering + Construction。

Step 3: LoggingValidator でラップ

inner: V フィールド + validate_transaction で log + 委譲。

Step 4: Builder 内で wrap

let validator = LoggingValidator { inner: inner_validator }; → pool に validator を渡す。

Step 5: Dev チェーンで確認

cargo run -- --dev + tx 送信 → ターミナルログ確認。

Step 6: カスタム precompile スケッチ

ExecutorBuilder 実装、extra_precompiles フィールド + PoolBuilder 同形。

Step 7: 1 行で別コンポーネント差し替え

.pool(...).executor(...)1 行変更

答え合わせ

  • CustomPoolBuilder::build_pool の 3 合成要素: Validators(署名 / nonce 等のチェック)+ Ordering(tx 順序、デフォルト CoinbaseTipOrdering)+ Construction(InMemoryBlobStore + 上記合成 → EthTransactionPool)。Pool は 単体機能ではなく合成、3 要素どれも独立に差し替え可能。
  • LoggingValidator ラップが clean な理由: inner: V フィールドで委譲、既存 validator の全機能保持 + ログだけ追加 = decorator パターン。pool 内部に手を入れず、新しい validator として登録するだけ。
  • executor 差し替えに pool パターンが転用できる理由: ExecutorBuilderPoolBuilder と同形(build_executor(self, ctx) メソッド + コンテキスト引数 + コンポーネント返す)。1 コンポーネント差し替えの作法を学んだら他は機械的 = SDK の最大の売り。

合格基準

  • CustomPoolBuilder::build_pool の 3 合成要素を言える。
  • LoggingValidator ラップパターンを書ける。
  • Dev チェーン + tx 送信で log 確認手順を辿れる。
  • カスタム precompile = executor 差し替え と判断できる。
  • main.rs の 1 行変更で別コンポーネント差し替え可能と理解している。

まとめ(3行)

  • カスタム pool ビルダー = (validators + ordering + blob store) の合成、LoggingValidator で wrap して副作用注入が clean パターン。
  • Dev チェーン(cargo run -- --dev)+ cast send で動作確認、ログが出なければ Builder が内側 validator を渡してしまうのが典型ミス。
  • パターンは全コンポーネントに転用可能(ExecutorBuilder で precompile 追加 etc)= SDK の最大の売り、1 コンポーネント差し替え覚えたら他は機械的。