Release manifest and the proving economics: site/release-manifest.json served at /release.json (network and chain id, source commits and fingerprints, mining class and dataset, finality rule v3, proof program ids, verifier off per P21, fees, versions per platform, the proving view, generated date); /build, /economics, /miner, /evidence and the litepaper read it at build through data-rm spans and a gate check holds the pages to it; /light, /receipt and the light client say two thirds of total weight; 4463 marked historical in older docs; dated log entries labelled historical; the evidence page's sixth label (activated) and the reference-repository wording; /economics gains "Who pays for proving": three incomes apart, the burned base fee stated, the measured 24-hour payout, the halving walk computed from emission.mjs at build, per card per hour, the two routes priced, no price
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
abf5a5a606
commit
ffd6751e7c
27 changed files with 456 additions and 80 deletions
7
docs/build/build.md
vendored
7
docs/build/build.md
vendored
|
|
@ -16,11 +16,12 @@ This file is the source of [igneum.network/build](https://igneum.network/build).
|
|||
|
||||
| | Devnet 3 | Testnet | Mainnet |
|
||||
|---|---|---|---|
|
||||
| Chain id | 4464 (`0x1170`) since its class v5 floor on 8 October 2026; 4463 below it | 4462 (`0x116e`) | 4461 (`0x116d`) |
|
||||
| Network id | `igneum-devnet-3` | `igneum-testnet-1` | not started |
|
||||
| RPC | `https://rpc.devnet.igneum.network` (a node you run serves `http://127.0.0.1:26790`) | `https://rpc.testnet.igneum.network` (answers; nothing mines there yet, so a transaction waits) | none |
|
||||
| Chain id | {{rm:network.chain_id}} (`{{rm:network.chain_id_hex}}`) since its class v5 floor on 8 October 2026; 4463 below it | 4462 (`0x116e`) | 4461 (`0x116d`) |
|
||||
| Network id | `{{rm:network.id}}` | `igneum-testnet-1` | not started |
|
||||
| RPC | `{{rm:network.rpc}}` (a node you run serves `http://127.0.0.1:26790`) | `https://rpc.testnet.igneum.network` (answers; nothing mines there yet, so a transaction waits) | none |
|
||||
| Coins | no value, resets without notice | no value, resets with notice | not started |
|
||||
| Symbol, decimals | IGN, 18 | IGN, 18 | IGN, 18 |
|
||||
| Machine-readable | [/release.json](/release.json): the chain id, the source commits and fingerprints, the mining class, the finality rule, the proof program ids, the fee schedule and the current version per platform, with a generated date | | |
|
||||
|
||||
Devnet 3 is where you build today. It is a developer network: it resets without notice and its coins have no value. Its public RPC takes the read methods, `eth_sendRawTransaction` and a wRPC websocket at `/ws`, at 20 requests a second per address. The public testnet exists, its RPC answers, and no blocks are being produced on it until it opens. Mainnet has no date. Devnet 3 answered 4463 until its class v5 floor at DAA 68,400 on 8 October 2026 and 4464 from it; read it with `eth_chainId` rather than fixing it, since a transaction signed for the wrong id is refused.
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ What a builder expects on day one, what Igneum has, and the order to build the r
|
|||
|
||||
| Item | Expected | Igneum has | Lacks | Order |
|
||||
|---|---|---|---|---|
|
||||
| EVM | Cancun, same bytecode | Cancun opcodes, precompiles 0x01 to 0x09, revm 43; chain ids 4461 / 4462 / 4463; viem 2.57 drove deploy, write, read and fee estimation through stock paths | EIP-7702, 0x0a, blobs (by design); the documented differences table of spec 7.1 must be in the developer docs | Docs: 1 |
|
||||
| EVM | Cancun, same bytecode | Cancun opcodes, precompiles 0x01 to 0x09, revm 43; chain ids 4461 / 4462 / 4463 (historical: Devnet 3 answers chain id 4464 since its class v5 floor at DAA 68,400 on 8 October 2026, 4463 below it; the current id is in /release.json); viem 2.57 drove deploy, write, read and fee estimation through stock paths | EIP-7702, 0x0a, blobs (by design); the documented differences table of spec 7.1 must be in the developer docs | Docs: 1 |
|
||||
| RPC | Full `eth_*`, `debug_*`, `trace_*`, subscriptions | 25 `eth_*` methods, `igneum_getTransactionStatus`, `igneum_getSegment`, `igneum_getBudgets`, `igneum_estimateGas` (both dimensions); HTTP JSON-RPC 2.0 with batches, port 26790 | `eth_getProof`, `eth_subscribe`, `debug_traceTransaction`, `trace_block`, `eth_getUncle*`, `IgneumInfo` (design 10.3 item 7); `pending`, `safe`, `finalized` all resolve to the executed tip | 2 (debug and subscribe are what Blockscout and Foundry's debugger need) |
|
||||
| RPC providers | A public endpoint, then third parties | None public. The devnet is the team's three nodes and a seed | A public devnet RPC with a rate limit; chain-id registration on ethereum-lists/chains before the public testnet (design 8.1) | 3 |
|
||||
| Tooling templates | Hardhat and Foundry work | Designed: templates "ship with the devnet" with the three things to tell a developer (design 8.3) | The templates themselves; a `vm.warp` note (past timestamps rejected) | 1 |
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ Rule change, 4 October 2026 (findings F-exec-A and F-exec-B of the attack suite,
|
|||
| Miner address | Low 20 bytes of the header's `vote_key_hash` (`evm::miner_evm_address`) | The body has no `miner` field yet. A miner that wants to spend its rewards passes `--vote-key-hash 0x000000000000000000000000<address>` to `igneum-miner`. After the finality branch merges, `vote_key_hash` is the hash of a BLS key, so this rule must give way to the body's `miner` field (10.4) |
|
||||
| Units | 1 sompi = 1e10 wei; 1 IGN = 1e18 wei | Subsidies come from `igneum::block_subsidy` in 8-decimal sompi; the EVM is 18-decimal. The open "8 or 18 decimals" decision is unchanged; this is the fixed scaling at the bridge named there |
|
||||
| Rewards | 80% of every blue block's subsidy to its miner, 20% to the proving pool escrow `0x...0220`, both credited in the segment that merges the block; reds unpaid | Design 4.4, with the pool held in a keyless account until proof records exist |
|
||||
| Simnet | Devnet block rate and depths (1 BPS, k 18, mergeset 180, merge depth 3,600) with proof of work skipped; chain id 4463 shared with the devnet | A CPU test network of the devnet DAG shape |
|
||||
| Simnet | Devnet block rate and depths (1 BPS, k 18, mergeset 180, merge depth 3,600) with proof of work skipped; chain id 4463 shared with the devnet (historical: Devnet 3 answers chain id 4464 since its class v5 floor at DAA 68,400 on 8 October 2026, 4463 below it; the current id is in /release.json) | A CPU test network of the devnet DAG shape |
|
||||
| Mempool hold | A transaction stays in every template until a block carrying it is added to the DAG (any block, this node's or a peer's: the executor subscribes to consensus `BlockAdded`); then it is held for 30 s or until the executor removes it (executed) or offers it again (a chain block skipped it); a transaction skipped twice is dropped | Kaspa's own rule (`mining/src/manager.rs`, `handle_new_block_transactions`). Replaced the 4-second hand-out cooldown on 5 October 2026 (fork `tx-gossip`, `pool.rs IN_BLOCK_HOLD`, `service.rs listen_block_added`): the cooldown made a sender mineable 1 s in 5 and inclusion came in 50-s bursts (bench-log, 5 October 2026 afternoon); with the hold a transaction is offered to every template until a block has it |
|
||||
| Reorgs | Post-segment states for the last 64 chain blocks; deeper reorgs replay from genesis | Observed depth on the test network: 1 to 3 with Poisson-paced miners. A fixed per-template hold had made the three stub miners mine in lockstep rounds, and with equal work per block the GHOSTDAG hash tie-break then kept two equal-work chains alive from genesis (flips 48 deep every few seconds); `igneum-miner --hold-ms` is exponential now |
|
||||
| Block tags | `pending`, `safe` and `finalized` all resolve to the executed tip | The virtual's segment is not executed eagerly and no certified checkpoint exists on this branch; the RPC does not pretend otherwise |
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ Full-header mode (spec 10.4 item 6, the lottery verified on the phone) is not in
|
|||
| Key storage | iOS Secure Enclave through the Keychain with device-only, biometric-gated access; Android Keystore with StrongBox when the device has it, else TEE-backed. The signing key for an EVM transaction is secp256k1, which the enclaves do not sign natively, so the private key is wrapped by an enclave key and unwrapped into memory only for the duration of one signature (approximate: the common pattern on both platforms) | Designed |
|
||||
| Seed | BIP-39, 24 words, generated on device; shown once, confirmed by re-entering words in random positions (spec 8.5 item 1's rule for the desktop app, applied here); optional second confirmation after 24 hours | Designed |
|
||||
| Derivation | BIP-44 path `m/44'/coin'/0'/0/n` with Igneum's SLIP-44 coin type; the coin type is unregistered (Open, P1 below) and the app uses Ethereum's 60 until it is, so a seed restored into any Ethereum wallet shows the same addresses | Designed, Open |
|
||||
| Address | 20-byte EVM address, chain id 4461 / 4462 / 4463 (spec 7.4), EIP-155 signatures, transaction types 0, 1, 2 (design document, execution layer, 1.1) | Designed |
|
||||
| Address | 20-byte EVM address, chain id 4461 / 4462 / 4463 (spec 7.4) (historical: Devnet 3 answers chain id 4464 since its class v5 floor at DAA 68,400 on 8 October 2026, 4463 below it; the current id is in /release.json), EIP-155 signatures, transaction types 0, 1, 2 (design document, execution layer, 1.1) | Designed |
|
||||
| Gas | One gas limit and one price, as the node quotes them with proving gas folded in (spec 7.1, "quoted gas price"); the app shows the quote and never lets a user set a limit below the estimate | Designed |
|
||||
| Status line | Every transaction shows executed, proven, locked as `igneum_getTransactionStatus` reports them, with the app's own verification state beside it: "locked" is shown only when the engine has verified the certificate that covers the block | Designed |
|
||||
| Backup | None by the project. The app offers no cloud backup of the seed and refuses the platform's automatic keychain sync for the wallet entries; the user writes the words down | Designed (spec 8.5 item 3) |
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -115,7 +115,7 @@ Implements `docs/design/execution-layer.md` D1 to D10 and section 8.2 on a 3-nod
|
|||
|
||||
| File (vendor/igneum-node-exec/) | What changed | Why | Risk | Upstream-merge note |
|
||||
|---|---|---|---|---|
|
||||
| `consensus/core/src/evm.rs` (new), `consensus/core/src/lib.rs`, `consensus/core/Cargo.toml` (sha3) | `EvmTransaction = Vec<u8>` (raw EIP-2718 bytes), `evm_tx_hash` (keccak256), `evm_chain_id` (4461 / 4462 / 4463, simnet shares the devnet id), `miner_evm_address` (low 20 bytes of `vote_key_hash`), `BLOCK_EXECUTION_GAS_LIMIT` 30 M, `MAX_EVM_BODY_BYTES` 1 MiB, the `EvmTemplateSource` trait | Design D1, D8, 8.1; the devnet rule for the `miner` address until the body carries a 20-byte field | Low | Pure addition. |
|
||||
| `consensus/core/src/evm.rs` (new), `consensus/core/src/lib.rs`, `consensus/core/Cargo.toml` (sha3) | `EvmTransaction = Vec<u8>` (raw EIP-2718 bytes), `evm_tx_hash` (keccak256), `evm_chain_id` (4461 / 4462 / 4463, simnet shares the devnet id) (historical: Devnet 3 answers chain id 4464 since its class v5 floor at DAA 68,400 on 8 October 2026, 4463 below it; the current id is in /release.json), `miner_evm_address` (low 20 bytes of `vote_key_hash`), `BLOCK_EXECUTION_GAS_LIMIT` 30 M, `MAX_EVM_BODY_BYTES` 1 MiB, the `EvmTemplateSource` trait | Design D1, D8, 8.1; the devnet rule for the `miner` address until the body carries a 20-byte field | Low | Pure addition. |
|
||||
| `consensus/core/src/block.rs` | `Block` and `MutableBlock` gain `evm_transactions` (`Arc<Vec<EvmTransaction>>` / `Vec<EvmTransaction>`); `Block::new` keeps its signature (empty EVM list), `with_evm_transactions`, `from_arcs_with_evm`; `MutableBlock::set_evm_transactions` recomputes the merkle root and re-finalizes the header | Design D1: EVM transactions in the body | Medium by spread: every `Block {..}` literal in the tree had to name the field (six sites) | Upstream additions of `Block` literals will fail to compile until the field is added; the compiler finds them. |
|
||||
| `consensus/core/src/merkle.rs` | `calc_block_hash_merkle_root(utxo_txs, evm_txs)`: leaves are Kaspa's transaction hashes followed by keccak256 of each raw EVM transaction | Design D7: `hash_merkle_root` is reused as the body commitment and now covers the EVM transactions. With no EVM transactions the root equals Kaspa's, so no genesis hash moved | Low | Pure addition; `calc_hash_merkle_root` is untouched. |
|
||||
| `consensus/src/model/stores/block_transactions.rs` | Stored body is `BlockBody(utxo_txs, evm_txs)`; `get_evm`, `insert_batch` and `insert` take both | One store, one write per body | Low | Database format changed: a node from before this branch must resync. `insert` has one more argument; upstream call sites conflict trivially. |
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ Blockscout indexes through standard JSON-RPC. Its documented requirements (docs.
|
|||
|
||||
Cost of one instance on Hetzner (the price list the seeds are on, `docs/plans/seed-nodes.md`: cx23 2 vCPU 4 GB at USD 6.49 net a month; larger types not priced here): Blockscout's own AWS example is 4 vCPU 16 GB plus a 2 vCPU 8 GB database. The matching Hetzner shape is one box in the 8 GB to 16 GB class plus Postgres on the same box for a devnet, a second box for the database when the chain carries real traffic. Price it from the Hetzner API when the box is ordered; the figure here is approximate: USD 15 to 40 a month for the single box, under USD 80 for two. Plus an Igneum node on the same box or next to it (Blockscout wants a local, unlimited RPC; the public `rpc.testnet.igneum.network` is rate limited to 20 req/s, `docs/plans/testnet-go.md`).
|
||||
|
||||
What it costs in work, in hours not weeks: the two missing `eth_` methods (small, same shape as their by-number siblings); a decision on tracing (the `debug_` namespace with revm inspectors, design 8.2, is the larger piece and is not needed to run Blockscout without internal transactions); Blockscout's env file and a Docker compose on the box; the chain's entry in its config (chain id 4463 devnet, 4462 testnet, 4461 mainnet, design 8.1); contract verification through Sourcify or Blockscout's own verifier microservice.
|
||||
What it costs in work, in hours not weeks: the two missing `eth_` methods (small, same shape as their by-number siblings); a decision on tracing (the `debug_` namespace with revm inspectors, design 8.2, is the larger piece and is not needed to run Blockscout without internal transactions); Blockscout's env file and a Docker compose on the box; the chain's entry in its config (chain id 4463 devnet, 4462 testnet, 4461 mainnet (historical: Devnet 3 answers chain id 4464 since its class v5 floor at DAA 68,400 on 8 October 2026, 4463 below it; the current id is in /release.json), design 8.1); contract verification through Sourcify or Blockscout's own verifier microservice.
|
||||
|
||||
## 3. What Igneum needs that must be ours
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ How to read the licence column. "Verified" means the LICENSE file or the crate's
|
|||
| BLS12-381 | Curve by Barreto, Lynn and Scott; blst by Supranational. Licence approximate: Apache-2.0. https://github.com/supranational/blst (not yet cloned) | Unchanged, credited. The header carries the BLAKE2b hash of a G1 compressed public key (48 bytes); every voter signs every 30-s checkpoint; signatures aggregate | Finality rule v2 needs one aggregate signature per checkpoint from thousands of keys | Spec section 3.1 (W1) and 2.4; `sim/results_v2.md` for the rule itself. Signature cost not yet measured |
|
||||
| Hashing in the node | rusty-kaspa `crypto/hashes`, ISC, verified. Crates linked by the fork, licences read from the local cargo registry: blake2b_simd 1.0.2 (MIT), blake3 1.8.3 (CC0-1.0 or Apache-2.0), sha2 0.10.8 (MIT or Apache-2.0), keccak 0.1.6 (Apache-2.0 or MIT); sha3 0.10.8 not in the local registry, approximate: MIT or Apache-2.0 | Unchanged, credited. BLAKE2b with domain separation for block, transaction and PoW pre-hashes (`BlockHash`, `TransactionHash`, ...), BLAKE3 keyed for the sequencing-commitment and payload hashers, SHA-256 for ECDSA signing hashes, cSHAKE256 (Keccak) in the kHeavyHash stub that stays as the default engine and for pruning-proof block levels | The chain's hash for everything except the lottery is the one the forked commit uses (spec 0.6), so nothing unverified enters consensus | `hash_override_nonce_time` gained one field (fork-divergence row 1); every header-hash test vector was regenerated and the four genesis hashes re-derived |
|
||||
| Address format | Bitcoin BIP 173 character set (bech32, Pieter Wuille and Greg Maxwell; BIP licence approximate: BSD-2-Clause) with the CashAddr polymod checksum of Bitcoin Cash (the source cites bch.info), as implemented in rusty-kaspa `crypto/addresses/src/bech32.rs`, ISC, verified | Prefixes only: `igneum`, `igneumtest`, `igneumsim`, `igneumdev` (Kaspa: `kaspa`, `kaspatest`, `kaspasim`, `kaspadev`). The script public key behind an address is unchanged | No Igneum address string may parse as a Kaspa address on any network | Fork-divergence row 9; test vectors in `addresses` and `txscript` regenerated and passing |
|
||||
| The EVM | Ethereum (Yellow Paper and ethereum/execution-specs, CC0 approximate). Cancun opcode set, precompiles 0x01 to 0x09 | Semantics on a DAG: `block.number` is selected-chain height, `block.timestamp` is non-decreasing by a max rule, `prevrandao` is the VDF epoch seed, chain ids 4461, 4462, 4463; 0x0a absent; gas has a second dimension | Blocks on a DAG have no single parent and no header state root; proving cost is a second resource; the random beacon must be unbiasable | Spec section 7.1 (normative table); devnet measurement R9 for timestamp drift; `ethereum/tests` replay in the differential plan |
|
||||
| The EVM | Ethereum (Yellow Paper and ethereum/execution-specs, CC0 approximate). Cancun opcode set, precompiles 0x01 to 0x09 | Semantics on a DAG: `block.number` is selected-chain height, `block.timestamp` is non-decreasing by a max rule, `prevrandao` is the VDF epoch seed, chain ids 4461, 4462, 4463 (historical: Devnet 3 answers chain id 4464 since its class v5 floor at DAA 68,400 on 8 October 2026, 4463 below it; the current id is in /release.json); 0x0a absent; gas has a second dimension | Blocks on a DAG have no single parent and no header state root; proving cost is a second resource; the random beacon must be unbiasable | Spec section 7.1 (normative table); devnet measurement R9 for timestamp drift; `ethereum/tests` replay in the differential plan |
|
||||
| kHeavyHash (kept as a stub) | Kaspa, rusty-kaspa `crypto/hashes/src/pow_hashers.rs` and `consensus/pow/src/matrix.rs`, ISC, verified | Kept untouched as `HeavyHashEngine`, the default engine when the `igneum-pow` feature is off, and the block-level source for pruning proofs until seeds are threaded through | Lets the devnet run and lets upstream pow changes merge cleanly | Fork-divergence rows 14 and 15; open item in the same file (pruning-proof block levels) |
|
||||
|
||||
## What is new in Igneum
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ Nothing in consensus changes for any of this: the segment claim already commits
|
|||
|
||||
| Parameter | Value | Label |
|
||||
|---|---|---|
|
||||
| Chain id | 4461 / 4462 / 4463 (mainnet / testnet / devnet) | Designed |
|
||||
| Chain id | 4461 / 4462 / 4463 (mainnet / testnet / the shared devnet); Devnet 3 answers 4464 since its class v5 floor at DAA 68,400 (8 October 2026) and 4463 below it, the id a function of the block's DAA score | Designed; the Devnet 3 value measured (the current id is in /release.json) |
|
||||
| BLOCKHASH reach | 256 chain blocks | Designed (Ethereum's) |
|
||||
| PREVRANDAO source | 10-minute epoch VDF output, keccak256 with the chain height | Designed |
|
||||
| Opcode set, precompiles | Cancun; 0x01 to 0x09 | Designed |
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -253,7 +253,7 @@ table{min-width:560px}
|
|||
<p><strong>The EVM you already know.</strong> Solidity deploys unchanged. Standard JSON-RPC, EIP-1559 transactions, the chain id in the signature, <code>cancun</code> as the EVM version. Gas has two dimensions on Igneum, execution and proving, and the node folds the second into the price it quotes, so <code>eth_estimateGas</code> and <code>eth_gasPrice</code> work as they do on Ethereum.</p>
|
||||
</section>
|
||||
<section class="doc-section"><h2 id="networks">Networks</h2>
|
||||
<div class="tbl"><table><thead><tr><th></th><th>Devnet 3</th><th>Testnet</th><th>Mainnet</th></tr></thead><tbody><tr><td>Chain id</td><td>4464 (<code>0x1170</code>) since its class v5 floor on 8 October 2026; 4463 below it</td><td>4462 (<code>0x116e</code>)</td><td>4461 (<code>0x116d</code>)</td></tr><tr><td>Network id</td><td><code>igneum-devnet-3</code></td><td><code>igneum-testnet-1</code></td><td>not started</td></tr><tr><td>RPC</td><td><code>https://rpc.devnet.igneum.network</code> (a node you run serves <code>http://127.0.0.1:26790</code>)</td><td><code>https://rpc.testnet.igneum.network</code> (answers; nothing mines there yet, so a transaction waits)</td><td>none</td></tr><tr><td>Coins</td><td>no value, resets without notice</td><td>no value, resets with notice</td><td>not started</td></tr><tr><td>Symbol, decimals</td><td>IGN, 18</td><td>IGN, 18</td><td>IGN, 18</td></tr></tbody></table></div>
|
||||
<div class="tbl"><table><thead><tr><th></th><th>Devnet 3</th><th>Testnet</th><th>Mainnet</th></tr></thead><tbody><tr><td>Chain id</td><td><span data-rm="network.chain_id">4464</span> (<code><span data-rm="network.chain_id_hex">0x1170</span></code>) since its class v5 floor on 8 October 2026; 4463 below it</td><td>4462 (<code>0x116e</code>)</td><td>4461 (<code>0x116d</code>)</td></tr><tr><td>Network id</td><td><code><span data-rm="network.id">igneum-devnet-3</span></code></td><td><code>igneum-testnet-1</code></td><td>not started</td></tr><tr><td>RPC</td><td><code><span data-rm="network.rpc">https://rpc.devnet.igneum.network</span></code> (a node you run serves <code>http://127.0.0.1:26790</code>)</td><td><code>https://rpc.testnet.igneum.network</code> (answers; nothing mines there yet, so a transaction waits)</td><td>none</td></tr><tr><td>Coins</td><td>no value, resets without notice</td><td>no value, resets with notice</td><td>not started</td></tr><tr><td>Symbol, decimals</td><td>IGN, 18</td><td>IGN, 18</td><td>IGN, 18</td></tr><tr><td>Machine-readable</td><td><a href="/release.json">/release.json</a>: the chain id, the source commits and fingerprints, the mining class, the finality rule, the proof program ids, the fee schedule and the current version per platform, with a generated date</td><td></td><td></td></tr></tbody></table></div>
|
||||
<p>Devnet 3 is where you build today. It is a developer network: it resets without notice and its coins have no value. Its public RPC takes the read methods, <code>eth_sendRawTransaction</code> and a wRPC websocket at <code>/ws</code>, at 20 requests a second per address. The public testnet exists, its RPC answers, and no blocks are being produced on it until it opens. Mainnet has no date. Devnet 3 answered 4463 until its class v5 floor at DAA 68,400 on 8 October 2026 and 4464 from it; read it with <code>eth_chainId</code> rather than fixing it, since a transaction signed for the wrong id is refused.</p>
|
||||
</section>
|
||||
<section class="doc-section"><h2 id="endpoints-and-tools">Endpoints and tools</h2>
|
||||
|
|
|
|||
|
|
@ -131,11 +131,27 @@ function meta(title, desc, path) {
|
|||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
||||
<link rel="manifest" href="/site.webmanifest">`;
|
||||
}
|
||||
// The release manifest (8 October 2026, an accepted external review): site/release-manifest.json is served at /release.json
|
||||
// (vercel.json rewrite) and every number a status page carries from it sits in <span data-rm="a.b.c">...</span>, filled here
|
||||
// at build; tools/ci/release-manifest-check.mjs holds the committed pages to the manifest and the manifest to its sources.
|
||||
const RM = JSON.parse(readFileSync(join(here, 'release-manifest.json'), 'utf8'));
|
||||
export function rmValue(path) {
|
||||
const v = path.split('.').reduce((o, k) => (o == null ? undefined : o[k]), RM);
|
||||
if (v === undefined) throw new Error(`release-manifest.json: no value at ${path}`);
|
||||
return typeof v === 'number' && !/_id$/.test(path) ? v.toLocaleString('en-GB') : String(v); // an id is never grouped
|
||||
}
|
||||
export function fillManifest(html) {
|
||||
// the hand pages carry <span data-rm>; the markdown sources carry {{rm:path}} (their renderer escapes raw HTML)
|
||||
html = html.replace(/\{\{rm:([a-z0-9_.-]+)\}\}/g, (m, p) => `<span data-rm="${p}">${rmValue(p).replace(/&/g, '&').replace(/</g, '<')}</span>`);
|
||||
return html.replace(/<span data-rm="([a-z0-9_.-]+)">[^<]*<\/span>/g, (m, p) => `<span data-rm="${p}">${rmValue(p).replace(/&/g, '&').replace(/</g, '<')}</span>`);
|
||||
}
|
||||
function sectionise(bodyHtml) {
|
||||
// the markdown renderer's flat stream, cut at every h2 into <section class="doc-section"> so the contents rail and the
|
||||
// text filter work on sections; anything before the first h2 is its own section
|
||||
const parts = bodyHtml.split(/(?=<h2 id=")/);
|
||||
return parts.filter(p => p.trim()).map(p => `<section class="doc-section">${p}</section>`).join('\n');
|
||||
// a dated log entry is a historical record (8 October 2026): the label sits under its heading, the current state is /release.json
|
||||
const hist = (p) => p.replace(/^(<h2 id="(\d{4}-\d{2}-\d{2})[^"]*">[\s\S]*?<\/h2>)/, (m, h, d) => `${h}<p class="src"><b>Historical record</b> of ${d}: the code and the chain as they stood that day. The chain's current state is the <a href="/release.json">release manifest</a>.</p>`);
|
||||
return parts.filter(p => p.trim()).map(p => `<section class="doc-section">${hist(p)}</section>`).join('\n');
|
||||
}
|
||||
function page(title, desc, bodyHtml, toc, note, { path = '/bench', heading = title, eyebrow, crumb, filter = true } = {}) {
|
||||
const active = path.replace(/^\//, '');
|
||||
|
|
@ -288,7 +304,8 @@ for (const [src, file, title, desc, heading, lead, eyebrow] of [
|
|||
]) {
|
||||
const mdp = join(docs, 'build', src);
|
||||
if (!existsSync(mdp)) continue; // the gate builds a copy of site/ alone: the committed page stands, as for /bench
|
||||
const { html, toc } = md(readFileSync(mdp, 'utf8'));
|
||||
const { html: mdHtml, toc } = md(readFileSync(mdp, 'utf8'));
|
||||
const html = fillManifest(mdHtml);
|
||||
const path = '/' + file.replace(/\.html$/, '');
|
||||
writeFileSync(join(here, file), page(title, desc, html, toc, lead, { path, heading, crumb: heading, eyebrow, filter: false }).replace('<p class="gen">Generated from the repository at build time. Times are UTC. Machine names are model names.</p>', `<p class="gen">Generated from docs/build/${src} in the repository at build time. Times are UTC.</p>`));
|
||||
built.push(file);
|
||||
|
|
@ -378,7 +395,7 @@ function renderEvidence(html) {
|
|||
const start = src.indexOf('\n| # | Claim |'); const end = src.indexOf('\n## Count by status');
|
||||
if (start < 0 || end < 0) throw new Error('evidence.md: claims table not found');
|
||||
const rows = src.slice(start, end).split('\n').filter(l => /^\| \d+ \|/.test(l));
|
||||
const LABELS = ['designed', 'implemented', 'tested by the team', 'reproduced externally', 'reviewed independently'];
|
||||
const LABELS = ['designed', 'implemented', 'activated', 'tested by the team', 'reproduced externally', 'reviewed independently'];
|
||||
const inl = t => esc(t.trim()).replace(/`([^`]+)`/g, (m, c) => `<code>${c}</code>`);
|
||||
const cells = l => l.replace(/^\| /, '').replace(/ \|$/, '').split(' | ');
|
||||
const counts = Object.fromEntries(LABELS.map(k => [k, 0]));
|
||||
|
|
@ -424,6 +441,30 @@ for (const [file, active] of PAGES) {
|
|||
html = stampDownloads(html, file, downloads);
|
||||
html = stampMarks(html);
|
||||
if (file === 'evidence.html') html = renderEvidence(html);
|
||||
// /economics, "Who pays for proving" (8 October 2026, an accepted external review): the model table is computed here from
|
||||
// site/lib/emission.mjs (the node's constants) and the release manifest, never typed; the measured rows on the page are the
|
||||
// observer's proving view as read that day. Labels: the per-day pool is modelled from the schedule; the per-shard and per-key
|
||||
// lines hold today's measured shard and key counts constant; nothing here is a price.
|
||||
if (file === 'economics.html' && html.includes('<!-- proving-model:start -->')) {
|
||||
const em = await import('./lib/emission.mjs');
|
||||
const ign = (s) => Number(em.sompiToIgn(s));
|
||||
const fmt = (n, d = 2) => n.toLocaleString('en-GB', { maximumFractionDigits: d, minimumFractionDigits: d });
|
||||
const PV = RM.proving_view; // the measured 24-hour read on the page (shards paid, IGN paid, keys, planned)
|
||||
const daa = Number(PV.read_daa);
|
||||
const poolShare = Number(RM.fees.subsidy_split.proving_pool_percent) / 100;
|
||||
const rows = [];
|
||||
const line = (label, subsidyIgn, when, note) => {
|
||||
const poolBlock = subsidyIgn * poolShare, poolDay = poolBlock * 86400;
|
||||
rows.push(`<tr><td>${label}</td><td class="num">${fmt(subsidyIgn, 3)}</td><td class="num">${fmt(poolBlock, 3)}</td><td class="num">${fmt(poolDay, 0)}</td><td class="num">${fmt(poolDay / PV.shards_planned_24h, 2)}</td><td class="num">${fmt(poolDay / 24 / PV.prover_keys_24h, 1)}</td><td>${when}${note ? `; ${note}` : ''}</td></tr>`);
|
||||
};
|
||||
line('Today, inside the launch ramp', ign(em.blockSubsidy(daa)), `DAA ${daa.toLocaleString('en-GB')}, ${(em.rampFactor(daa) * 100).toFixed(1)} percent of the full rate`, 'modelled; the measured payout is in the row above');
|
||||
const names = ['Full rate (years 1 to 2)', 'After the first halving (years 3 to 4)', 'After the second (years 5 to 6)', 'After the third (years 7 to 8)', 'After the fourth (years 9 to 10)', 'After the fifth (years 11 to 12)'];
|
||||
names.forEach((n, p) => line(n, ign(em.subsidyPerSecond(p)), `period ${p} of the schedule`, p === 0 ? 'from day 30' : ''));
|
||||
const table = `<div class="tbl"><table><thead><tr><th>When</th><th>Subsidy per block, IGN</th><th>Pool per block, IGN</th><th>Pool per day, IGN</th><th>Per planned shard, IGN</th><th>Per prover key per hour, IGN</th><th>Basis</th></tr></thead><tbody>${rows.join('')}</tbody></table></div>
|
||||
<p class="src"><b>Modelled</b> from <code>emission.rs</code> (the schedule as <code>site/lib/emission.mjs</code> carries it) and the release manifest at build time, with today's measured shard count (${PV.shards_planned_24h.toLocaleString('en-GB')} planned in 24 hours) and key count (${PV.prover_keys_24h}) held constant; the per-shard figure is the pool's credit per planned shard, the per-key figure the pool per day divided by the keys and by 24. More shards or more keys lower both; a block's unproven shards leave their credit in the escrow.</p>`;
|
||||
html = inject(html, 'proving-model', table, file);
|
||||
}
|
||||
html = fillManifest(html);
|
||||
if (file === 'index.html' && html.includes('<!-- journey:start -->')) html = inject(html, 'journey', `<script type="application/json" id="journey-data">${JSON.stringify(journey).replace(/</g, '\\u003c')}</script>`, file);
|
||||
// /income (8 October 2026): the calculator's card list is the bench table's current rows, stock and tuned apart, inlined
|
||||
// at build so the page needs no fetch; a row's watts carry their class when they were read under another (watts_class)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Igneum economics: the emission as the node encodes it</title>
|
||||
<meta name="description" content="The emission schedule, the 80/20 split, the fee routes, the proving-fee market, the switchable 1 percent client fee and the decimals, each as a table with its file and line in the node. No price, no projection.">
|
||||
<meta name="description" content="The emission schedule, the 80/20 split, the fee routes, the proving-fee market, the switchable 1 percent client fee, who pays for proving under the halvings, and the decimals, each as a table with its file and line in the node. No price.">
|
||||
<link rel="canonical" href="https://igneum.network/economics">
|
||||
<!-- head:start -->
|
||||
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
|
||||
|
|
@ -193,7 +193,7 @@
|
|||
<section class="page-hero"><div class="container">
|
||||
<div class="breadcrumb"><a href="/">Igneum</a><span>/</span><span>Economics</span></div>
|
||||
<h1>The economics, as the node encodes them.</h1>
|
||||
<p class="lead">Every number below is read from the node’s source at one commit, with the file and the line. No price of IGN appears and nothing is projected: this page says what the code pays, to whom, and from what.</p>
|
||||
<p class="lead">Every number below is read from the node’s source at one commit, with the file and the line, and the constants the chain runs on are the <a href="/release.json">release manifest</a>’s, filled at build time (node <span data-rm="source.node.commit">f8da7515</span>, <span data-rm="read_at_short">read 8 October 2026, 15:50 UK</span>). No price of IGN appears and nothing is projected: this page says what the code pays, to whom, and from what.</p>
|
||||
</div></section>
|
||||
<section class="section"><div class="container">
|
||||
<p class="src"><b>Source:</b> the node fork, branch <code>release-0.3.25-node</code> at <code>e0644958</code> (the Devnet 3 cut of 8 October 2026), confirmed by the node lane on 8 October 2026; every line number is that commit’s. Devnet 3 inherits the devnet parameters (<code>consensus/core/src/config/params.rs</code> 2173 and 2207).</p>
|
||||
|
|
@ -203,7 +203,7 @@
|
|||
<div class="tbl"><table>
|
||||
<thead><tr><th>Field</th><th><code>CURRENT</code> (Devnet 3 today, mainnet)</th><th><code>TESTNET_1</code> (igneum-testnet-1)</th><th>Where</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Launch rate</td><td class="num">3,168,808,781 base units a DAA second (10<sup>9</sup> IGN x 10<sup>8</sup> / 31,557,600, floored: one billion IGN in year one)</td><td class="num">100 IGN a DAA second</td><td><code>emission.rs</code> 85 to 123; <code>igneum.rs</code> 34</td></tr>
|
||||
<tr><td>Launch rate</td><td class="num"><span data-rm="fees.emission.launch_rate_base_units_per_daa_second">3,168,808,781</span> base units a DAA second (10<sup>9</sup> IGN x 10<sup>8</sup> / 31,557,600, floored: one billion IGN in year one)</td><td class="num">100 IGN a DAA second</td><td><code>emission.rs</code> 85 to 123; <code>igneum.rs</code> 34</td></tr>
|
||||
<tr><td>Ramp</td><td class="num">2,592,000 s (30 days) from 10 percent</td><td class="num">7,776,000 s (90 days) from 10 percent</td><td><code>igneum.rs</code> 76 <code>launch_ramp</code></td></tr>
|
||||
<tr><td>Step</td><td class="num">63,115,200 s (two years), the rate halved each step (decay 2<sup>31</sup> of 2<sup>32</sup>)</td><td class="num">2,629,800 s (a month), the rate times 2<sup>-1/24</sup> each step (decay 4,172,697,914 of 2<sup>32</sup>: a two-year half-life)</td><td><code>emission.rs</code> 85 to 123</td></tr>
|
||||
<tr><td>Tail</td><td class="num">none: the curve runs to zero and the sum is the hard cap, 4,000,000,000 IGN</td><td class="num">1 percent of supply a year (100 basis points) once the glide falls below it</td><td><code>emission.rs</code> 69 to 71</td></tr>
|
||||
|
|
@ -216,8 +216,8 @@
|
|||
<div class="tbl"><table>
|
||||
<thead><tr><th>Share</th><th>Goes to</th><th>State</th><th>Where</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="num">80 percent</td><td>the miner whose key found the block</td><td>in consensus on every network</td><td><code>consensus/core/src/igneum.rs</code> 44 (<code>PROVING_POOL_SHARE_PERCENT = 20</code>), applied at 87 and 202</td></tr>
|
||||
<tr><td class="num">20 percent</td><td>the proving pool, paid per shard to the provers of a segment record from a keyless escrow account</td><td>in consensus; the escrow is <code>PROVING_POOL_ADDRESS</code> in the execution layer</td><td><code>igneum.rs</code> 44; <code>igneum/exec/src/config.rs</code></td></tr>
|
||||
<tr><td class="num"><span data-rm="fees.subsidy_split.miner_percent">80</span> percent</td><td>the miner whose key found the block</td><td>in consensus on every network</td><td><code>consensus/core/src/igneum.rs</code> 44 (<code>PROVING_POOL_SHARE_PERCENT = 20</code>), applied at 87 and 202</td></tr>
|
||||
<tr><td class="num"><span data-rm="fees.subsidy_split.proving_pool_percent">20</span> percent</td><td>the proving pool, paid per shard to the provers of a segment record from a keyless escrow account</td><td>in consensus; the escrow is <code>PROVING_POOL_ADDRESS</code> in the execution layer</td><td><code>igneum.rs</code> 44; <code>igneum/exec/src/config.rs</code></td></tr>
|
||||
<tr><td>A silent block’s bonus</td><td>a slice of the producer’s share moved to the pool when the producer did not sign its checkpoints, nothing destroyed</td><td>designed and in the code, not active: <code>signing_bonus_activation_daa</code> is <code>u64::MAX</code> on every object</td><td><code>igneum.rs</code> 96 <code>silent_split</code>; <code>params.rs</code> 1717</td></tr>
|
||||
<tr><td class="num">0</td><td>any team, foundation, fund or treasury</td><td>no such output exists in the subsidy and no protocol tip reaches any address</td><td><code>igneum.rs</code>; spec 05 section 5.5</td></tr>
|
||||
</tbody>
|
||||
|
|
@ -228,7 +228,7 @@
|
|||
<thead><tr><th>Route</th><th>What happens</th><th>State</th><th>Where</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Base fee, both gas dimensions</td><td>burned in full: gas used times the execution base fee, pgas used times the proving base fee, debited and credited to no one</td><td>in the code on Devnet 3</td><td><code>igneum/exec/src/executor.rs</code> 320 to 371 (327 and 357, 328 and 360)</td></tr>
|
||||
<tr><td>Priority fee (the tip)</td><td>80 percent to the block’s miner; 20 percent to the developer registrations of the contracts whose code ran, pro rata by each frame’s gas; an unregistered frame’s part is credited to nobody, which is a burn</td><td>in the code on Devnet 3</td><td><code>executor.rs</code> 335 to 339; <code>igneum/exec/src/pgas.rs</code> 290; <code>igneum/exec/src/config.rs</code> 76 (<code>DEVELOPER_SHARE_PERCENT = 20</code>)</td></tr>
|
||||
<tr><td>Priority fee (the tip)</td><td><span data-rm="fees.priority_fee.miner_percent">80</span> percent to the block’s miner; <span data-rm="fees.priority_fee.developer_percent">20</span> percent to the developer registrations of the contracts whose code ran, pro rata by each frame’s gas; an unregistered frame’s part is credited to nobody, which is a burn</td><td>in the code on Devnet 3</td><td><code>executor.rs</code> 335 to 339; <code>igneum/exec/src/pgas.rs</code> 290; <code>igneum/exec/src/config.rs</code> 76 (<code>DEVELOPER_SHARE_PERCENT = 20</code>)</td></tr>
|
||||
<tr><td>External proving jobs</td><td>90 percent to the provers who delivered, 10 percent burned, once jobs settle in IGN</td><td>designed, not in the code: no constant exists; at launch a job is paid on the customer’s own chain</td><td>spec 05 section 5.4; the litepaper’s Proving section</td></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
|
|
@ -246,6 +246,30 @@
|
|||
</tbody>
|
||||
</table></div>
|
||||
|
||||
<h2 id="who-pays-for-proving">Who pays for proving</h2>
|
||||
<p>Three incomes, kept apart. Each has its own source and its own state.</p>
|
||||
<div class="tbl"><table><thead><tr><th>Income</th><th>Where it comes from</th><th>Who gets it</th><th>State</th></tr></thead><tbody>
|
||||
<tr><td>Block security</td><td>the block subsidy (<span data-rm="fees.subsidy_split.miner_percent">80</span> percent of each block) and <span data-rm="fees.priority_fee.miner_percent">80</span> percent of the priority fee</td><td>the miner whose key found the block</td><td>in consensus on every network (measured)</td></tr>
|
||||
<tr><td>Internal proving</td><td>the proving pool: a fifth of each block’s subsidy (<span data-rm="fees.subsidy_split.proving_pool_percent">20</span> percent), paid per shard against a valid proof record, plus the provers’ part of the priority fee</td><td>the prover keys that delivered the shards</td><td>in consensus on Devnet 3 (measured below)</td></tr>
|
||||
<tr><td>External customers</td><td>payments from other chains for proofs, settled in IGN: <span data-rm="fees.external_jobs.provers_percent">90</span> percent to the provers, <span data-rm="fees.external_jobs.burn_percent">10</span> percent burned</td><td>the provers who took the job</td><td>designed, not implemented: no constant exists, no job has been paid</td></tr>
|
||||
</tbody></table></div>
|
||||
<p><b>The proving base fee pays nobody.</b> A transaction’s pgas times the proving base fee is burned in full today (<code>executor.rs</code>, the base-fee row above). It is not a prover’s income and it does not fill the pool. The pool is filled by the subsidy alone, and the subsidy halves every two years.</p>
|
||||
<h3>What the pool pays, measured</h3>
|
||||
<p>On Devnet 3 in the 24 hours to <span data-rm="proving_view.read_at">12:16 UK on 8 October 2026</span>: <span data-rm="proving_view.shards_paid_24h">8,209</span> shards paid, <span data-rm="proving_view.ign_paid_24h">9,913.09</span> IGN in all, to <span data-rm="proving_view.prover_keys_24h">29</span> prover keys; <span data-rm="proving_view.shards_planned_24h">40,502</span> shards planned, so about a fifth of the planned shards were proven and paid and the rest left their credit in the escrow; <span data-rm="proving_view.paid_per_hour">905</span> shards paid in the last hour; the lag from a proven block to the block that pays p50 <span data-rm="proving_view.lag_p50_daa">514</span> and p90 <span data-rm="proving_view.lag_p90_daa">953</span> DAA seconds. Per shard paid that is about 1.21 IGN; per key about 12 shards and 14 IGN an hour averaged over the day, across keys that prove at very different rates (measured; the observer’s proof tables through <code>/api/explorer?proving=1</code>).</p>
|
||||
<h3>What reaches provers under the schedule, modelled</h3>
|
||||
<p>Low fees and no external demand, which is the chain today: the pool is a fifth of the subsidy and nothing else. The table holds today’s shard count and key count constant and walks the halvings. No price of IGN appears; whether a row covers a card’s electricity depends on the price, which this page does not state.</p>
|
||||
<!-- proving-model:start -->
|
||||
<div class="tbl"><table><thead><tr><th>When</th><th>Subsidy per block, IGN</th><th>Pool per block, IGN</th><th>Pool per day, IGN</th><th>Per planned shard, IGN</th><th>Per prover key per hour, IGN</th><th>Basis</th></tr></thead><tbody><tr><td>Today, inside the launch ramp</td><td class="num">3.894</td><td class="num">0.779</td><td class="num">67,286</td><td class="num">1.66</td><td class="num">96.7</td><td>DAA 65,900, 12.3 percent of the full rate; modelled; the measured payout is in the row above</td></tr><tr><td>Full rate (years 1 to 2)</td><td class="num">31.688</td><td class="num">6.338</td><td class="num">547,570</td><td class="num">13.52</td><td class="num">786.7</td><td>period 0 of the schedule; from day 30</td></tr><tr><td>After the first halving (years 3 to 4)</td><td class="num">15.844</td><td class="num">3.169</td><td class="num">273,785</td><td class="num">6.76</td><td class="num">393.4</td><td>period 1 of the schedule</td></tr><tr><td>After the second (years 5 to 6)</td><td class="num">7.922</td><td class="num">1.584</td><td class="num">136,893</td><td class="num">3.38</td><td class="num">196.7</td><td>period 2 of the schedule</td></tr><tr><td>After the third (years 7 to 8)</td><td class="num">3.961</td><td class="num">0.792</td><td class="num">68,446</td><td class="num">1.69</td><td class="num">98.3</td><td>period 3 of the schedule</td></tr><tr><td>After the fourth (years 9 to 10)</td><td class="num">1.981</td><td class="num">0.396</td><td class="num">34,223</td><td class="num">0.84</td><td class="num">49.2</td><td>period 4 of the schedule</td></tr><tr><td>After the fifth (years 11 to 12)</td><td class="num">0.990</td><td class="num">0.198</td><td class="num">17,112</td><td class="num">0.42</td><td class="num">24.6</td><td>period 5 of the schedule</td></tr></tbody></table></div>
|
||||
<p class="src"><b>Modelled</b> from <code>emission.rs</code> (the schedule as <code>site/lib/emission.mjs</code> carries it) and the release manifest at build time, with today's measured shard count (40,502 planned in 24 hours) and key count (29) held constant; the per-shard figure is the pool's credit per planned shard, the per-key figure the pool per day divided by the keys and by 24. More shards or more keys lower both; a block's unproven shards leave their credit in the escrow.</p>
|
||||
<!-- proving-model:end -->
|
||||
<h3>Per card, per hour, now and at each halving</h3>
|
||||
<p>A card’s proving income is its shards times the credit per shard. Measured rates: an RTX 5090 proves an empty live shard beside its miner in 7.0 to 7.7 s and a full shard in 10.9 s alone, about 37 s a shard end to end (export, cut, key set-up, prove, sign, submit), 1.4 shards a minute; an RTX 3060 (12 GB) proves the v1 shard beside its miner in 37.5 s; an RTX 4060 (8 GB) proves it alone in 18.4 s (the engineering log, 4 to 6 October 2026). At those rates one card can prove 80 to 100 shards an hour, more than the <span data-rm="proving_view.paid_per_hour">905</span> an hour the whole chain paid to <span data-rm="proving_view.prover_keys_24h">29</span> keys, so today a prover is limited by the shards on offer, not by its card: the hourly figure per key in the table is the ceiling an evenly shared pool gives, and a 5090 and a 3060 take the same credit per shard. At each halving the credit per shard halves with the pool; a card’s watts do not. (Modelled from the measured rates; the chain’s shard count is the real limit.)</p>
|
||||
<h3>Two honest routes to sustain it</h3>
|
||||
<div class="tbl"><table><thead><tr><th>Route</th><th>What it does</th><th>What it is worth</th><th>State</th></tr></thead><tbody>
|
||||
<tr><td>A defined share of fees to the proving pool</td><td>a fixed percentage of the base fees burned today (both gas dimensions) credited to the pool instead, by a rule at genesis or a class change</td><td>each percent of the share adds one percent of the day’s burned base fees to the pool. Priced per unit, not predicted: at 100,000 IGN of base fees burned in a day (a hypothetical volume, not a forecast) a 10 percent share adds 10,000 IGN a day, about today’s measured payout; at Devnet 3’s fee volume today it adds almost nothing, because almost nothing is burned</td><td>designed as an option; no constant exists</td></tr>
|
||||
<tr><td>External settlement in IGN</td><td>other chains pay for proofs in IGN; <span data-rm="fees.external_jobs.provers_percent">90</span> percent to the provers, <span data-rm="fees.external_jobs.burn_percent">10</span> percent burned</td><td>the price a prover must charge is the subsidy it forgoes while it proves: per shard, (card hash ÷ network hash) × 0.8 × the block subsidy × shard seconds. For an RTX 5090 at 136 MH/s on a 1 GH/s network and a 37 s shard that is about 16 IGN a shard inside the ramp today and about 128 IGN at the full rate, against the pool’s 1.2 IGN a shard measured; the quote falls as one over network hash and is competitive near 100 GH/s (the litepaper, Building on Igneum)</td><td>designed; the settlement contract is not built</td></tr>
|
||||
</tbody></table></div>
|
||||
<p>The chip question (whether a chip gets built against the hash, and what it would earn) is a conditional argument of its own on the litepaper’s <a href="/litepaper#chip-model">chip model</a>, not part of this page.</p>
|
||||
<h2>Units and decimals</h2>
|
||||
<div class="tbl"><table>
|
||||
<thead><tr><th>Network</th><th>Consensus side</th><th>EVM side</th><th>Where</th></tr></thead>
|
||||
|
|
@ -258,7 +282,7 @@
|
|||
<p>The symbol is IGN on every network. A wallet shows 18 decimals everywhere; on Devnet 3 the chain pays in 8 and the bridge shows the same amount at 18.</p>
|
||||
|
||||
<h2>What this page does not do</h2>
|
||||
<p>It names no price, projects no income and models no market. The <a href="/income">income page</a> turns these rules into IGN a day for a card you pick, and money a day only at a price you type. Devnet and testnet IGN have no value.</p>
|
||||
<p>It names no price and models no market; the proving model above walks the subsidy schedule at today’s measured shard and key counts and states no price. The <a href="/income">income page</a> turns these rules into IGN a day for a card you pick, and money a day only at a price you type. Devnet and testnet IGN have no value.</p>
|
||||
<p>Not legal advice.</p>
|
||||
</div></section>
|
||||
</main>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -265,7 +265,7 @@ export function verifyBalance(cp, proof, deps) {
|
|||
const step = (name, fn) => { try { const d = fn(); steps.push({ name, ok: true, detail: d }); return d; } catch (e) { steps.push({ name, ok: false, detail: String(e.message || e) }); throw e; } };
|
||||
const done = extra => ({ ...extra, steps, ms: Math.round((now() - t0) * 10) / 10 });
|
||||
try {
|
||||
const cert = step('certificate: BLS aggregate over the checkpoint, 2/3 of active and 17/30 of total weight', () => {
|
||||
const cert = step('certificate: BLS aggregate over the checkpoint, 2/3 of active and 2/3 of total weight', () => {
|
||||
const r = verifyCheckpoint(cp, { blake2b, bls });
|
||||
if (!r.verified) throw new Error(r.reason);
|
||||
return `checkpoint ${r.index}, ${r.signers} of ${r.voters} voters, ${(r.weight_fraction_total * 100).toFixed(1)}% of total weight, ${r.headers_checked} headers to the previous lock`;
|
||||
|
|
@ -338,7 +338,7 @@ export function verifyReceipt(receipt, deps) {
|
|||
const done = extra => ({ ...extra, steps, ms: Math.round((now() - t0) * 10) / 10 });
|
||||
try {
|
||||
const cp = receipt.checkpoint.certificate;
|
||||
const cert = step('certificate: BLS aggregate over the checkpoint, 2/3 of active and 17/30 of total weight', () => {
|
||||
const cert = step('certificate: BLS aggregate over the checkpoint, 2/3 of active and 2/3 of total weight', () => {
|
||||
const r = verifyCheckpoint(cp, { blake2b, bls });
|
||||
if (!r.verified) throw new Error(r.reason);
|
||||
if (strip(cp.hash) !== strip(receipt.checkpoint.hash) || receipt.chain_id !== cp.chain_id) throw new Error('the receipt names another checkpoint or chain than its certificate');
|
||||
|
|
|
|||
|
|
@ -5241,7 +5241,7 @@ function verifyReceipt(receipt2, deps2) {
|
|||
const done = (extra) => ({ ...extra, steps, ms: Math.round((now() - t0) * 10) / 10 });
|
||||
try {
|
||||
const cp = receipt2.checkpoint.certificate;
|
||||
const cert = step("certificate: BLS aggregate over the checkpoint, 2/3 of active and 17/30 of total weight", () => {
|
||||
const cert = step("certificate: BLS aggregate over the checkpoint, 2/3 of active and 2/3 of total weight", () => {
|
||||
const r2 = verifyCheckpoint(cp, { blake2b: blake2b2, bls: bls2 });
|
||||
if (!r2.verified) throw new Error(r2.reason);
|
||||
if (strip(cp.hash) !== strip(receipt2.checkpoint.hash) || receipt2.chain_id !== cp.chain_id) throw new Error("the receipt names another checkpoint or chain than its certificate");
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@
|
|||
|
||||
<h2 id="checked">What is checked, in order</h2>
|
||||
<ol>
|
||||
<li>The certificate: the aggregate BLS signature of the signers over <code>igneum-vote-v1/igneum-devnet-3 || index || checkpoint hash</code>, the canonical voter order, the rule (two thirds of active weight, 17/30 of total), and the header chain from the previous lock.</li>
|
||||
<li>The certificate: the aggregate BLS signature of the signers over <code>igneum-vote-v1/igneum-devnet-3 || index || checkpoint hash</code>, the canonical voter order, the rule (two thirds of active weight and two thirds of total weight), and the header chain from the previous lock.</li>
|
||||
<li>The header path from the carrier block up to the certified checkpoint: every header hash recomputes (BLAKE2b-256 keyed BlockHash over the header fields), every next header names the previous as a direct parent.</li>
|
||||
<li>The coinbase transaction is in the carrier block: its hash recomputes (BLAKE2b-256 keyed TransactionHash over the serialized transaction) and the merkle path reaches the header's <code>hash_merkle_root</code>.</li>
|
||||
<li>The segment record in the coinbase extra data: the aggregator's BLS signature over the record under the network's tag, whether the key is a voter with weight or a prove-only key, and the statement names the chain and the block whose state it commits.</li>
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ body.all .pager{display:none}
|
|||
<div class="cap">Five layers plus the external proving market. A block flows down the column; outside demand feeds the same miners from the side.</div>
|
||||
</div>
|
||||
<h3>Live on Devnet 3, 7 October 2026</h3>
|
||||
<p>Devnet 3 (igneum-devnet-3, chain id 4463) made its first block at 18:06 UK on 7 October 2026 with every upgrade on from block zero, and locked its first checkpoint at 20:02 UK. The first devnet ran from 3 October 2026 and took each upgrade by height. Coins on Devnet 3 have no value and the chain may be reset. What is on it:</p>
|
||||
<p>Devnet 3 (<span data-rm="network.id">igneum-devnet-3</span>, chain id <span data-rm="network.chain_id">4464</span> since its class v5 floor at DAA <span data-rm="mining.class_since_daa">68,400</span>, 4463 from genesis to the floor; the chain’s current state is the <a href="/release.json">release manifest</a>) made its first block at 18:06 UK on 7 October 2026 with every upgrade on from block zero, and locked its first checkpoint at 20:02 UK. The first devnet ran from 3 October 2026 and took each upgrade by height. Coins on Devnet 3 have no value and the chain may be reset. What is on it:</p>
|
||||
<div class="tbl"><table>
|
||||
<thead><tr><th>Layer</th><th>State</th><th>Since</th></tr></thead>
|
||||
<tbody>
|
||||
|
|
|
|||
|
|
@ -290,6 +290,7 @@ pre b{color:var(--molten-text);font-weight:500}
|
|||
<div class="download-platform"><span class="osmark" data-os="hive" title="HiveOS"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round" stroke-linecap="round"><path d="M12 2.6 20.2 7.3v9.4L12 21.4 3.8 16.7V7.3z"/><path d="M12 7.4 16 9.7v4.6L12 16.6 8 14.3V9.7z"/><path d="M12 7.4V2.6M16 9.7l4.2-2.4M16 14.3l4.2 2.4M12 16.6v4.8M8 14.3l-4.2 2.4M8 9.7 3.8 7.3"/></svg></span><div><h3>Ember for Linux and HiveOS</h3><p>a tarball for rigs and Hive flight sheets</p></div></div>
|
||||
<p class="download-note">For the rig people. One tarball, the miner and the node inside, the same signed manifest as the desktop apps.</p>
|
||||
<a data-dl="miner-hive" href="https://dl.igneum.network/public/igneum-miner-hive.tar.gz" class="btn primary"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12m-5-5 5 5 5-5M5 16v4h14v-4"/></svg>Download the tarball <span data-dl-meta="miner-hive" style="font-weight:400;opacity:.85">v0.3.22 · 28.8 MB</span></a>
|
||||
<p class="fair" id="versions">Current versions: Windows <span data-rm="versions.miner-windows.version">0.3.20</span>, macOS <span data-rm="versions.miner-mac.version">0.3.20</span>, HiveOS <span data-rm="versions.miner-hive.version">0.3.22</span>, the wallet <span data-rm="versions.wallet-mac.version">0.1.5</span>; the node on Devnet 3 <span data-rm="source.node.version_string">igneumd/2.1.0-f8da7515</span>, <span data-rm="read_at_short">read 8 October 2026, 15:50 UK</span>. The machine-readable list, with every file’s SHA-256, is <a href="/release.json">/release.json</a>.</p>
|
||||
<div class="hive-sheet" id="hive"><b>HiveOS Flight Sheet.</b> Miner: <b>Custom</b>. Installation URL: <code data-dl-url="miner-hive">https://dl.igneum.network/dl/public/igneum-hive-0.3.22.tar.gz</code>. Miner name <code>igneum</code>, wallet and worker <code>0x<your 40-hex payout address>.%WORKER_NAME%</code>. Hive itself is untested on our side: tell us what breaks.</div>
|
||||
<p class="sha">sha256 <span data-dl-sha="miner-hive">8ad6dcef9edc57dcd33e8d5a97cbef5cdda0e384a6e56d3f62f8903029c4c764</span></p>
|
||||
</div>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -265,7 +265,7 @@
|
|||
<li>The transaction hash is keccak256 of the raw signed transaction in the file, so the recipient, the amount and the data are the signed ones.</li>
|
||||
<li>The transaction is a leaf of the including block's <code>hash_merkle_root</code> (BLAKE2b-256 keyed MerkleBranchHash up the path).</li>
|
||||
<li>Every header from the including block to the certified checkpoint recomputes and links to the next by a direct parent.</li>
|
||||
<li>The certificate over that checkpoint verifies: the aggregate BLS signature of the signers, the voter order, two thirds of active weight and 17/30 of total.</li>
|
||||
<li>The certificate over that checkpoint verifies: the aggregate BLS signature of the signers, the voter order, two thirds of active weight and two thirds of total weight.</li>
|
||||
</ol>
|
||||
<p class="note">Not proven by the file: the execution result (status, gas) is carried as the node reported it and labelled so; the voter table with weights comes from the node (spec 10.1). Both are named on the result.</p>
|
||||
<p class="asof">Devnet 3, no value. The chain may reset. A demonstration of the verification path, not a product.</p>
|
||||
|
|
|
|||
243
site/release-manifest.json
Normal file
243
site/release-manifest.json
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
{
|
||||
"format": "igneum-release-manifest-v1",
|
||||
"generated": "2026-10-08T15:55:00Z",
|
||||
"read_at": "read 8 October 2026, 15:50 UK, from the Devnet 3 seed and the fleet census (the node lane), the download index and the ELF manifest",
|
||||
"labels": {
|
||||
"measured": "read from a running node, a binary or a record",
|
||||
"designed": "in the specification or the design document, not yet carried by code on the live chain",
|
||||
"modelled": "a model's figure",
|
||||
"claimed": "stated by a third party, not measured here"
|
||||
},
|
||||
"network": {
|
||||
"id": "igneum-devnet-3",
|
||||
"name": "Igneum Devnet 3",
|
||||
"kind": "developer network: resets without notice, its coins have no value",
|
||||
"chain_id": 4464,
|
||||
"chain_id_hex": "0x1170",
|
||||
"chain_id_below_floor": 4463,
|
||||
"chain_id_note": "4464 since the class v5 floor at DAA 68,400 (crossed 8 October 2026, 12:57 UK); 4463 from genesis to the floor; a transaction carries the id in force at its block",
|
||||
"genesis": {
|
||||
"first_block_utc": "2026-10-07T17:06:00Z",
|
||||
"first_lock_utc": "2026-10-07T19:02:00Z",
|
||||
"note": "every upgrade on from block zero: era VDF, finality v3, fees v1, proving v1, the latency ladder at rung 0"
|
||||
},
|
||||
"rpc": "https://rpc.devnet.igneum.network",
|
||||
"explorer": "https://igneum.network/explorer",
|
||||
"faucet": "https://faucet.igneum.network",
|
||||
"vote_domain": "igneum-vote-v1/igneum-devnet-3",
|
||||
"decimals": 8,
|
||||
"block_target_seconds": 1,
|
||||
"consensus_digest": "2066aa57505e5ecbd585d061364abb0032d5b5b29cc41c54f4b38cb81c2ba6eb",
|
||||
"label": "measured",
|
||||
"others": [
|
||||
{
|
||||
"id": "igneum-testnet-1",
|
||||
"chain_id": 4462,
|
||||
"chain_id_hex": "0x116e",
|
||||
"rpc": "https://rpc.testnet.igneum.network",
|
||||
"state": "armed: three seed nodes and the public RPC are up, nothing mines until the go word",
|
||||
"genesis_hash": "87617621714af1bf33bd17f291f90a7e0bff760a669bba53083ea8c0f7cbd840",
|
||||
"decimals": 18
|
||||
},
|
||||
{
|
||||
"id": "igneum-mainnet",
|
||||
"chain_id": 4461,
|
||||
"chain_id_hex": "0x116d",
|
||||
"state": "not started",
|
||||
"decimals": 18
|
||||
}
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"repository": "https://git.igneum.network/igneum-network/igneum",
|
||||
"repository_note": "the public reference repository: the specifications, the igneum-pow crate, the simulators and the test material; the full node, the miner and the proving code open later",
|
||||
"node": {
|
||||
"fork_of": "rusty-kaspa v2.1.0 (01b532e8)",
|
||||
"branch": "release-0.3.25-node",
|
||||
"commit": "f8da7515",
|
||||
"version_string": "igneumd/2.1.0-f8da7515",
|
||||
"pin": "c9ad753a (the move of 8 October 2026, 15:25 UK)",
|
||||
"pending": "acaf08b0, cut 15:43 UK, gates running, its move not yet named",
|
||||
"label": "measured"
|
||||
},
|
||||
"pow": {
|
||||
"crate": "igneum-pow",
|
||||
"version": "0.2.0",
|
||||
"commit": "1c420786",
|
||||
"freeze": "class-v5-freeze 2026-10-07",
|
||||
"tree_fingerprint": "cbc5bd0aa10585c8576e71e37a8ee47a045ae51754e9ddf749d0c21e6a535f88",
|
||||
"binary_line": "igneum-pow fingerprint cbc5bd0aa10585c8 (the freeze)",
|
||||
"below_floor": "017e7037 (class v4 sub-version 3, the audit-freeze-2026-10-07 tag) paired below the class v5 floor",
|
||||
"label": "measured"
|
||||
},
|
||||
"app": {
|
||||
"name": "Ember",
|
||||
"channel": "devnet-3",
|
||||
"note": "the shipped artefacts and their SHA-256 are the versions block; the app ships from the same repository"
|
||||
}
|
||||
},
|
||||
"mining": {
|
||||
"class": "v5",
|
||||
"class_since_daa": 68400,
|
||||
"class_at_genesis": "v4",
|
||||
"class_note": "class v4 (the latency shadow, about 100,000 integer ops per hash at the ladder's rung 0) from block zero; class v5 (the dataset keyed by the chain's own state) from DAA 68,400",
|
||||
"block_version": 1538,
|
||||
"program": {
|
||||
"epoch_daa": 3600,
|
||||
"epoch_lead_daa": 601,
|
||||
"vdf_minutes": 10,
|
||||
"instructions": 64,
|
||||
"loads_per_program": 16,
|
||||
"lanes": 32,
|
||||
"registers_per_lane": 8,
|
||||
"dataset_reads_per_hash": 128
|
||||
},
|
||||
"dataset": {
|
||||
"items_log2": 24,
|
||||
"size": "1 GiB on the devnets (2^24 items at the genesis size)",
|
||||
"keyed_by": "the execution state after the epoch's reference block (class v5)",
|
||||
"cache": "256 MiB day cache (class v3 lineage)",
|
||||
"growth": "no growth step is set on Devnet 3; the genesis-fixed schedule of spec 1.13.3 is a mainnet matter; class v6's step schedule (5.5 / 8.5 / 11.5 GiB) is a design on its branch, not live"
|
||||
},
|
||||
"ladder": {
|
||||
"rung": 0,
|
||||
"ops_per_hash": "about 100,000",
|
||||
"move_rule": "90 percent of blue blocks in each of seven consecutive days, one rung at a time"
|
||||
},
|
||||
"label": "measured; the dataset growth line designed"
|
||||
},
|
||||
"finality": {
|
||||
"rule": "v3",
|
||||
"since_checkpoint_daa": 0,
|
||||
"statement": "a checkpoint locks when the signers hold at least two thirds of active weight and at least two thirds of total weight; weight is blue blocks per vote key over a flat 30-day window of past-median time",
|
||||
"checkpoint_interval_daa": 30,
|
||||
"checkpoint_depth": 20,
|
||||
"weight_window_seconds": 2592000,
|
||||
"presence_window_seconds": 7200,
|
||||
"dust": 5,
|
||||
"presence": 20,
|
||||
"aggregators": 8,
|
||||
"ban_daa": 7200,
|
||||
"certificate_fold_daa": 3,
|
||||
"frozen_table": "the weight table is frozen at the last certified checkpoint on the selected chain (spec 03 Q5), active for every checkpoint from DAA 0 on Devnet 3",
|
||||
"node_line": "Finality v2 (igneum-devnet-3): interval 30 depth 20 window 7200 DAA dust 5 presence 20 aggregators 8 ban 7200 fold 3; rule v3 from checkpoint DAA 0",
|
||||
"label": "measured"
|
||||
},
|
||||
"proving": {
|
||||
"verifier_in_consensus": "off",
|
||||
"verifier_note": "proving v0: every producer verifies off the consensus path; consensus checks the record's statement against native execution and its signature; the in-consensus verifier switches on when the proven share of blocks reads one (ledger P21)",
|
||||
"shard_market": "on from DAA 0: segment records of 8 blocks, unproven after 600 DAA, aggregator share 1,000 bps",
|
||||
"proof_system": "SP1",
|
||||
"sp1_circuit_version": "v6.1.0",
|
||||
"sp1_crate_version": "6.8.1",
|
||||
"pinned_at": "2026-10-05T16:20:38Z",
|
||||
"shard_program_id": "0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a",
|
||||
"shard_elf_sha256": "0x150f4c05a2951fc56174a87089707a030b18df8fbe7e053a66459edb83053083",
|
||||
"shard_vk_sha256": "0x8b4da5bff86d963f4210a78e5d800a1cd00ab41b158f6962f4ac009edc249d4c",
|
||||
"aggregator_program_id": "0x474678f35f7545db28055d5e5bbc308231d84a5a072202087a2a8d5b09123896",
|
||||
"aggregator_elf_sha256": "0x143d9c243dd12e87e90be71f6b8cd42353e513bf8ce78903ef6f972f1bc9aa7b",
|
||||
"aggregator_vk_sha256": "0xad17bc1ae5be816554dbb13cb5b4d242678adfb8e1a4f7247ceb8b5ba9001b9f",
|
||||
"manifest_file": "proving/igneum-prove/elf/manifest.json",
|
||||
"label": "measured"
|
||||
},
|
||||
"fees": {
|
||||
"subsidy_split": {
|
||||
"miner_percent": 80,
|
||||
"proving_pool_percent": 20,
|
||||
"where": "consensus/core/src/igneum.rs 44"
|
||||
},
|
||||
"base_fee": {
|
||||
"route": "burned in full, both gas dimensions (gas times the execution base fee, pgas times the proving base fee)",
|
||||
"where": "igneum/exec/src/executor.rs 320 to 371"
|
||||
},
|
||||
"priority_fee": {
|
||||
"miner_percent": 80,
|
||||
"developer_percent": 20,
|
||||
"note": "the developer part goes to the registrations of the contracts whose code ran, pro rata by each frame's gas; an unregistered frame's part is a burn",
|
||||
"where": "executor.rs 335 to 339; pgas.rs 290"
|
||||
},
|
||||
"proving_fee_ceiling": {
|
||||
"value": 90000,
|
||||
"multiple": 4,
|
||||
"note": "the proving-fee ceiling of the live object"
|
||||
},
|
||||
"external_jobs": {
|
||||
"provers_percent": 90,
|
||||
"burn_percent": 10,
|
||||
"state": "designed, not in the code"
|
||||
},
|
||||
"dev_fee": {
|
||||
"percent": 1,
|
||||
"mechanism": "one block template in a hundred with the dev payout address, a counter, not a draw",
|
||||
"default": "on",
|
||||
"off": "--dev-fee 0, the app switch, DEV_FEE=0 on HiveOS; none in pool mode",
|
||||
"where": "igneum/miner/src/main.rs 718"
|
||||
},
|
||||
"emission": {
|
||||
"launch_rate_base_units_per_daa_second": 3168808781,
|
||||
"year_one_ign": 1000000000,
|
||||
"ramp_seconds": 2592000,
|
||||
"ramp_start_percent": 10,
|
||||
"halving_seconds": 63115200,
|
||||
"cap_ign": 4000000000,
|
||||
"tail": "none",
|
||||
"where": "consensus/core/src/emission.rs 85 to 123; igneum.rs 34, 76"
|
||||
},
|
||||
"label": "measured in the code on Devnet 3; external jobs designed"
|
||||
},
|
||||
"versions": {
|
||||
"miner-hive": {
|
||||
"version": "0.3.22",
|
||||
"file": "igneum-hive-0.3.22.tar.gz",
|
||||
"sha256": "8ad6dcef9edc57dcd33e8d5a97cbef5cdda0e384a6e56d3f62f8903029c4c764",
|
||||
"size": 28771228,
|
||||
"url": "https://dl.igneum.network/dl/public/igneum-hive-0.3.22.tar.gz"
|
||||
},
|
||||
"miner-mac": {
|
||||
"version": "0.3.20",
|
||||
"file": "Igneum-Miner-0.3.20.dmg",
|
||||
"sha256": "73796c5febc2050646f038c1f8c32a1d854033d125d0c02478b983ea8020419e",
|
||||
"size": 44467804,
|
||||
"url": "https://dl.igneum.network/dl/public/Igneum-Miner-0.3.20.dmg"
|
||||
},
|
||||
"miner-windows": {
|
||||
"version": "0.3.20",
|
||||
"file": "Igneum-Miner-Setup-0.3.20.exe",
|
||||
"sha256": "45b2f3fb54f40f839cde8efac53b40eca3738a4009af1a4e356f6445883df6b5",
|
||||
"size": 63025372,
|
||||
"url": "https://dl.igneum.network/dl/public/Igneum-Miner-Setup-0.3.20.exe"
|
||||
},
|
||||
"wallet-mac": {
|
||||
"version": "0.1.5",
|
||||
"file": "Igneum-Wallet-0.1.5.dmg",
|
||||
"sha256": "daf259f272934f0c1a8155ef68762c344164ae8a77c8d0621d0fceed0fb163fa",
|
||||
"size": 20122390,
|
||||
"url": "https://dl.igneum.network/dl/public/Igneum-Wallet-0.1.5.dmg"
|
||||
}
|
||||
},
|
||||
"versions_updated": "2026-10-07T20:43:23Z",
|
||||
"sources": {
|
||||
"network": "docs/build/build.md (the Networks table); the node lane's read of build-1's seed",
|
||||
"node": "docs/plans/release-0.3.22.md and the node lane's read; the version string from igneumd --version",
|
||||
"pow": "packaging/pow-freeze.txt; the binaries' fingerprint line",
|
||||
"finality": "docs/spec/03-finality.md 3.3; the node's start line",
|
||||
"proving": "proving/igneum-prove/elf/manifest.json; docs/fud-ledger.md P21",
|
||||
"fees": "site/economics.html (the file and line of every constant); site/lib/emission.mjs",
|
||||
"versions": "site/downloads.json (dl.igneum.network's index)"
|
||||
},
|
||||
"read_at_short": "read 8 October 2026, 15:50 UK",
|
||||
"proving_view": {
|
||||
"read_at": "12:16 UK on 8 October 2026",
|
||||
"read_daa": 65900,
|
||||
"read_daa_note": "approximate: DAA seconds since genesis at 17:06 UTC on 7 October 2026, one a second",
|
||||
"shards_paid_24h": 8209,
|
||||
"ign_paid_24h": "9,913.09",
|
||||
"prover_keys_24h": 29,
|
||||
"shards_planned_24h": 40502,
|
||||
"paid_per_hour": 905,
|
||||
"lag_p50_daa": 514,
|
||||
"lag_p90_daa": 953,
|
||||
"source": "the observer's proof tables through /api/explorer?proving=1, the proving page",
|
||||
"label": "measured"
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,10 @@
|
|||
"source": "/address/:addr",
|
||||
"destination": "/address"
|
||||
},
|
||||
{
|
||||
"source": "/release.json",
|
||||
"destination": "/release-manifest.json"
|
||||
},
|
||||
{
|
||||
"source": "/benchmarks",
|
||||
"destination": "/miners"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ no founder name, personal login, earlier business or personal address in any tra
|
|||
every check in tools/ci/checks.txt has its run line here and every run line is listed (a conflict resolution cannot drop a check unseen; self-test first)
|
||||
site build (in a temporary copy here, in place only inside GitHub Actions)
|
||||
internal link check of site/*.html
|
||||
the release manifest: site/release-manifest.json parses, its versions are downloads.json's, its proof ids the ELF manifest's, /release.json served, every data-rm span on a committed page carries its value
|
||||
every served page carries the slim bar (mark, Mine, Network, Learn, Download) with every route in its panels and the sheet (self-test, then the tree)
|
||||
vendor marks: site/lib/marks.mjs is brand/marks/vendor-marks.mjs byte for byte (the app and the site draw one set)
|
||||
the phone menu opens and is seen at 390 px on every page (self-test first; needs the box or CI browser, says so without one)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ function resolves(target) {
|
|||
return candidates.some(c => existsSync(c) && statSync(c).isFile());
|
||||
}
|
||||
|
||||
// a literal vercel.json rewrite (no :param) resolves to its destination (/release.json -> /release-manifest.json, 8 October 2026)
|
||||
const rewrites = Object.fromEntries((JSON.parse(readFileSync(join(site, 'vercel.json'), 'utf8')).rewrites || []).filter(r => !r.source.includes(':')).map(r => [r.source, r.destination]));
|
||||
const resolvesOrRewritten = (p) => resolves(p) || (rewrites[p.replace(/[?].*$/, '')] != null && resolves(rewrites[p.replace(/[?].*$/, '')]));
|
||||
for (const page of pages) {
|
||||
const html = readFileSync(join(site, page), 'utf8');
|
||||
const ids = new Set([...html.matchAll(/\sid="([^"]+)"/g)].map(m => m[1]));
|
||||
|
|
@ -34,7 +37,7 @@ for (const page of pages) {
|
|||
checked++;
|
||||
if (t.startsWith('#')) { if (t.length > 1 && !ids.has(t.slice(1))) broken.push(`${page}: fragment ${t}`); continue; }
|
||||
const [path, frag] = t.split('#');
|
||||
if (!resolves(path)) { broken.push(`${page}: ${t}`); continue; }
|
||||
if (!resolvesOrRewritten(path)) { broken.push(`${page}: ${t}`); continue; }
|
||||
if (frag) {
|
||||
const rel = path.replace(/[?].*$/, '').replace(/^\//, '');
|
||||
const file = [join(site, rel), join(site, `${rel}.html`), join(site, rel, 'index.html')].find(c => existsSync(c) && statSync(c).isFile());
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ never_push_checks() {
|
|||
tree_checks() {
|
||||
run "site build (in a temporary copy here, in place only inside GitHub Actions)" site_build
|
||||
run "internal link check of site/*.html" node tools/ci/link-check.mjs
|
||||
run "the release manifest: site/release-manifest.json parses, its versions are downloads.json's, its proof ids the ELF manifest's, /release.json served, every data-rm span on a committed page carries its value" node tools/ci/release-manifest-check.mjs
|
||||
run "every served page carries the slim bar (mark, Mine, Network, Learn, Download) with every route in its panels and the sheet (self-test, then the tree)" bash -c 'node tools/ci/site-nav-check.mjs --self-test && node tools/ci/site-nav-check.mjs'
|
||||
run "vendor marks: site/lib/marks.mjs is brand/marks/vendor-marks.mjs byte for byte (the app and the site draw one set)" cmp brand/marks/vendor-marks.mjs site/lib/marks.mjs
|
||||
run "the phone menu opens and is seen at 390 px on every page (self-test first; needs the box or CI browser, says so without one)" node tools/site/sheet-test.mjs --self-test
|
||||
|
|
|
|||
50
tools/ci/release-manifest-check.mjs
Normal file
50
tools/ci/release-manifest-check.mjs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Release manifest check (8 October 2026, an accepted external review): site/release-manifest.json is served at /release.json
|
||||
// and is the one source of the chain's identity, sources, class, finality rule, proof ids, fees and versions on the status
|
||||
// pages. This holds it together: the manifest parses and carries every block; its versions are the download index's; its
|
||||
// proof ids are the ELF manifest's (when the tree carries it); vercel.json serves it at /release.json; every <span data-rm>
|
||||
// on a committed page carries the manifest's value; and no forbidden string is in it.
|
||||
// node tools/ci/release-manifest-check.mjs exit 1 listing every fault
|
||||
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||
const site = join(root, 'site');
|
||||
const fails = [];
|
||||
const m = JSON.parse(readFileSync(join(site, 'release-manifest.json'), 'utf8'));
|
||||
for (const k of ['format', 'generated', 'read_at', 'network', 'source', 'mining', 'finality', 'proving', 'fees', 'versions', 'sources']) if (!(k in m)) fails.push(`manifest: no "${k}" block`);
|
||||
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(m.generated || '')) fails.push('manifest: "generated" is not an ISO UTC date');
|
||||
if (m.network?.chain_id !== 4464 || m.network?.chain_id_hex !== '0x1170') fails.push('manifest: Devnet 3 chain id is 4464 (0x1170)');
|
||||
if (!/two thirds of active weight and at least two thirds of total weight/.test(m.finality?.statement || '')) fails.push('manifest: the finality statement must say two thirds of active and two thirds of total weight');
|
||||
if (m.proving?.verifier_in_consensus !== 'off') fails.push('manifest: the verifier is off in consensus (ledger P21) until that row moves');
|
||||
const dl = JSON.parse(readFileSync(join(site, 'downloads.json'), 'utf8'));
|
||||
for (const [k, v] of Object.entries(dl.files)) {
|
||||
const w = m.versions?.[k];
|
||||
if (!w) { fails.push(`manifest: versions.${k} missing (in downloads.json)`); continue; }
|
||||
for (const f of ['version', 'file', 'sha256', 'size']) if (w[f] !== v[f]) fails.push(`manifest: versions.${k}.${f} is ${w[f]}, downloads.json says ${v[f]}`);
|
||||
}
|
||||
if (m.versions_updated !== dl.updated) fails.push(`manifest: versions_updated ${m.versions_updated} is not downloads.json's ${dl.updated}`);
|
||||
const elfp = join(root, 'proving', 'igneum-prove', 'elf', 'manifest.json');
|
||||
if (existsSync(elfp)) {
|
||||
const e = JSON.parse(readFileSync(elfp, 'utf8'));
|
||||
for (const [a, b] of [['shard_program_id', e.shard.program_id], ['shard_elf_sha256', e.shard.elf_sha256], ['shard_vk_sha256', e.shard.vk_sha256], ['aggregator_program_id', e.aggregator.program_id], ['aggregator_elf_sha256', e.aggregator.elf_sha256], ['aggregator_vk_sha256', e.aggregator.vk_sha256], ['sp1_circuit_version', e.sp1_circuit_version], ['sp1_crate_version', e.sp1_crate_version], ['pinned_at', e.pinned_at]])
|
||||
if (m.proving[a] !== b) fails.push(`manifest: proving.${a} is ${m.proving[a]}, the ELF manifest says ${b}`);
|
||||
}
|
||||
const vercel = JSON.parse(readFileSync(join(site, 'vercel.json'), 'utf8'));
|
||||
if (!(vercel.rewrites || []).some(r => r.source === '/release.json' && r.destination === '/release-manifest.json')) fails.push('vercel.json: no rewrite /release.json -> /release-manifest.json');
|
||||
const value = (p) => { const v = p.split('.').reduce((o, k) => (o == null ? undefined : o[k]), m); return v === undefined ? undefined : (typeof v === 'number' && !/_id$/.test(p) ? v.toLocaleString('en-GB') : String(v)); };
|
||||
let spans = 0;
|
||||
for (const f of readdirSync(site).filter(f => f.endsWith('.html'))) {
|
||||
const html = readFileSync(join(site, f), 'utf8');
|
||||
for (const [, p, text] of html.matchAll(/<span data-rm="([a-z0-9_.-]+)">([^<]*)<\/span>/g)) {
|
||||
spans++;
|
||||
const want = value(p);
|
||||
if (want === undefined) fails.push(`${f}: data-rm="${p}" names no manifest value`);
|
||||
else if (text.replace(/&/g, '&').replace(/</g, '<') !== want) fails.push(`${f}: data-rm="${p}" carries "${text.slice(0, 60)}", the manifest says "${want.slice(0, 60)}" (rebuild the site)`);
|
||||
}
|
||||
}
|
||||
if (spans < 10) fails.push(`only ${spans} data-rm spans on the committed pages; the status pages read the manifest`);
|
||||
const raw = readFileSync(join(site, 'release-manifest.json'), 'utf8');
|
||||
for (const pat of readFileSync(join(root, 'tools', 'ci', 'forbidden-strings.txt'), 'utf8').split('\n').filter(l => l && !l.startsWith('#')))
|
||||
if (new RegExp(pat).test(raw)) fails.push(`manifest: forbidden string ${pat}`);
|
||||
if (fails.length) { console.error(`release-manifest check: ${fails.length} fault(s)\n ${fails.join('\n ')}`); process.exit(1); }
|
||||
console.log(`release-manifest check: manifest ${m.generated}, ${Object.keys(m.versions).length} platform versions match downloads.json, proof ids match the ELF manifest, /release.json served, ${spans} data-rm spans carry the manifest's values`);
|
||||
Loading…
Reference in a new issue