Lux Proposals
← All proposals
LP-0214Draft

Status

Draft. No backwards compatibility. No flags. No replay.

Closes the light-client item from LP-200 §"Future work". Composes on

LP-201's Kademlia DHT (Layer C) for chunk retrieval and on the P3Q

precompile family (LP-218 + LP-220) for local cert verification.

Activated at the genesis of the new final Lux network:

2025-12-25 16:20 Pacific (unix 1766708400). Predates every light-

client release. The pre-Quasar Edition Lux network (2020–2025) had no

P3Q precompile and no Kademlia DHT and is a separate network out of

scope.

Abstract

A Lux light client is a stateless verifier. It downloads block

headers, validator-set transitions, and state proofs from the Kademlia

DHT (LP-201 Layer C). It verifies QuasarCerts locally using the P3Q

precompile family (LP-218 P3Q-Pulsar at slot 0x012205, LP-220 P3Q-

Corona at slot 0x012206, future LP-221 P3Q-Magnetar at slot

0x012207). It does not store full state. It does not run consensus.

It does not validate transactions. It verifies a single state value at

a single height, with full cryptographic finality, in under 100 ms on a

1 Gbps LAN link.

The mechanism: a state-root commitment lives in every block header. A

state proof — Verkle (KZG, ~200 bytes per proof) or Merkle Patricia

(~1 KiB per proof) — proves a specific (key, value) pair under that

state root. The client fetches header + proof via DHT parallel chunk

reconstruction (LP-201 §"Layer C"), verifies the QuasarCert on the

header locally via the appropriate P3Q precompile leg, verifies the

proof against the state root, and is done.

Storage requirements scale linearly with retained history. The minimum

is one current block header and one current validator set — under

200 KiB combined. Clients that retain headers for N years on a busy

chain (60 blocks/sec, 32 KiB/header on a Pulsar-strict chain) pay

~60 GiB per year, which is the upper bound; most clients retain only a

sliding window plus checkpoint snapshots and pay under 100 MiB

indefinitely. The mathematical floor is one block header — anything

beyond is a UX choice.

The trust model is minimal. The genesis bootstrap validator set is

signed N-of-N at chain creation; thereafter validator-set transitions

are themselves QuasarCert-verified by the light client at the heights

where they occur. DHT peers are cross-validated by content hash — a

peer that returns chunks that do not match the requested sha256

content hash is rejected, no reputation system needed. The light client

never trusts a single peer for any single byte.

Why a light client

Three separable use cases drive this LP. Each is independently

sufficient.

1. Mobile and embedded wallets. A phone wallet that displays a user's

balance for an asset on a tenant EVM cannot run a full Z-Chain

node. It needs to verify that "address X has balance Y" with full

cryptographic finality. Today this is done by trusting an RPC

endpoint. With a light client, the wallet downloads the current

block header + state proof + QuasarCert and verifies locally. No

trusted RPC.

2. Cross-chain bridges. A bridge contract on Chain A needs to

verify that an event was committed on Chain B. The bridge runs a

light client of Chain B inside Chain A's EVM. Each state proof

from Chain B is verified by a P3Q precompile call on Chain A. No

trusted relayer.

3. In-browser inspector. A block explorer that lets a user

independently verify "this transaction is final on a tenant

EVM, parent L1 finality verified" without round-tripping to any

trusted indexer. The verification runs in the browser via wasm-

compiled Pulsar/Corona verifier cores.

In all three, the unifying requirement is: **fetch a small amount of

data, verify locally with no consensus participation, return a yes/no

answer about a state value.**

Trust model

The light client trusts exactly two things:

Trust 1 — The genesis bootstrap validator set

Chain genesis fixes an initial validator set, signed N-of-N by the

initial signing keys. This signature is embedded in the genesis block

and is checked once at first sync. Thereafter, validator-set transitions

are governed on-chain (LP-204 Tier-1 validator rotation) and the light

client tracks them by verifying the QuasarCert that finalised each

transition.

The light client is therefore as secure as the chain it tracks, with

