レッスン2 — カスタム gossip 構築(Reth 上の MEV-Boost 系メッセージング)
問い
Searcher ノードを走らせている。収益性の高い bundle を見つけた → 信頼できる builder の小集団にだけ送りたい。eth/68 transaction gossip では public 前提で「全員が全員を relay」モデル、「この特定の peer 集合にだけ送る」概念を持たない。そのギャップを埋めるカスタムサブプロトコルとは?
原理(最小モデル)
- eth/68 の 3 前提(カスタムは全部壊す). Public(接続持つ誰でも見える)+ consensus 関連 + peer 全員 relay 協力。
- カスタムが必要な 3 要素. Private gossip(特定 peer 集合のみ)+ アプリ層 routing(capability ベース)+ カスタム署名(chain 固有認証)。
- 本番例 4 つ. MEV-Boost bundle(private orderflow)+ 共有 sequencer pre-confirmation + 決済レール merchant attestation + L2 sequencer 間 coordination。
bundle/1プロトコル 4 メッセージ. Hello(handshake)+ BundleAnnounce(hash + 期待利益)+ BundleRequest(hash + 要求者署名)+ BundleData(暗号化 payload)。- Private プロトコルの discovery 3 パターン. Allowlist(peer ID ハードコード)+ Out-of-band 招待(Discord / GitHub で peer ID 配布、MEV-Boost)+ Tor routing(ネットワーク位置完全隠蔽)。
- MEV-Boost の 4 役割対応. Relayer(売り)+ Builder(組み立て)+ Proposer(勝ち選択)+ Sealed-bid auction。
- DOS 対策 4 軸. Announce spam(peer ごとレート制限 + 署名要求)+ 偽 bundle(要求時署名検証)+ 帯域食いつぶし(peer ごと上限 + eviction)+ Sybil(peer ID allowlist or PoS 紐付け)。
具体例
eth/68 の 3 前提とカスタムが破る部分:
| eth/68 前提 | カスタムで壊す |
|---|---|
| Public(接続持つ誰でも見える) | Private(特定 peer 集合のみ) |
| Consensus 関連 | アプリ層 routing |
| Peer 全員 relay 協力 | カスタム署名 |
bundle/1 プロトコルの 4 メッセージ:
| メッセージ | 目的 |
|---|---|
| Hello | Handshake — サポート共有 |
| BundleAnnounce | 「Hash X は bundle、~Y gas 抽出」 |
| BundleRequest | 「Bundle X 送って」 |
| BundleData | フル bundle(送信者向け暗号化) |
メッセージ定義:
#[derive(Debug, RlpDecodable, RlpEncodable)]
pub enum BundleMessage {
Hello { protocol_version: u8, peer_capabilities: u64 },
BundleAnnounce { bundle_hash: B256, expected_profit_gwei: u64 },
BundleRequest { bundle_hash: B256, requester_signature: Bytes },
BundleData { bundle_hash: B256, encrypted_payload: Bytes },
}
ハンドラ実装:
use reth_network::SubProtocol;
use std::collections::HashMap;
pub struct BundleProtocol {
known_bundles: HashMap<B256, Bundle>,
peer_set: HashSet<PeerId>,
signer: PrivateKeySigner,
}
impl SubProtocol for BundleProtocol {
const NAME: &'static [u8] = b"bundle";
const VERSION: u8 = 1;
const MESSAGE_COUNT: u8 = 4;
fn on_handshake(&mut self, peer: PeerId) -> eyre::Result<()> {
// Peer が allowlist に含まれているかを認証
if !self.peer_set.contains(&peer) {
return Err(eyre!("not authorized peer"));
}
Ok(())
}
fn on_message(&mut self, peer: PeerId, msg: Bytes) -> eyre::Result<Option<Bytes>> {
let parsed: BundleMessage = decode(&msg)?;
match parsed {
BundleMessage::Hello { protocol_version, .. } => {
tracing::info!(peer = ?peer, version = protocol_version, "peer joined");
Ok(None)
}
BundleMessage::BundleAnnounce { bundle_hash, expected_profit_gwei } => {
// 手元に bundle がなければ要求する
if !self.known_bundles.contains_key(&bundle_hash) {
let request = BundleMessage::BundleRequest {
bundle_hash,
requester_signature: self.signer.sign(&bundle_hash)?.to_bytes(),
};
Ok(Some(encode(&request)))
} else {
Ok(None)
}
}
BundleMessage::BundleRequest { bundle_hash, requester_signature } => {
// 要求側の署名を検証
verify_signature(&bundle_hash, &requester_signature)?;
// Bundle を送信
if let Some(bundle) = self.known_bundles.get(&bundle_hash) {
let encrypted = encrypt_for_peer(&peer, bundle.serialize());
let response = BundleMessage::BundleData {
bundle_hash,
encrypted_payload: encrypted,
};
Ok(Some(encode(&response)))
} else {
Ok(None)
}
}
BundleMessage::BundleData { bundle_hash, encrypted_payload } => {
// 復号して保存
let decrypted = decrypt_from_peer(&peer, encrypted_payload)?;
let bundle = Bundle::deserialize(&decrypted)?;
self.known_bundles.insert(bundle_hash, bundle);
Ok(None)
}
}
}
}
Reth への登録:
use reth_node_builder::NodeBuilder;
let bundle_protocol = BundleProtocol::new(allowlisted_peers, signer);
let node = NodeBuilder::new(config)
.with_components(
Components::default()
.add_sub_protocol(bundle_protocol)
)
.launch()
.await?;
Private プロトコルの discovery 3 パターン:
| パターン | 仕組み | 例 |
|---|---|---|
| Allowlist ベース | 参加者 peer ID をハードコード | 内部 sequencer ネット |
| Out-of-band 招待 | 別チャネル(Discord / GitHub)で peer ID 配布 | MEV-Boost |
| Tor routing | ネットワーク位置完全隠蔽 | 高プライバシー chain |
MEV-Boost の 4 役割対応:
| 概念 | 実装 |
|---|---|
| Relayer と builder の分離 | Relayer(売り窓口)+ Builder(組立て)+ Proposer(勝ち選択) |
| Sealed-bid auction | Builder 入札、proposer が最高額選択 |
| 評判ベース | Builder 評判蓄積、relayer が不正参加者追跡 |
| ネットワーク層 trust | 全て private p2p で完結、on-chain には乗らない |
DOS 対策 4 軸:
| 攻撃 | Mitigation |
|---|---|
| アナウンス spam | Peer ごとレート制限 + 署名付きアナウンス必須 |
| 偽 bundle | Announce 時署名要求、要求時に検証 |
| 帯域食いつぶし | Peer ごと帯域上限 + 超過で eviction |
| Sybil(偽 peer 大量) | Peer ID allowlist or PoS 紐付け |
失敗例(誤解)
「eth/68 で MEV bundle を gossip すれば良い」— 間違い。eth/68 は public 前提、bundle がネットワーク上の全員に漏れる → front-run される → searcher の収益消失。「private gossip」概念がない。
「discv5 で private peer 集合を見つけられる」— 間違い。デフォルト discv5 は capability list を 丸ごとアナウンス → ネットワークスキャンする者が「このノードは bundle/1 サポート」と特定可能 → プライバシー目標台無し。Allowlist / Out-of-band / Tor のいずれか。
「DOS 対策は後回しで良い」— 間違い。カスタムプロトコルは新たな攻撃面、デフォルト eth/68 の実戦防御がない。コアロジックと並行して保護を組み込む。最低 4 軸(spam / 偽 bundle / 帯域 / Sybil)を最初から。
🛑 予測。 Private な peer 集合に「MEV bundle を売りに出す」メッセージをブロードキャストしたい。なぜ eth/68 transaction を使わない? 別 gossip プロトコルを立ち上げる利点は?(答え: ① eth/68 は public 前提、bundle がネットワーク上の全員に漏れる → front-run される → searcher 収益消失、② eth/68 は「特定 peer 集合へ送る」概念がない、全員 relay モデル = 制御不能、③ eth/68 は consensus データ前提、アプリ層 routing(builder への入札、relayer の選択)が表現不能。カスタムサブプロトコル 3 利益 = ① Private(peer set 制御)+ ② Application-layer routing(capability ベース)+ ③ Custom 署名(chain 固有認証)。MEV-Boost / 共有 sequencer / 決済レール / 共有 sequencer coordination はすべてこのパターン。)
ステップで組み立てる
Step 1: eth/68 の 3 前提と破る部分
Public / Consensus / 全員 relay → Private / アプリ層 routing / カスタム署名。
Step 2: bundle/1 4 メッセージ
Hello / Announce / Request / Data。
Step 3: SubProtocol 4 必須要素
NAME / VERSION / MESSAGE_COUNT / on_message。
Step 4: Private discovery 3 パターン
Allowlist / Out-of-band(MEV-Boost)/ Tor。discv5 は丸ごとアナウンスで使えない。
Step 5: MEV-Boost 4 役割の対応
Relayer / Builder / Proposer / Sealed-bid auction。
Step 6: DOS 対策 4 軸を最初から
Spam(レート + 署名)+ 偽 bundle(署名検証)+ 帯域(上限 + eviction)+ Sybil(allowlist / PoS)。
Step 7: chain 固有用途への当てはめ
Telos(intent matching)/ mppsol(決済 attestation)/ Hyperliquid(独自 transport)/ Tempo(決済 finality hint)。カスタム gossip は MEV / 決済 / sequencer coordination の自然な拡張点。
答え合わせ
- eth/68 がカスタムに合わない 3 理由: ① Public 前提(MEV bundle が漏れる)、② Consensus 関連メッセージ前提(アプリ層 routing 不可)、③ 全員 relay 協力前提(特定 peer 集合に送る概念なし)。カスタムサブプロトコルの 3 利益(Private / Application routing / Custom 署名)が eth/68 の 3 前提を反転。
- discv5 が Private プロトコルに使えない理由: capability list を 丸ごとアナウンス → ネットワークスキャンする者が「このノードは
bundle/1サポート」と特定可能 → プライバシー目標台無し。代替は Allowlist(peer ID ハードコード)/ Out-of-band 招待(Discord / GitHub、MEV-Boost)/ Tor routing。 - DOS 対策を最初から組む理由: カスタムプロトコルは新たな攻撃面、デフォルト eth/68 の実戦防御がない → 1 peer がコードを介して他 peer に flood 可能 = DOS amplifier 化。Coreロジックと並行して 4 軸(spam / 偽 bundle / 帯域 / Sybil)保護を組む。後回しは致命的。
合格基準
- eth/68 の 3 前提とカスタムが破る部分を言える。
bundle/1の 4 メッセージを即答できる。- SubProtocol の 4 必須要素を言える。
- Private discovery 3 パターン(Allowlist / Out-of-band / Tor)を例で言える。
- DOS 対策 4 軸を即答できる。
まとめ(3行)
- カスタム gossip = eth/68 の 3 前提(Public / Consensus / 全員 relay)を破る chain 固有メッセージング、3 利益(Private peer set + アプリ層 routing + カスタム署名)。
- 実装は SubProtocol trait の 4 要素(NAME + VERSION + MESSAGE_COUNT + on_message)+ Reth NodeBuilder で 1 行登録、eth/68 と同 RLPx 接続上で並走。
- 本番例 = MEV-Boost(Relayer + Builder + Proposer + Sealed-bid auction)+ 共有 sequencer coordination + 決済レール attestation、DOS 対策 4 軸(spam / 偽 / 帯域 / Sybil)を最初から組む。