Proving v1: the decisions (N 8, T 600, a tenth to the aggregator, H = tip + 14,400, no sortition, Apple off) with the other networks' rules read tonight; the profile rows wait for the sweep

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-05 20:22:32 +00:00
parent e790695aee
commit ccf59ea613
3 changed files with 79 additions and 34 deletions

View file

@ -4,19 +4,24 @@
//!
//! | Machine | Default | Why |
//! |---|---|---|
//! | NVIDIA card with 12 GB or more, Windows, WSL2 (Ubuntu-24.04) answers | on | the SP1 CUDA prover runs inside WSL2 (src/prover.rs) |
//! | NVIDIA card with 12 GB or more, Windows, WSL2 silent | off, with the Set up hint | nothing can prove until the distribution exists |
//! | NVIDIA card with 12 GB or more, Linux | on | the host runs next to the engine |
//! | Apple silicon | off | the CPU prover is minutes per shard; on until it is measured on the GPU |
//! | no NVIDIA card with 12 GB | off | the 12 GB gate (spec 5.1, ledger P1); measured on a 32 GB card only so far |
//! | NVIDIA card with 20 GB or more that mines, Windows with WSL2 (Ubuntu-24.04) answering, or Linux | on | mining and proving on one card peaked at 16,751 MiB on the RTX 5090 (bench-log 5 October 2026, "proving v1"): a 24 GB card has 7 GB of headroom |
//! | NVIDIA card with 16 GB or more that is NOT an enabled mining card | on (prove-only) | the prover alone peaked at 13,816 MiB on a shard and about 15.0 GB with the chained aggregation |
//! | NVIDIA card of 16 to 20 GB that mines | off, with the line saying why | 16,751 MiB does not fit in 16,384; "prove when idle" needs an idle detector the app does not have (the 0.3.12 item) |
//! | NVIDIA card under 16 GB | off | 13,816 MiB does not fit in a 12 GB card on this SP1 build |
//! | Windows with a qualifying card but WSL2 silent | off, with the Set up hint | nothing can prove until the distribution exists |
//! | Apple silicon | off | the M5 Max CPU took 41 to 55 s for an EMPTY shard's compressed proof under load and 272 s for a 200-pgas shard; a full shard was never under 60 s (bench-log 4 and 5 October 2026) |
//!
//! The default never switches an explicit on back off, and Settings always wins afterwards.
//! Decided 5 October 2026 (delegated by the project lead: "deploy what is absolute best"), docs/plans/proving-v1.md. The default
//! never switches an explicit on back off, and Settings always wins afterwards.
use crate::state::CardState;
/// The gate card: 12 GB (spec 5.1, "a shard on a 12 GB card"). `nvidia-smi` reports MiB; 12 GB cards report
/// 12,288 MiB or a little under (the RTX 3060 12 GB reports 12,288), so the test is at 11.5 GB.
pub const MIN_VRAM_MB: u64 = 11_776;
/// A card that mines AND proves needs this much: the measured mine-and-prove peak is 16,751 MiB (5 October 2026,
/// `chain-pc2-pv1c`), so 20 GB; `nvidia-smi` reports MiB and a 24 GB card reports 24,564, so the test is at 19.5 GB.
pub const MIN_VRAM_MB_MINING: u64 = 19_968;
/// A card that only proves needs this much: the prover alone peaked at 13,816 MiB on a shard, about 15.0 GB with the
/// chained aggregation (the same entry), so 16 GB; a 16 GB card reports 16,384 or a little under, the test is at 15.5 GB.
pub const MIN_VRAM_MB_PROVE_ONLY: u64 = 15_872;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Decision {
@ -32,20 +37,26 @@ fn gb(mb: u64) -> u64 {
/// `os` is `std::env::consts::OS` ("windows", "linux", "macos"); `wsl_answers` is read on Windows only.
pub fn decide(cards: &[CardState], os: &str, wsl_answers: Option<bool>) -> Decision {
let nvidia: Vec<&CardState> = cards.iter().filter(|c| c.vendor == "nvidia").collect();
let able: Vec<&CardState> = nvidia.iter().copied().filter(|c| c.vram_mb >= MIN_VRAM_MB).collect();
// a mining card needs 20 GB (the measured mine-and-prove peak of 16.8 GB), a card that only proves 16 GB
let able: Vec<&CardState> = nvidia.iter().copied().filter(|c| c.vram_mb >= if c.enabled { MIN_VRAM_MB_MINING } else { MIN_VRAM_MB_PROVE_ONLY }).collect();
let off = |line: String| Decision { on: false, line };
if os == "macos" {
return off("proving stays off on Apple silicon until the GPU prover is measured there; Settings switches it on (CPU, slow)".into());
return off("proving stays off on Apple silicon: the M5 Max CPU took 41 to 55 s for an empty shard and minutes for a full one; Settings switches it on (CPU, slow)".into());
}
let Some(best) = able.iter().max_by_key(|c| c.vram_mb) else {
let seen = if nvidia.is_empty() {
"no NVIDIA card".to_string()
} else {
nvidia.iter().map(|c| format!("{} {} GB", c.name, gb(c.vram_mb))).collect::<Vec<_>>().join(", ")
nvidia.iter().map(|c| format!("{} {} GB{}", c.name, gb(c.vram_mb), if c.enabled { ", mining" } else { "" })).collect::<Vec<_>>().join(", ")
};
return off(format!("proving off by default: no NVIDIA card with 12 GB or more ({seen}); Settings switches it on"));
let why = if nvidia.iter().any(|c| c.enabled && c.vram_mb >= MIN_VRAM_MB_PROVE_ONLY) {
"mining and proving on one card needs 20 GB (measured peak 16.8 GB); switch the card's mining off to prove on it, or Settings switches proving on anyway"
} else {
"no NVIDIA card with 20 GB or more mining, or 16 GB or more free of mining (the prover peaks at 13.8 GB on a shard)"
};
return off(format!("proving off by default: {why} ({seen})"));
};
let card = format!("{} ({} GB)", best.name, gb(best.vram_mb));
let card = format!("{} ({} GB{})", best.name, gb(best.vram_mb), if best.enabled { ", mining too" } else { ", proving only" });
match os {
"windows" => match wsl_answers {
Some(true) => Decision { on: true, line: format!("proving on by default: {card} with WSL2 (Ubuntu-24.04 answers); Settings switches it off") },
@ -61,14 +72,17 @@ mod tests {
use super::*;
fn card(vendor: &str, name: &str, vram_mb: u64) -> CardState {
CardState { vendor: vendor.into(), name: name.into(), vram_mb, ..Default::default() }
CardState { vendor: vendor.into(), name: name.into(), vram_mb, enabled: true, ..Default::default() }
}
fn idle(vendor: &str, name: &str, vram_mb: u64) -> CardState {
CardState { enabled: false, ..card(vendor, name, vram_mb) }
}
#[test]
fn a_5090_with_wsl2_on_windows_is_on() {
let d = decide(&[card("nvidia", "NVIDIA GeForce RTX 5090", 32_607), card("amd", "AMD Radeon(TM) Graphics", 512)], "windows", Some(true));
assert!(d.on);
assert!(d.line.starts_with("proving on by default: NVIDIA GeForce RTX 5090 (32 GB) with WSL2"), "{}", d.line);
assert!(d.line.starts_with("proving on by default: NVIDIA GeForce RTX 5090 (32 GB, mining too) with WSL2"), "{}", d.line);
}
#[test]
@ -80,11 +94,20 @@ mod tests {
}
#[test]
fn linux_needs_no_wsl2_and_the_12_gb_gate_holds() {
assert!(decide(&[card("nvidia", "NVIDIA GeForce RTX 3060", 12_288)], "linux", None).on);
let d = decide(&[card("nvidia", "NVIDIA GeForce RTX 3080", 10_240)], "linux", None);
fn linux_needs_no_wsl2_and_the_memory_gates_hold() {
// a mining 4090 (24 GB) is on; a mining 16 GB card is off with the reason; the same 16 GB card not mining is on
assert!(decide(&[card("nvidia", "NVIDIA GeForce RTX 4090", 24_564)], "linux", None).on);
let d = decide(&[card("nvidia", "NVIDIA GeForce RTX 5080", 16_303)], "linux", None);
assert!(!d.on);
assert!(d.line.contains("no NVIDIA card with 12 GB or more (NVIDIA GeForce RTX 3080 10 GB)"), "{}", d.line);
assert!(d.line.contains("mining and proving on one card needs 20 GB") && d.line.contains("RTX 5080 16 GB, mining"), "{}", d.line);
let d = decide(&[idle("nvidia", "NVIDIA GeForce RTX 5080", 16_303)], "linux", None);
assert!(d.on);
assert!(d.line.contains("(16 GB, proving only)"), "{}", d.line);
// a 12 GB card is off either way (the prover alone peaks at 13.8 GB); a 10 GB card too
let d = decide(&[idle("nvidia", "NVIDIA GeForce RTX 3060", 12_288)], "linux", None);
assert!(!d.on);
assert!(d.line.contains("no NVIDIA card with 20 GB or more mining, or 16 GB or more free of mining") && d.line.contains("RTX 3060 12 GB"), "{}", d.line);
assert!(!decide(&[card("nvidia", "NVIDIA GeForce RTX 3080", 10_240)], "linux", None).on);
assert!(!decide(&[card("amd", "Radeon RX 9070 XT", 16_384)], "linux", None).on, "no CUDA prover for AMD yet");
assert!(decide(&[], "linux", None).line.contains("no NVIDIA card"));
}
@ -99,7 +122,7 @@ mod tests {
#[test]
fn the_biggest_qualifying_card_is_named() {
let d = decide(&[card("nvidia", "RTX 3060", 12_288), card("nvidia", "RTX 5090", 32_607)], "linux", None);
assert!(d.line.contains("RTX 5090 (32 GB)"), "{}", d.line);
let d = decide(&[idle("nvidia", "RTX 5080", 16_303), card("nvidia", "RTX 5090", 32_607)], "linux", None);
assert!(d.line.contains("RTX 5090 (32 GB, mining too)"), "{}", d.line);
}
}

View file

@ -21,7 +21,7 @@ that say how many cards cover the chain.
| Step | What | State |
|---|---|---|
| 1 | Prover on by default (`app/igneum-app/src/provedefault.rs`, `engine.rs apply_prove_default`): on at install when the machine can prove (NVIDIA card with 12 GB or more; WSL2 answering on Windows; Linux native; Apple silicon off until measured), never switching an explicit on back off; the Settings switch line and the tile line say why. Unit tests (5). The cost of proving on a mining machine: PC 2 job `prover-cost-pc2-pv1` (5 min mining alone, 5 min with the prover, the GPU memory peak and the host RAM peak, the sp1-gpu-server's compiled SM targets) | Implemented; the measurement is HELD (coordinator, 19:00Z): PC 2's RTX 5090 worker has been exiting on a pack seed mismatch since 18:35Z, so the first run's "mining alone" is 0 MH/s and void; re-run after the go |
| 2 | Segment aggregation: `SegmentRecord` (586 bytes, the aggregator guest's 340-byte statement inline), section `IGNS` before the shard section, p2p message 72 at protocol 14, the native block statement and the veto, the credit split (`split_pool_credit`), the payout at the carrier, `igneum-miner sign-segment-record`, the RPCs; host modes `chain` (consecutive fixtures), `aggregate` (live shard proofs from the pool, a run of blocks in one process) and `verify-segment` (the node's verifier, pinned aggregator key); the app's aggregator step (`prover.rs aggregate_once`) | Implemented, unit-tested (consensus core 2 new tests, exec 2, params 1); the GPU measurement (N = 2, 4, 8 blocks on PC 2) is HELD with step 1; the Mac CPU run of `--mode chain` over 2 live blocks is the known-finished case |
| 2 | Segment aggregation: `SegmentRecord` (586 bytes, the aggregator guest's 340-byte statement inline), section `IGNS` before the shard section, p2p message 75 at protocol 15 (14 went to the EVM transaction relay in 0.3.10), the native block statement and the veto, the credit split (`split_pool_credit`), the payout at the carrier, `igneum-miner sign-segment-record`, the RPCs; host modes `chain` (consecutive fixtures), `aggregate` (live shard proofs from the pool, a run of blocks in one process) and `verify-segment` (the node's verifier, pinned aggregator key); the app's aggregator step (`prover.rs aggregate_once`) | Implemented, unit-tested (consensus core 2 new tests, exec 2, params 1); the GPU measurement (N = 2, 4, 8 blocks on PC 2) is HELD with step 1; the Mac CPU run of `--mode chain` over 2 live blocks is the known-finished case |
| 3 | Coverage: `tools/proving-v1/coverage.mjs` (the proven-block share and the on-chain proof latency over a window from one node's RPC, the live page beside it) | Implemented and run for 3 min (below); the 30-min window waits for the fleet |
| 4 | The chain rule and the unproven rule in consensus behind `proving_v1_activation_daa` (spec 7.8 items 2, 6, 7); unit tests; the fast-time 3-node harness `tools/proving-v1/net.mjs` (ports 29950+, suffix 956, trust mode) with the known-finished and known-failed cases | Implemented; the harness run waits for the Mac build of the fork (`vendor/igneum-node/target-pv1`) |
| 5 | This plan: the rollout for 0.3.11 and the project lead's decisions | Written below |
@ -84,13 +84,35 @@ the rolling upgrade does not partition the network. The order, each step with it
record (`igneum_getSegmentRecords`) and `igneum_getProvingStatus.v1.segmentsInWindow` after H.
6. **The mandatory rule** stays off: no switch exists for it yet; it gets one when the measured share is one.
## What the project lead must decide
## The decisions (Decided 5 October 2026, delegated: the project lead, "I have no idea for most of this stuff so do a lot of research and deploy what is absolute best")
Each with its rule, its number and its evidence. The deploy is the DEVNET through 0.3.11 (not the public testnet).
### What the other networks do (read 5 October 2026, 20:05 to 20:15 UTC; every figure from the page named, else labelled approximate)
| Network | Unit proven | Deadline | What a miss costs | Who is paid what | Measured latency |
|---|---|---|---|---|---|
| Taiko Alethia (L2BEAT page, protocol v2.1.0 notes) | a batch of blocks | proving window 2 h, cooldown 2 h (v2.1.0, February 2025); the Shasta inbox targets a 4-h proof submission cadence | the proposer's liveness bond is credited back in full when the batch is proved inside the window, half when outside; mainnet currently sets minBond and livenessBond to 0 | the prover earns the proving fee; two of four proofs needed (SGX Geth, SGX Reth, SP1, RISC0, at least one ZK) | 100% ZK coverage of mainnet blocks reached December 2025 (blockchain.news); preconfirmations 2 s |
| Boundless (docs.boundless.network, proof lifecycle) | one request | the requester's timeout (example 3,600 s) and a lock timeout (example 2,700 s); a reverse Dutch auction ramps the price from the minimum to the maximum over a ramp-up (example 300 s) | the locked collateral (example 5 ZKC) is slashed and used to pay another prover who fulfils the request | the prover's fee = the bid minus the market fee | not stated on the page |
| Succinct Prover Network (docs.succinct.xyz, SPN architecture and quickstart) | one request | the requester's deadline (the quickstart example: 10 minutes, 50 PROVE staked to bid, 100 PROVE maximum fee) | part or all of the winning prover's collateral slashed "according to protocol rules" | a reverse auction: the lowest bidder is assigned | "real-time", no number on the page |
| Aztec (docs.aztec.network economics; L2BEAT; forum) | an epoch of 32 blocks (38 min 24 s), a proof may cover one checkpoint (1 min 12 s) up to one epoch; maximum proof window 1 h 16 min | the epoch is declared failed only when its submission window expires | an unproven epoch is reorged out (no reward); proposals under discussion remove bonds and pay every prover that delivers on time | 400 AZTEC a slot: 70% sequencers, 30% provers (120 AZTEC), provers' share by an activity score | the public testnet proved by community provers (zkcloud blog), no page number |
| zkSync Era, Linea, Scroll (eco.com comparisons) | a batch | none on chain (the operator proves) | none | the operator | proof latency about 30 min (zkSync Era), 75 min (Linea), 90 min (Scroll), approximate |
Reading. Nobody pays an aggregator as a separate role: Aztec's 30% goes to whoever delivers the epoch proof, Taiko's fee to whoever proves the batch, the markets to the request's winner. Deadlines run from 10 minutes (Succinct's example) through 1 h (Boundless' example) to 2 h (Taiko) and 1 h 16 min (Aztec's maximum window); a miss forfeits the reward or part of a bond, and the slashed value goes to the prover who steps in (Boundless). Igneum has no bond on shards by decision (spec 7.2 item 4), so the forfeit here is the reward only.
### The decisions
| Decision | Decided | Rule and number | Evidence |
|---|---|---|---|
| `proving_v1_segment_blocks` (N) | **8** | Aggregation is a fixed cost per block, not per segment: 9.6 to 9.7 s for every chained block on a mining 5090, 7.9 s unchained (`chain-pc2-pv1c`), so N buys nothing in card time; it sets the record cadence and the forfeit. At N = 8 and 1 block/s a record every 8 s, a 1.27 MB proof gossiped every 8 s (159 KB/s per path, half of N = 4's 318 KB/s), and a missed segment forfeits 8 blocks' aggregator share (8 x 0.088 IGN at today's credit). The chain for 8 blocks cost 135.6 s cold on a mining card (66.8 s for 4), a fifth of T; pipelined per block it is 17 s after the last block. Aztec proves 32 blocks (38 min) as one; 8 blocks at 1 block/s is 8 s of chain, so the record lands well inside the minute the litepaper promises | bench-log "proving v1" chain rows; Aztec economics page |
| `proving_v1_unproven_daa` (T) | **600** DAA s (10 min) | T = p99 x 10: the measured block-to-carried-record latency of a shard record is p99 52 to 62 s (two 30-min windows), a cold chain of 8 adds 136 s and relay plus inclusion 10 to 40 s, about 240 s worst case; 600 leaves 2.5x on that and equals the 600-block record window of v0, so nothing is payable past it either way. Succinct's example deadline is the same 10 minutes; Boundless' example 1 h, Taiko 2 h, Aztec up to 1 h 16 min: Igneum's blocks are 1 s and its proofs seconds, so the shortest of the field. The forfeited aggregator share of an unproven segment STAYS IN THE POOL ESCROW (it is never paid, as an unproven shard's part today): no burn and no roll-over, the rule the pool already has, and the escrow is what later proofs are paid from | coverage rows; `chain-pc2-pv1c`; the table above |
| `proving_v1_aggregator_share_bps` | **1,000** (a tenth) | The aggregator's card time per block is 9.7 s on a mining card against 4 x 10.6 s of shard proofs at `B_p` (19% of the card time) and 2.5 s against 42.5 s with the card to itself (6%); on tonight's empty blocks it is half the card time. A tenth of every attested block's pool credit sits between the two full-block ratios, pays a role no other network pays separately (Aztec pays its 30% to whoever delivers the epoch; the markets pay the winner), and leaves the shard provers 90%, which the fast-time harness showed paid exactly (shardWei 90% of the credit). The pool's 20% emission share itself is unchanged (spec 2.5, 5.3) | `chain-pc2-pv1c`; bench-log 4 October 5090 rows; the harness |
| `proving_v1_activation_daa` (H) | **the devnet tip + 14,400 at publish** (4 h at 1 block/s), set by the 0.3.11 publisher in the same override object as `program_class_v3` | tonight's rule for consensus switches (the coordinator, 5 October 2026); the digest moves only once H is set, so the rolling update does not partition |
| Aggregator sortition | **none in v1**: the first valid record carried wins | design 5.3's VRF draw (O-7.3) with one or two aggregators on the devnet changes nothing; the segment grid and the deadline already bound the race; revisit when a second aggregator exists |
| Apple silicon default | **off** | the gate was "a shard under 60 s with the miner running": the M5 Max CPU took 41.3 and 55.4 s for EMPTY shards under tonight's load and 272 s for a 200-pgas shard on 4 October; a full shard at `S_p` was never under 60 s. Settings switches it on | bench-log "proving v1" CPU chain row; 4 October CPU rows |
| The prover profile per card and the 12 GB and 16 GB gates (the project lead: "make sure we can prove on 12gb cards"; "is there any way we can make 12gb cards mine and prove?") | **measured on PC 2, the rows below** | the SP1 6.8.1 GPU server reads `ELEMENT_THRESHOLD`, `HEIGHT_THRESHOLD`, `SHARD_SIZE` and the `SP1_WORKER_NUM_*`/`BUFFER_SIZE` knobs from the environment it inherits (`sp1-core-executor-6.8.1/src/opts.rs`, `sp1-prover-6.8.1/src/worker/config.rs`); the app passes a profile per card (`provedefault.rs`) and the host forwards it | the sweep job `memsweep-pc2-pv1` and the miner-on run |
### The prover profiles (filled from the sweep)
(the table of peak against knobs against shard time, with the miner stopped and with the miner running, lands here when `memsweep-pc2-pv1` and the miner-on run report)
| Decision | Proposed | Why |
|---|---|---|
| `proving_v1_segment_blocks` (N) | 4 | measured: aggregation is a fixed 9.7 s per block on a mining 5090 whatever N, so N only sets how often a record is carried (every 4 s at 1 block/s) and how much a missed deadline forfeits (4 blocks' aggregator share); 8 halves the record traffic for the same card time |
| `proving_v1_unproven_daa` (T) | 600 | equals the 600-block record window of v0: nothing is payable for a segment after it either way; tonight's measured latency from block to carried shard record is p99 52 to 62 s, so 600 leaves 10x |
| `proving_v1_aggregator_share_bps` | 1,000 (a tenth) | the aggregation is one recursion per block, far cheaper than the shards; a tenth pays a second role without starving the shard provers; it is a consensus parameter in the digest |
| `proving_v1_activation_daa` (H) | 24 h after the 0.3.11 publish | the fee-switch rule |
| Aggregator sortition | none in v1 (first valid record wins) | design 5.3's VRF draw is O-7.3; with one or two aggregators on the devnet a draw changes nothing yet |
| Apple silicon default | off | until the GPU prover is measured on a Mac |

View file

@ -78,7 +78,7 @@ Nothing in consensus changes for any of this: the segment claim already commits
| Records per block | 8 | Implemented, 7.7 (Designed value) |
| Payout per shard | the segment's pool credit in equal parts, remainder to shard 0, paid by the carrying segment | Implemented, 7.7 |
| Proving v0 activation | `proving_v0_activation_daa`, default never | Implemented, 7.7 |
| Segment record (v1) | per segment of `proving_v1_segment_blocks` chain blocks, 586 bytes (the aggregator guest's 340-byte statement inline), BLS-signed by the aggregator's vote key, in the coinbase extra data before the shard record section (`IGNS`); proof bytes on p2p message 72 (protocol 14) | Implemented, 7.8 (branch `proving-v1`, 5 October 2026) |
| Segment record (v1) | per segment of `proving_v1_segment_blocks` chain blocks, 586 bytes (the aggregator guest's 340-byte statement inline), BLS-signed by the aggregator's vote key, in the coinbase extra data before the shard record section (`IGNS`); proof bytes on p2p message 75 (protocol 15) | Implemented, 7.8 (branch `proving-v1`, 5 October 2026) |
| Segment records per block | 2 | Implemented, 7.8 (Designed value) |
| Segment length `N` | `proving_v1_segment_blocks`, 4 | Implemented, value Designed (the project lead decides at 0.3.11) |
| Unproven deadline `T` | `proving_v1_unproven_daa`, 600 DAA s after the segment's last chain block | Implemented, value Designed (the project lead decides at 0.3.11) |
@ -127,7 +127,7 @@ Added 5 October 2026 (the project lead: "open the proving round asap"; branch `p
1. **The aggregated proof.** The aggregator guest of 7.6 (pinned, `elf/igneum-prove-aggregator`) verifies every shard proof of one chain block and, by recursion, the previous chain block's aggregated proof (`AggInput.prev`): its public values (`BlockOutput`, 340 bytes, mirrored in consensus as `BlockStatement`) carry `chain_len`, the number of consecutive chain blocks the proof attests, and `agg_vk`, the aggregator's own id whenever `chain_len > 1`. One compressed proof of constant size therefore attests any run of consecutive chain blocks (measured: `docs/bench-log.md`, "proving v1, aggregated chains on the RTX 5090"). This is design 5.3's "segment N verifies N-1" realised inside the proof rather than beside it.
2. **Segments.** From the first chain block `A` whose DAA score reaches `proving_v1_activation_daa`, chain blocks are grouped in fixed segments of `N = proving_v1_segment_blocks`: segment `k` is `A + kN ..= A + kN + N - 1`. The grid is a pure function of the chain, so every node names the same segments.
3. **The record.** One `SegmentRecord` (`kaspa_consensus_core::proving`): version 2, `first`, `last`, the hash of chain block `last`, the aggregator's BLS vote key, the payout address, the aggregated proof's 340 public values inline, the SHA-256 of the proof bytes, and a BLS signature over all of it under `IGNEUM_SEGMENT_RECORD_V1`, domain-separated with the network name. 586 bytes. The statement the verifier checks is keccak256 of the public values.
4. **Carriage.** Records ride in the coinbase extra data as a section `records || len_le32 || "IGNS"` placed before the shard record section (`IGNP`), which sits before the finality section; at most 2 per block. A node that does not read the section sees miner bytes. The proof bytes travel beside the record on p2p message `IgneumSegmentRecordMessage` (type 72, protocol version 14; peers at 13 never receive it) and through `igneum_submitSegmentRecord`.
4. **Carriage.** Records ride in the coinbase extra data as a section `records || len_le32 || "IGNS"` placed before the shard record section (`IGNP`), which sits before the finality section; at most 2 per block. A node that does not read the section sees miner bytes. The proof bytes travel beside the record on p2p message `IgneumSegmentRecordMessage` (type 75, protocol version 15; peers at 14 and below never receive it; 14 is the EVM transaction relay of 0.3.10) and through `igneum_submitSegmentRecord`.
5. **What every node checks on a carried record (consensus).** The segment is aligned on the grid and its last block is on the executor's own chain, at most 600 chain blocks behind the carrier; the signature verifies; the public values equal the node's native block statement for chain block `last` in every field but `provers` and `chain_len` (`chain_id`, `number`, `block_hash`, `parent_hash`, `shard_count`, `tx_commitment`, `pre_root`, `post_root`, the keccak of the shard receipts roots, gas, pgas, executed, skipped, `shard_vk` = the pinned shard program id, `agg_vk` = the pinned aggregator id when `chain_len > 1` and zero otherwise); `chain_len` is at least `N` and at most the chain height; the chain rule of item 6; and the deadline of item 7. A record that fails is ignored, not a fault of the block: it pays nothing. The SP1 proof is not verified on this path (item 8).
6. **The chain rule.** A record whose `chain_len > N` chains to the previous segment's proof by recursion (the proof itself verified it), and is valid whatever the chain says about that segment. A record whose `chain_len = N` starts a fresh chain and is valid only for the first segment of the grid, or when the previous segment is unproven (item 7). So a proven segment is followed only by records that verify it; an unproven one may be skipped.
7. **The unproven rule.** A segment whose last chain block is more than `T = proving_v1_unproven_daa` DAA seconds old at the carrier, with no paid record, is unproven: the pool pays nothing for it (its aggregator share stays in the escrow), a record for it carried after the deadline is invalid, and the next segment may start a fresh chain. A segment with a paid record is proven; before its deadline it is pending. `igneum_getProvingStatus` reports the three counts over the record window.