one exception: **a light client that joins after chain genesis without

verifying every intermediate validator-set transition is trusting that

the latest validator set is a legitimate descendant of the genesis

set.** This is solved by either (a) verifying every transition from

genesis (linear in chain age — feasible on a fast chain), or (b)

accepting a checkpoint from a trusted source at first sync (the

operator-policy "long-range attack" tradeoff).

LP-214 specifies option (a) as the default — the light client MUST

verify every validator-set-transition QuasarCert from the genesis block

forward. Option (b) is an opt-in for clients that cannot afford the

linear sync time; it is operator policy, not protocol.

Trust 2 — Content-addressed DHT integrity

Kademlia DHT (LP-201 Layer C) is content-addressed: every value is keyed

by sha256(value). A peer that returns a value whose hash does not

match the requested key is silently rejected. The light client fetches

each chunk in parallel from multiple peers (LP-201 §"Layer C"

parameters: bucket size k=20, concurrency α=3) and accepts the first

chunk whose hash matches the request. Collusion is detected by a single

honest peer returning the correct chunk; the DHT need not be majority-

honest, it need only be content-addressed.

The light client does NOT trust:

Storage requirements

A light client at steady state carries:

| Item | Size per element | Cadence | Storage @ 1 yr on busy chain |
|---|---|---|---|
| Block headers | ~32 KiB on Pulsar-strict (BLS leg + Corona leg + Pulsar leg + Magnetar leg, validator-bitmap + 4 agg sigs); ~16 KiB on PQ-fast; ~8 KiB on PQ-off | per block | 60 blocks/sec × 86,400 s × 365 d × 32 KiB = ~60 TiB upper bound, but see below |
| Validator-set transitions | ~1 MiB (validator list + 4 pubkeys per validator × 1000 validators) | epoch (~1/day on a fast chain) | ~365 MiB/year |
| State-root commitments | 32 bytes (embedded in header — counted once) | per block | (no double-count) |
| Most-recent block header | ~32 KiB | latest only | 32 KiB |
| Most-recent validator set | ~1 MiB | latest only | 1 MiB |

The 60 TiB number is the upper bound for a client that retains

every header from a busy chain (busy-tenant projection:

60 blocks/sec × 32 KiB each). No real client does this.

Three retention modes the light client SHOULD support:

| Mode | Headers retained | Storage on busy chain @ 1 yr | Use case |
|---|---|---|---|
| Minimum | Latest header only, latest validator set | ~1 MiB total | Mobile wallet, browser inspector |
| Recent | Last 1024 headers + every validator-set transition | ~33 MiB + ~365 MiB = ~400 MiB | Bridge with bounded reorg-attestation window |
| Full headers | Every header from genesis | ~60 TiB upper bound | Long-running indexer that wants to replay any height |

The light client also retains, per active query, one state proof. State

proofs are pinned for the lifetime of the query and then garbage

collected. A client that issues 100 concurrent queries against a 60-byte

Verkle proof + 32-byte key + 32-byte value keeps ~12 KiB of proof data

hot.

For the typical mobile-wallet workload — verify your own balance at

each app foreground — the steady-state footprint is under 2 MiB.

State proof schemes

Two schemes are specified. The chain picks one at genesis; mixed schemes

within a chain are not supported.

Verkle trees (recommended for new chains)

Verkle trees use KZG polynomial commitments. Properties:

