Pool protocol after Stratum V2 job declaration: member-checked or member-built templates, JSON over TLS, shares at target64 << s on the 32-lane unit, one vote key per operator held by the member, votes relayed and carried by the pool with a chain-only drop test. Light client: trust table, checkpoint-mode bytes per day, pinned seed list, the read-only node API, the homepage card's steps. Phone app: wallet, miner monitor, node card, store rules, build plan on journey.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
144 lines
23 KiB
Markdown
144 lines
23 KiB
Markdown
# Igneum protocol specification, section 10: the light client
|
|
|
|
Spec version 0.1, 3 October 2026. Status of this section: Designed. Nothing here is implemented. The design is `docs/design/execution-layer.md` section 7 (D14: at launch a light client verifies the execution proof chain from a certificate it is given; phase two adds the consensus proof) and ledger entries P3 and P4, both conceded: a phone verifies "in milliseconds" only with a curve-based wrapper nobody has measured, and a trustless light client needs a consensus proof that is phase two. This section says what a phone or a browser verifies at launch, what it is given, what it fetches, and what it trusts, so that the homepage's card and the litepaper's sentence can be made true as written.
|
|
|
|
Terms. A light client holds no state and executes nothing. It verifies three things about the chain: that a checkpoint is certified (section 3), that a state root is the output of the proven execution up to that checkpoint (section 7 and the design document, sections 2 and 5), and that an account or a storage slot has a given value under that root (`eth_getProof`). "Launch" is the phase one client; "phase two" is the client with the consensus proof.
|
|
|
|
## 10.1 Trust assumptions, stated plainly
|
|
|
|
| What the client trusts | At launch | Phase two | How the trust is bounded |
|
|
|---|---|---|---|
|
|
| Its own binary and the seed list inside it | Yes | Yes | The release process of section 8: reproducible build, hash beside the download, release key in genesis. A store build of the phone app is re-signed by the store (`docs/design/phone-app.md`, section 8) |
|
|
| The first certificate and voter set it starts from (the trusted checkpoint) | Yes, given out of band: shipped in the release and refreshed from `N` of `M` seed nodes that agree | Replaced by the consensus proof: any certificate is checked from genesis | The same assumption Ethereum light clients make for a sync-committee checkpoint (design document, section 7), approximate |
|
|
| The voter list and weights at each new checkpoint | Yes, unless the client runs in full-header mode (10.4) and recomputes them | No | Weight is a function of 30 days of headers (W2); a checkpoint-mode client cannot recompute it and asks nodes, with `N` of `M` agreement (10.6). The header commitment of O-10.2 would remove this row without the consensus proof |
|
|
| The BLS certificate's signers | Two thirds of active and 56.7% of total 30-day weight did not equivocate (section 3.7 item 1) | Same | The chain's own finality assumption; nothing a client can do about it |
|
|
| The proof system | The wrapped verifier key shipped in the client is the one the chain's proof system version uses (design document, section 5.6) | Same | A soundness bug is a light-client problem by design (ledger P7, section 5.7 emergency path); a full node executes natively and rejects a forged proof, a light client does not, and the client says so in its status |
|
|
| The nodes it fetches from | Only for liveness and for the voter set row above; headers, certificates and proofs are verified, not trusted | Only for liveness | A lying node can withhold or delay; it cannot forge a certificate, a proof or a header's PoW |
|
|
| The lottery hash of each header | Not verified in checkpoint mode; verified in full-header mode on the phone (10.4) | Covered by the consensus proof | A certified checkpoint is signed by weight, and weight is blocks, so a client that trusts certificates trusts the PoW behind them |
|
|
|
|
What "no node, no trust, no middleman" would require: all of phase two plus full-header mode, or phase two alone for a client that is content to trust certificates for PoW. The homepage sentence is rewritten in 10.8.
|
|
|
|
## 10.2 What the client holds
|
|
|
|
| Datum | Size | Source | Label |
|
|
|---|---|---|---|
|
|
| Checkpoint headers, one per certified index, or every header in full-header mode | About 400 bytes per header (10.5) | Nodes | Designed, approximate |
|
|
| The latest certificate per index it follows | 268 bytes plus one bit per voter (10.5) | Nodes, p2p message 70 relayed by a node | Implemented sizes (section 3.10, `docs/fork-divergence.md`) |
|
|
| The voter list and weights at the latest checkpoint | 60 bytes per voter (48-byte key, 8-byte weight, 4-byte participation count) | Nodes (`getFinalityWeights`), or recomputed | Designed |
|
|
| The latest wrapped segment proof and its public inputs | About 400 bytes (10.5) | Nodes (`igneum_getSegment`, the design document's RPC) | Designed, approximate; the measurement is R4 of the design document (ledger P3) |
|
|
| The VDF proof per epoch, full-header mode only | 516 bytes per epoch, 24 a day | Nodes, by `seed_source` | Measured (section 4.4) |
|
|
| The 256 MiB cache for the day, full-header mode only | 256 MiB in RAM, rebuilt daily in 0.2 s on a CPU | Computed from the day key (section 1.8) | Measured (section 1.12) |
|
|
| The seed node list | Under 4 KB | The release | Designed |
|
|
|
|
## 10.3 Sync at launch, step by step
|
|
|
|
1. Start from the trusted checkpoint shipped in the release: index `i0`, `hash(C_i0)`, the certificate, the voter list and weights at `C_i0`, and the latest wrapped proof at or below `C_i0`.
|
|
2. Open connections to at least `N` seed nodes (10.6). Ask each for the highest certified index it holds and for its trusted-checkpoint view of `i0`; if fewer than `N` agree with the shipped data, stop and tell the user, because the binary or the seed list is wrong.
|
|
3. For each index `i` from `i0 + 1` to the highest certified, in order: fetch the header of `C_i` and its certificate (10.7), verify the certificate (10.4 item 2), and accept `C_i` as certified. A client that was offline for a long time MAY skip to the latest certified index after one verification per presence window (240 indices) rather than per index, because a certificate is valid on its own against the voter set the client holds; the voter set it holds ages by that much, which is the trade of 10.4 item 3.
|
|
4. Fetch the latest proof record whose segment is at or below the latest certified checkpoint; verify the wrapped proof (10.4 item 4). Its `post_root` is now the state root the client reads from.
|
|
5. Read balances and storage with `eth_getProof` against `post_root` and verify the Merkle-Patricia proofs locally; nothing a node returns about state is accepted without one.
|
|
6. Subscribe to new certificates (10.7) and repeat items 3 to 5 as they arrive, about every 30 s.
|
|
|
|
The client shows, at every moment: the latest certified index and its age, the latest proven segment and its distance behind the checkpoint, and which of the two the balance on screen rests on (design document, section 2.3: a balance is "proven and locked" only when the proof's segment is at or below the certified checkpoint).
|
|
|
|
## 10.4 What is verified per update
|
|
|
|
1. **Header.** `hash(C_i)` recomputes from the header's fields (the chain's BLAKE2b-based hash, section 0.6, over the fields of section 2.4). In checkpoint mode nothing links `C_i` to `C_i-1` except the rule that a certified checkpoint's selected chain passes through every earlier certified checkpoint (C3), which the client cannot check without the path; it relies on the certificate's signers having checked it, which is the finality assumption of 10.1. In full-header mode the client holds every header, checks each parent list against headers it holds, computes the selected chain and confirms the path.
|
|
2. **Certificate.** The bitmap is over the canonical voter list of `C_i` (keys above dust and not stripped, sorted by key hash; section 3.10 C3). The client sums the public keys of the set bits (G1 additions), verifies the aggregate signature over `"igneum-vote-v1/" || chain_id || 0 || index || hash(C_i)` under the vote tag (C2), sums the signers' weight and checks both tests of Q3: at least two thirds of active weight (weight times participation) and at least 17/30 of total weight. Cost: one aggregation of up to V keys and one pairing check, which on a phone's CPU is milliseconds with a native BLS12-381 library and unmeasured in a browser (Open, O-10.3; a figure from memory would be approximate and is not given).
|
|
3. **Voter set.** The client needs the voter list, weights and participation at `C_i`. Checkpoint mode fetches them from nodes and accepts the set when `N` of `M` nodes return the same list root (a hash over the sorted list); full-header mode recomputes W2 and Q2 from headers and the votes in bodies, which needs bodies' finality sections as well as headers (the vote carriage of Q2), and that is why full-header mode costs what 10.5 says. A set that was fetched is marked "voter set: from nodes" in the status; a recomputed one is marked "voter set: verified".
|
|
4. **Proof.** The wrapped segment proof is verified with the shipped verifier key against the public inputs of the proof record (`segment`, `pre_root`, `post_root`, `receipts`, `version`; design document, section 5.4). The segment proof for N verifies the proof for N minus 1 recursively (design document, section 5.3), so the client keeps only the latest and never verifies a chain of them. The client checks that `segment` is a chain block at or below the latest certified checkpoint: in checkpoint mode by asking nodes for the chain height of the segment's block and accepting `N` of `M` agreement, in full-header mode from its own selected chain.
|
|
5. **State.** `eth_getProof` results are verified against `post_root` as on Ethereum.
|
|
6. **Seeds, full-header mode only.** The epoch seed named by `seed_source` is verified with the VDF proof (516 bytes, 4.47 ms per verify, Measured, section 4.4); the header's lottery hash is then evaluated with the CPU verifier (section 1.11): 0.441 ms per header on one M5 Max core, 38 s of one core per day of headers (derived); the phone and browser figures are O-10.4.
|
|
|
|
Phase two replaces items 2 and 3 with one check: a consensus proof whose statement is "checkpoint `C_i` is certified under finality rule version 2 over the 30-day window ending at `C_i`" (design document, section 7), folded into the segment proof, so one wrapped verification covers finality and execution. The voter set is then never fetched and the "from nodes" status disappears.
|
|
|
|
## 10.5 Bytes per day
|
|
|
|
Sizes used. Header: the fixed fields of the forked `Header` (`consensus/core/src/header.rs`: version 2, three 32-byte roots, timestamp 8, bits 4, nonce 8, DAA score 8, blue work up to 24, blue score 8, pruning point 32, `vote_key_hash` 32; Implemented) plus `seed_source` and `proof_ref` (32 each, Designed, section 2.4) come to 286 bytes; the parents by level are variable and at 1 BPS with GHOSTDAG k 18 add a few 32-byte hashes, so 400 bytes per header is the working figure (Designed, approximate; the devnet measurement is O-10.1). Certificate: index 8, checkpoint 32, voter count 4, bitmap one bit per voter, aggregate signature 96, aggregator key hash 32, sortition proof 96 (section 3.10 C3; G2 signatures are 96 bytes, `docs/fork-divergence.md`): 268 bytes plus the bitmap. Wrapped proof: a Groth16 proof over bn254 is three group elements, about 128 bytes compressed and 256 uncompressed, and a Plonk proof is under 1 KB (approximate, from memory; the design document names both and R4 measures); with the public inputs of 10.4 item 4 (130 bytes) and the prover list, 400 bytes per proof record is the working figure (Designed, approximate). Checkpoints: one per 30 blocks, 2,880 a day at 1 BPS. VDF proofs: 516 bytes, 24 a day (Measured).
|
|
|
|
| Mode | What is fetched per day | 1,000 voters | 10,000 voters | Label |
|
|
|---|---|---|---|---|
|
|
| Checkpoint mode (launch default) | 2,880 x (header 400 + certificate + proof 400) | 3.44 MB | 6.68 MB | Designed, approximate |
|
|
| Checkpoint mode, proof on demand | 2,880 x (header + certificate), plus one proof per app open | 2.28 MB plus 400 bytes per open | 5.52 MB plus 400 bytes per open | Designed, approximate |
|
|
| Full-header mode (verifies the selected chain and the lottery) | 86,400 headers + 2,880 certificates + 2,880 proofs + 24 VDF proofs, plus the bodies' finality sections for Q2 (not counted; O-10.1) | 36.9 MB plus finality sections | 40.1 MB plus finality sections | Designed, approximate |
|
|
| Per-block proof mode (a proof record for every chain block) | Full-header mode plus 86,400 proofs | 70.3 MB | 73.5 MB | Designed, approximate; not a mode the client offers, listed because the design proves every block and a client could fetch every proof |
|
|
| Phase two, consensus proof per checkpoint | 2,880 x (header 400 + folded proof 400) | 2.30 MB | 2.30 MB | Designed, approximate |
|
|
| Phase two, on demand | One header and one folded proof per open | 800 bytes per open | 800 bytes per open | Designed, approximate |
|
|
| Initial voter set, checkpoint mode | 60 bytes per voter, once per sync | 60 KB | 600 KB | Designed |
|
|
|
|
A phone on checkpoint mode uses about 100 MB a month (derived). A browser tab open for a minute on phase two uses about 1 KB. The certificate's bitmap grows linearly with voters and the per-key votes carried in bodies grow with voters times checkpoints, which is the scaling question of O-3.12 and section 9.7 item 4.
|
|
|
|
## 10.6 Node discovery: a seed list in the client, no DNS
|
|
|
|
1. The release carries a seed list: at least 8 entries of `(address, port, node identity key)`, the identity key being the node's p2p identity (rusty-kaspa's per-node id at the forked commit, forward reference) so the client pins the node and not a name. Addresses are IP literals; the client never resolves a name to find a node and works with DNS blocked or poisoned.
|
|
2. On every start the client asks each reachable seed for its peer list (a read-only RPC, 10.7) and keeps a local set of up to 64 nodes, preferring nodes that answered correctly last time; the seed list is the fallback, never the only set. The user MAY add their own node (the node card of `docs/design/phone-app.md`), which is then preferred for everything and still cross-checked against `N` of `M` others for the voter set.
|
|
3. `N` of `M`: the client fetches the voter-set root and the segment height (10.4 items 3 and 4) from `M` distinct nodes and accepts when `N` agree. Designed values `M = 5`, `N = 3`; Open (O-10.5) with the eclipse test. Headers, certificates and proofs need no agreement because they are verified.
|
|
4. In a browser the transport is WebSocket over TLS (`wss`, rusty-kaspa's wRPC at the forked commit), and a browser will only open `wss` to a host whose certificate a public CA issued, which in practice means a DNS name. So the browser client depends on the seed nodes' DNS names and CA certificates for transport, and on nothing else: the data it receives is verified as above. This is stated on the card (10.8) and is the reason the phone app, which can pin a node key on a raw address, is the client that meets item 1 in full.
|
|
5. A node that returns a header, certificate or proof that fails verification is dropped from the local set for the session and the event is shown.
|
|
|
|
## 10.7 What the client needs from a node
|
|
|
|
Read-only, served by every full node over the RPC of the fork, and over `wss` for browsers. Existing methods are those of section 3.10 and the design document's section 8.2; the rest are new and Designed.
|
|
|
|
| Method | Exists | Returns | Used in |
|
|
|---|---|---|---|
|
|
| `getFinalityCheckpoints(last N)` | Yes (section 3.10) | Index, hash, blue score, DAA, state, weights, aggregator | 10.3 item 3 |
|
|
| `getFinalityWeights` | Yes | Per key: hash, pubkey, blocks, voter, participation, stripped-until | 10.4 item 3 (checkpoint mode) |
|
|
| `igneum_getCertificate(index)` | New | The certificate bytes of section 3.10 C3 | 10.4 item 2 |
|
|
| `igneum_getHeader(hash)` and `igneum_getHeaders(from_hash, count)` | New (Kaspa's `getBlock` with `includeTransactions: false` is close) | Headers, wire-encoded | 10.3, full-header mode |
|
|
| `igneum_getVoterSetRoot(index)` | New | Hash of the canonical voter list with weights and participation at `C_index` | 10.4 item 3 |
|
|
| `igneum_getSegment(number)` | Design document 8.2 | Mergeset, executed set, shard plan, proof record | 10.4 item 4 |
|
|
| `igneum_getLatestProof(at_or_below_hash)` | New | The newest proof record whose segment is at or below the named chain block, with the chain height of that segment | 10.3 item 4 |
|
|
| `igneum_getVdfProof(seed_source)` | New (section 4.4 says proofs are "retrievable by `seed_source` from any peer") | `(T, y, pi)`, 516 bytes | Full-header mode |
|
|
| `eth_getProof`, `eth_getBalance`, `eth_call` | Design document 8.2 | Ethereum semantics against a named root | 10.3 item 5 |
|
|
| `igneum_getTransactionStatus(hash)` | Design document 8.2 | executed, proven, locked | The wallet's status line |
|
|
| `igneum_getPeers` | New (Kaspa's `getPeerAddresses` is close) | Addresses and identity keys | 10.6 item 2 |
|
|
| `eth_subscribe(newHeads)`, `FinalityLock` notification | Yes (section 3.10 notifications) | Push of new chain blocks and locks | 10.3 item 6 |
|
|
|
|
Every method above is read-only and needs no authentication; a node MAY rate-limit by address. The node card of the phone app carries a token only to let an operator read their own miner statistics from their own node (phone-app design, section 5), not to read the chain.
|
|
|
|
## 10.8 The homepage card, made true
|
|
|
|
The card on `site/index.html` reads today: "This tab · light client. Your browser will verify Igneum. One proof checked here, in milliseconds. No node, no trust, no middleman. Live at testnet." with cells BLOCK PROOF, CHECKPOINT, PROOF SYSTEM, VERIFIED TODAY. The ledger's overclaims 9, 25 and 38 already ask for the "no trust" and "milliseconds" claims to be re-scoped. What the card will do, at the public testnet (journey phase 5), step by step:
|
|
|
|
1. The page loads a WebAssembly build of the light-client engine (the same engine as the phone app, `docs/design/phone-app.md`, section 7) with the testnet's trusted checkpoint and seed list compiled in, and shows the checkpoint's index and date in the CHECKPOINT cell with the words "starting point, shipped with this page".
|
|
2. It opens `wss` to three seed nodes by name (10.6 item 4), asks each for the latest certified index, and shows "asking 3 nodes".
|
|
3. It fetches the latest certificate and the voter set, verifies the certificate against the voter set (10.4 item 2) and shows the signed weight as a fraction of total in the CHECKPOINT cell ("locked by 71% of 30-day weight, voter set from 3 nodes"). The words "from nodes" stay until phase two.
|
|
4. It fetches the latest proof record at or below that checkpoint and verifies the wrapped proof in the tab; the BLOCK PROOF cell shows the segment's height, the proof's size in bytes and the measured verification time in that browser, in milliseconds or whatever it was. The PROOF SYSTEM cell shows the version from the proof record.
|
|
5. VERIFIED TODAY counts proofs this tab verified since it opened, never a number from the server.
|
|
6. The sentence under the heading becomes: "This tab checks the latest locked checkpoint and the latest block proof itself. It fetches from three nodes by name and takes the voter list from them until the consensus proof lands (phase two)." The eyebrow stays "light client" and the PREVIEW pill is replaced by the testnet's name.
|
|
7. If any step fails, the card says which, in the same cell, and shows nothing it did not verify.
|
|
|
|
The sentence "Your browser will verify Igneum" is true under that card. "No node, no trust, no middleman" is not true before phase two and is removed now, not at testnet (ledger overclaims list, items 9 and 38).
|
|
|
|
## 10.9 Parameters in this section
|
|
|
|
| Parameter | Value | Label |
|
|
|---|---|---|
|
|
| Default mode | Checkpoint mode | Designed |
|
|
| Header size | 286 bytes fixed fields plus parents, 400 working figure | Implemented (fields), Designed (total), Open O-10.1 |
|
|
| Certificate size | 268 bytes plus one bit per voter | Implemented (section 3.10) |
|
|
| Wrapped proof record | 400 bytes working figure | Designed, approximate, Open (R4, ledger P3) |
|
|
| Checkpoints per day | 2,880 | Designed (C1 at 1 BPS) |
|
|
| Bytes per day, checkpoint mode, 1,000 voters | 3.44 MB | Designed, derived |
|
|
| Bytes per day, phase two | 2.30 MB, or 800 bytes per open | Designed, derived |
|
|
| VDF proof | 516 bytes, 4.47 ms verify | Measured (section 4.4) |
|
|
| Lottery verify per header | 0.441 ms on one M5 Max core | Measured (section 1.11) |
|
|
| Seed list | at least 8 pinned nodes, IP literals, in the release | Designed |
|
|
| `M`, `N` | 5, 3 | Designed, Open O-10.5 |
|
|
| Local peer set | up to 64 | Designed |
|
|
| Browser transport | `wss` to named hosts | Designed; the DNS dependency is stated |
|
|
|
|
## 10.10 Open items
|
|
|
|
| Id | Item | What closes it | Gate |
|
|
|---|---|---|---|
|
|
| O-10.1 | Header bytes on the wire at Igneum parameters, and the bytes of the bodies' finality sections a full-header client needs for Q2, are estimates | Record both over a day of the phase 3 devnet at 1 BPS; replace the 400-byte figure and fill the full-header row | 2 |
|
|
| O-10.2 | A checkpoint-mode client takes the voter set from nodes. A header field committing to the canonical voter list with weights and participation at that block (computable by every node from the block's past, like `pruning_point`) would let the client verify the set from the checkpoint header and a Merkle proof, without the consensus proof | Decision at gate 3 (consensus engineer and cryptographer): the cost is one more 32-byte header field and the incremental weight window section 3.10 already needs for mainnet; the alternative is to wait for phase two | 3 |
|
|
| O-10.3 | BLS aggregate verification time on a phone CPU and in WebAssembly in a browser, at 1,000 and 10,000 voters | Measure `fast_aggregate_verify` with the forked `blst` build on an iPhone and an Android phone, and a WebAssembly BLS12-381 library in Chrome and Safari; record milliseconds per certificate | 3 |
|
|
| O-10.4 | Full-header mode on a phone: 256 MiB cache in RAM, 0.2-s daily fill, 38 s of CPU per day of headers, all from the M5 Max figures; nothing on a phone or in a browser | Run the CPU verifier on a mid-range phone and in WebAssembly against one day of devnet headers; record RAM, time and battery; decide whether the mode ships on phones, in browsers, or on neither | 3 |
|
|
| O-10.5 | `M = 5, N = 3` for the voter-set agreement is a guess | Re-run the devnet eclipse test of O-3.7 with a light client among the eclipsed; find the smallest `N` at which the eclipsed client reports "voter set disputed" rather than a wrong set | 3 |
|
|
| O-10.6 | The trusted checkpoint's refresh: how old a shipped checkpoint may be before the client refuses to start from it, and how the release carries a new one | Decision with section 8's release process: candidate, no older than one presence window of 240 indices plus one release cycle; the client refuses an older one and asks for an update | 3 |
|
|
| O-10.7 | The wrapped proof's size and verification time on a phone and in a browser (ledger P3, design document R4) | The phase 2 benchmark: wrap a segment proof to Groth16 and Plonk on a 12 GB and a 24 GB card, record proof bytes and verification time on a phone and in WebAssembly | phase 2 |
|
|
| O-10.8 | The consensus proof's statement, its cost and its latency behind the checkpoint are unwritten (design document, section 7, phase two) | The phase two design: a zkVM program over the window's headers and certificates with the W2, Q2 and Q3 rules; cost on consumer hardware; the latency added to "locked" | phase 2 |
|
|
| O-10.9 | The node identity key used for pinning is a forward reference to the fork's p2p identity | Name the key and its encoding in `docs/fork-divergence.md` at the first seed-list release | 2 |
|