レッスン0 — OpenHL CLOB を作る(Reth 基盤の上に matching engine を載せる)
問い
前コース(Consensus)は実 Reth EVM を通じて 0.02 秒で block を確定する chain で終わった。だが確定していたのは 空の block — トランザクションもマッチングも価格発見もない。どうやって約定(取引)を生成し、それを committed block に流すか?
注: OpenHL コースのコードブロックは原則として手元で実行可能な形で示す。ただし
<file>などのプレースホルダや答え合わせ用コマンドは、各レッスンの指示に従って置換してから実行すること。
原理(最小モデル)
- CLOB(Central Limit Order Book)= price-time-priority の matching engine。 価格優先・時間優先で板を管理し、「HYPE を 25 USD で 10 buy」と「25 USD で 5 sell」を実際の約定(fill)に変換する。Hyperliquid の核となる仕組み。
- pure state machine — I/O なし、完全に deterministic。 マイクロ秒で走り、同じ入力列から必ず同じ約定列を生む。これが consensus に載せられる前提。
- 約定は bridge 経由で payload に流す。 matching engine が生成した fill を
LiveRethEvmBridge::build_payloadが拾い、consensus が commit する block に運ぶ。 - スコープは 2 段(openhl の Stage 名)。 Stage 8a = CLOB 本体(pure state machine)、Stage 8d = それを bridge に接続し payload へ約定を流す段。本コースはこの 2 つを再現する。
具体例
maker: HYPE 25 USD で 5 sell(先に book に置かれた resting order)
taker: HYPE 25 USD で 10 buy(その流動性を消費する incoming order)
↓ matching engine
Fill { price: 25, qty: 5 }(taker の残り 5 は book に resting)
ゴールは cargo test clob_fills_flow_into_payload — 実際の約定が matching engine → build_payload → payload に流れ、consensus が commit する。
失敗例(誤解)
「psyto/openhl を clone してそのコードベースで進めればいい」は誤り。本コースは build-along — matching engine を my-openhl/ でゼロから書き、reference に diff する。reference から start すると「答え合わせを写経する」モードに逆戻りし、摩擦から得られる学び(逆順 bid / FIFO / cancel-then-cleanup invariant)を取りこぼす。
ここまでで「CLOB は何で、なぜ pure state machine か」は着地した。ここから先はスコープ・前提・13レッスンのロードマップに入る。L1 以降は実際に Rust を書く。
🛑 セルフチェック。 次に進む前に 1 文で言えるか:本コースが Consensus になかった何を追加するのか? 答えに「約定を生成する matching engine、その約定が committed block に流れる」が入らなければ、下の「終了時に手にするもの」を読み直す。
終了時に手にするもの
新規 crates/clob/ crate:
- マイクロ秒で走る price-time-priority matching engine(pure state machine、I/O なし、deterministic)
Book+Order+Fill型(CEX が「order book」と呼ぶものに対応)- テスト 12 個合格: hand-trace シナリオ 9 個(空 book / FIFO 優先 / market order の流動性枯渇 / partial 約定 / cancel / マッチ後の no-crossed-book)+ proptest invariant 3 個(256 ケース × 3 = 768 ランダムシナリオ — quantity conservation / no-crossed-book always / determinism = replayability)
加えて crates/evm/ に integration test clob_fills_flow_into_payload — 実 Reth node を bootstrap し、bridge の CLOB に maker bid + crossing taker sell を submit、結果の約定が次の build_payload 出力に現れることを assert、かつ 過去の payload に遡って約定が attach されない(drain は forward-only)ことを assert。
終了時にも手にしないもの(意図的な scope cut)
- custom EVM precompile(CLOB state を read/write、= Step 3)
- funding rate state machine(= Step 4)
- 約定を EVM-executable トランザクションとして encode(openhl の Stage 9 より先)
- liquidation / mark-vs-index pricing / レバレッジ上限
約定は生成され committed block に運ばれるが、まだ parallel list — スマートコントラクトから読める Ethereum トランザクションではない。それを足すのが Step 3。
前提
- Consensus(Step 1)完了 —
crates/evm/src/live_node.rsにLiveRethEvmBridge<P>(provider/chain_spec/validator/ optionalengine_handle)が存在すること。 - Rust 1.95+(
rust-toolchain.tomlで pin 済み)。 BTreeMap/VecDeque/Reverse<T>/ proptest に慣れていること(「最高値から辿る reverse-ordering trick」が初耳ならstd::collections::BTreeMapの doc を一読)。
不要: matching-engine 経験 / order book 読解スキル / multi-validator(引き続き single-validator)。
セットアップ確認(今やる)
cd ~/code/openhl-reference
git fetch origin
git log --oneline | grep -E "(55a9dff|428cc26)" # Stage 8a / 8d の SHA が見えるはず
cd ~/code/my-openhl
cargo test -p openhl-evm --release 2>&1 | tail -10
# 期待: reth_dev_node_bootstraps / live_bridge_builds_on_real_genesis /
# commit_sends_forkchoice_to_engine_when_handle_installed が pass
これらが pass すれば start point として正しい。pass しなければ先に Consensus(Step 1)を完了させる。
13レッスンのロードマップ
| # | build するもの | 終了時テスト |
|---|---|---|
| 0 | Orientation(本レッスン) | セットアップ確認 |
| 1 | CLOB newtype(AccountId/OrderId/Price/Qty/Side/OrderType) | cargo check -p openhl-clob |
| 2 | Order / Fill / FillResult | 型がコンパイル |
| 3 | Book struct + Reverse<Price> trick | cargo check -p openhl-clob |
| 4 | submit_order — Limit order、in-book matching | resting order とマッチ |
| 5 | submit_order — Market order + crossing + partial | エッジケース挙動 |
| 6 | cancel + 空 level の cleanup | cancel-by-id が動く |
| 7 | hand-trace unit test 9 個 | 9 個 pass |
| 8 | proptest invariant 3 個 | 768 ランダムシナリオ pass |
| 9 | LiveRethEvmBridge に clob + pending_fills + submit_order | bridge がコンパイル |
| 10 | build_payload が pending fill を drain | 約定が payload に現れる |
| 11 | clob_fills_flow_into_payload integration test | フルパイプライン pass |
| 12 | Capstone | (振り返り) |
レッスン11 がマイルストーン — matching engine の約定が BFT engine を通って実 block に流れる。レッスン12 で「まだ何が足りないか(約定がスマートコントラクトから読めない = Step 3)」を明示する。
答え合わせの規律
各レッスン(1〜11)は SHA 55a9dff(Stage 8a)または 428cc26(Stage 8d)を cite する。テストが pass した後:
cd ~/code/openhl-reference
git checkout 55a9dff # レッスン 9〜11 では 428cc26
diff -u ~/code/my-openhl/crates/clob/src/types.rs ./crates/clob/src/types.rs
本質(型・制御フロー)が一致していればよい。空白・命名は違って OK。
合格基準
cargo test clob_fills_flow_into_payloadを通せる(コース完走時)。- CLOB の price-time-priority を説明できる。
- maker / taker と、約定を buffer する matching engine と同期 emit する engine のトレードオフを推論できる。
まとめ(3行)
- CLOB は price-time-priority の pure state machine — deterministic だから consensus に載せられる。
- 約定(fill)を生成し、bridge 経由で
build_payloadから committed block に流す。 - 本コースは Stage 8a(CLOB 本体)+ 8d(bridge 接続)。約定の EVM 実行可能化(precompile)は Step 3。