| Property | Value |
|---|---|
| Proof size | ~200 bytes per (key, value) pair |
| Verifier cost | One pairing check, ~5 ms on CPU, ~50 μs on Blackwell |
| State-root commitment size | 48 bytes (G1 point) |
| Trusted setup | Required (KZG ceremony) — Lux uses the existing Ethereum KZG ceremony output (4096-degree) |
| PQ security | None — Verkle/KZG is classical (broken under Shor's) |

Verkle is the lightweight choice. A PQ-augmented Verkle scheme is out

of scope for LP-214; if/when one is standardised, LP-215 (or similar)

adds it. For now: **a chain using Verkle has Verkle-classical state

proofs and Polaris-PQ block-cert proofs**. The cert protects the state

root; the state-proof verifier needs to be trusted only as far as the

KZG SRS is trusted, which is the same trust model as Ethereum's Verkle

deployment.

Merkle Patricia trees (Ethereum-compatible)

Merkle Patricia trees are the Ethereum-compatible scheme. Properties:

| Property | Value |
|---|---|
| Proof size | ~1 KiB per (key, value) pair (worst case 32 nibbles × 32-byte siblings = 1024 B + branch hashes) |
| Verifier cost | ~32 keccak256 hashes, ~30 μs on CPU |
| State-root commitment size | 32 bytes (keccak256 of root node) |
| Trusted setup | None |
| PQ security | None for keccak preimage resistance (Grover halves preimage strength; 256-bit keccak → ~128-bit PQ); collision resistance is ~128 bits classical, ~85 bits PQ |

Merkle is the conservative choice for EVM-compatible chains. Most

tenant EVMs use Merkle Patricia today. The Lux primary network may

migrate to Verkle at an activation marker yet TBD.

Chain-level selection

A chain declares its state-proof scheme in its genesis block:


GenesisBlock {
    ...
    StateProofScheme uint8  // 0x01 = Merkle Patricia, 0x02 = Verkle
    ...
}

Light clients dispatch on this byte at first sync. There is no

backwards-compatibility transition path — a chain that switches schemes

mid-life forks. This is intentional; backwards-compatibility hurts more

than it helps for state-proof verification, which is hot-path on every

single query.

Verification path

End-to-end verification of "value of key K at block height H":


1. Request block header at height H
   client → DHT FindValue(contentHash = sha256("hdr/" || chainID || H))
   ← block-header chunks, parallel from α=3 peers, content-hash verified

2. Verify QuasarCert on the header
   client → P3Q-Pulsar precompile @ 0x012205   (in-process, GPU if available)
   ← bool

   For PQ-strict chains: also verify Corona leg via P3Q-Corona @ 0x012206
   For PQ-heavy chains:  also verify Magnetar leg via P3Q-Magnetar @ 0x012207
   For PQ-off chains:    verify BLS leg (classical, no P3Q needed)

3. Verify validator-set transition chain back to genesis
   client → already-cached validator-set transitions
   ← (run once at first sync; thereafter only re-verify on new transitions)

4. Request state proof for key K under the state root in the header
   client → DHT FindValue(contentHash = sha256("proof/" || chainID || H || K))
   ← state-proof bytes (Verkle ~200 B or Merkle ~1 KiB)

5. Verify proof against state root from step 1
   client → in-process Verkle/Merkle verifier
   ← (value, bool)

6. Done. Return (value, verified=true) to caller.

Every step is independently complete. Steps 1-2 establish header

finality. Step 3 establishes validator-set legitimacy. Steps 4-5

establish state-value membership. No step braids into another.

Cert verification cost on the client

The P3Q precompile cost varies by leg. Measured on Blackwell sm_120

(LP-203 GPU verify bench) and on M-series CPU:

| Leg | Verify time on Blackwell | Verify time on CPU | Source |
|---|---|---|---|
| BLS pairing (classical) | ~10 μs | ~6 ms | LP-218 cost table |
| P3Q-Pulsar (ML-DSA-65) | ~50 μs | ~80 μs | LP-218 cost table |
| P3Q-Corona (Module-LWE threshold) | ~1 ms | ~3 ms | LP-220 cost table (projection) |
| P3Q-Magnetar (lattice aggregate) | ~50 μs | ~100 μs | LP-221 cost table (projection) |

Total cert verify on PQ-heavy mode per LP-217 (BLS + Corona + Pulsar +

Magnetar; was "Polaris" in LP-017) on CPU: ~9.2 ms. On Blackwell:

~1.1 ms.

For a wallet on a phone (CPU only), the dominant cost is the Corona

leg. A wallet that pins CertModeFloor = PQ-fast (LP-217) verifies

BLS + Pulsar only: ~6 ms on CPU. A wallet that pins

CertModeFloor = strict-PQ-fast (drops BLS) verifies Pulsar only:

~80 μs on CPU. Operator-policy choice; protocol does not constrain.

Sub-100 ms catch-up

The catch-up latency budget for a cold-start light client on a 1 Gbps

LAN link to ~3 healthy DHT peers:

| Phase | Cost | Source |
|---|---|---|
| QUIC handshake | ~1 ms (LAN; one RTT) | LP-201 |
| DHT FindNode bootstrap | ~5 ms (one RTT to bootstrap peer, single hop on populated network) | LP-201 §"Layer C" |
| Parallel chunk fetch — block header | 10 peers × 64 KiB chunks at 1 Gbps = 6.4 Mb in 6.4 ms (parallel) | LP-201 chunk-fetch model |
| QuasarCert verify | ~1.1 ms on Blackwell, ~9.2 ms on CPU (PQ-heavy mode per LP-217; was "Polaris" in LP-017) | this LP §"Cert verify cost" |
| Parallel chunk fetch — state proof | ~200 B (Verkle) or ~1 KiB (Merkle), single chunk, ~1 ms | LP-201 |
| State proof verify | ~5 ms (Verkle) or ~30 μs (Merkle) | this LP §"State proof schemes" |
| Total — Verkle + PQ-heavy cert + Blackwell | ~15 ms | composition |
| Total — Merkle + PQ-heavy cert + Blackwell | ~10 ms | composition |
| Total — Verkle + PQ-fast cert + CPU | ~17 ms | composition |
| Total — Merkle + PQ-fast cert + CPU | ~13 ms | composition |
| Total — Merkle + PQ-off cert + CPU | ~13 ms (no PQ verify; BLS verify dominates) | composition |

All four totals are under 100 ms. The 100 ms target is met with margin

on every supported configuration on both server-class and laptop-class

hardware. The mobile case (slower CPU, ~3-5× slowdown) brings PQ-fast

on CPU to ~45 ms, still under budget.

The wide-area network case is dominated by RTT, not by cryptography.

A WAN with 50 ms one-way latency adds ~50 ms × 3 (handshake + two DHT

lookups) = ~150 ms of network time, putting cold-start over the 100 ms

target. For WAN clients the budget is sub-200 ms, not sub-100 ms.

The 100 ms target is a LAN/edge target.

Why DHT parallel fetch matters

A single peer at 1 Gbps delivers a 1 MiB validator set in 8 ms. Ten

peers in parallel deliver chunks of the same set such that the *slowest*

chunk dominates — not the *sum* of chunks. The end-to-end fetch time

is bounded by the slowest of the parallel streams, which on a populated

DHT is the median peer's latency, not the worst peer's latency. This

is the LP-201 §"Layer C" content-addressed parallel fetch model. A

chunk that does not arrive (timeout) is silently replaced by another

peer's chunk; the failed peer is decremented in the routing table.

The light client thus benefits from DHT health automatically: more

peers = lower tail latency. Below ~5 peers the model degrades; below

3 the parallel-fetch optimisation is lost and the client falls back to

serial fetch from whatever peer is reachable. The LP-201 minimum DHT

size (10 peers for healthy operation) is assumed throughout.

Wire schemas

LP-214 introduces three new schema IDs in the 0xF0..0xF2 range. This

range is allocated cleanly per the LP-200 schema-ID map: 0xC0..0xCB

chains-VM (LP-186), 0xD0..0xDF P2P (LP-201), 0xE0..0xE1 DAG

(LP-208), 0xE2..0xE7 cross-shard (LP-211), 0xE8..0xEF reserved

future, 0xF0..0xF2 light-client (this LP), 0xF3..0xFF reserved

future. No collision with any existing allocation.

> Note for the LP-208 author: the LP-208 spec file still lists

> DAGHeader at 0xF0 and DAGBody at 0xF1 — this is stale. The canonical

> session handoff at /tmp/SESSION-HANDOFF.md §"Schema ID Allocation"

> overrode LP-208 to 0xE0..0xE1 to avoid the LP-218 rollup-VM

> collision. LP-214 takes 0xF0..0xF2 per the canonical map. LP-208

> needs a follow-up edit to bring its file into line with the handoff.

| Schema ID | Envelope | Kind | Purpose |
|---|---|---|---|
| 0xF0 | LightClientBlockHeaderRequest | 0x50 | Light client → peer: fetch block header at given height (or by content hash) |
| 0xF1 | LightClientStateProofRequest | 0x51 | Light client → peer: fetch state proof for (blockHeight, key) |
| 0xF2 | LightClientCertProof | 0x52 | Peer → light client: fetched cert + state proof bundle (response wrapper) |

LightClientBlockHeaderRequest (0xF0, Kind 0x50)

Fixed-section layout:

| Offset | Field | Size | Description |
|---|---|---|---|
| 0 | Kind | 1 | 0x50 |
| 1 | Version | 1 | 0x01 |
| 2 | ChainID | 4 | Chain identifier (per LP-018 chain ID map) |
| 6 | BlockHeight | 8 | Requested block height; zero = "latest finalised" |
| 14 | BlockContentHash | 32 | Optional: sha256 of header; zero = "any header at the height" |
| 46 | CertMinTier | 1 | Minimum cert tier required (LP-217 mode) |
| 47 | RequestID | 4 | Client-chosen request correlation ID |

Total fixed payload: 51 bytes.

LightClientStateProofRequest (0xF1, Kind 0x51)

Fixed-section layout:

| Offset | Field | Size | Description |
|---|---|---|---|
| 0 | Kind | 1 | 0x51 |
| 1 | Version | 1 | 0x01 |
| 2 | ChainID | 4 | Chain identifier |
| 6 | BlockHeight | 8 | Block height at which to evaluate the proof |
| 14 | StateRoot | 32 | State root the proof must commit under |
| 46 | KeyHash | 32 | sha256 of the state key being queried (hides the key from passive observers) |
| 78 | ProofScheme | 1 | 0x01 Merkle Patricia, 0x02 Verkle |
| 79 | RequestID | 4 | Client-chosen request correlation ID |

Total fixed payload: 83 bytes.

LightClientCertProof (0xF2, Kind 0x52)

Server's response carrying header + cert + state proof in one envelope.

Fixed-section layout:

| Offset | Field | Size | Description |
|---|---|---|---|
| 0 | Kind | 1 | 0x52 |
| 1 | Version | 1 | 0x01 |
| 2 | RequestID | 4 | Echoed from the request |
| 6 | ChainID | 4 | Echoed from the request |
| 10 | BlockHeight | 8 | Block height of the included header |
| 18 | CertTier | 1 | Actual cert tier of this proof (≥ request's CertMinTier) |
| 19 | ProofScheme | 1 | Echoed from the state-proof request (0x00 if not a state-proof response) |
| 20 | BlockHeaderOff | 4 | Offset into tail of block-header bytes |
| 24 | QuasarCertOff | 4 | Offset into tail of QuasarCert bytes |
| 28 | StateProofOff | 4 | Offset into tail of state-proof bytes (zero if header-only response) |
| 32 | KeyValueOff | 4 | Offset into tail of the value bytes (zero if header-only response) |

Total fixed payload: 36 bytes.

Variable tail: block-header bytes (LP-186 block-VM wire), QuasarCert

bytes (LP-182 QuasarCert wire), optional state-proof bytes (Merkle or

Verkle, per scheme), optional key-value bytes.

Composition with other LPs

LP-214 composes; it does not braid.

| LP | Role in LP-214 | Composition surface |
|---|---|---|
| LP-022 | The wire format every request/response uses | ZAP buffers, no codec hop |
| LP-182 | The QuasarCert format the light client verifies | client embeds an LP-182 parser |
| LP-186 | The block-header format the light client reads | client embeds the relevant chain-VM block-VM wire parser (one per supported chain) |
| LP-200 | The "buffer IS the value" guarantee that lets ZAP frames flow client-to-DHT-to-client | no re-encode anywhere |
| LP-201 | The transport (QUIC) + content-addressed fetch (DHT) | client uses LP-201 stream types 0xDA ChunkRequest, 0xDE DHTFindValue, 0xDF DHTStore |
| LP-202 | The cert tier degradation contract — client must observe whichever tier the chain actually produced, never silently degrade | client surfaces actual tier in CertTier field of response |
| LP-204 | Multi-chain — one light client per chain | client maintains separate (chainID, height-cursor, validator-set) state per chain |
| LP-217 | Cert profile modes — client pins CertMinTier per request | LP-214 §"Wire schemas" CertMinTier field |
| LP-218 | P3Q-Pulsar precompile for in-process verify | client calls precompile or a CPU/wasm equivalent |
| LP-220 | P3Q-Corona precompile (future) | when LP-220 ships, light client adds Corona-leg verify |

The light client does NOT compose with:

Failure modes

The light client is engineered for graceful degradation. The failure

modes are enumerated; for each, the recovery is specified.

Peer collusion on DHT

A malicious peer returns a block header that is well-formed but whose

QuasarCert does not actually attest to the block content (e.g., the

peer crafts a header with a different state root and signs it with a

forged sig).

Recovery: The QuasarCert verify step (§"Verification path" step 2)

fails because the forged sig does not validate under the legitimate

validator set's group public key. The client rejects the chunk and

retries from another peer. No special protocol needed; cryptographic

verification IS the recovery.

DHT poisoning

A coordinated set of peers all return the same forged header (same

sha256, all peers in agreement). The content-hash check passes because

all peers agree. The QuasarCert check still catches it because the

forgery cannot produce a valid Pulsar / Corona / Magnetar signature

without compromising the actual validator set's private keys.

Recovery: Cert verify rejects. Client retries via different DHT

buckets (Kademlia routing-table diversity). If the entire DHT is

poisoned, the client falls back to mDNS LAN bootstrap peers (LP-201

Layer B); if those are also compromised, the client is operating in a

fully adversarial network and cannot proceed — this is the assumed

upper bound for any cryptographic protocol and not a failure mode

LP-214 attempts to recover.

State-root replay

An attacker presents a valid old block header with a valid old state

proof for a now-stale key (e.g., a token transfer that has since been

overridden by a later transfer).

Recovery: The client embeds BlockHeight in the

LightClientCertProof response and the caller must verify the proof

is at-or-after the height of interest. For "what is the current

balance" queries, the client requests the latest finalised header

(BlockHeight = 0 in the request schema means "latest"), and the

server's response carries the actual latest height. The client

verifies that the returned height is monotonically ≥ its previously-

seen height; a regression signals adversarial caching and the response

is rejected.

For "what was the balance at height H" queries, the client supplies a

specific height and the response is verified at that height — replay

of an old proof at the requested height is the correct answer, not an

attack.

Validator-set transition skipping

An attacker presents a recent block header but skips the validator-set

transitions that occurred between the client's last-known height and

this header.

Recovery: The client always re-verifies the chain of validator-set

transitions before accepting a new header signed by a new validator

set. The header's ValidatorSetEpoch field (part of LP-182

QuasarCert) must match a validator set the client has independently

verified. A mismatch triggers a "fetch all missing validator-set

transitions" sub-protocol: the client requests every transition

QuasarCert from its last-known epoch up to the new epoch, verifies

each in sequence, and only then accepts the new header.

This is the only sub-protocol the light client runs. Everything

else is one-shot request/response.

DHT chunk timeout

A peer accepts the request but never returns the chunk.

Recovery: LP-201 §"Layer C" specifies α=3 concurrent peer queries

and a per-peer timeout. A timed-out peer is dropped from the active

query and another peer from the routing table is contacted. The

client's catch-up budget includes this redundancy — the published

sub-100 ms LAN figure assumes α=3 healthy peers, and degrades

gracefully (linearly in α) below that.

Light-client cert mode

A consequence question: does the light client need its OWN cert mode?

For example, on a chain operating at PQ-strict, can a light client

choose to verify "PQ-fast only" if it only needs partial finality

guarantees for low-value queries?

**Decision: yes, the light client carries its own CertMinTier field,

independent of the chain's operating mode.** This is the

CertMinTier byte in LightClientBlockHeaderRequest (§"Wire

schemas").

The semantics:

This is consistent with LP-217 cert modes and LP-202 tier degradation

— the chain's cert tier is the ceiling, and the light client

chooses any floor at or below the ceiling. A wallet looking up its

balance for display can accept PQ-off (BLS-only) on a chain running

PQ-strict, getting ~5 ms response time and BLS-classical security. A

custody operation against the same chain pins `CertMinTier = PQ-

strict` and gets full PQ-heavy finality (per LP-217; was "Polaris" in

LP-017) at ~15 ms response time. Same chain, same data, different

security/latency tradeoffs at the verifier side.

The protocol-level point: **PQ-strict at the validator does not force

PQ-strict at the light client.** Operator-policy choice; the protocol

provides the mechanism, not the policy.

Performance characteristics

Measured under LP-201 assumptions (1 Gbps LAN, healthy DHT with 10+

peers, Pulsar-strict chain on Blackwell-class validators):

| Metric | Cold start | Warm (subsequent query) |
|---|---|---|
| Time-to-first-verified-state-value | ~15 ms (Verkle + PQ-heavy cert + Blackwell) | ~5 ms (state proof only, cert cached) |
| Bytes downloaded | ~6.5 MiB (header chunks + 1 MiB validator set + ~200 B proof) | ~32 KiB (one header) + ~200 B (one proof) |
| CPU cost (Blackwell GPU available) | ~1.1 ms verify | ~50 μs verify |
| CPU cost (CPU only, PQ-heavy mode) | ~9.2 ms verify | ~9.2 ms verify |
| Memory footprint | ~10 MiB (header + validator set + buffer headroom) | ~2 MiB (sliding window) |
| Concurrent queries supported | ~100 simultaneous (limited by DHT routing capacity, not crypto) | ~1000 simultaneous |

The warm-query case is the steady-state UX target. A wallet that has

recently fetched a header keeps the QuasarCert cached for the lifetime

of that header's epoch and only fetches new state proofs as the user

issues queries — ~5 ms per query on Blackwell, ~30 μs per query on

Merkle-Patricia + CPU.

Architectural decisions

Light client is stateless except for headers + validator sets

The light client does NOT maintain a mempool, an execution state, a

fork-choice tree, or a peer-reputation system. It maintains:

That is the whole client state. This is what makes "sub-100 ms cold

start" possible — the client has no warm-up phase to perform.

One scheme per chain (Verkle XOR Merkle)

A chain declares its scheme at genesis and does not change. The

verifier code path is dispatched once per chain on first sync; there

is no runtime mode selection per query. Mixed schemes within a chain

would require dispatch overhead on every state proof and is rejected

by LP-214.

No partial-merkle "compact" proofs

Some chains use compact-proof schemes (e.g., RLP-encoded proof nodes

with shared prefixes elided). LP-214 mandates the full proof —

every sibling, every prefix. The size overhead (a few hundred bytes

per proof) is dwarfed by network RTT; the simplification in client

verifier code (no prefix-reconstruction step) is worth it. Honest

tradeoff: ~30% larger proofs, ~50% simpler client code.

Light client verifies validator-set transitions from genesis

The long-range-attack class of vulnerability is solved by verifying

every validator-set transition from genesis. This costs linear-in-

chain-age time at first sync. On a chain with daily epochs, a 5-year-

old chain is ~1825 transitions × ~1.1 ms each = ~2 seconds of one-

time verify work. This is paid once at first sync; subsequent syncs

incremental from last-known epoch.

A "trusted checkpoint" opt-in is permitted for clients that cannot

afford the linear sync time (e.g., a freshly-installed phone wallet

joining a 10-year-old chain). The checkpoint is operator policy —

typically the chain operator publishes a recent validator-set hash

that wallets can pin at install time. LP-214 specifies the trust-from-

genesis path as the default; the checkpoint shortcut is an opt-in.

No light-client incentive layer

A full node serving light-client requests consumes bandwidth and CPU.

There is no protocol-level micropayment, no light-client-pays-full-

node scheme. The expectation is that full nodes serve light clients

as a public good (analogous to Ethereum's eth/68 light protocol). If

this proves operationally insufficient at scale, a future LP can add

an incentive layer; LP-214 does not.

The DHT model (LP-201) makes this less painful than it might be: the

full node's marginal cost per light-client query is dominated by one

hash lookup + one ZAP buffer send, not by any computation. Bandwidth

is the constraint, not CPU.

Reference implementation

| Artifact | Path | Purpose |
|---|---|---|
| Go reference implementation | ~/work/lux/lightclient/ (new package) | Pure-Go light client, headerly + state-proof verify |
| Wasm build | ~/work/lux/lightclient/wasm/ | Browser-loadable wasm bundle for in-browser inspectors |
| iOS/Android FFI | ~/work/lux/lightclient/ffi/ | C-callable interface for mobile wallets |
| Verkle verifier | ~/work/lux/lightclient/verkle/ | Verkle proof verifier (reuses luxfi/kzg) |
| Merkle Patricia verifier | ~/work/lux/lightclient/mpt/ | Merkle Patricia verifier (no shared code with full-node MPT) |
| P3Q precompile bindings | ~/work/lux/lightclient/p3q/ | Calls into the LP-218 / LP-220 / LP-221 verifier cores |
| Wire schema codegen | ~/work/lux/zap/v2/codegen/schemas/lp-214/ | ZAP v2 codegen declarations for 0xF0..0xF2 |

The reference implementation lives in ~/work/lux/lightclient/. It is

a NEW package; there is no v1 light client to migrate. The

implementation MUST be byte-equality tested against a full-node-

computed reference: for any (chain, height, key) tuple, the light

client's verified value MUST equal the full node's stored value.

Test cases

The light client conformance tests live at

~/work/lux/lightclient/conformance/ and exercise:

| Test | What it verifies |
|---|---|
| TestColdSync_Genesis | Light client cold-starts on a fresh chain (genesis only), verifies the genesis validator set |
| TestSync_ValidatorSetTransition | Light client tracks N validator-set transitions back-to-back, each via QuasarCert |
| TestStateProof_Verkle_Verify | Verkle proof for a known (key, value) verifies under a known state root |
| TestStateProof_Merkle_Verify | Merkle Patricia proof for a known (key, value) verifies under a known state root |
| TestQuasarCert_PQOff_Reject | Header with PQ-off cert rejected when client pins CertMinTier = PQ-fast |
| TestDHTPeerCollusion_ContentHashMismatch | Light client rejects a chunk whose sha256 does not match request hash |
| TestDHTPeerForgedCert | Light client rejects a chunk whose QuasarCert is forged (valid sha256 but invalid sig) |
| TestValidatorSetSkip_FetchMissingEpochs | Light client triggers epoch-fill sub-protocol when a header references an unknown validator set |
| TestColdStart_Latency_LAN | Cold-start time < 100 ms on a 1 Gbps LAN setup with α=3 DHT peers |
| TestWarmQuery_Latency_LAN | Steady-state per-query time < 10 ms on warm cache |

All tests run in the standard go test ./... harness and are gated by

-tags=lightclient_conformance to keep CI cycle time low on routine

runs.

Activation marker


activates: 2025-12-25T16:20:00-08:00
activates-unix: 1766708400

The light client wire schemas (0xF0..0xF2) are accepted by the DHT and

by full-node responders from the genesis block of the new final Lux

network. There is no pre-activation light-client implementation; it is

NEW from genesis.

Cross-references

Future work

Copyright

Copyright and related rights waived via CC0.