FABRKNT
Consensus Engineering — Building L1 Consensus on Reth
Reading real consensus code
Lesson 7 of 12·CONTENT15 min40 XP

Treat this page as a workbench, not a blog post. The goal is to extract a reusable mental model from the source and carry it into the rest of the Fabrknt stack.

Course
Consensus Engineering — Building L1 Consensus on Reth
Lesson role
CONTENT
Sequence
7 / 12

Lesson 7 — Reading bera-reth — Proof-of-Liquidity as consensus customization

Question

Berachain runs Proof-of-Liquidity — a consensus customisation where validator selection is based on staked liquidity (not just BGT tokens). Reading bera-reth shows how a real production chain customises consensus via Reth SDK.

Principle (minimum model)

  • Proof-of-Liquidity (PoL). Validators are chosen by their liquidity provision (LP positions on the BGT pool) rather than just token stake. Aligns incentives between validators and liquidity providers.
  • bera-reth implementation. Forks Reth SDK; overrides the Consensus trait to use PoL's validator-selection logic. Uses Reth's execution engine unchanged.
  • Validator selection. select_validators(epoch) -> Vec<Address> — queries the on-chain LP pool, ranks by liquidity, returns the top N. Called once per epoch.
  • Slashing customisation. PoL slashing burns LP positions instead of staked tokens. Custom slashing logic in the validate_state hook.
  • Why this matters. Demonstrates how Reth SDK lets a chain customise consensus without forking the entire codebase. Bera-reth ships its custom Consensus impl + uses upstream Reth for everything else.
  • Trade-offs. PoL incentivises real liquidity but introduces validator-LP centralisation risk (big LPs dominate). Berachain mitigates via cap on LP-weight.
  • Pattern for custom L1s. This is the template: fork Reth SDK lightly + override consensus + ship.

Worked example + steps

Reading bera-reth — Proof-of-Liquidity as consensus customization

Most "we have a different consensus" pitches turn out to be PoS with a renamed token. Berachain's Proof-of-Liquidity (PoL) is the rare exception: it actually changes who's allowed to validate and where the rewards go. And the implementation — berachain/bera-reth — is in production, on Reth, with the diff small enough to read in an afternoon.

This is the practical surface of "swap the consensus." If you want to know what a real custom L1 looks like on Reth, this is the one to study.

1. What Proof-of-Liquidity changes

The Berachain pitch:

  • In PoS: validators stake a native token. The token's only utility is staking.
  • In PoL: validators stake BGT (Bera Governance Token — Berachain's non-transferable governance asset), but BGT is earned by providing liquidity to BEX (BeraSwap, Berachain's native AMM/DEX).

The cascade:

  1. Users provide liquidity to BEX → earn BGT
  2. BGT can be staked → earn validator privileges
  3. Validators earn fees → flow back to LPs as rewards

This makes validator economics aligned with DEX liquidity. The chain's most active users (LPs) are also the most reward-aligned validators.

2. What stays the same as Ethereum PoS

A lot. PoL is PoS with a twist, not a new family:

FeaturePoSPoLSame?
Validator set boundedYes (~1M)Yes (smaller, e.g., ~100)Same structure
Slashing for double-signingYesYesSame
Finality gadgetCasper FFGCometBFT-styleDifferent engine
Block time12s~2sDifferent
Token modelSingle (ETH)Dual (BGT + Bera)Different

The fundamental consensus model (BFT-style finality, slashing) is the same. What changes is:

  • Who can be a validator (LP-based gating)
  • How rewards flow (back to LPs, not just stakers)
  • Block time (faster, ~2s)

3. The Reth-side changes

Reading bera-reth's architecture:

bera-reth/
├── consensus/          ← Custom consensus impl
├── chainspec/          ← Berachain mainnet/testnet specs
├── evm/                ← Custom EVM config + precompiles
├── node/               ← NodeBuilder wiring
└── rpc/                ← bera_* RPC namespace

The customization points (same pattern as Tempo or any Reth-based L1):

3.1 consensus/

The Consensus trait impl validates Berachain-specific block properties:

  • Proposer is in active validator set (LP-gated)
  • Block timestamp within delta (fast 2s blocks have tighter timestamp bounds)
  • Validator signature scheme: BLS aggregated signatures, not ECDSA

3.2 evm/

Custom precompiles for PoL economics:

  • Reward distribution precompile: when a block commits, native rewards flow to LPs proportionally
  • Validator registry precompile: track active validator set on-chain

Both of these are pre-execution hooks in the executor — they run before transactions, updating chain state automatically.

🔍 Find in repo. Open bera-reth's evm/ crate and find the executor impl. What hooks does it run before the first transaction in each block? This is the PoL reward flow.

4. The chain spec — what makes a chain "different"

bera-reth's chainspec shows what changes at the spec level:

  • Custom fork heights: their own activation table for upgrades
  • Custom genesis: BGT pre-mint, validator initial allocations
  • Custom precompile addresses: PoL hooks at reserved addresses
  • Custom base fee params: tuned for 2s blocks

This is structurally identical to Ethereum's chainspec but semantically different. Your custom L1 will have its own version.

5. Reading the consensus integration

Read in this order:

  1. bera-reth/consensus/src/lib.rs — the Consensus impl
  2. bera-reth/node/src/lib.rs — where it's wired into NodeBuilder
  3. bera-reth/chainspec/src/lib.rs — what protocol parameters it depends on
  4. bera-reth/evm/src/lib.rs — what executor hooks run with this consensus

Each file is small (<500 lines). The whole bera-reth-as-consensus-customization is ~2000 lines on top of standard Reth.

That's the headline: a full custom L1 is 2000 lines of Reth customization if you have a Tendermint-style consensus engine ready. Most of the work is integration, not protocol re-implementation.

6. The lesson for your L1

Studying bera-reth as a reference:

  • Your custom chain crate ~= bera-reth's crate structure
  • Your Consensus impl ~= bera-reth's, with your validator set rules
  • Your executor hooks ~= bera-reth's PoL hooks, with your business logic
  • Your NodeBuilder wiring ~= bera-reth's, calling your components

bera-reth is the template. Tempo's node crate (tempoxyz/tempo) is now public and you can verify the same shape directly — different business logic (payments-priority instead of PoL), same skeleton. Same compose-don't-fork pattern: their tempoxyz/reth is 0 commits ahead of upstream.

7. Practice

  1. Clone bera-reth (or browse on GitHub)
  2. Compare to the directory structure of reth's own crates/optimism/ from Expert Module 3
  3. Identify the 3 files most specific to PoL (the parts you'd most need to customize for a different L1)
  4. Estimate: if you forked bera-reth to make a Tempo-style L1, how much would you change vs keep?

Final check: in one sentence, what's the structural answer to "how do I ship a custom L1 on Reth?" If your answer is more than "implement the Consensus trait + custom executor hooks + NodeBuilder wiring," you're overcomplicating.

Summary (3 lines)

  • bera-reth = Reth SDK fork with PoL (Proof-of-Liquidity) consensus. Validators picked by LP positions, not just stake.
  • Custom Consensus trait + custom validator selection + custom slashing (burns LP positions). Execution engine unchanged.
  • Template for custom L1s: fork Reth SDK lightly + override consensus + ship. Next: quiz on reading consensus internals.