レッスン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 セクション:
- Validators — 署名 / nonce / その他のチェック
- Ordering — tx 順序付け戦略選択(デフォルト
CoinbaseTipOrdering) - Construction —
InMemoryBlobStore+ 上記選択を合成して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] })
)
CustomPoolBuilder が PoolBuilder 実装と同じやり方で ExecutorBuilder を実装。パターンがそのまま転用 — これが SDK の要点。1 コンポーネント差し替えたら、別のは機械的作業。
失敗例(誤解)
「pool は validator だけ」— 間違い。Pool は (validator、ordering、blob store) の 合成。3 要素どれも独立に差し替え可能。
「LoggingValidator を構築しただけで log 発火」— 間違い。Builder で 内側 validator を渡してしまっている ことが典型ミス。validator 変数を pool に渡す必要、構築だけでは効果なし。
「executor 差し替えは pool 差し替えと別の作法」— 間違い。ExecutorBuilder が PoolBuilder と同形(build_executor メソッド + コンテキスト引数)。1 コンポーネント差し替え方を学んだら他は機械的。
ステップで組み立てる
Step 1: CustomPoolBuilder を読む
src/main.rs で CustomPoolBuilder 構造体 + 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 パターンが転用できる理由:
ExecutorBuilderがPoolBuilderと同形(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 コンポーネント差し替え覚えたら他は機械的。