Skip to content
← Build OpenHL — from `cargo init` to a single-validator devnet
Orientation
Lesson 1 of 16·CONTENT20 min60 XP
Course
Build OpenHL — from `cargo init` to a single-validator devnet
Lesson role
CONTENT
Sequence
1 / 16

Build OpenHL — from cargo init to a single-validator devnet

Question

Build OpenHL, an open-source Hyperliquid-shaped chain, in Rust: from cargo init to a single-validator Malachite BFT round wired to an in-process Reth node.

Principle (minimum model)

  • 16 lessons across 8 modules: Orientation → Foundations → Contract types → EL test double → CL types → Engine integration → Live Reth → Capstone.
  • Two upstream engines plus your glue. Reth v2.2.0 (execution) and Malachite v0.5.0 (Tendermint-style BFT consensus), joined by a four-method ConsensusBridge trait that you write.
  • One answer key. Each lesson cites the psyto/openhl commit where the same code first appeared — e.g. 75be9de / 5fc7ca1 (workspace + pins), 13113db (contract types + trait), 3b43586 (in-memory bridge), 708472c (first block through the engine actors), 0cac571 (commit via Reth's Engine API).
  • Milestone, then live Reth. Lesson 10: first_block_via_engine_actors decides one block through Malachite's real actor pipeline, with a stub bridge in place of the EVM. Lessons 11–14 put a live in-process Reth dev node behind the same trait. Lesson 15 reviews both proofs and what is still missing.
  • Scope. The consensus substrate only — no CLOB, precompiles, funding/oracle/liquidation or vaults (and so no working perp DEX yet).
  • Prerequisites. Rust 1.95+, cargo workspaces, async/await and trait impls. No prior Reth, Malachite or consensus knowledge needed.
  • Design analogy. Hyperliquid also pairs a BFT consensus (HyperBFT) with an EVM (HyperEVM). Its node is closed source and not Reth-based, so OpenHL shows a Hyperliquid-shaped design, not how Hyperliquid is built.

Worked example + steps

Build OpenHL — from cargo init to a single-validator devnet

This is not a course you read. This is a course you build.

Over the next 16 lessons, you'll start from cargo init on an empty directory and end with a Rust workspace that compiles, decides a block through Malachite's real engine actors, and builds, validates and commits blocks against a real in-process Reth node. The codebase you end with is your own — written by you, line by line — and it will look almost exactly like psyto/openhl at the same Stage. That repo is your answer key.

Hyperliquid runs on a closed-source stack — HyperBFT consensus, HyperCore matching engine, HyperEVM execution. There is no public Rust reference. OpenHL is what the open-source version looks like, and this course is how you build the Module 1 substrate of it yourself.

Why a CLOB? Hyperliquid's design choice is price-time-priority order book matching because its target market — top-tier crypto-native perps — has enough continuous retail flow for the order book to do real local price discovery. RFQ systems (Variational, Paradigm) win the long tail of assets by quoting just-in-time and hedging on a primary venue; pool-based designs (GMX-style) optimize cold-start at the cost of tail economics. You're about to build the engine for the slice of the market where CLOB is the right answer. The openhl CLOB course's capstone reflects on this tradeoff in depth — for now, the design context is enough to start.

1. What you'll have at the end

A 30-second BFT primer first. BFT consensus runs in rounds of three phases:

  • propose — one chosen validator broadcasts a block proposal
  • prevote — every validator broadcasts a yes/no/nil vote on the proposal
  • precommit — validators lock their vote

A block is decided (= final) once ≥ 2/3 of validators have precommitted it. Within each round the proposer is selected deterministically from the validator set; if the round fails (no quorum), the protocol advances to the next round with a different proposer.

Malachite is the Rust BFT engine that drives this state machine; your job in this course is to wire your application (header construction, EVM execution) into it via a Context trait.

Keep five words in hand — propose, prevote, precommit, decided, proposer — and the rest of the course vocabulary lands cleanly.

By the end of Lesson 10, on your own machine, cargo test first_block_via_engine_actors passes a single-validator BFT round through Malachite's real engine actors — about 0.02 seconds, per the openhl commit that introduced it (708472c). At that point a stub bridge stands in for the EVM. Lessons 11–14 put a live in-process Reth dev node behind the same bridge. The two are proven separately:

Proof 1 (Lesson 10, stub EVM):
  your run_engine_app →
    Malachite engine actors (proposer election, prevote, precommit) →
      build_payload / payload_ready (StubBridge) →
        decided block

Proof 2 (Lessons 12–14, live Reth, called from tests):
  LiveRethEvmBridge::build_payload  → Reth dev-node provider → header construction
  LiveRethEvmBridge::validate_payload → EthBeaconConsensus header checks
  LiveRethEvmBridge::commit         → fork_choice_updated to Reth's Engine API

Joining the two — run_engine_app driving LiveRethEvmBridge in one test — is left as the next step after the course. Every line of both paths is code you wrote. None of it is magic; all of it is open. By the time you finish, you can:

  • Read any line of psyto/openhl Module 1 code and explain why it's there
  • Modify any part of the bridge contract and run the tests to see what breaks
  • Fork the substrate to start your own Hyperliquid-shape chain — psyto/openhl becomes your reference implementation, not your dependency

2. What you won't have at the end

This course covers openhl Build arc Module 1 only — the consensus substrate. It does NOT cover:

  • Module 2: the CLOB matching engine
  • Module 3: custom EVM precompiles that read CLOB state
  • Module 4: funding, oracle, liquidations
  • Module 5: protocol-native vault primitive

Modules 2 and 3, plus Module 4's funding and liquidation (including ADL), each have their own course later in this curriculum; the oracle and the vault are not covered by a course. When you finish this course you have the substrate — the BFT-EVM contract, the actor wiring, the live-Reth integration. You do not have a working perp DEX. The perp DEX is Modules 2 through 5 on top of what you build here.

This is honest scoping. A "build your own Hyperliquid" course that promises everything in 16 lessons would be lying to you.

3. How this course works

Every lesson has the same shape:

  1. Goal. "By the end of this lesson, cargo test <name> will pass." That test does not pass right now. You will make it pass.
  2. Recap. Where you are in the workspace. What the last lesson built. What the test landscape looks like as of now.
  3. Plan. What you're about to build, and what design choices the openhl maintainers made when they built it the first time.
  4. Walk-through. Step-by-step code. Type it, save it, run cargo check after each step.
  5. Test. Run cargo test <name>. It should pass. If it doesn't, here are the typical mistakes.
  6. Design reflection. One or two load-bearing decisions you just encoded. We come back to these in later lessons.
  7. Answer key. Git-checkout point in psyto/openhl where the same code lives. Diff your code against that SHA if you want to verify.
  8. Next lesson. What gets built next, and why.

The lesson is the instruction set. The code you write is the artifact. psyto/openhl at the matching SHA is the answer key.

4. Prerequisites

You need:

  • Rust 1.95+. rustup default 1.95.0 or newer.
  • Git. You'll clone psyto/openhl once as the answer key.
  • Basic comfort with cargo workspaces, async/await, and trait impls. If #[async_trait] and impl Trait for Foo { ... } are new vocabulary, this course will move too fast. Take the introductory Reth courses first.
  • An editor that handles Rust well. VS Code + rust-analyzer is fine. Vim/Helix/Emacs are fine.
  • Plenty of free disk space — 20 GB or more. Reth's compile graph is large, and you'll build it in two workspaces (yours and the reference); a check-only build of a subset of Reth already fills about 4 GB of target/.

You do not need:

  • Any prior consensus-protocol knowledge (we explain BFT as we go)
  • Any prior Reth knowledge (lesson 1 introduces the dep)
  • Any prior Malachite knowledge (lesson 1 introduces it too)
  • A multi-machine setup (everything runs single-process on your laptop)

5. Setup (do this now)

You will have two directories on your machine:

  • ~/code/my-openhl/ — your workspace. You write code here. This is yours.
  • ~/code/openhl-reference/ — a clone of psyto/openhl. You read code here when you want to compare. This is read-only.
# Your workspace
mkdir -p ~/code/my-openhl && cd ~/code/my-openhl
cargo init --lib
# (the package name will default to `my-openhl` from the directory name. Lesson 1
#  restructures this into a workspace where the inner crates are `openhl-types`
#  / `openhl-consensus` / …, so the root package name disappears at that point.
#  We'll also delete the default lib.rs in Lesson 1 — this `cargo init` only exists
#  to give git a starting commit to track against.)

# Pin the same Rust toolchain in your own workspace too
printf '[toolchain]\nchannel = "1.95.0"\n' > rust-toolchain.toml

# Answer-key reference
mkdir -p ~/code && cd ~/code
git clone https://github.com/psyto/openhl.git openhl-reference
cd openhl-reference
cargo check  # this WILL take a long time the first time — Reth is big

psyto/openhl is archived (read-only), and its main goes far beyond this course — at main some Module 1 tests, including first_block_via_engine_actors, are marked #[ignore]. Always compare against the SHA each lesson cites, not against main.

If cargo check in openhl-reference passes, you have the right toolchain. Move on. If it fails, fix toolchain version first — rust-toolchain.toml in that repo pins Rust 1.95.0, and you've just dropped the same pin into my-openhl/, so rustup should auto-install the required toolchain for both.

6. The 16-lesson map

Each row is one lesson. Each lesson ends with a passing cargo test.

#ModuleWhat you buildEnd-of-lesson test
Lesson 0Orientation(this lesson)setup confirmed
Lesson 1Foundationsworkspace + Reth & Malachite pinnedcargo check --workspace clean
Lesson 2Contract typesopenhl-types primitives (BlockHash, PayloadId, ...)cargo test -p openhl-types
Lesson 3Contract traitConsensusBridge trait — 4 messages as async fnscargo check -p openhl-consensus
Lesson 4EL test doubleInMemoryEvmBridge — fake EVM for testingInMemoryEvmBridge tests pass
Lesson 5Reth-typed bridgeRethEvmBridge — same contract, real Reth typesRethEvmBridge tests pass
Lesson 6CL typesOpenHlContext + 10 Context sub-typescontext compiles
Lesson 7SigningOpenHlSigningProvider — Ed25519 sign/verifysign/verify round-trip
Lesson 8CodecOpenHlCodec — the codec slot the engine demandscodec round-trip
Lesson 9NodeOpenHlNode + the first start_engine callengine start/stop smoke
Lesson 10App looprun_engine_app — the actor pipeline that ties it all togetherfirst_block_via_engine_actors — Module 1 milestone, BFT round closes
Lesson 11Live Rethbootstrap a real Reth dev-node in a testreth_dev_node_bootstraps
Lesson 12Live bridge — build pathLiveRethEvmBridge (build_payload side) reads parent from a live providerlive_bridge_builds_on_real_genesis
Lesson 13Live bridge — validate pathLiveRethEvmBridge (validate_payload side) wires EthBeaconConsensus for real header validationvalidate-path tests
Lesson 14Live bridge — commit pathLiveRethEvmBridge (commit side) wires fork_choice_updated via Reth's in-process Engine APIcommit_sends_forkchoice_to_engine_when_handle_installed
Lesson 15Capstonereview what you built and what is still missing (codec stubs, newPayload, multi-validator, WAL, slashing)no new code

Lesson 10 is the major milestone. Finishing Lesson 10, you have BFT consensus producing a block end-to-end through your actor system. Lessons 11–14 swap your stub Reth for real Reth. Lesson 15 takes stock: the Module 1 commits of psyto/openhl (through 0cac571) have no single test that runs run_engine_app against LiveRethEvmBridge, so the two halves are proven separately, and joining them is the natural next step beyond the course. (Later openhl stages wire the two together in bin/openhl, from 7a52443 on.)

7. The answer-key discipline

Every lesson cites a psyto/openhl SHA — the commit where the same code first appeared. After you finish the lesson and your test passes:

cd ~/code/openhl-reference
git checkout <SHA-from-lesson>
# Now compare. Your code in ~/code/my-openhl/ should be ~equivalent.
diff -ru ~/code/my-openhl/crates/types ./crates/types

Your code will differ in trivial ways (whitespace, variable names, comment wording). What matters: types, signatures, control flow are equivalent. If those diverge meaningfully, the lesson didn't land; re-read the design-reflection section and adjust.

8. Setup confirmation — the actual Lesson 0 exercise

Before you move to Lesson 1, run all of this and confirm it all passes:

# 1. Rust version
rustc --version    # expect: rustc 1.95.x or later

# 2. Your workspace exists
ls ~/code/my-openhl    # expect: Cargo.toml, rust-toolchain.toml, src/

# 3. Reference exists and compiles
cd ~/code/openhl-reference && cargo check    # expect: "Finished" eventually

If all three pass, you are set up correctly. Move to Lesson 1.

💡 Self-check before moving on

In one sentence, can you state the difference between ~/code/my-openhl and ~/code/openhl-reference?

If you can't say in your own words "one is the real workspace I write line by line, the other is the mirror I only consult when I'm stuck," re-read §5 before starting Lesson 1. If you blur this distinction now, you'll eventually write code into openhl-reference by mistake and lose the boundary between what you wrote and what you borrowed. Make the boundary muscle memory before moving on.

Summary (3 lines)

  • Build openhl's consensus substrate = Malachite BFT + Reth behind a ConsensusBridge trait. 16 lessons / 8 modules.
  • Each lesson cites the psyto/openhl SHA to diff against; milestone is first_block_via_engine_actors (Lesson 10), then live Reth (Lessons 11–14).
  • Prerequisites: Rust async + traits; no prior Reth/Malachite needed. Design analogy: a Hyperliquid-shaped BFT + EVM chain (Hyperliquid's own node is closed source).