From 5d4a522744148d21149692d73665ef9a61935a34 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:21:28 +0000 Subject: [PATCH 01/13] TV-02: the two supply-replay implementations, the compare harness and the hand-written reorg fixtures, committed before any replay ran Implementation A (tools/token-value/tv-02/replay-node): Rust, calls the node's own issuance code (kaspa-consensus-core by path from vendor/igneum-node, bf60948a) per block, cross-checked against the node's second path igneum::block_subsidy_units. Implementation B (replay-spec): standalone Python 3 from the rules in words (rules-v0 from the litepaper and economics page; v0.1 adds the genesis rule the node lane cited), grouped sums, no shared code. Fixtures: two reorgs (ramp end k=1 to 2, the first step k=2 to 3) with every expected integer written by hand (closed forms evaluated with bc), the no-undo ledger as the known-failed case. Co-Authored-By: Claude Opus 5.5 --- tools/token-value/tv-02/compare.py | 165 ++++++++ .../tv-02/fixtures/reorg-first-step-k2.json | 47 +++ .../tv-02/fixtures/reorg-ramp-end-k1.json | 47 +++ .../token-value/tv-02/replay-node/Cargo.toml | 24 ++ .../token-value/tv-02/replay-node/src/main.rs | 377 ++++++++++++++++++ .../tv-02/replay-spec/replay_spec.py | 308 ++++++++++++++ .../tv-02/replay-spec/rules-v0-litepaper.json | 47 +++ .../replay-spec/rules-v0.1-genesis-rule.json | 48 +++ tools/token-value/tv-02/run-a.sh | 28 ++ tools/token-value/tv-02/run-b.sh | 19 + 10 files changed, 1110 insertions(+) create mode 100755 tools/token-value/tv-02/compare.py create mode 100644 tools/token-value/tv-02/fixtures/reorg-first-step-k2.json create mode 100644 tools/token-value/tv-02/fixtures/reorg-ramp-end-k1.json create mode 100644 tools/token-value/tv-02/replay-node/Cargo.toml create mode 100644 tools/token-value/tv-02/replay-node/src/main.rs create mode 100755 tools/token-value/tv-02/replay-spec/replay_spec.py create mode 100644 tools/token-value/tv-02/replay-spec/rules-v0-litepaper.json create mode 100644 tools/token-value/tv-02/replay-spec/rules-v0.1-genesis-rule.json create mode 100755 tools/token-value/tv-02/run-a.sh create mode 100755 tools/token-value/tv-02/run-b.sh diff --git a/tools/token-value/tv-02/compare.py b/tools/token-value/tv-02/compare.py new file mode 100755 index 000000000..b659cfb58 --- /dev/null +++ b/tools/token-value/tv-02/compare.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""TV-02 comparison harness: reads implementation A's and B's outputs and reports exact equality or the first +differing row. Standalone Python 3, no third-party modules, shares no code with A or B. + + compare.py straight CSV: height,daa_score,subsidy,cumulative + compare.py reorg + CSV: step,height,daa_score,subsidy,cumulative + compare.py selftest the known-failed cases first, then a known-equal case + +Exit 0 when everything compared is equal, 1 on any difference, 2 on bad input. The verdict line starts with +EQUAL or DIFFER (never PASS: the independent panel reads the evidence). +""" +import csv +import io +import json +import sys + +STRAIGHT_HEADER = ["height", "daa_score", "subsidy", "cumulative"] +REORG_HEADER = ["step", "height", "daa_score", "subsidy", "cumulative"] + + +def read_rows(text, header): + r = list(csv.reader(io.StringIO(text))) + if not r or r[0] != header: + raise ValueError("header %r is not %r" % (r[0] if r else None, header)) + out = [] + for i, row in enumerate(r[1:], start=2): + if len(row) != len(header): + raise ValueError("line %d has %d fields" % (i, len(row))) + out.append(row) + return out + + +def ints(row, first_int): + return row[:first_int] + [int(x) for x in row[first_int:]] + + +def compare_straight_text(a_text, b_text): + a = [ints(r, 0) for r in read_rows(a_text, STRAIGHT_HEADER)] + b = [ints(r, 0) for r in read_rows(b_text, STRAIGHT_HEADER)] + res = {"rows_a": len(a), "rows_b": len(b)} + for i in range(max(len(a), len(b))): + ra = a[i] if i < len(a) else None + rb = b[i] if i < len(b) else None + if ra != rb: + cols = [] if ra is None or rb is None else [STRAIGHT_HEADER[j] for j in range(4) if ra[j] != rb[j]] + res.update(equal=False, first_difference={"row": i + 1, "a": ra, "b": rb, "columns": cols}) + # diagnostic: is B - A a constant offset in the cumulative over the rows both share by height? + ah = {r[0]: r for r in a} + bh = {r[0]: r for r in b} + common = sorted(set(ah) & set(bh)) + offs = sorted({bh[h][3] - ah[h][3] for h in common}) + res["heights_only_in_a"] = len(set(ah) - set(bh)) + res["heights_only_in_b"] = len(set(bh) - set(ah)) + res["cumulative_offsets_b_minus_a_on_shared_heights"] = [str(x) for x in offs[:5]] + (["..."] if len(offs) > 5 else []) + return res + res.update(equal=True, last_row=a[-1] if a else None) + return res + + +def compare_reorg_text(a_text, b_text, fixture, straight_text): + a = [ints(r, 1) for r in read_rows(a_text, REORG_HEADER)] + b = [ints(r, 1) for r in read_rows(b_text, REORG_HEADER)] + res = {"fixture": fixture.get("id"), "checks": []} + + def check(name, ok, detail): + res["checks"].append({"check": name, "ok": bool(ok), "detail": detail}) + + first = None + for i in range(max(len(a), len(b))): + ra = a[i] if i < len(a) else None + rb = b[i] if i < len(b) else None + if ra != rb: + first = {"row": i + 1, "a": ra, "b": rb} + break + check("A equals B, every ledger row", first is None, first or {"rows": len(a)}) + for name, rows in (("A", a), ("B", b)): + by = {} + for r in rows: + by.setdefault(r[0], []).append(r) + e = fixture["expected"] + anc = by["ancestor"][0] + check(name + " ancestor = fixture", str(anc[4]) == e["ancestor"]["cumulative"] and str(anc[3]) == e["ancestor"]["subsidy"] + and anc[1] == e["ancestor"]["height"], {"got": anc, "want": e["ancestor"]}) + otip = by["orphan"][-1] + check(name + " orphan tip = fixture", str(otip[4]) == e["orphan_tip"]["cumulative"] and str(otip[3]) == e["orphan_tip"]["subsidy"] + and otip[1] == e["orphan_tip"]["height"], {"got": otip, "want": e["orphan_tip"]}) + rb_ = by["rollback"][-1] + check(name + " after rollback = fixture", str(rb_[4]) == e["after_rollback"]["cumulative"], {"got": rb_, "want": e["after_rollback"]}) + fin = by["final"][0] + check(name + " final = fixture", str(fin[4]) == e["final"]["cumulative"] and str(fin[3]) == e["final"]["subsidy"] + and fin[1] == e["final"]["height"], {"got": fin, "want": e["final"]}) + nv = by["known_failed_no_undo"][0] + check(name + " known-failed ledger = fixture's known-failed value", str(nv[4]) == e["known_failed_no_undo"]["cumulative"], + {"got": nv, "want": e["known_failed_no_undo"]}) + check(name + " known-failed ledger is refused (differs from final)", nv[4] != fin[4], {"naive": nv[4], "final": fin[4]}) + if straight_text is not None: + s = {r[0]: r for r in (ints(x, 0) for x in read_rows(straight_text, STRAIGHT_HEADER))} + fin = [r for r in a if r[0] == "final"][0] + srow = s.get(fin[1]) + check("final = straight chain at the same height", srow is not None and srow[3] == fin[4] and srow[2] == fin[3], + {"final": fin, "straight": srow}) + res["equal"] = all(c["ok"] for c in res["checks"]) + return res + + +def verdict(res): + return ("EQUAL" if res["equal"] else "DIFFER") + " " + json.dumps(res) + + +def selftest(): + """Known-failed first: a corrupted cumulative, a dropped row and a no-undo ledger must each be reported.""" + good = "height,daa_score,subsidy,cumulative\n0,0,0,0\n1,0,10,10\n2,1,11,21\n" + bad_cum = "height,daa_score,subsidy,cumulative\n0,0,0,0\n1,0,10,10\n2,1,11,22\n" + short = "height,daa_score,subsidy,cumulative\n0,0,0,0\n1,0,10,10\n" + r = compare_straight_text(good, bad_cum) + assert not r["equal"] and r["first_difference"]["row"] == 3 and r["first_difference"]["columns"] == ["cumulative"], r + r = compare_straight_text(good, short) + assert not r["equal"] and r["first_difference"]["b"] is None, r + fx = {"id": "t", "expected": {"ancestor": {"height": 1, "subsidy": "10", "cumulative": "10"}, + "orphan_tip": {"height": 2, "subsidy": "11", "cumulative": "21"}, + "after_rollback": {"cumulative": "10"}, + "final": {"height": 3, "subsidy": "12", "cumulative": "33"}, + "known_failed_no_undo": {"cumulative": "44"}}} + led = ("step,height,daa_score,subsidy,cumulative\nancestor,1,0,10,10\norphan,2,1,11,21\nrollback,2,1,11,10\n" + "replacement,2,1,11,21\nreplacement,3,2,12,33\nfinal,3,2,12,33\nknown_failed_no_undo,3,2,12,44\n") + straight3 = good + "3,2,12,33\n" + naive_led = led.replace("rollback,2,1,11,10\nreplacement,2,1,11,21\nreplacement,3,2,12,33\nfinal,3,2,12,33", + "rollback,2,1,11,21\nreplacement,2,1,11,32\nreplacement,3,2,12,44\nfinal,3,2,12,44") + r = compare_reorg_text(led, naive_led, fx, straight3) + assert not r["equal"], r + r = compare_reorg_text(naive_led, naive_led, fx, straight3) + assert not r["equal"] and any(not c["ok"] and "final = fixture" in c["check"] for c in r["checks"]), r + # then the known-equal case + assert compare_straight_text(good, good)["equal"] + r = compare_reorg_text(led, led, fx, straight3) + assert r["equal"], r + print("selftest: corrupted cumulative, dropped row and no-undo ledger each reported; equal inputs read EQUAL") + + +def main(a): + try: + if len(a) == 4 and a[1] == "straight": + res = compare_straight_text(open(a[2]).read(), open(a[3]).read()) + res.update(a=a[2], b=a[3]) + elif len(a) in (5, 6) and a[1] == "reorg": + fx = json.load(open(a[4])) + st = open(a[5]).read() if len(a) == 6 else None + res = compare_reorg_text(open(a[2]).read(), open(a[3]).read(), fx, st) + res.update(a=a[2], b=a[3]) + elif len(a) == 2 and a[1] == "selftest": + selftest() + return 0 + else: + print(__doc__, file=sys.stderr) + return 2 + except (ValueError, KeyError, IndexError) as e: + print("BAD INPUT %s" % e) + return 2 + print(verdict(res)) + return 0 if res["equal"] else 1 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/tools/token-value/tv-02/fixtures/reorg-first-step-k2.json b/tools/token-value/tv-02/fixtures/reorg-first-step-k2.json new file mode 100644 index 000000000..7e8acd0ce --- /dev/null +++ b/tools/token-value/tv-02/fixtures/reorg-first-step-k2.json @@ -0,0 +1,47 @@ +{ + "network": "devnet4", + "written": "2026-10-09T08:40Z, by hand from the rules (rules-v0.1: the CURRENT schedule at 18 decimals, 1 block a second, genesis issues nothing, block h has DAA score h - 1), before either implementation ran a replay; the closed forms below were written by hand and their digits evaluated with bc on build-5 (a calculator on the written expressions, no replay code)", + "symbols": { + "F": "31688087810000000000 (the launch rate per DAA second, 3,168,808,781 x 10^10; at 1 block a second the full-rate block)", + "H": "15844043905000000000 (F halved once at 18 decimals, floor(F / 2); F is even at 18 decimals, so no floor is lost here, unlike the 8-decimal 1,584,404,390)", + "R": "2592000 (ramp seconds)", + "X": "63115200 (the first step, two years of DAA seconds)", + "ramp_term": "schedule(d) = floor(F (10R + 90d) / (100R)) = F/10 + floor(d G / 9) for d < R, G = F / 320000 = 99025274406250 = 9q + 1, q = 11002808267361, so schedule(d) = F/10 + d q + floor(d / 9)", + "ramp_sum": "sum over d = 0..R-1 = R F / 10 + q R (R - 1) / 2 + 9 (0 + 1 + ... + 287999) = 45174523722296485498848000 (R = 9 x 288000)" + }, + "id": "reorg-first-step-k2", + "description": "the first halving at DAA X: the ancestor is height X-1 (DAA X-2); an orphan branch of 2 blocks (heights X, X+1: DAA X-1 full rate, DAA X the first halved block) is replaced by 3 (heights X, X+1, X+2)", + "ancestor_height": 63115199, + "orphan_len": 2, + "replacement_len": 3, + "expected": { + "ancestor": { + "height": 63115199, + "daa_score": 63115198, + "subsidy": "31688087810000000000", + "cumulative": "1963038968176400675498848000", + "derivation": "ramp_sum + (X - 1 - R) F: DAA 0..X-2" + }, + "orphan_tip": { + "height": 63115201, + "subsidy": "15844043905000000000", + "cumulative": "1963039015708532390498848000", + "derivation": "ancestor + F + H" + }, + "after_rollback": { + "height": 63115199, + "cumulative": "1963038968176400675498848000" + }, + "final": { + "height": 63115202, + "daa_score": 63115201, + "subsidy": "15844043905000000000", + "cumulative": "1963039031552576295498848000", + "derivation": "ancestor + F + 2H; equals the straight chain's cumulative at height X+2" + }, + "known_failed_no_undo": { + "cumulative": "1963039079084708010498848000", + "derivation": "ancestor + (F + H) + (F + 2H): the orphan's F + H counted twice; must be refused" + } + } +} diff --git a/tools/token-value/tv-02/fixtures/reorg-ramp-end-k1.json b/tools/token-value/tv-02/fixtures/reorg-ramp-end-k1.json new file mode 100644 index 000000000..76b67621e --- /dev/null +++ b/tools/token-value/tv-02/fixtures/reorg-ramp-end-k1.json @@ -0,0 +1,47 @@ +{ + "network": "devnet4", + "written": "2026-10-09T08:40Z, by hand from the rules (rules-v0.1: the CURRENT schedule at 18 decimals, 1 block a second, genesis issues nothing, block h has DAA score h - 1), before either implementation ran a replay; the closed forms below were written by hand and their digits evaluated with bc on build-5 (a calculator on the written expressions, no replay code)", + "symbols": { + "F": "31688087810000000000 (the launch rate per DAA second, 3,168,808,781 x 10^10; at 1 block a second the full-rate block)", + "H": "15844043905000000000 (F halved once at 18 decimals, floor(F / 2); F is even at 18 decimals, so no floor is lost here, unlike the 8-decimal 1,584,404,390)", + "R": "2592000 (ramp seconds)", + "X": "63115200 (the first step, two years of DAA seconds)", + "ramp_term": "schedule(d) = floor(F (10R + 90d) / (100R)) = F/10 + floor(d G / 9) for d < R, G = F / 320000 = 99025274406250 = 9q + 1, q = 11002808267361, so schedule(d) = F/10 + d q + floor(d / 9)", + "ramp_sum": "sum over d = 0..R-1 = R F / 10 + q R (R - 1) / 2 + 9 (0 + 1 + ... + 287999) = 45174523722296485498848000 (R = 9 x 288000)" + }, + "id": "reorg-ramp-end-k1", + "description": "ramp end at DAA R: the ancestor is the last ramp block (height R, DAA R-1); an orphan branch of 1 block (height R+1, DAA R, the first full-rate block) is replaced by 2 (heights R+1, R+2)", + "ancestor_height": 2592000, + "orphan_len": 1, + "replacement_len": 2, + "expected": { + "ancestor": { + "height": 2592000, + "daa_score": 2591999, + "subsidy": "31688076807191732638", + "cumulative": "45174523722296485498848000", + "derivation": "subsidy F/10 + (R-1) q + floor((R-1)/9) = F/10 + 2591999 q + 287999; cumulative ramp_sum" + }, + "orphan_tip": { + "height": 2592001, + "subsidy": "31688087810000000000", + "cumulative": "45174555410384295498848000", + "derivation": "ramp_sum + F" + }, + "after_rollback": { + "height": 2592000, + "cumulative": "45174523722296485498848000" + }, + "final": { + "height": 2592002, + "daa_score": 2592001, + "subsidy": "31688087810000000000", + "cumulative": "45174587098472105498848000", + "derivation": "ramp_sum + 2F; equals the straight chain's cumulative at height R+2" + }, + "known_failed_no_undo": { + "cumulative": "45174618786559915498848000", + "derivation": "ramp_sum + 3F: a ledger that forgets to subtract the orphan counts F twice; must be refused" + } + } +} diff --git a/tools/token-value/tv-02/replay-node/Cargo.toml b/tools/token-value/tv-02/replay-node/Cargo.toml new file mode 100644 index 000000000..cb82722d8 --- /dev/null +++ b/tools/token-value/tv-02/replay-node/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "tv02-replay-node" +version = "0.1.0" +edition = "2024" +description = "TV-02 implementation A: the issued-supply replay in the node's language, calling the node's own issuance code (kaspa-consensus-core) per block" +license = "MIT" +publish = false + +# A standalone workspace. The node fork is read by path from the repository's vendor/igneum-node (the fork checkout at +# the commit the run names, outside version control, as pool/ and app/igneum-wallet read it). Nothing here changes the +# node: the crate only calls kaspa-consensus-core's public issuance functions. +[workspace] + +[[bin]] +name = "tv02-replay-node" +path = "src/main.rs" + +[dependencies] +kaspa-consensus-core = { path = "../../../../vendor/igneum-node/consensus/core" } +serde_json = "1" + +[profile.release] +overflow-checks = true +debug-assertions = true diff --git a/tools/token-value/tv-02/replay-node/src/main.rs b/tools/token-value/tv-02/replay-node/src/main.rs new file mode 100644 index 000000000..000000282 --- /dev/null +++ b/tools/token-value/tv-02/replay-node/src/main.rs @@ -0,0 +1,377 @@ +//! TV-02 implementation A: the issued-supply replay in the node's language. +//! +//! Every per-block subsidy here is the node's own function, called through its public API at the commit the run +//! names: `EmissionTable::block_subsidy` (the path `CoinbaseManager::calc_block_subsidy` takes, consensus/src/ +//! processes/coinbase.rs 262-266, and the value body validation compares the coinbase payload against). Every block +//! is also read through the node's second, independent issuance path, `igneum::block_subsidy_units` (the u128 twin of +//! the 33-entry table rule); a block where the two differ is counted and named in the summary. +//! +//! Model: a straight chain. Genesis (height 0, DAA score 0) issues nothing: it never enters the DAA window +//! (consensus/src/processes/window.rs 176-179 puts it in mergeset_non_daa) and expected_coinbase_transaction skips +//! non-DAA blues (coinbase.rs 139). So block 1 has DAA score 0 (difficulty.rs 31-32: the selected parent's score plus +//! the mergeset's DAA blocks, none for block 1) and the block at height h >= 1 has DAA score h - 1, issuing the +//! schedule's subsidy at that score. "cumulative" at height h is the issued supply of the blocks at heights 0..=h, +//! the sum of the schedule over DAA scores 0..=h-1, in base units, exact (u128, overflow checks on). The node lane +//! confirmed the genesis rule on bf60948a (9 October 2026, 09:25 UK). +//! +//! Commands (outputs are CSV with the header height,daa_score,subsidy,cumulative): +//! straight +//! reorg +//! genesis-payload +//! Networks: devnet4 (devnet4_params()), mainnet (MAINNET_PARAMS), devnet (DEVNET_PARAMS). + +use kaspa_consensus_core::{ + config::params::{DEVNET_PARAMS, MAINNET_PARAMS, Params, TESTNET_PARAMS, devnet3_params, devnet4_params}, + emission::{EmissionTable, Tail}, + igneum, + unit::{self, Amount}, +}; +use std::collections::BTreeMap; +use std::io::Write; +use std::sync::Mutex; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::Instant; + +struct Net { + name: String, + params: Params, + table: EmissionTable, + bps: u64, + decimals: u8, +} + +impl Net { + fn new(name: &str) -> Net { + let params = match name { + "devnet4" => devnet4_params(), + "mainnet" => MAINNET_PARAMS, + "devnet" => DEVNET_PARAMS, + other => panic!("unknown network {other} (devnet4, mainnet, devnet)"), + }; + let table = params.emission.table(); + let bps = params.bps(); + let decimals = params.base_unit_decimals; + Net { name: name.to_string(), params, table, bps, decimals } + } + + /// The node's subsidy for a block at `daa` (the coinbase manager's path). + #[inline] + fn subsidy(&self, daa: u64) -> u128 { + self.table.block_subsidy(daa, self.bps) + } + + /// The node's second path (igneum.rs `block_subsidy_units`), defined for the CURRENT shape only. + #[inline] + fn subsidy_units(&self, daa: u64) -> u128 { + igneum::block_subsidy_units(daa, self.bps, self.decimals).0 + } +} + +/// The row set's DAA scores (the README's rule, implemented here and independently in B): the probes, every +/// transition's before/at/after, and one step past the terminal transition. Returns (sorted DAA scores, labels, the +/// first DAA score that pays nothing). The CSV row of DAA score d is the block at height d + 1; genesis is its own row. +fn rows(net: &Net) -> (Vec, BTreeMap, u64) { + let s = &net.params.emission; + let bps = net.bps; + let mut labels: BTreeMap = BTreeMap::new(); + let mut add = |h: u64, l: String, labels: &mut BTreeMap| { + labels.entry(h).and_modify(|e| { e.push_str("; "); e.push_str(&l) }).or_insert(l); + }; + add(0, "daa 0 (block 1)".into(), &mut labels); + add(1, "probe 1".into(), &mut labels); + add(2, "probe 2".into(), &mut labels); + if bps > 1 { + add(bps - 1, "second 0 last block".into(), &mut labels); + add(bps, "second 1 first block".into(), &mut labels); + add(bps + 1, "second 1 second block".into(), &mut labels); + } + for d in 1..30u64 { + add(d * 86_400 * bps, format!("ramp day {d}"), &mut labels); + } + add(bps * s.ramp_seconds / 2, "ramp midpoint".into(), &mut labels); + let mut transitions: Vec<(u64, String)> = vec![(bps * s.ramp_seconds, "ramp end".into())]; + let mut zero = 0u64; + for k in 1..=200u64 { + let x = k * s.step_seconds * bps; + transitions.push((x, format!("step {k}"))); + if net.subsidy(x) == 0 { + zero = x; + break; + } + } + assert!(zero > 0, "the curve did not reach zero within 200 steps"); + for (x, l) in &transitions { + add(x - 1, format!("before {l}"), &mut labels); + add(*x, format!("at {l}"), &mut labels); + add(x + 1, format!("after {l}"), &mut labels); + } + add(zero - 1, "terminal (last block with a subsidy)".into(), &mut labels); + add(zero + s.step_seconds * bps, "one step past the terminal".into(), &mut labels); + let heights: Vec = labels.keys().copied().collect(); + (heights, labels, zero) +} + +#[derive(Default, Clone, Copy)] +struct Chunk { + sum: u128, + path_mismatch: u64, + first_mismatch: Option, + shape_violation: u64, +} + +/// Sums blocks `a..=b` one by one, with the two-path cross-check and the shape check (non-decreasing through the +/// ramp, non-increasing after it). +fn sum_chunk(net: &Net, a: u64, b: u64) -> Chunk { + let ramp_end = net.bps * net.params.emission.ramp_seconds; + let check_units = matches!(net.params.emission.tail, Tail::Cap); + let mut c = Chunk::default(); + let mut prev = if a > 0 { Some(net.subsidy(a - 1)) } else { None }; + let mut d = a; + loop { + let s = net.subsidy(d); + if check_units { + let u = net.subsidy_units(d); + if u != s { + c.path_mismatch += 1; + if c.first_mismatch.is_none() { + c.first_mismatch = Some(d); + } + } + } + if let Some(p) = prev { + let ok = if d < ramp_end { s >= p } else if d - 1 >= ramp_end { s <= p } else { true }; + if !ok { + c.shape_violation += 1; + } + } + c.sum = c.sum.checked_add(s).expect("sum overflows u128"); + prev = Some(s); + if d == b { + break; + } + d += 1; + } + c +} + +/// Cumulative issued supply at each height of `heights` (sorted, distinct), every block from genesis summed once. +fn cumulative_at(net: &Net, heights: &[u64]) -> (Vec, Chunk) { + const CHUNK: u64 = 1 << 24; + // segments: [0, h0], [h0+1, h1], ...; each split into chunks of at most CHUNK blocks + let mut jobs: Vec<(usize, u64, u64)> = Vec::new(); + let mut lo = 0u64; + for (i, &h) in heights.iter().enumerate() { + if h < lo { + continue; + } + let mut a = lo; + while a <= h { + let b = (a.saturating_add(CHUNK - 1)).min(h); + jobs.push((i, a, b)); + a = b + 1; + } + lo = h + 1; + } + let next = AtomicUsize::new(0); + let results: Mutex> = Mutex::new(Vec::with_capacity(jobs.len())); + let threads = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1); + std::thread::scope(|sc| { + for _ in 0..threads { + sc.spawn(|| { + loop { + let j = next.fetch_add(1, Ordering::Relaxed); + if j >= jobs.len() { + break; + } + let (i, a, b) = jobs[j]; + let c = sum_chunk(net, a, b); + results.lock().unwrap().push((i, c)); + } + }); + } + }); + let mut seg = vec![0u128; heights.len()]; + let mut total = Chunk::default(); + for (i, c) in results.into_inner().unwrap() { + seg[i] = seg[i].checked_add(c.sum).unwrap(); + total.path_mismatch += c.path_mismatch; + total.shape_violation += c.shape_violation; + total.first_mismatch = match (total.first_mismatch, c.first_mismatch) { + (Some(x), Some(y)) => Some(x.min(y)), + (x, y) => x.or(y), + }; + } + let mut cum = Vec::with_capacity(heights.len()); + let mut run = 0u128; + for s in seg { + run = run.checked_add(s).unwrap(); + cum.push(run); + } + (cum, total) +} + +fn write_csv(path: &str, rows: &[(u64, u64, u128, u128)]) { + let mut f = std::fs::File::create(path).unwrap(); + writeln!(f, "height,daa_score,subsidy,cumulative").unwrap(); + for (h, d, s, c) in rows { + writeln!(f, "{h},{d},{s},{c}").unwrap(); + } +} + +fn straight(name: &str, out: &str, labels_out: &str, summary_out: &str) { + let t0 = Instant::now(); + let net = Net::new(name); + let (daas, labels, zero) = rows(&net); + let (cum, chk) = cumulative_at(&net, &daas); + // genesis issues nothing; the block of DAA score d sits at height d + 1 + let mut rows: Vec<(u64, u64, u128, u128)> = vec![(0, 0, 0, 0)]; + rows.extend(daas.iter().zip(cum.iter()).map(|(&d, &c)| (d + 1, d, net.subsidy(d), c))); + write_csv(out, &rows); + let mut lf = std::fs::File::create(labels_out).unwrap(); + writeln!(lf, "height,label").unwrap(); + writeln!(lf, "0,\"genesis (issues nothing)\"").unwrap(); + for (d, l) in &labels { + writeln!(lf, "{},\"{l}\"", d + 1).unwrap(); + } + let terminal = zero; // the height of the block of DAA zero - 1, the last that pays + let issued_at_terminal = rows.iter().find(|r| r.0 == terminal).unwrap().3; + let last = rows.last().unwrap(); + let cap = igneum::supply_cap_units(net.decimals); + let s = &net.params.emission; + let summary = serde_json::json!({ + "implementation": "A (Rust, the node's issuance code by path)", + "node_commit": std::env::var("TV02_NODE_COMMIT").unwrap_or_default(), + "network": net.name, + "network_id": net.params.net.to_string(), + "bps": net.bps, + "base_unit_decimals": net.decimals, + "schedule": { + "launch_rate": s.launch_rate.to_string(), + "ramp_seconds": s.ramp_seconds, + "ramp_start_percent": s.ramp_start_percent, + "step_seconds": s.step_seconds, + "step_decay_q32": s.step_decay_q32, + "tail": format!("{:?}", s.tail), + }, + "blocks_replayed_per_block": last.0 + 1, + "genesis_rule": "genesis issues nothing; height h >= 1 has DAA score h - 1 (window.rs 176-179, difficulty.rs 31-32, coinbase.rs 139)", + "rows": rows.len(), + "first_zero_subsidy_daa": zero, + "first_zero_subsidy_height": zero + 1, + "terminal_height": terminal, + "issued_at_terminal": issued_at_terminal.to_string(), + "issued_one_step_past_terminal": last.3.to_string(), + "supply_cap_units": cap.to_string(), + "cap_minus_issued": (cap as i128 - issued_at_terminal as i128).to_string(), + "cap_breach": issued_at_terminal > cap, + "issuance_after_terminal": (last.3 - issued_at_terminal).to_string(), + "node_two_paths_mismatched_blocks": chk.path_mismatch, + "node_two_paths_first_mismatch_height": chk.first_mismatch, + "shape_violations": chk.shape_violation, + "far_future_subsidy_u64_max_div_2": net.subsidy(u64::MAX / 2).to_string(), + "seconds": t0.elapsed().as_secs_f64(), + }); + std::fs::write(summary_out, serde_json::to_string_pretty(&summary).unwrap() + "\n").unwrap(); + println!("{}", serde_json::to_string(&summary).unwrap()); +} + +/// The reorg fixture: the common ancestor, an orphan branch of k blocks, rolled back and replaced by k+1. The +/// ledger keeps one record per applied block and a rollback subtracts exactly the records it pops. The known-failed +/// ledger (rollback that forgets to subtract) is run beside it and written as its own row. +fn reorg(fixture: &str, out: &str) { + let fx: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(fixture).unwrap()).unwrap(); + let net = Net::new(fx["network"].as_str().unwrap()); + let anc = fx["ancestor_height"].as_u64().unwrap(); + let k = fx["orphan_len"].as_u64().unwrap(); + let k1 = fx["replacement_len"].as_u64().unwrap(); + assert_eq!(k1, k + 1, "the fixture replaces k blocks by k+1"); + assert!(anc >= 1, "the ancestor is a block after genesis"); + // heights; the block at height h >= 1 has DAA score h - 1 + let (cum, chk) = cumulative_at(&net, &[anc - 1]); + assert_eq!(chk.path_mismatch, 0); + let mut rows: Vec<(String, u64, u64, u128, u128)> = Vec::new(); + let mut ledger: Vec<(u64, u128)> = Vec::new(); + let mut total = cum[0]; + let mut naive = cum[0]; + rows.push(("ancestor".into(), anc, anc - 1, net.subsidy(anc - 1), total)); + for h in anc + 1..=anc + k { + let s = net.subsidy(h - 1); + ledger.push((h, s)); + total += s; + naive += s; + rows.push(("orphan".into(), h, h - 1, s, total)); + } + for _ in 0..k { + let (h, s) = ledger.pop().unwrap(); + total -= s; + rows.push(("rollback".into(), h, h - 1, s, total)); + } + for h in anc + 1..=anc + k1 { + let s = net.subsidy(h - 1); + ledger.push((h, s)); + total += s; + naive += s; + rows.push(("replacement".into(), h, h - 1, s, total)); + } + let tip = anc + k1; + rows.push(("final".into(), tip, tip - 1, net.subsidy(tip - 1), total)); + rows.push(("known_failed_no_undo".into(), tip, tip - 1, net.subsidy(tip - 1), naive)); + let mut f = std::fs::File::create(out).unwrap(); + writeln!(f, "step,height,daa_score,subsidy,cumulative").unwrap(); + for (st, h, d, s, c) in &rows { + writeln!(f, "{st},{h},{d},{s},{c}").unwrap(); + } + println!("reorg {} final {} naive {}", fx["id"].as_str().unwrap_or(""), total, naive); +} + +/// What the node reads as the subsidy field of each compiled genesis coinbase payload at that network's own unit +/// (the slicing of `CoinbaseManager::deserialize_coinbase_payload`, coinbase.rs 231-236: blue score u64, the subsidy +/// at `amount_wire_len()`, script version u16, script length u8), beside the schedule's subsidy at DAA 0. +fn genesis_payload(out: &str) { + let mut v = Vec::new(); + for (name, p) in [ + ("mainnet", MAINNET_PARAMS), + ("testnet-1", TESTNET_PARAMS), + ("devnet", DEVNET_PARAMS), + ("devnet-3", devnet3_params()), + ("devnet-4", devnet4_params()), + ] { + let payload = p.genesis.coinbase_payload; + let row = unit::with_decimals_for_test(p.base_unit_decimals, || { + let w = unit::amount_wire_len(); + let field = Amount::from_consensus_bytes(&payload[8..8 + w]).map(|a| a.0); + let version = u16::from_le_bytes([payload[8 + w], payload[9 + w]]); + let spk_len = payload[10 + w]; + serde_json::json!({ + "network": name, + "base_unit_decimals": p.base_unit_decimals, + "amount_wire_len": w, + "payload_len": payload.len(), + "subsidy_field_read_at_network_unit": field.map(|x| x.to_string()), + "subsidy_field_equals_one_ign_at_network_unit": field == Some(unit::unit(p.base_unit_decimals)), + "script_version_read": version, + "script_len_read": spk_len, + "one_ign_at_network_unit": unit::unit(p.base_unit_decimals).to_string(), + "max_amount_at_network_unit": unit::max_amount().to_string(), + "schedule_subsidy_at_daa_0": p.emission.table().block_subsidy(0, p.bps()).to_string(), + }) + }); + v.push(row); + } + let s = serde_json::to_string_pretty(&v).unwrap(); + std::fs::write(out, s.clone() + "\n").unwrap(); + println!("{s}"); +} + +fn main() { + let a: Vec = std::env::args().collect(); + match a.get(1).map(|s| s.as_str()) { + Some("straight") if a.len() == 6 => straight(&a[2], &a[3], &a[4], &a[5]), + Some("reorg") if a.len() == 4 => reorg(&a[2], &a[3]), + Some("genesis-payload") if a.len() == 3 => genesis_payload(&a[2]), + _ => { + eprintln!("usage: straight | reorg | genesis-payload "); + std::process::exit(2); + } + } +} diff --git a/tools/token-value/tv-02/replay-spec/replay_spec.py b/tools/token-value/tv-02/replay-spec/replay_spec.py new file mode 100755 index 000000000..9e5a22aee --- /dev/null +++ b/tools/token-value/tv-02/replay-spec/replay_spec.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python3 +"""TV-02 implementation B: the issued-supply replay written from the supply rules in words, standalone Python 3. + +No code is shared with implementation A (the Rust crate that calls the node). B reads a rules file (the interim +transcription of the litepaper and economics page, rules-v0-litepaper.json, or the TV-01 lane's supply-spec.json +through `load_rules`) and computes every quantity with Python integers. + +Method (deliberately not A's): A sums every block one by one. B groups blocks. Inside the ramp every block of one +DAA second pays the same, so B sums seconds and multiplies by the blocks in each second; after the ramp every block +of one step pays the same, so B multiplies the step's subsidy by the number of its blocks. Only the partial second +or partial step at the end of a range is counted block by block. + +Model: a straight chain, genesis at height 0. Two rules the words must state, read from the rules file: +genesis_issues ("schedule_at_daa_0" or "nothing") and daa_of_height ("height": block h has DAA score h, or +"height_minus_one": block h >= 1 has DAA score h - 1). cumulative(h) is the issued supply of the blocks at heights 0..h. + +Usage: + replay_spec.py straight + replay_spec.py reorg + replay_spec.py selftest +""" +import csv +import json +import sys +import time + + +class Schedule: + def __init__(self, rules, net): + r = rules["rules"] + n = rules["networks"][net] + self.net = net + self.bps = int(n["bps"]) + self.decimals = int(n["decimals"]) + self.year = int(r["year_seconds"]) + fd = int(r["launch_rate_floor_decimals"]) + floor_rate = int(r["year_one_emission_ign"]) * 10 ** fd // self.year + if self.decimals >= fd: + self.rate0 = floor_rate * 10 ** (self.decimals - fd) + else: + self.rate0 = int(r["year_one_emission_ign"]) * 10 ** self.decimals // self.year + self.step = int(r["step_seconds"]) + self.ramp_s = int(r["ramp_seconds"]) + self.ramp_start = int(r["ramp_start_percent"]) + self.order = r["per_block_order"] + if r["tail"] != "cap": + raise SystemExit("B replays a capped schedule only; tail %r is outside TV-02's ratified D01 policy" % r["tail"]) + self.cap = int(r["supply_cap_ign"]) * 10 ** self.decimals + self.genesis_issues = r.get("genesis_issues", "schedule_at_daa_0") + self.daa_of_height = r.get("daa_of_height", "height") + if self.genesis_issues not in ("schedule_at_daa_0", "nothing"): + raise SystemExit("unknown genesis_issues %r" % self.genesis_issues) + if self.daa_of_height not in ("height", "height_minus_one"): + raise SystemExit("unknown daa_of_height %r" % self.daa_of_height) + + # --- the per-block rule, from the words --- + def rate(self, k): + """Per-second rate in step k: the launch rate halved k times, floored each time (equal to rate0 // 2**k).""" + x = self.rate0 + for _ in range(k): + x //= 2 + if x == 0: + break + return x + + def ramp(self, x, s): + if s >= self.ramp_s: + return x + num = self.ramp_start * self.ramp_s + (100 - self.ramp_start) * s + return x * num // (100 * self.ramp_s) + + def subsidy_of_second(self, s): + k = s // self.step + if self.order == "divide_then_ramp": + return self.ramp(self.rate(k) // self.bps, s) + if self.order == "ramp_then_divide": + return self.ramp(self.rate(k), s) // self.bps + raise SystemExit("unknown per_block_order %r" % self.order) + + def subsidy(self, d): + return self.subsidy_of_second(d // self.bps) + + def zero_step(self): + """The first step k >= 1 whose blocks pay nothing.""" + k = 1 + while self.subsidy_of_second(k * self.step) != 0: + k += 1 + if k > 1000: + raise SystemExit("the curve does not reach zero") + return k + + # --- grouped sums --- + def blocks_in_seconds(self, s0, s1, h): + """Number of blocks with height <= h whose second lies in [s0, s1).""" + lo = s0 * self.bps + hi = min(s1 * self.bps - 1, h) + return max(0, hi - lo + 1) + + def genesis_amount(self): + return self.subsidy(0) if self.genesis_issues == "schedule_at_daa_0" else 0 + + def daa(self, h): + """DAA score of the block at height h >= 1.""" + return h if self.daa_of_height == "height" else h - 1 + + def height_of_daa(self, d): + return d if self.daa_of_height == "height" else d + 1 + + def block_subsidy_at_height(self, h): + return self.genesis_amount() if h == 0 else self.subsidy(self.daa(h)) + + def issued(self, h): + """Issued supply of the blocks at heights 0..h: genesis, then blocks 1..h at their DAA scores.""" + if h == 0: + return self.genesis_amount() + lo, hi = self.daa(1), self.daa(h) + return self.genesis_amount() + self.cumulative(hi) - self.cumulative(lo - 1) + + def cumulative(self, h): + """Sum of the schedule over DAA scores 0..h.""" + if h < 0: + return 0 + last_s = h // self.bps + total = 0 + # the ramp, second by second + for s in range(0, min(self.ramp_s, last_s + 1)): + total += self.blocks_in_seconds(s, s + 1, h) * self.subsidy_of_second(s) + # after the ramp, step by step + k = self.ramp_s // self.step + while True: + s0 = max(self.ramp_s, k * self.step) + s1 = (k + 1) * self.step + if s0 > last_s: + break + n = self.blocks_in_seconds(s0, s1, h) + total += n * self.subsidy_of_second(s0) + k += 1 + return total + + # --- the row set (the README's rule) --- + def rows(self): + bps = self.bps + labels = {} + + def add(h, label): + labels[h] = labels[h] + "; " + label if h in labels else label + + add(0, "daa 0") + add(1, "probe 1") + add(2, "probe 2") + if bps > 1: + add(bps - 1, "second 0 last block") + add(bps, "second 1 first block") + add(bps + 1, "second 1 second block") + for d in range(1, 30): + add(d * 86400 * bps, "ramp day %d" % d) + add(bps * self.ramp_s // 2, "ramp midpoint") + kz = self.zero_step() + transitions = [(bps * self.ramp_s, "ramp end")] + [(k * self.step * bps, "step %d" % k) for k in range(1, kz + 1)] + for x, l in transitions: + add(x - 1, "before " + l) + add(x, "at " + l) + add(x + 1, "after " + l) + zero = kz * self.step * bps + add(zero - 1, "terminal (last block with a subsidy)") + add(zero + self.step * bps, "one step past the terminal") + hl = {} + for d, l in labels.items(): + h = self.height_of_daa(d) + hl[h] = (hl[h] + "; " + l) if h in hl else l + hl[0] = ("genesis; " + hl[0]) if 0 in hl else "genesis (issues nothing)" + return sorted(hl), hl, zero + + +def load_rules(path): + with open(path) as f: + doc = json.load(f) + if "rules" in doc and "networks" in doc: + return doc + raise SystemExit("rules file %s has no rules/networks block; the TV-01 spec adapter maps it in tv01_adapter.py" % path) + + +def straight(rules_path, net, out, labels_out, summary_out): + t0 = time.time() + rules = load_rules(rules_path) + sc = Schedule(rules, net) + heights, labels, zero = sc.rows() + with open(out, "w", newline="") as f: + w = csv.writer(f, lineterminator="\n") + w.writerow(["height", "daa_score", "subsidy", "cumulative"]) + cum = {} + for h in heights: + cum[h] = sc.issued(h) + w.writerow([h, 0 if h == 0 else sc.daa(h), sc.block_subsidy_at_height(h), cum[h]]) + with open(labels_out, "w", newline="") as f: + w = csv.writer(f, lineterminator="\n", quoting=csv.QUOTE_NONNUMERIC) + f.write("height,label\n") + for h in heights: + f.write('%d,"%s"\n' % (h, labels[h])) + term = sc.height_of_daa(zero - 1) + summary = { + "implementation": "B (Python 3, from the rules in words)", + "rules_file": rules_path, + "rules_id": rules.get("id"), + "network": net, + "bps": sc.bps, + "base_unit_decimals": sc.decimals, + "launch_rate": str(sc.rate0), + "first_zero_subsidy_daa": zero, + "first_zero_subsidy_height": sc.height_of_daa(zero), + "genesis_issues": sc.genesis_issues, + "daa_of_height": sc.daa_of_height, + "terminal_height": term, + "issued_at_terminal": str(cum[term]), + "issued_one_step_past_terminal": str(cum[heights[-1]]), + "supply_cap_units": str(sc.cap), + "cap_minus_issued": str(sc.cap - cum[term]), + "cap_breach": cum[term] > sc.cap, + "issuance_after_terminal": str(cum[heights[-1]] - cum[term]), + "rows": len(heights), + "seconds": round(time.time() - t0, 3), + } + with open(summary_out, "w") as f: + json.dump(summary, f, indent=2) + f.write("\n") + print(json.dumps(summary)) + + +def reorg(rules_path, fixture_path, out): + rules = load_rules(rules_path) + with open(fixture_path) as f: + fx = json.load(f) + sc = Schedule(rules, fx["network"]) + anc = int(fx["ancestor_height"]) + k = int(fx["orphan_len"]) + k1 = int(fx["replacement_len"]) + if k1 != k + 1: + raise SystemExit("the fixture replaces k blocks by k+1") + total = sc.issued(anc) + naive = total + ledger = [] + rows = [("ancestor", anc, sc.block_subsidy_at_height(anc), total)] + for h in range(anc + 1, anc + k + 1): + s = sc.block_subsidy_at_height(h) + ledger.append((h, s)) + total += s + naive += s + rows.append(("orphan", h, s, total)) + for _ in range(k): + h, s = ledger.pop() + total -= s + rows.append(("rollback", h, s, total)) + for h in range(anc + 1, anc + k1 + 1): + s = sc.block_subsidy_at_height(h) + ledger.append((h, s)) + total += s + naive += s + rows.append(("replacement", h, s, total)) + tip = anc + k1 + rows.append(("final", tip, sc.block_subsidy_at_height(tip), total)) + rows.append(("known_failed_no_undo", tip, sc.block_subsidy_at_height(tip), naive)) + with open(out, "w") as f: + f.write("step,height,daa_score,subsidy,cumulative\n") + for st, h, s, c in rows: + f.write("%s,%d,%d,%d,%d\n" % (st, h, 0 if h == 0 else sc.daa(h), s, c)) + print("reorg %s final %d naive %d" % (fx.get("id", ""), total, naive)) + + +def selftest(): + """B's own known-failed case first, then its cross-check of the grouped sum against a block-by-block sum.""" + rules = { + "rules": {"year_seconds": 31557600, "year_one_emission_ign": 1000000000, "launch_rate_floor_decimals": 8, + "step_seconds": 600, "ramp_seconds": 50, "ramp_start_percent": 10, "per_block_order": "divide_then_ramp", + "tail": "cap", "supply_cap_ign": 4000000000}, + "networks": {"t1": {"bps": 1, "decimals": 18}, "t3": {"bps": 3, "decimals": 18}, "t10": {"bps": 10, "decimals": 8}}, + } + # known-failed: a grouped sum that forgets the partial last second must disagree with the block-by-block sum + sc = Schedule(rules, "t3") + h = 3 * 20 + 1 + brute = sum(sc.subsidy(d) for d in range(h + 1)) + wrong = sum(3 * sc.subsidy_of_second(s) for s in range(h // 3)) + assert wrong != brute, "the known-failed grouping was not caught" + for net in ("t1", "t3", "t10"): + sc = Schedule(rules, net) + run = 0 + end = sc.bps * sc.step * 4 + 7 + for d in range(end): + run += sc.subsidy(d) + if d % 97 == 0 or d in (sc.bps * sc.ramp_s - 1, sc.bps * sc.ramp_s, sc.bps * sc.step - 1, sc.bps * sc.step): + assert sc.cumulative(d) == run, (net, d, sc.cumulative(d), run) + assert sc.rate(3) == sc.rate0 // 8 + print("selftest: known-failed grouping caught; grouped sum equals the block-by-block sum on 3 networks") + + +def main(a): + if len(a) == 7 and a[1] == "straight": + straight(a[2], a[3], a[4], a[5], a[6]) + elif len(a) == 5 and a[1] == "reorg": + reorg(a[2], a[3], a[4]) + elif len(a) == 2 and a[1] == "selftest": + selftest() + else: + print(__doc__, file=sys.stderr) + sys.exit(2) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/tools/token-value/tv-02/replay-spec/rules-v0-litepaper.json b/tools/token-value/tv-02/replay-spec/rules-v0-litepaper.json new file mode 100644 index 000000000..945940103 --- /dev/null +++ b/tools/token-value/tv-02/replay-spec/rules-v0-litepaper.json @@ -0,0 +1,47 @@ +{ + "id": "tv02-rules-v0-litepaper", + "status": "interim: written from the published words before the TV-01 supply spec landed; replaced by the spec when it lands", + "sources": [ + "site/litepaper.html, Economics > Supply: hard cap of 4 billion IGN, emission starts at 1 billion a year and halves every two years for ever, ramps from 10% to 100% over the first 30 days", + "site/litepaper.html, Finality: emission per block is a schedule keyed to difficulty-adjusted time, every block in the window is paid at that rate", + "site/economics.html, Emission table: a year is 31,557,600 seconds; launch rate the literal 3,168,808,781 (10^9 IGN x 10^8 / 31,557,600 floored) widened to 18 decimals; ramp 2,592,000 s from 10 percent; step 63,115,200 s, the rate halved each step; tail none, the curve runs to zero", + "site/economics.html, Units and decimals: the Igneum 2.0 devnet and mainnet are 18 decimals" + ], + "rules": { + "year_seconds": 31557600, + "year_one_emission_ign": 1000000000, + "launch_rate_floor_decimals": 8, + "launch_rate_rule": "rate0 = floor(year_one_emission_ign * 10^launch_rate_floor_decimals / year_seconds) * 10^(decimals - launch_rate_floor_decimals)", + "step_seconds": 63115200, + "step_rule": "rate(k) = floor(rate(k-1) / 2), at the network's unit", + "ramp_seconds": 2592000, + "ramp_start_percent": 10, + "ramp_rule": "ramped = floor(x * (start * R + (100 - start) * s) / (100 * R)) for s < R, else x", + "seconds_rule": "s = floor(daa_score / bps)", + "per_block_order": "divide_then_ramp", + "per_block_order_source": "ASSUMED: the words give a per-second rate and blocks per second but not the order of the floor; the natural reading (the second's per-block share, then the ramp) is taken and flagged for the spec", + "tail": "cap", + "supply_cap_ign": 4000000000, + "genesis_issues": "schedule_at_daa_0", + "genesis_issues_source": "ASSUMED: the words say nothing about genesis; read as a block like every other, paying the schedule at DAA 0", + "daa_of_height": "height", + "daa_of_height_source": "ASSUMED: the words say one block a second and a schedule keyed to DAA time; read as the block at height h carrying DAA score h" + }, + "networks": { + "devnet4": { + "bps": 1, + "decimals": 18, + "source": "site/economics.html: the Igneum 2.0 devnet, 18 decimals, one block a second" + }, + "mainnet": { + "bps": 10, + "decimals": 18, + "source": "site/litepaper.html: 1 / s blocks, rising to 10 (designed); site/economics.html: mainnet 18 decimals" + }, + "devnet": { + "bps": 1, + "decimals": 8, + "source": "site/economics.html: an earlier devnet paid in 8 decimals" + } + } +} diff --git a/tools/token-value/tv-02/replay-spec/rules-v0.1-genesis-rule.json b/tools/token-value/tv-02/replay-spec/rules-v0.1-genesis-rule.json new file mode 100644 index 000000000..35c7485c6 --- /dev/null +++ b/tools/token-value/tv-02/replay-spec/rules-v0.1-genesis-rule.json @@ -0,0 +1,48 @@ +{ + "id": "tv02-rules-v0.1-litepaper-plus-genesis-rule", + "status": "interim: v0 with the two rules the words leave out filled by the node lane's written answer (9 October 2026, 09:25 UK), cited by file and line, not by code; replaced by the TV-01 spec when it lands", + "sources": [ + "site/litepaper.html, Economics > Supply: hard cap of 4 billion IGN, emission starts at 1 billion a year and halves every two years for ever, ramps from 10% to 100% over the first 30 days", + "site/litepaper.html, Finality: emission per block is a schedule keyed to difficulty-adjusted time, every block in the window is paid at that rate", + "site/economics.html, Emission table: a year is 31,557,600 seconds; launch rate the literal 3,168,808,781 (10^9 IGN x 10^8 / 31,557,600 floored) widened to 18 decimals; ramp 2,592,000 s from 10 percent; step 63,115,200 s, the rate halved each step; tail none, the curve runs to zero", + "site/economics.html, Units and decimals: the Igneum 2.0 devnet and mainnet are 18 decimals", + "the node lane's answer to TV-02, 9 October 2026 09:25 UK (window.rs 176-179, coinbase.rs 139, difficulty.rs 31-32 at bf60948a)" + ], + "rules": { + "year_seconds": 31557600, + "year_one_emission_ign": 1000000000, + "launch_rate_floor_decimals": 8, + "launch_rate_rule": "rate0 = floor(year_one_emission_ign * 10^launch_rate_floor_decimals / year_seconds) * 10^(decimals - launch_rate_floor_decimals)", + "step_seconds": 63115200, + "step_rule": "rate(k) = floor(rate(k-1) / 2), at the network's unit", + "ramp_seconds": 2592000, + "ramp_start_percent": 10, + "ramp_rule": "ramped = floor(x * (start * R + (100 - start) * s) / (100 * R)) for s < R, else x", + "seconds_rule": "s = floor(daa_score / bps)", + "per_block_order": "divide_then_ramp", + "per_block_order_source": "ASSUMED: the words give a per-second rate and blocks per second but not the order of the floor; the natural reading (the second's per-block share, then the ramp) is taken and flagged for the spec", + "tail": "cap", + "supply_cap_ign": 4000000000, + "genesis_issues": "nothing", + "genesis_issues_source": "the node lane, 9 October 2026 09:25 UK: genesis never enters the DAA window (consensus/src/processes/window.rs 176-179) and the coinbase pays only blues outside mergeset_non_daa (coinbase.rs 139): genesis is never rewarded", + "daa_of_height": "height_minus_one", + "daa_of_height_source": "consequence of the same rule: a block's DAA score is its selected parent's plus its mergeset's DAA blocks (difficulty.rs 31-32), so block 1 has DAA score 0 and block h has h - 1" + }, + "networks": { + "devnet4": { + "bps": 1, + "decimals": 18, + "source": "site/economics.html: the Igneum 2.0 devnet, 18 decimals, one block a second" + }, + "mainnet": { + "bps": 10, + "decimals": 18, + "source": "site/litepaper.html: 1 / s blocks, rising to 10 (designed); site/economics.html: mainnet 18 decimals" + }, + "devnet": { + "bps": 1, + "decimals": 8, + "source": "site/economics.html: an earlier devnet paid in 8 decimals" + } + } +} diff --git a/tools/token-value/tv-02/run-a.sh b/tools/token-value/tv-02/run-a.sh new file mode 100755 index 000000000..48a60dd52 --- /dev/null +++ b/tools/token-value/tv-02/run-a.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# TV-02 implementation A, on its build box (build-5), never on the Mac. Records its pid file before any work and +# removes it on exit. Usage: run-a.sh [build|straight |reorg |genesis] +# The node fork is read from /vendor/igneum-node at the commit checked out there (the run prints it). +set -euo pipefail +REPO="$1"; OUT="$2"; shift 2 +PID="$OUT/run-a.pid" +mkdir -p "$OUT" +echo "$$ $(date -u +%FT%TZ) tv02-a $*" > "$PID" +trap 'rm -f "$PID"' EXIT +export PATH="$HOME/.cargo/bin:$PATH" +CRATE="$REPO/tools/token-value/tv-02/replay-node" +export TV02_NODE_COMMIT="$(git -C "$REPO/vendor/igneum-node" rev-parse HEAD)" +BIN="$CRATE/target/release/tv02-replay-node" +echo "node commit $TV02_NODE_COMMIT" +case "$1" in + build) + (cd "$CRATE" && cargo build --release 2>&1) + sha256sum "$BIN" ;; + straight) + "$BIN" straight "$2" "$OUT/a-$2.csv" "$OUT/a-$2-labels.csv" "$OUT/a-$2-summary.json" ;; + reorg) + id="$(basename "$2" .json)" + "$BIN" reorg "$2" "$OUT/a-reorg-$id.csv" ;; + genesis) + "$BIN" genesis-payload "$OUT/a-genesis-payload.json" ;; + *) echo "unknown command $1" >&2; exit 2 ;; +esac diff --git a/tools/token-value/tv-02/run-b.sh b/tools/token-value/tv-02/run-b.sh new file mode 100755 index 000000000..55d908fd5 --- /dev/null +++ b/tools/token-value/tv-02/run-b.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# TV-02 implementation B, on its build box (build-6), never on the Mac. Records its pid file before any work and +# removes it on exit. Usage: run-b.sh [selftest|straight |reorg ] +# Output names carry the rules id's tag (the third argument's basename) so runs on different rules never overwrite. +set -euo pipefail +DIR="$1"; OUT="$2"; RULES="$3"; shift 3 +PID="$OUT/run-b.pid" +mkdir -p "$OUT" +echo "$$ $(date -u +%FT%TZ) tv02-b $*" > "$PID" +trap 'rm -f "$PID"' EXIT +TAG="$(basename "$RULES" .json)" +echo "rules $RULES sha256 $(sha256sum "$RULES" | cut -c1-64)" +echo "replay_spec.py sha256 $(sha256sum "$DIR/replay-spec/replay_spec.py" | cut -c1-64)" +case "$1" in + selftest) python3 "$DIR/replay-spec/replay_spec.py" selftest; python3 "$DIR/compare.py" selftest ;; + straight) python3 "$DIR/replay-spec/replay_spec.py" straight "$RULES" "$2" "$OUT/b-$TAG-$2.csv" "$OUT/b-$TAG-$2-labels.csv" "$OUT/b-$TAG-$2-summary.json" ;; + reorg) id="$(basename "$2" .json)"; python3 "$DIR/replay-spec/replay_spec.py" reorg "$RULES" "$2" "$OUT/b-$TAG-reorg-$id.csv" ;; + *) echo "unknown command $1" >&2; exit 2 ;; +esac From 6720d629af2a6624cc67cc16ca84d296f77ad76c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:25:53 +0000 Subject: [PATCH 02/13] TV-01 Phase 0: the supply spec (cap as the invariant under D01, two schedule profiles in exact integers, 13 litepaper-versus-code rows, 4 BLOCKED) supply-spec.json and supply-spec.md under docs/plans/igneum-2.0-master/token-value/phase0/tv-01/; the generator (tools/token-value/tv-01/gen-spec.py, anchored to every cited line at release-2.0.2-node a284380b and release-2.0.3-node-k6 bf60948a, and equal on every field to the consensus code's own dump run on build-9); the checker (tools/token-value/tv-01/check-spec.py: four known-failed cases first, then total + shortfall = cap exactly); the registry batch tools/ci/batches/tv-01-20261009-01.json (TV-01 NOT RUN, for the CI steward). Mainnet as compiled issues 3,963,038,999.610200467816224 IGN in all, 36,961,000.39 IGN under the cap, zero from DAA 39,131,424,000. The litepaper's year-five tail-vote sentence is named superseded by D01. Documents and tests only; nothing activates. Co-Authored-By: Claude Opus 5.5 --- .../token-value/phase0/tv-01/README.md | 47 + .../token-value/phase0/tv-01/supply-spec.json | 3181 +++++++++++++++++ .../token-value/phase0/tv-01/supply-spec.md | 170 + tools/ci/batches/tv-01-20261009-01.json | 19 + tools/token-value/tv-01/check-spec.py | 250 ++ .../tv-01/code-dump/schedule-bf60948a.jsonl | 139 + .../tv-01/code-dump/tv01_schedule.rs | 60 + tools/token-value/tv-01/gen-spec.py | 573 +++ 8 files changed, 4439 insertions(+) create mode 100644 docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md create mode 100644 docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json create mode 100644 docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md create mode 100644 tools/ci/batches/tv-01-20261009-01.json create mode 100644 tools/token-value/tv-01/check-spec.py create mode 100644 tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl create mode 100644 tools/token-value/tv-01/code-dump/tv01_schedule.rs create mode 100644 tools/token-value/tv-01/gen-spec.py diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md new file mode 100644 index 000000000..1512dd89e --- /dev/null +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md @@ -0,0 +1,47 @@ +# TV-01, Phase 0: the supply spec + +Gate TV-01 ("Resolve and freeze the monetary contract", rules-and-gates.json sha256 `10ce307329e1663dcb6dce849317c9276c19252de5f483e9ee06015fc77cbe3d`). Verdict: **NOT RUN**. This is a document and its tests. Nothing here activates anything. + +## Files + +| File | What it is | +|---|---| +| `supply-spec.json` | the machine-readable spec: a schema block, the cap, the rules, the activation clocks, two schedule profiles (each an ordered list of phases with start DAA score, per-block subsidy integer, rule name and source line), the coinbase split, fees, supply accounting, the superseded tail-vote text, the conflicts table, and the sources by sha | +| `supply-spec.md` | the same in words, with the conflicts table, the D01 resolution and the founder's four questions | +| `../../../../../../tools/token-value/tv-01/gen-spec.py` | the generator; every figure in both files is its output | +| `../../../../../../tools/token-value/tv-01/check-spec.py` | the checker: four known-failed cases first, then the spec summed to the cap in exact integers | +| `../../../../../../tools/token-value/tv-01/code-dump/` | the consensus code's own schedule (`tv01_schedule.rs`, run on build-9 against bf60948a) and its output, `schedule-bf60948a.jsonl` (sha256 `3e4b5ac3c2f43057a13ace7d781ad714e385a94105623c0e0dac4adc417d8056`) | + +## Sources + +| Source | Revision | sha256 | +|---|---|---| +| site/litepaper.html | igneum master 6824d49cc32def4463b643293a5622545618f7a9 | `35ac65e151339089ef6ddeea3842b24d95e00d327a2b12faa9ea190ff85e463f` | +| igneum-node, release-2.0.2-node | a284380bdd62d5d0d972cb38f2cbd1daeedda69d | per file in `supply-spec.json` `sources.node.files` | +| igneum-node, release-2.0.3-node-k6 | bf60948a01467bb54eaab9e50d32b91058f11242 | the same files, byte-identical except kaspad/src/daemon.rs, whose override refusal is the same rule | +| pool/src/payout.rs, config.rs, pplns.rs | igneum master 6824d49c | in `supply-spec.json` `sources.pool` | + +## Method + +1. I read the volume's VR-01, VR-02, TV-01, TV-04 and D01, and the litepaper's supply, fee and security sections. +2. I grepped the node fork's consensus crate and execution crate for subsidy, coinbase, deflationary and emission, then read the emission table, the coinbase manager, the network objects, the genesis blocks and the EVM reward rule. +3. I transcribed the schedule constants into the generator. Each one is anchored to its line at both node revisions. +4. The generator computes both profiles in exact integers. It compares every phase, both ramps and both totals with the code dump, and they agree on every field. +5. The checker regenerates the phases from the spec's own rules, as a third implementation, and reconciles total + shortfall = cap exactly. + +## Results + +- **Mainnet as compiled (10 blocks a second)**: issuance totals 3,963,038,999.610200467816224 IGN. The shortfall under the cap is exactly 36,961,000,389,799,532,183,776,000 base units. The subsidy is zero from DAA 39,131,424,000, the start of year 125. +- **Devnet 4 profile (1 block a second)**: the same curve per second. The total differs from mainnet by 15,473,592,000 base units of per-block flooring. +- **Conflicts table**: 13 rows. Nine are resolved: C01 by D01, C12 by D01, and seven taken to the code with a text correction where one is owed. Four are BLOCKED on questions for the founder, each with a default: C04 the block rate, C08 the per-block crediting rule, C09 the mainnet genesis clock, C11 the pool's custody. + +## Commands + +``` +python3 tools/token-value/tv-01/gen-spec.py --node-git --igneum-root . \ + --igneum-rev 6824d49cc32def4463b643293a5622545618f7a9 \ + --code-dump tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl \ + --out docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json \ + --md docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md +python3 tools/token-value/tv-01/check-spec.py +``` diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json new file mode 100644 index 000000000..7a4fadbd5 --- /dev/null +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json @@ -0,0 +1,3181 @@ +{ + "schema": { + "name": "igneum-supply-spec", + "version": "1.0.0", + "integers": "every amount is a decimal string of base units (amounts reach 4 x 10^27, past JSON's exact range); DAA scores are JSON integers (all under 2^53)", + "phase_fields": { + "index": "0-based, contiguous", + "kind": "ramp | constant | terminal", + "rule": "the rule's name", + "start_daa_score": "inclusive", + "end_daa_score": "exclusive; null for the terminal phase", + "per_block_subsidy": "base units per block at every DAA score of the phase (null for the ramp, whose per-block value is its formula)", + "phase_sum": "base units the phase issues at one subsidy per DAA score", + "source": "file:line in the node fork at both named revisions" + }, + "checker": "tools/token-value/tv-01/check-spec.py" + }, + "gate": "TV-01", + "verdict": "NOT RUN", + "status": "Phase 0 document. Activates nothing; changes no node, no chain and no served page. The verdict stays NOT RUN until the independent panel reads it.", + "decision": { + "id": "D01", + "title": "Cap and tail", + "ruling": "capped issuance, subject to TV-04 (the founder's ratification, 9 October 2026, 09:2x UK)", + "effect": "the cap is the invariant; the litepaper's year-five tail-vote sentence is superseded (C01)" + }, + "invariant": { + "rule": "sum over all blocks of the per-block subsidy <= cap, with no tail, no vote, no price or revenue trigger and no emergency mint", + "how_enforced": "by the schedule, not by a running counter: every block's coinbase must carry exactly the scheduled subsidy (coinbase.rs:262) and the EVM state credits by the same rule (executor.rs:181 to 211); the cap constant itself is only compared in tests (igneum.rs:154). The schedule sums to the cap minus the shortfall below, so the cap is never reached.", + "change_policy": "the schedule is a compiled genesis parameter on mainnet; the daemon refuses an override file there (kaspad/src/daemon.rs, network.rs:266); a change is a new release with a new consensus digest and an explicit incompatible-policy process (VR-06)" + }, + "units": { + "coin": "IGN", + "base_unit": "wei (one consensus base unit is one EVM wei)", + "decimals": 18, + "base_units_per_coin": "1000000000000000000", + "source": [ + "consensus/core/src/unit.rs:38", + "consensus/core/src/config/params.rs:1816" + ] + }, + "cap": { + "base_units": "4000000000000000000000000000", + "coins": "4000000000", + "kind": "invariant", + "source": [ + "consensus/core/src/igneum.rs:37", + "consensus/core/src/igneum.rs:154" + ] + }, + "rules": { + "launch_rate_per_second": "31688087810000000000", + "launch_rate_note": "floor(10^9 x 10^8 / 31,557,600) = 3,168,808,781 at 8 decimals, times 10^10: 31.68808781 IGN a DAA second", + "year_seconds": 31557600, + "ramp_seconds": 2592000, + "ramp_start_percent": 10, + "step_seconds": 63115200, + "step_decay_q32": 2147483648, + "decay_rule": "rate[k+1] = floor(rate[k] x step_decay_q32 / 2^32) (a halving every two years)", + "per_block": "per_block(d) = ramp(floor(rate[floor(s / step_seconds)] / bps), s), s = floor(d / bps)", + "tail": "cap", + "source": [ + "consensus/core/src/emission.rs:103 to 110", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:316 to 333", + "consensus/core/src/config/params.rs:1858" + ] + }, + "activation_clocks": { + "genesis": "DAA score 0 (C09: the compiled mainnet genesis still carries Kaspa's 1,312,860; BLOCKED)", + "ramp_end": { + "mainnet": 25920000, + "devnet-4": 2592000, + "seconds": 2592000 + }, + "halvings": "every 63,115,200 DAA seconds (2 years of 365.25 days): DAA score k x 631,152,000 at 10 blocks/s, k x 63,115,200 at 1 block/s", + "terminal": { + "mainnet": 39131424000, + "devnet-4": 4102488000 + }, + "signing_bonus": "mainnet never (params.rs:1809); testnet-1 from 0", + "pool_split": "mainnet never (params.rs:1851)", + "per_block_rule_N8": "mainnet never (params.rs:1812; C08 BLOCKED); testnet-1 from 0" + }, + "schedules": [ + { + "profile": "mainnet", + "network": "mainnet (MAINNET_PARAMS as compiled)", + "bps": 10, + "decimals": 18, + "genesis_daa_score": 0, + "time_key": "seconds = floor(daa_score / bps); the per-block subsidy is constant inside a DAA second", + "phases": [ + { + "index": 0, + "kind": "ramp", + "rule": "launch-ramp", + "start_daa_score": 0, + "end_daa_score": 25920000, + "per_block_subsidy": null, + "ramp": { + "full_per_block": "3168808781000000000", + "start_percent": 10, + "ramp_seconds": 2592000, + "first_block": "316880878100000000", + "last_block": "3168807680719173263", + "formula": "per_block(d) = floor(full_per_block x (start_percent x ramp_seconds + (100 - start_percent) x s) / (100 x ramp_seconds)), s = floor(d / bps)" + }, + "phase_sum": "45174523722296485488480000", + "source": [ + "consensus/core/src/emission.rs:316", + "consensus/core/src/emission.rs:329" + ] + }, + { + "index": 1, + "kind": "constant", + "rule": "full-rate-after-ramp", + "start_daa_score": 25920000, + "end_daa_score": 631152000, + "per_block_subsidy": "3168808781000000000", + "decay_step": 0, + "rate_per_second": "31688087810000000000", + "phase_sum": "1917864476142192000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 2, + "kind": "constant", + "rule": "halving-1", + "start_daa_score": 631152000, + "end_daa_score": 1262304000, + "per_block_subsidy": "1584404390500000000", + "decay_step": 1, + "rate_per_second": "15844043905000000000", + "phase_sum": "999999999872856000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 3, + "kind": "constant", + "rule": "halving-2", + "start_daa_score": 1262304000, + "end_daa_score": 1893456000, + "per_block_subsidy": "792202195250000000", + "decay_step": 2, + "rate_per_second": "7922021952500000000", + "phase_sum": "499999999936428000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 4, + "kind": "constant", + "rule": "halving-3", + "start_daa_score": 1893456000, + "end_daa_score": 2524608000, + "per_block_subsidy": "396101097625000000", + "decay_step": 3, + "rate_per_second": "3961010976250000000", + "phase_sum": "249999999968214000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 5, + "kind": "constant", + "rule": "halving-4", + "start_daa_score": 2524608000, + "end_daa_score": 3155760000, + "per_block_subsidy": "198050548812500000", + "decay_step": 4, + "rate_per_second": "1980505488125000000", + "phase_sum": "124999999984107000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 6, + "kind": "constant", + "rule": "halving-5", + "start_daa_score": 3155760000, + "end_daa_score": 3786912000, + "per_block_subsidy": "99025274406250000", + "decay_step": 5, + "rate_per_second": "990252744062500000", + "phase_sum": "62499999992053500000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 7, + "kind": "constant", + "rule": "halving-6", + "start_daa_score": 3786912000, + "end_daa_score": 4418064000, + "per_block_subsidy": "49512637203125000", + "decay_step": 6, + "rate_per_second": "495126372031250000", + "phase_sum": "31249999996026750000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 8, + "kind": "constant", + "rule": "halving-7", + "start_daa_score": 4418064000, + "end_daa_score": 5049216000, + "per_block_subsidy": "24756318601562500", + "decay_step": 7, + "rate_per_second": "247563186015625000", + "phase_sum": "15624999998013375000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 9, + "kind": "constant", + "rule": "halving-8", + "start_daa_score": 5049216000, + "end_daa_score": 5680368000, + "per_block_subsidy": "12378159300781250", + "decay_step": 8, + "rate_per_second": "123781593007812500", + "phase_sum": "7812499999006687500000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 10, + "kind": "constant", + "rule": "halving-9", + "start_daa_score": 5680368000, + "end_daa_score": 6311520000, + "per_block_subsidy": "6189079650390625", + "decay_step": 9, + "rate_per_second": "61890796503906250", + "phase_sum": "3906249999503343750000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 11, + "kind": "constant", + "rule": "halving-10", + "start_daa_score": 6311520000, + "end_daa_score": 6942672000, + "per_block_subsidy": "3094539825195312", + "decay_step": 10, + "rate_per_second": "30945398251953125", + "phase_sum": "1953124999751671559424000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 12, + "kind": "constant", + "rule": "halving-11", + "start_daa_score": 6942672000, + "end_daa_score": 7573824000, + "per_block_subsidy": "1547269912597656", + "decay_step": 11, + "rate_per_second": "15472699125976562", + "phase_sum": "976562499875835779712000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 13, + "kind": "constant", + "rule": "halving-12", + "start_daa_score": 7573824000, + "end_daa_score": 8204976000, + "per_block_subsidy": "773634956298828", + "decay_step": 12, + "rate_per_second": "7736349562988281", + "phase_sum": "488281249937917889856000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 14, + "kind": "constant", + "rule": "halving-13", + "start_daa_score": 8204976000, + "end_daa_score": 8836128000, + "per_block_subsidy": "386817478149414", + "decay_step": 13, + "rate_per_second": "3868174781494140", + "phase_sum": "244140624968958944928000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 15, + "kind": "constant", + "rule": "halving-14", + "start_daa_score": 8836128000, + "end_daa_score": 9467280000, + "per_block_subsidy": "193408739074707", + "decay_step": 14, + "rate_per_second": "1934087390747070", + "phase_sum": "122070312484479472464000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 16, + "kind": "constant", + "rule": "halving-15", + "start_daa_score": 9467280000, + "end_daa_score": 10098432000, + "per_block_subsidy": "96704369537353", + "decay_step": 15, + "rate_per_second": "967043695373535", + "phase_sum": "61035156242239420656000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 17, + "kind": "constant", + "rule": "halving-16", + "start_daa_score": 10098432000, + "end_daa_score": 10729584000, + "per_block_subsidy": "48352184768676", + "decay_step": 16, + "rate_per_second": "483521847686767", + "phase_sum": "30517578121119394752000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 18, + "kind": "constant", + "rule": "halving-17", + "start_daa_score": 10729584000, + "end_daa_score": 11360736000, + "per_block_subsidy": "24176092384338", + "decay_step": 17, + "rate_per_second": "241760923843383", + "phase_sum": "15258789060559697376000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 19, + "kind": "constant", + "rule": "halving-18", + "start_daa_score": 11360736000, + "end_daa_score": 11991888000, + "per_block_subsidy": "12088046192169", + "decay_step": 18, + "rate_per_second": "120880461921691", + "phase_sum": "7629394530279848688000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 20, + "kind": "constant", + "rule": "halving-19", + "start_daa_score": 11991888000, + "end_daa_score": 12623040000, + "per_block_subsidy": "6044023096084", + "decay_step": 19, + "rate_per_second": "60440230960845", + "phase_sum": "3814697265139608768000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 21, + "kind": "constant", + "rule": "halving-20", + "start_daa_score": 12623040000, + "end_daa_score": 13254192000, + "per_block_subsidy": "3022011548042", + "decay_step": 20, + "rate_per_second": "30220115480422", + "phase_sum": "1907348632569804384000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 22, + "kind": "constant", + "rule": "halving-21", + "start_daa_score": 13254192000, + "end_daa_score": 13885344000, + "per_block_subsidy": "1511005774021", + "decay_step": 21, + "rate_per_second": "15110057740211", + "phase_sum": "953674316284902192000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 23, + "kind": "constant", + "rule": "halving-22", + "start_daa_score": 13885344000, + "end_daa_score": 14516496000, + "per_block_subsidy": "755502887010", + "decay_step": 22, + "rate_per_second": "7555028870105", + "phase_sum": "476837158142135520000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 24, + "kind": "constant", + "rule": "halving-23", + "start_daa_score": 14516496000, + "end_daa_score": 15147648000, + "per_block_subsidy": "377751443505", + "decay_step": 23, + "rate_per_second": "3777514435052", + "phase_sum": "238418579071067760000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 25, + "kind": "constant", + "rule": "halving-24", + "start_daa_score": 15147648000, + "end_daa_score": 15778800000, + "per_block_subsidy": "188875721752", + "decay_step": 24, + "rate_per_second": "1888757217526", + "phase_sum": "119209289535218304000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 26, + "kind": "constant", + "rule": "halving-25", + "start_daa_score": 15778800000, + "end_daa_score": 16409952000, + "per_block_subsidy": "94437860876", + "decay_step": 25, + "rate_per_second": "944378608763", + "phase_sum": "59604644767609152000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 27, + "kind": "constant", + "rule": "halving-26", + "start_daa_score": 16409952000, + "end_daa_score": 17041104000, + "per_block_subsidy": "47218930438", + "decay_step": 26, + "rate_per_second": "472189304381", + "phase_sum": "29802322383804576000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 28, + "kind": "constant", + "rule": "halving-27", + "start_daa_score": 17041104000, + "end_daa_score": 17672256000, + "per_block_subsidy": "23609465219", + "decay_step": 27, + "rate_per_second": "236094652190", + "phase_sum": "14901161191902288000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 29, + "kind": "constant", + "rule": "halving-28", + "start_daa_score": 17672256000, + "end_daa_score": 18303408000, + "per_block_subsidy": "11804732609", + "decay_step": 28, + "rate_per_second": "118047326095", + "phase_sum": "7450580595635568000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 30, + "kind": "constant", + "rule": "halving-29", + "start_daa_score": 18303408000, + "end_daa_score": 18934560000, + "per_block_subsidy": "5902366304", + "decay_step": 29, + "rate_per_second": "59023663047", + "phase_sum": "3725290297502208000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 31, + "kind": "constant", + "rule": "halving-30", + "start_daa_score": 18934560000, + "end_daa_score": 19565712000, + "per_block_subsidy": "2951183152", + "decay_step": 30, + "rate_per_second": "29511831523", + "phase_sum": "1862645148751104000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 32, + "kind": "constant", + "rule": "halving-31", + "start_daa_score": 19565712000, + "end_daa_score": 20196864000, + "per_block_subsidy": "1475591576", + "decay_step": 31, + "rate_per_second": "14755915761", + "phase_sum": "931322574375552000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 33, + "kind": "constant", + "rule": "halving-32", + "start_daa_score": 20196864000, + "end_daa_score": 20828016000, + "per_block_subsidy": "737795788", + "decay_step": 32, + "rate_per_second": "7377957880", + "phase_sum": "465661287187776000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 34, + "kind": "constant", + "rule": "halving-33", + "start_daa_score": 20828016000, + "end_daa_score": 21459168000, + "per_block_subsidy": "368897894", + "decay_step": 33, + "rate_per_second": "3688978940", + "phase_sum": "232830643593888000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 35, + "kind": "constant", + "rule": "halving-34", + "start_daa_score": 21459168000, + "end_daa_score": 22090320000, + "per_block_subsidy": "184448947", + "decay_step": 34, + "rate_per_second": "1844489470", + "phase_sum": "116415321796944000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 36, + "kind": "constant", + "rule": "halving-35", + "start_daa_score": 22090320000, + "end_daa_score": 22721472000, + "per_block_subsidy": "92224473", + "decay_step": 35, + "rate_per_second": "922244735", + "phase_sum": "58207660582896000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 37, + "kind": "constant", + "rule": "halving-36", + "start_daa_score": 22721472000, + "end_daa_score": 23352624000, + "per_block_subsidy": "46112236", + "decay_step": 36, + "rate_per_second": "461122367", + "phase_sum": "29103829975872000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 38, + "kind": "constant", + "rule": "halving-37", + "start_daa_score": 23352624000, + "end_daa_score": 23983776000, + "per_block_subsidy": "23056118", + "decay_step": 37, + "rate_per_second": "230561183", + "phase_sum": "14551914987936000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 39, + "kind": "constant", + "rule": "halving-38", + "start_daa_score": 23983776000, + "end_daa_score": 24614928000, + "per_block_subsidy": "11528059", + "decay_step": 38, + "rate_per_second": "115280591", + "phase_sum": "7275957493968000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 40, + "kind": "constant", + "rule": "halving-39", + "start_daa_score": 24614928000, + "end_daa_score": 25246080000, + "per_block_subsidy": "5764029", + "decay_step": 39, + "rate_per_second": "57640295", + "phase_sum": "3637978431408000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 41, + "kind": "constant", + "rule": "halving-40", + "start_daa_score": 25246080000, + "end_daa_score": 25877232000, + "per_block_subsidy": "2882014", + "decay_step": 40, + "rate_per_second": "28820147", + "phase_sum": "1818988900128000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 42, + "kind": "constant", + "rule": "halving-41", + "start_daa_score": 25877232000, + "end_daa_score": 26508384000, + "per_block_subsidy": "1441007", + "decay_step": 41, + "rate_per_second": "14410073", + "phase_sum": "909494450064000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 43, + "kind": "constant", + "rule": "halving-42", + "start_daa_score": 26508384000, + "end_daa_score": 27139536000, + "per_block_subsidy": "720503", + "decay_step": 42, + "rate_per_second": "7205036", + "phase_sum": "454746909456000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 44, + "kind": "constant", + "rule": "halving-43", + "start_daa_score": 27139536000, + "end_daa_score": 27770688000, + "per_block_subsidy": "360251", + "decay_step": 43, + "rate_per_second": "3602518", + "phase_sum": "227373139152000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 45, + "kind": "constant", + "rule": "halving-44", + "start_daa_score": 27770688000, + "end_daa_score": 28401840000, + "per_block_subsidy": "180125", + "decay_step": 44, + "rate_per_second": "1801259", + "phase_sum": "113686254000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 46, + "kind": "constant", + "rule": "halving-45", + "start_daa_score": 28401840000, + "end_daa_score": 29032992000, + "per_block_subsidy": "90062", + "decay_step": 45, + "rate_per_second": "900629", + "phase_sum": "56842811424000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 47, + "kind": "constant", + "rule": "halving-46", + "start_daa_score": 29032992000, + "end_daa_score": 29664144000, + "per_block_subsidy": "45031", + "decay_step": 46, + "rate_per_second": "450314", + "phase_sum": "28421405712000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 48, + "kind": "constant", + "rule": "halving-47", + "start_daa_score": 29664144000, + "end_daa_score": 30295296000, + "per_block_subsidy": "22515", + "decay_step": 47, + "rate_per_second": "225157", + "phase_sum": "14210387280000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 49, + "kind": "constant", + "rule": "halving-48", + "start_daa_score": 30295296000, + "end_daa_score": 30926448000, + "per_block_subsidy": "11257", + "decay_step": 48, + "rate_per_second": "112578", + "phase_sum": "7104878064000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 50, + "kind": "constant", + "rule": "halving-49", + "start_daa_score": 30926448000, + "end_daa_score": 31557600000, + "per_block_subsidy": "5628", + "decay_step": 49, + "rate_per_second": "56289", + "phase_sum": "3552123456000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 51, + "kind": "constant", + "rule": "halving-50", + "start_daa_score": 31557600000, + "end_daa_score": 32188752000, + "per_block_subsidy": "2814", + "decay_step": 50, + "rate_per_second": "28144", + "phase_sum": "1776061728000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 52, + "kind": "constant", + "rule": "halving-51", + "start_daa_score": 32188752000, + "end_daa_score": 32819904000, + "per_block_subsidy": "1407", + "decay_step": 51, + "rate_per_second": "14072", + "phase_sum": "888030864000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 53, + "kind": "constant", + "rule": "halving-52", + "start_daa_score": 32819904000, + "end_daa_score": 33451056000, + "per_block_subsidy": "703", + "decay_step": 52, + "rate_per_second": "7036", + "phase_sum": "443699856000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 54, + "kind": "constant", + "rule": "halving-53", + "start_daa_score": 33451056000, + "end_daa_score": 34082208000, + "per_block_subsidy": "351", + "decay_step": 53, + "rate_per_second": "3518", + "phase_sum": "221534352000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 55, + "kind": "constant", + "rule": "halving-54", + "start_daa_score": 34082208000, + "end_daa_score": 34713360000, + "per_block_subsidy": "175", + "decay_step": 54, + "rate_per_second": "1759", + "phase_sum": "110451600000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 56, + "kind": "constant", + "rule": "halving-55", + "start_daa_score": 34713360000, + "end_daa_score": 35344512000, + "per_block_subsidy": "87", + "decay_step": 55, + "rate_per_second": "879", + "phase_sum": "54910224000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 57, + "kind": "constant", + "rule": "halving-56", + "start_daa_score": 35344512000, + "end_daa_score": 35975664000, + "per_block_subsidy": "43", + "decay_step": 56, + "rate_per_second": "439", + "phase_sum": "27139536000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 58, + "kind": "constant", + "rule": "halving-57", + "start_daa_score": 35975664000, + "end_daa_score": 36606816000, + "per_block_subsidy": "21", + "decay_step": 57, + "rate_per_second": "219", + "phase_sum": "13254192000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 59, + "kind": "constant", + "rule": "halving-58", + "start_daa_score": 36606816000, + "end_daa_score": 37237968000, + "per_block_subsidy": "10", + "decay_step": 58, + "rate_per_second": "109", + "phase_sum": "6311520000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 60, + "kind": "constant", + "rule": "halving-59", + "start_daa_score": 37237968000, + "end_daa_score": 37869120000, + "per_block_subsidy": "5", + "decay_step": 59, + "rate_per_second": "54", + "phase_sum": "3155760000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 61, + "kind": "constant", + "rule": "halving-60", + "start_daa_score": 37869120000, + "end_daa_score": 38500272000, + "per_block_subsidy": "2", + "decay_step": 60, + "rate_per_second": "27", + "phase_sum": "1262304000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 62, + "kind": "constant", + "rule": "halving-61", + "start_daa_score": 38500272000, + "end_daa_score": 39131424000, + "per_block_subsidy": "1", + "decay_step": 61, + "rate_per_second": "13", + "phase_sum": "631152000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 63, + "kind": "terminal", + "rule": "terminal-zero", + "start_daa_score": 39131424000, + "end_daa_score": null, + "per_block_subsidy": "0", + "decay_step": 62, + "rate_per_second": "6", + "phase_sum": "0", + "source": [ + "consensus/core/src/emission.rs:109", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + } + ], + "phase_count": 64, + "last_nonzero_phase": { + "index": 62, + "per_block_subsidy": "1", + "start_daa_score": 38500272000, + "end_daa_score": 39131424000 + }, + "terminal": { + "start_daa_score": 39131424000, + "decay_step": 62, + "start_year": 124, + "behaviour": "per-block subsidy 0 at every later DAA score; the cap is never reached; blocks are paid by fees alone" + }, + "total_issuance": "3963038999610200467816224000", + "cap_shortfall": "36961000389799532183776000", + "scheduled_supply_at_year_end": { + "1": "963038999991632485488480000", + "2": "1963038999864488485488480000", + "5": "3213038999705558485488480000", + "10": "3838038999626093485488480000", + "12": "3900538999618146985488480000", + "20": "3959132749610697141738480000", + "50": "3963038880400910946483264000", + "100": "3963038999610196921373136000" + } + }, + { + "profile": "devnet-4", + "network": "Devnet 4 (devnet4_params, 1 block a second, the litepaper's launch rate)", + "bps": 1, + "decimals": 18, + "genesis_daa_score": 0, + "time_key": "seconds = floor(daa_score / bps); the per-block subsidy is constant inside a DAA second", + "phases": [ + { + "index": 0, + "kind": "ramp", + "rule": "launch-ramp", + "start_daa_score": 0, + "end_daa_score": 2592000, + "per_block_subsidy": null, + "ramp": { + "full_per_block": "31688087810000000000", + "start_percent": 10, + "ramp_seconds": 2592000, + "first_block": "3168808781000000000", + "last_block": "31688076807191732638", + "formula": "per_block(d) = floor(full_per_block x (start_percent x ramp_seconds + (100 - start_percent) x s) / (100 x ramp_seconds)), s = floor(d / bps)" + }, + "phase_sum": "45174523722296485498848000", + "source": [ + "consensus/core/src/emission.rs:316", + "consensus/core/src/emission.rs:329" + ] + }, + { + "index": 1, + "kind": "constant", + "rule": "full-rate-after-ramp", + "start_daa_score": 2592000, + "end_daa_score": 63115200, + "per_block_subsidy": "31688087810000000000", + "decay_step": 0, + "rate_per_second": "31688087810000000000", + "phase_sum": "1917864476142192000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 2, + "kind": "constant", + "rule": "halving-1", + "start_daa_score": 63115200, + "end_daa_score": 126230400, + "per_block_subsidy": "15844043905000000000", + "decay_step": 1, + "rate_per_second": "15844043905000000000", + "phase_sum": "999999999872856000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 3, + "kind": "constant", + "rule": "halving-2", + "start_daa_score": 126230400, + "end_daa_score": 189345600, + "per_block_subsidy": "7922021952500000000", + "decay_step": 2, + "rate_per_second": "7922021952500000000", + "phase_sum": "499999999936428000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 4, + "kind": "constant", + "rule": "halving-3", + "start_daa_score": 189345600, + "end_daa_score": 252460800, + "per_block_subsidy": "3961010976250000000", + "decay_step": 3, + "rate_per_second": "3961010976250000000", + "phase_sum": "249999999968214000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 5, + "kind": "constant", + "rule": "halving-4", + "start_daa_score": 252460800, + "end_daa_score": 315576000, + "per_block_subsidy": "1980505488125000000", + "decay_step": 4, + "rate_per_second": "1980505488125000000", + "phase_sum": "124999999984107000000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 6, + "kind": "constant", + "rule": "halving-5", + "start_daa_score": 315576000, + "end_daa_score": 378691200, + "per_block_subsidy": "990252744062500000", + "decay_step": 5, + "rate_per_second": "990252744062500000", + "phase_sum": "62499999992053500000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 7, + "kind": "constant", + "rule": "halving-6", + "start_daa_score": 378691200, + "end_daa_score": 441806400, + "per_block_subsidy": "495126372031250000", + "decay_step": 6, + "rate_per_second": "495126372031250000", + "phase_sum": "31249999996026750000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 8, + "kind": "constant", + "rule": "halving-7", + "start_daa_score": 441806400, + "end_daa_score": 504921600, + "per_block_subsidy": "247563186015625000", + "decay_step": 7, + "rate_per_second": "247563186015625000", + "phase_sum": "15624999998013375000000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 9, + "kind": "constant", + "rule": "halving-8", + "start_daa_score": 504921600, + "end_daa_score": 568036800, + "per_block_subsidy": "123781593007812500", + "decay_step": 8, + "rate_per_second": "123781593007812500", + "phase_sum": "7812499999006687500000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 10, + "kind": "constant", + "rule": "halving-9", + "start_daa_score": 568036800, + "end_daa_score": 631152000, + "per_block_subsidy": "61890796503906250", + "decay_step": 9, + "rate_per_second": "61890796503906250", + "phase_sum": "3906249999503343750000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 11, + "kind": "constant", + "rule": "halving-10", + "start_daa_score": 631152000, + "end_daa_score": 694267200, + "per_block_subsidy": "30945398251953125", + "decay_step": 10, + "rate_per_second": "30945398251953125", + "phase_sum": "1953124999751671875000000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 12, + "kind": "constant", + "rule": "halving-11", + "start_daa_score": 694267200, + "end_daa_score": 757382400, + "per_block_subsidy": "15472699125976562", + "decay_step": 11, + "rate_per_second": "15472699125976562", + "phase_sum": "976562499875835905942400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 13, + "kind": "constant", + "rule": "halving-12", + "start_daa_score": 757382400, + "end_daa_score": 820497600, + "per_block_subsidy": "7736349562988281", + "decay_step": 12, + "rate_per_second": "7736349562988281", + "phase_sum": "488281249937917952971200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 14, + "kind": "constant", + "rule": "halving-13", + "start_daa_score": 820497600, + "end_daa_score": 883612800, + "per_block_subsidy": "3868174781494140", + "decay_step": 13, + "rate_per_second": "3868174781494140", + "phase_sum": "244140624968958944928000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 15, + "kind": "constant", + "rule": "halving-14", + "start_daa_score": 883612800, + "end_daa_score": 946728000, + "per_block_subsidy": "1934087390747070", + "decay_step": 14, + "rate_per_second": "1934087390747070", + "phase_sum": "122070312484479472464000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 16, + "kind": "constant", + "rule": "halving-15", + "start_daa_score": 946728000, + "end_daa_score": 1009843200, + "per_block_subsidy": "967043695373535", + "decay_step": 15, + "rate_per_second": "967043695373535", + "phase_sum": "61035156242239736232000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 17, + "kind": "constant", + "rule": "halving-16", + "start_daa_score": 1009843200, + "end_daa_score": 1072958400, + "per_block_subsidy": "483521847686767", + "decay_step": 16, + "rate_per_second": "483521847686767", + "phase_sum": "30517578121119836558400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 18, + "kind": "constant", + "rule": "halving-17", + "start_daa_score": 1072958400, + "end_daa_score": 1136073600, + "per_block_subsidy": "241760923843383", + "decay_step": 17, + "rate_per_second": "241760923843383", + "phase_sum": "15258789060559886721600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 19, + "kind": "constant", + "rule": "halving-18", + "start_daa_score": 1136073600, + "end_daa_score": 1199188800, + "per_block_subsidy": "120880461921691", + "decay_step": 18, + "rate_per_second": "120880461921691", + "phase_sum": "7629394530279911803200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 20, + "kind": "constant", + "rule": "halving-19", + "start_daa_score": 1199188800, + "end_daa_score": 1262304000, + "per_block_subsidy": "60440230960845", + "decay_step": 19, + "rate_per_second": "60440230960845", + "phase_sum": "3814697265139924344000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 21, + "kind": "constant", + "rule": "halving-20", + "start_daa_score": 1262304000, + "end_daa_score": 1325419200, + "per_block_subsidy": "30220115480422", + "decay_step": 20, + "rate_per_second": "30220115480422", + "phase_sum": "1907348632569930614400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 22, + "kind": "constant", + "rule": "halving-21", + "start_daa_score": 1325419200, + "end_daa_score": 1388534400, + "per_block_subsidy": "15110057740211", + "decay_step": 21, + "rate_per_second": "15110057740211", + "phase_sum": "953674316284965307200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 23, + "kind": "constant", + "rule": "halving-22", + "start_daa_score": 1388534400, + "end_daa_score": 1451649600, + "per_block_subsidy": "7555028870105", + "decay_step": 22, + "rate_per_second": "7555028870105", + "phase_sum": "476837158142451096000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 24, + "kind": "constant", + "rule": "halving-23", + "start_daa_score": 1451649600, + "end_daa_score": 1514764800, + "per_block_subsidy": "3777514435052", + "decay_step": 23, + "rate_per_second": "3777514435052", + "phase_sum": "238418579071193990400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 25, + "kind": "constant", + "rule": "halving-24", + "start_daa_score": 1514764800, + "end_daa_score": 1577880000, + "per_block_subsidy": "1888757217526", + "decay_step": 24, + "rate_per_second": "1888757217526", + "phase_sum": "119209289535596995200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 26, + "kind": "constant", + "rule": "halving-25", + "start_daa_score": 1577880000, + "end_daa_score": 1640995200, + "per_block_subsidy": "944378608763", + "decay_step": 25, + "rate_per_second": "944378608763", + "phase_sum": "59604644767798497600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 27, + "kind": "constant", + "rule": "halving-26", + "start_daa_score": 1640995200, + "end_daa_score": 1704110400, + "per_block_subsidy": "472189304381", + "decay_step": 26, + "rate_per_second": "472189304381", + "phase_sum": "29802322383867691200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 28, + "kind": "constant", + "rule": "halving-27", + "start_daa_score": 1704110400, + "end_daa_score": 1767225600, + "per_block_subsidy": "236094652190", + "decay_step": 27, + "rate_per_second": "236094652190", + "phase_sum": "14901161191902288000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 29, + "kind": "constant", + "rule": "halving-28", + "start_daa_score": 1767225600, + "end_daa_score": 1830340800, + "per_block_subsidy": "118047326095", + "decay_step": 28, + "rate_per_second": "118047326095", + "phase_sum": "7450580595951144000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 30, + "kind": "constant", + "rule": "halving-29", + "start_daa_score": 1830340800, + "end_daa_score": 1893456000, + "per_block_subsidy": "59023663047", + "decay_step": 29, + "rate_per_second": "59023663047", + "phase_sum": "3725290297944014400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 31, + "kind": "constant", + "rule": "halving-30", + "start_daa_score": 1893456000, + "end_daa_score": 1956571200, + "per_block_subsidy": "29511831523", + "decay_step": 30, + "rate_per_second": "29511831523", + "phase_sum": "1862645148940449600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 32, + "kind": "constant", + "rule": "halving-31", + "start_daa_score": 1956571200, + "end_daa_score": 2019686400, + "per_block_subsidy": "14755915761", + "decay_step": 31, + "rate_per_second": "14755915761", + "phase_sum": "931322574438667200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 33, + "kind": "constant", + "rule": "halving-32", + "start_daa_score": 2019686400, + "end_daa_score": 2082801600, + "per_block_subsidy": "7377957880", + "decay_step": 32, + "rate_per_second": "7377957880", + "phase_sum": "465661287187776000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 34, + "kind": "constant", + "rule": "halving-33", + "start_daa_score": 2082801600, + "end_daa_score": 2145916800, + "per_block_subsidy": "3688978940", + "decay_step": 33, + "rate_per_second": "3688978940", + "phase_sum": "232830643593888000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 35, + "kind": "constant", + "rule": "halving-34", + "start_daa_score": 2145916800, + "end_daa_score": 2209032000, + "per_block_subsidy": "1844489470", + "decay_step": 34, + "rate_per_second": "1844489470", + "phase_sum": "116415321796944000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 36, + "kind": "constant", + "rule": "halving-35", + "start_daa_score": 2209032000, + "end_daa_score": 2272147200, + "per_block_subsidy": "922244735", + "decay_step": 35, + "rate_per_second": "922244735", + "phase_sum": "58207660898472000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 37, + "kind": "constant", + "rule": "halving-36", + "start_daa_score": 2272147200, + "end_daa_score": 2335262400, + "per_block_subsidy": "461122367", + "decay_step": 36, + "rate_per_second": "461122367", + "phase_sum": "29103830417678400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 38, + "kind": "constant", + "rule": "halving-37", + "start_daa_score": 2335262400, + "end_daa_score": 2398377600, + "per_block_subsidy": "230561183", + "decay_step": 37, + "rate_per_second": "230561183", + "phase_sum": "14551915177281600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 39, + "kind": "constant", + "rule": "halving-38", + "start_daa_score": 2398377600, + "end_daa_score": 2461492800, + "per_block_subsidy": "115280591", + "decay_step": 38, + "rate_per_second": "115280591", + "phase_sum": "7275957557083200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 40, + "kind": "constant", + "rule": "halving-39", + "start_daa_score": 2461492800, + "end_daa_score": 2524608000, + "per_block_subsidy": "57640295", + "decay_step": 39, + "rate_per_second": "57640295", + "phase_sum": "3637978746984000", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 41, + "kind": "constant", + "rule": "halving-40", + "start_daa_score": 2524608000, + "end_daa_score": 2587723200, + "per_block_subsidy": "28820147", + "decay_step": 40, + "rate_per_second": "28820147", + "phase_sum": "1818989341934400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 42, + "kind": "constant", + "rule": "halving-41", + "start_daa_score": 2587723200, + "end_daa_score": 2650838400, + "per_block_subsidy": "14410073", + "decay_step": 41, + "rate_per_second": "14410073", + "phase_sum": "909494639409600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 43, + "kind": "constant", + "rule": "halving-42", + "start_daa_score": 2650838400, + "end_daa_score": 2713953600, + "per_block_subsidy": "7205036", + "decay_step": 42, + "rate_per_second": "7205036", + "phase_sum": "454747288147200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 44, + "kind": "constant", + "rule": "halving-43", + "start_daa_score": 2713953600, + "end_daa_score": 2777068800, + "per_block_subsidy": "3602518", + "decay_step": 43, + "rate_per_second": "3602518", + "phase_sum": "227373644073600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 45, + "kind": "constant", + "rule": "halving-44", + "start_daa_score": 2777068800, + "end_daa_score": 2840184000, + "per_block_subsidy": "1801259", + "decay_step": 44, + "rate_per_second": "1801259", + "phase_sum": "113686822036800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 46, + "kind": "constant", + "rule": "halving-45", + "start_daa_score": 2840184000, + "end_daa_score": 2903299200, + "per_block_subsidy": "900629", + "decay_step": 45, + "rate_per_second": "900629", + "phase_sum": "56843379460800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 47, + "kind": "constant", + "rule": "halving-46", + "start_daa_score": 2903299200, + "end_daa_score": 2966414400, + "per_block_subsidy": "450314", + "decay_step": 46, + "rate_per_second": "450314", + "phase_sum": "28421658172800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 48, + "kind": "constant", + "rule": "halving-47", + "start_daa_score": 2966414400, + "end_daa_score": 3029529600, + "per_block_subsidy": "225157", + "decay_step": 47, + "rate_per_second": "225157", + "phase_sum": "14210829086400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 49, + "kind": "constant", + "rule": "halving-48", + "start_daa_score": 3029529600, + "end_daa_score": 3092644800, + "per_block_subsidy": "112578", + "decay_step": 48, + "rate_per_second": "112578", + "phase_sum": "7105382985600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 50, + "kind": "constant", + "rule": "halving-49", + "start_daa_score": 3092644800, + "end_daa_score": 3155760000, + "per_block_subsidy": "56289", + "decay_step": 49, + "rate_per_second": "56289", + "phase_sum": "3552691492800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 51, + "kind": "constant", + "rule": "halving-50", + "start_daa_score": 3155760000, + "end_daa_score": 3218875200, + "per_block_subsidy": "28144", + "decay_step": 50, + "rate_per_second": "28144", + "phase_sum": "1776314188800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 52, + "kind": "constant", + "rule": "halving-51", + "start_daa_score": 3218875200, + "end_daa_score": 3281990400, + "per_block_subsidy": "14072", + "decay_step": 51, + "rate_per_second": "14072", + "phase_sum": "888157094400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 53, + "kind": "constant", + "rule": "halving-52", + "start_daa_score": 3281990400, + "end_daa_score": 3345105600, + "per_block_subsidy": "7036", + "decay_step": 52, + "rate_per_second": "7036", + "phase_sum": "444078547200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 54, + "kind": "constant", + "rule": "halving-53", + "start_daa_score": 3345105600, + "end_daa_score": 3408220800, + "per_block_subsidy": "3518", + "decay_step": 53, + "rate_per_second": "3518", + "phase_sum": "222039273600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 55, + "kind": "constant", + "rule": "halving-54", + "start_daa_score": 3408220800, + "end_daa_score": 3471336000, + "per_block_subsidy": "1759", + "decay_step": 54, + "rate_per_second": "1759", + "phase_sum": "111019636800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 56, + "kind": "constant", + "rule": "halving-55", + "start_daa_score": 3471336000, + "end_daa_score": 3534451200, + "per_block_subsidy": "879", + "decay_step": 55, + "rate_per_second": "879", + "phase_sum": "55478260800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 57, + "kind": "constant", + "rule": "halving-56", + "start_daa_score": 3534451200, + "end_daa_score": 3597566400, + "per_block_subsidy": "439", + "decay_step": 56, + "rate_per_second": "439", + "phase_sum": "27707572800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 58, + "kind": "constant", + "rule": "halving-57", + "start_daa_score": 3597566400, + "end_daa_score": 3660681600, + "per_block_subsidy": "219", + "decay_step": 57, + "rate_per_second": "219", + "phase_sum": "13822228800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 59, + "kind": "constant", + "rule": "halving-58", + "start_daa_score": 3660681600, + "end_daa_score": 3723796800, + "per_block_subsidy": "109", + "decay_step": 58, + "rate_per_second": "109", + "phase_sum": "6879556800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 60, + "kind": "constant", + "rule": "halving-59", + "start_daa_score": 3723796800, + "end_daa_score": 3786912000, + "per_block_subsidy": "54", + "decay_step": 59, + "rate_per_second": "54", + "phase_sum": "3408220800", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 61, + "kind": "constant", + "rule": "halving-60", + "start_daa_score": 3786912000, + "end_daa_score": 3850027200, + "per_block_subsidy": "27", + "decay_step": 60, + "rate_per_second": "27", + "phase_sum": "1704110400", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 62, + "kind": "constant", + "rule": "halving-61", + "start_daa_score": 3850027200, + "end_daa_score": 3913142400, + "per_block_subsidy": "13", + "decay_step": 61, + "rate_per_second": "13", + "phase_sum": "820497600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 63, + "kind": "constant", + "rule": "halving-62", + "start_daa_score": 3913142400, + "end_daa_score": 3976257600, + "per_block_subsidy": "6", + "decay_step": 62, + "rate_per_second": "6", + "phase_sum": "378691200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 64, + "kind": "constant", + "rule": "halving-63", + "start_daa_score": 3976257600, + "end_daa_score": 4039372800, + "per_block_subsidy": "3", + "decay_step": 63, + "rate_per_second": "3", + "phase_sum": "189345600", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 65, + "kind": "constant", + "rule": "halving-64", + "start_daa_score": 4039372800, + "end_daa_score": 4102488000, + "per_block_subsidy": "1", + "decay_step": 64, + "rate_per_second": "1", + "phase_sum": "63115200", + "source": [ + "consensus/core/src/emission.rs:107", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + }, + { + "index": 66, + "kind": "terminal", + "rule": "terminal-zero", + "start_daa_score": 4102488000, + "end_daa_score": null, + "per_block_subsidy": "0", + "decay_step": 65, + "rate_per_second": "0", + "phase_sum": "0", + "source": [ + "consensus/core/src/emission.rs:109", + "consensus/core/src/emission.rs:225", + "consensus/core/src/emission.rs:331" + ] + } + ], + "phase_count": 67, + "last_nonzero_phase": { + "index": 65, + "per_block_subsidy": "1", + "start_daa_score": 4039372800, + "end_daa_score": 4102488000 + }, + "terminal": { + "start_daa_score": 4102488000, + "decay_step": 65, + "start_year": 130, + "behaviour": "per-block subsidy 0 at every later DAA score; the cap is never reached; blocks are paid by fees alone" + }, + "total_issuance": "3963038999610200483289816000", + "cap_shortfall": "36961000389799516710184000", + "scheduled_supply_at_year_end": { + "1": "963038999991632485498848000", + "2": "1963038999864488485498848000", + "5": "3213038999705558485498848000", + "10": "3838038999626093485498848000", + "12": "3900538999618146985498848000", + "20": "3959132749610697141748848000", + "50": "3963038880400910949333816000", + "100": "3963038999610196931229475200" + } + } + ], + "coinbase_split": [ + { + "rule": "producer and proving pool", + "producer": "subsidy - floor(subsidy x 20 / 100)", + "proving_pool": "floor(subsidy x 20 / 100)", + "source": [ + "consensus/core/src/igneum.rs:44", + "consensus/core/src/igneum.rs:201 to 208" + ] + }, + { + "rule": "signing bonus", + "effect": "a silent producer's share loses floor(share x 1,000 / 10,000) to the proving pool; nothing destroyed; fees untouched", + "mainnet": "off (never)", + "source": [ + "consensus/core/src/igneum.rs:110", + "consensus/core/src/config/params.rs:1809 to 1810" + ] + }, + { + "rule": "native ledger", + "effect": "the EVM state credits the producer share to the block's IGNA address (or by its IGNW split once pool_split is active) and the pool share to escrow 0x0000000000000000000000000000000000000220, blue blocks only", + "source": [ + "igneum/exec/src/executor.rs:181 to 211", + "igneum/exec/src/config.rs:59" + ] + }, + { + "rule": "shadow ledger", + "effect": "the UTXO coinbase pays the producer outputs and one OP_RETURN pool output tagged igneum-proving-pool-v0; no UTXO transaction but the coinbase is valid, so none of it is ever spent", + "source": [ + "consensus/src/processes/coinbase.rs:126 to 168", + "consensus/src/pipeline/body_processor/body_validation_in_isolation.rs:54" + ] + }, + { + "rule": "pool", + "effect": "none in consensus; the reference pool keeps fee_percent (default 1.0) of the 80% it receives and pays members PPLNS from its address", + "source": [ + "pool/src/payout.rs:1 to 6", + "pool/src/config.rs:58", + "pool/src/pplns.rs:76" + ] + }, + { + "rule": "dev fee", + "effect": "none in consensus; the miner software requests 1 template in 100 with the dev address (off with --dev-fee 0); it moves the producer share of those blocks, creates nothing", + "source": [ + "site/litepaper.html:625" + ] + }, + { + "rule": "treasury, foundation, team, stake", + "effect": "none: no output, credit or address in consensus", + "source": [ + "consensus/src/processes/coinbase.rs:126 to 168", + "igneum/exec/src/executor.rs:181 to 211" + ] + }, + { + "rule": "genesis", + "effect": "the genesis coinbase has no outputs; its payload subsidy field is data; no premine", + "source": [ + "consensus/core/src/config/genesis.rs:26" + ] + } + ], + "fees": [ + { + "route": "execution base fee", + "effect": "burned (not credited)", + "source": "igneum/exec/src/executor.rs:319 to 321" + }, + { + "route": "proving charge", + "effect": "burned", + "source": "igneum/exec/src/executor.rs:319 to 321" + }, + { + "route": "priority fee", + "effect": "80% to the block's miner; 20% to the payees registered for the frames' code by own gas, unregistered parts burned", + "source": "igneum/exec/src/executor.rs:362 to 371; igneum/exec/src/pgas.rs:290" + }, + { + "route": "external job after the proof bridge", + "effect": "not in code (Designed, phase two): 90% provers, 10% burned per the litepaper", + "source": "site/litepaper.html:624" + } + ], + "supply_accounting": { + "issued": "the EVM state's subsidy credits (producer plus escrow), blue blocks only; bounded above by the schedule's per-DAA-score sum", + "burned": "execution base fees, proving charges and unregistered developer shares (EVM side); the UTXO OP_RETURN output is NOT counted as a burn (C06)", + "locked": "the proving escrow's balance: credited, not yet paid against a valid proof; unclaimed credit is stranded today (litepaper line 609, no rule returns it)", + "not_supply": "UTXO coinbase outputs (unspendable shadow record); any bridged or wrapped IGN on another chain (VR-02)", + "replay_model": "one scheduled subsidy per DAA score (the schedules above); a block is paid at its own DAA score, parallel blocks may share a score and a score may have no block, so the measured issuance differs from the model only near phase boundaries and only by the blocks merged across them; TV-02 reconciles the measured ledgers to this model in exact integers" + }, + "superseded": [ + { + "text": "One rule sits beside the cap. If external proving revenue is under one fifth of the block subsidy over any 90-day window after year 5, the question of a tail reward goes to the miners' signalling vote. The protocol never changes emission by itself.", + "where": "site/litepaper.html:630", + "by": "D01 (capped issuance, subject to TV-04)", + "status": "SUPERSEDED" + } + ], + "conflicts": [ + { + "id": "C01", + "topic": "cap versus the year-five tail vote", + "litepaper": { + "line": 630, + "words": "One rule sits beside the cap. If external proving revenue is under one fifth of the block subsidy over any 90-day window after year 5, the question of a tail reward goes to the miners' signalling vote. The protocol never changes emission by itself." + }, + "code": { + "where": "consensus/core/src/emission.rs:109 (Tail::Cap in EmissionSchedule::CURRENT); consensus/core/src/config/params.rs:1858 (mainnet carries CURRENT); kaspad/src/daemon.rs (params_compiled refuses an override file on mainnet and testnet); no signal path reads or writes the emission schedule", + "value": "tail = cap; the schedule is a compiled genesis parameter; no vote, no revenue trigger, no tail" + }, + "spec_takes": "the cap as the invariant; the tail-vote sentence is SUPERSEDED", + "why": "D01 (capped issuance, subject to TV-04), ratified by the founder 9 October 2026 09:2x UK; VR-01 (no unexplained tail escape, no price- or revenue-triggered issuance); the code already enforces only the capped schedule. If TV-04 shows capped security cannot pass, the volume's rule applies: delay launch or openly choose another design, never an undisclosed tail.", + "status": "RESOLVED (by D01, subject to TV-04)", + "follow_up": "the litepaper sentence is removed or replaced by the site lane; this landing does not edit site/" + }, + { + "id": "C02", + "topic": "year-one emission and the twelve-year figure", + "litepaper": { + "line": 578, + "words": "emission starts at 1 billion a year", + "line_2": 602, + "words_2": "3,938M IGN in the first 12 years of a 4,000M cap." + }, + "code": { + "where": "consensus/core/src/emission.rs:104 to 106 (launch rate floored, the 30-day ramp from 10%)", + "value": "year 1 issues 963,038,999.99163248548848 IGN at 10 blocks/s (963,038,999.991632485498848 at 1 block/s); the first 12 years issue 3,900,538,999.61814698548848 IGN" + }, + "spec_takes": "the code's figures", + "why": "the litepaper chart and caption ignore the launch ramp's withheld 36,961,000 IGN and the floor of the per-second rate; the spec carries what nodes pay", + "status": "RESOLVED (text correction owed by the site lane)" + }, + { + "id": "C03", + "topic": "terminal behaviour", + "litepaper": { + "line": 578, + "words": "halves every two years for ever" + }, + "code": { + "where": "consensus/core/src/emission.rs:225 (rate[k+1] = rate[k] x 2^31 >> 32, floored), :331 (per block = rate / bps, floored)", + "value": "the per-block subsidy floors to zero at decay step 62 (DAA score 39,131,424,000, the start of year 125) at 10 blocks/s and at step 65 (DAA score 4,102,488,000, the start of year 131) at 1 block/s; zero for ever after" + }, + "spec_takes": "the code: a finite schedule ending in a terminal zero phase", + "why": "integer halving ends; the last subsidy is 1 base unit (10^-18 IGN) per block; after it the security budget is fees alone (TV-04's question)", + "status": "RESOLVED (text correction owed: 'until the subsidy floors to zero')" + }, + { + "id": "C04", + "topic": "block rate at launch and its steps", + "litepaper": { + "line": 542, + "words": "one block a second at launch with scheduled steps to four and ten", + "line_2": 668, + "words_2": "31.688 IGN a block, 86,400 blocks a day" + }, + "code": { + "where": "consensus/core/src/config/params.rs:1789 (mainnet BlockrateParams::new::<10>()); consensus/src/processes/coinbase.rs:31 (the bps_history rescaling is gone) and :265 (subsidy keyed on daa / ONE bps)", + "value": "mainnet as compiled runs 10 blocks/s from genesis, 3.1688087810 IGN a block; the subsidy has no block-rate history, so a step from 1 to 10 blocks/s at DAA score 31,557,600 (one year in) would issue 4,863,038,999.495770867826592 IGN in all, an overshoot of 863,038,999.495770867826592 IGN over the cap" + }, + "spec_takes": "both profiles (10 blocks/s as compiled; 1 block/s as Devnet 4), identical per second; NO live block-rate step is cap-safe under the current code", + "why": "a block-rate step re-keys the schedule clock (seconds = daa / bps) and re-pays elapsed time", + "status": "BLOCKED", + "question": "For the founder: does mainnet launch at the compiled 10 blocks a second (the litepaper's 1-to-4-to-10 step plan removed), or at 1 with steps (then the node lane adds a block-rate-history subsidy rule before genesis, and TV-02 replays a step)? Default if unanswered: the spec freezes the compiled 10 blocks a second and the step plan is treated as text to correct." + }, + { + "id": "C05", + "topic": "unit", + "litepaper": { + "line": 568, + "words": "the base unit is Open" + }, + "code": { + "where": "consensus/core/src/unit.rs:38; consensus/core/src/config/params.rs:1816", + "value": "18 decimals on mainnet, testnet and Devnet 4: 1 IGN = 10^18 base units; one base unit is one EVM wei" + }, + "spec_takes": "18 decimals", + "why": "decided 6 October 2026 (O-2.6) and compiled; the cap is 4 x 10^27 base units", + "status": "RESOLVED (text correction owed)" + }, + { + "id": "C06", + "topic": "where the 20% proving share goes, and which ledger is native supply", + "litepaper": { + "line": 609, + "words": "the coinbase's 20% output goes to an unspendable script tagged igneum-proving-pool-v0 and is burned there. Provers are paid from a separate escrow in the execution state" + }, + "code": { + "where": "consensus/src/processes/coinbase.rs:163 (UTXO pool output to the OP_RETURN script); consensus/src/pipeline/body_processor/body_validation_in_isolation.rs:54 (no UTXO transaction but the coinbase: UTXO outputs can never be spent); igneum/exec/src/executor.rs:181 to 211 (the EVM state credits 80% to the producer and 20% to the escrow 0x...0220 for every blue block)", + "value": "two ledgers record each subsidy: the UTXO coinbase (unspendable by rule) and the EVM state (spendable)" + }, + "spec_takes": "native supply is the EVM state's credit; the UTXO coinbase is a non-spendable shadow record and its OP_RETURN 20% is NOT a burn of native supply; the escrow balance is issued and locked until paid per proof", + "why": "counting both ledgers doubles supply; counting the OP_RETURN as burned and the escrow as issued would understate and overstate at once (VR-02)", + "status": "RESOLVED (for the spec; TV-02 reconciles both ledgers to this rule)" + }, + { + "id": "C07", + "topic": "red blocks", + "litepaper": { + "line": 620, + "words": "80% the block's miner, 20% the proving pool for the provers of that block" + }, + "code": { + "where": "consensus/src/processes/coinbase.rs:152 to 159 (the UTXO coinbase pays a DAA red block's subsidy to the merging miner and the pool); igneum/exec/src/executor.rs:181 (the EVM credits blue blocks only)", + "value": "a red block's subsidy exists on the shadow ledger and not in native supply" + }, + "spec_takes": "native issuance = the blue blocks' subsidies; the per-DAA-score schedule sum is the upper bound", + "why": "the EVM ledger is native supply (C06); the bound keeps the cap invariant whatever the red share", + "status": "RESOLVED (TV-02 measures the red share; no text change needed beyond 'blue block')" + }, + { + "id": "C08", + "topic": "which DAA score a merged block is credited at", + "litepaper": { + "line": null, + "words": "(silent)" + }, + "code": { + "where": "consensus/core/src/config/params.rs:1812 (mainnet subsidy_per_block_activation_daa = never), :2232 (Devnet 4 inherits never), :1960 (testnet-1: 0)", + "value": "on mainnet and Devnet 4 the EVM credits a merged block the merging chain block's subsidy while the UTXO coinbase pays the block's own: one ramp second high per merged block, a whole halving step at a boundary" + }, + "spec_takes": "each block's own DAA score (ledger N8) as the normative rule", + "why": "exact integer agreement between ledgers and replays (TV-02) needs one rule; N8 is the one the testnet object already carries", + "status": "BLOCKED", + "question": "For the founder (the node lane owns the change): set subsidy_per_block_activation_daa = 0 in the mainnet object, as testnet-1 has it? Default if unanswered: the spec keeps N8 as normative and TV-02 reports the chain-block reading as a measured divergence." + }, + { + "id": "C09", + "topic": "the mainnet genesis clock", + "litepaper": { + "line": 775, + "words": "Genesis with no premine, 30-day ramp." + }, + "code": { + "where": "consensus/core/src/config/genesis.rs:92 (mainnet GENESIS daa_score 1312860, Kaspa's 2021 timestamp); the subsidy reads the absolute DAA score (emission.rs:329)", + "value": "as compiled, the first mainnet block would sit 131,286 DAA seconds into the ramp (paying 14.55% of full rate, not 10%) and the whole schedule would run 1.52 days early; issuance would be 510,841.7849105123888436 IGN lower" + }, + "spec_takes": "genesis at DAA score 0 (as Devnet 4 and testnet-1); the activation clocks below are counted from it", + "why": "the mainnet genesis is a Kaspa placeholder not yet cut; every Igneum network so far starts at DAA score 0", + "status": "BLOCKED", + "question": "For the founder: confirm the mainnet genesis is cut at DAA score 0. Default if unanswered: the spec freezes DAA score 0 and the genesis cut is checked against it." + }, + { + "id": "C10", + "topic": "priority fee to provers", + "litepaper": { + "line": 622, + "words": "80% the block's miner and provers; 20% the apps whose code ran, per call frame" + }, + "code": { + "where": "igneum/exec/src/executor.rs:362 to 371 (the tip less the developer share is credited to the block's miner alone); igneum/exec/src/pgas.rs:290 (20% developer share, unregistered parts burned)", + "value": "80% to the block's miner, none to provers; 20% to registered payees or burned" + }, + "spec_takes": "the code", + "why": "fees move existing supply and burn some; they never create supply", + "status": "RESOLVED (text correction owed: drop 'and provers')" + }, + { + "id": "C11", + "topic": "the reference pool's custody and fee", + "litepaper": { + "line": 647, + "words": "The reference pool never holds a member’s balance. Payouts come straight from the coinbase split" + }, + "code": { + "where": "pool/src/payout.rs:1 to 6 (the pool's address receives the 80% and pays members by transfers); pool/src/config.rs:58 (fee_percent 1.0); pool/src/pplns.rs:76; consensus/core/src/config/params.rs:1851 (mainnet pool_split_activation_daa = never: the IGNW coinbase split is off)", + "value": "the reference pool holds members' balances between rounds and keeps a 1% fee; the consensus split that would make the sentence true is off on mainnet and Devnet 4" + }, + "spec_takes": "the code (custodial PPLNS, 1% default fee, no supply effect)", + "why": "the payout route moves issued supply; it creates none", + "status": "BLOCKED", + "question": "For the founder: activate the IGNW coinbase split at mainnet genesis (pool_split_activation_daa = 0) so the litepaper sentence holds, or change the sentence to the custodial PPLNS pool with its 1% fee? Default if unanswered: the sentence is corrected to the code." + }, + { + "id": "C12", + "topic": "a tail schedule compiled into testnet-1", + "litepaper": { + "line": 630, + "words": "The cap stays at 4 billion. There is no tail emission." + }, + "code": { + "where": "consensus/core/src/emission.rs:116 to 122 (TESTNET_1: 100 IGN a block, monthly glide, a 1% a year tail); consensus/core/src/config/params.rs:2014 (testnet-1 carries it)", + "value": "the held testnet-1 object issues without a cap" + }, + "spec_takes": "not the monetary contract: the contract is EmissionSchedule::CURRENT with Tail::Cap", + "why": "D01; the testnet is held and superseded by 2.0; a revived testnet carries CURRENT or is labelled a different supply proposition", + "status": "RESOLVED (by D01)" + }, + { + "id": "C13", + "topic": "dead Kaspa subsidy fields in the mainnet object", + "litepaper": { + "line": null, + "words": "(silent)" + }, + "code": { + "where": "consensus/core/src/config/params.rs:1783 to 1784 (deflationary_phase_daa_score 15,519,600; pre_deflationary_phase_base_subsidy 50,000,000,000)", + "value": "read only into the consensus digest; the subsidy never reads them (coinbase.rs:31)" + }, + "spec_takes": "ignored: they are not part of the monetary contract", + "why": "a reader grepping 'deflationary' would otherwise find a second schedule that does not exist", + "status": "RESOLVED (the node lane may annotate or remove them at the genesis cut)" + } + ], + "sources": { + "volume": { + "path": "docs/plans/igneum-2.0-master/token-value/rules-and-gates.json", + "sha256": "10ce307329e1663dcb6dce849317c9276c19252de5f483e9ee06015fc77cbe3d" + }, + "litepaper": { + "path": "site/litepaper.html", + "igneum_commit": "6824d49cc32def4463b643293a5622545618f7a9", + "sha256": "35ac65e151339089ef6ddeea3842b24d95e00d327a2b12faa9ea190ff85e463f" + }, + "node": { + "repo": "igneum-node (build-1:/srv/igneum-node.git)", + "revisions": { + "release-2.0.2-node": "a284380bdd62d5d0d972cb38f2cbd1daeedda69d", + "release-2.0.3-node-k6": "bf60948a01467bb54eaab9e50d32b91058f11242" + }, + "files": { + "consensus/core/src/config/genesis.rs": { + "release-2.0.2-node": "967618cf3e54987b42993f6ed5c9221bfae6b10db0959c08f8bf24f3aa6eb627", + "release-2.0.3-node-k6": "967618cf3e54987b42993f6ed5c9221bfae6b10db0959c08f8bf24f3aa6eb627" + }, + "consensus/core/src/config/params.rs": { + "release-2.0.2-node": "0c07ddcf0c5383255f1b0cfba54c6f637375b9a5b5514ab4b2affdad717e787f", + "release-2.0.3-node-k6": "0c07ddcf0c5383255f1b0cfba54c6f637375b9a5b5514ab4b2affdad717e787f" + }, + "consensus/core/src/emission.rs": { + "release-2.0.2-node": "07b39c410222a43001c83dddbd85cff4a8a370184588dbc28ab48c8803964fbd", + "release-2.0.3-node-k6": "07b39c410222a43001c83dddbd85cff4a8a370184588dbc28ab48c8803964fbd" + }, + "consensus/core/src/igneum.rs": { + "release-2.0.2-node": "6c859d4268e653978bfa07aaaad2dff304635b892255368cbbadecca3c35705f", + "release-2.0.3-node-k6": "6c859d4268e653978bfa07aaaad2dff304635b892255368cbbadecca3c35705f" + }, + "consensus/core/src/network.rs": { + "release-2.0.2-node": "6b7a7a1148429888650e6b1059262d0dac86017bfe906e95a943201205290c85", + "release-2.0.3-node-k6": "6b7a7a1148429888650e6b1059262d0dac86017bfe906e95a943201205290c85" + }, + "consensus/core/src/unit.rs": { + "release-2.0.2-node": "d64d6d6f973e25b778c1eb558db41fad36a1457871a290cc01df0797a47b4d4f", + "release-2.0.3-node-k6": "d64d6d6f973e25b778c1eb558db41fad36a1457871a290cc01df0797a47b4d4f" + }, + "consensus/src/pipeline/body_processor/body_validation_in_isolation.rs": { + "release-2.0.2-node": "3a639e259840c5238400f4c5ed1df9dcf44368288b501f0a905f63f563678d54", + "release-2.0.3-node-k6": "3a639e259840c5238400f4c5ed1df9dcf44368288b501f0a905f63f563678d54" + }, + "consensus/src/processes/coinbase.rs": { + "release-2.0.2-node": "ef25b2412507d608df08fd22895e74262489f1fa460e6fe26f89877fb5cc6c63", + "release-2.0.3-node-k6": "ef25b2412507d608df08fd22895e74262489f1fa460e6fe26f89877fb5cc6c63" + }, + "igneum/exec/src/config.rs": { + "release-2.0.2-node": "fdc4ba56109ddab4dadfe3d184247cf2cda8774dc5542337acaa38c8da716aaf", + "release-2.0.3-node-k6": "fdc4ba56109ddab4dadfe3d184247cf2cda8774dc5542337acaa38c8da716aaf" + }, + "igneum/exec/src/executor.rs": { + "release-2.0.2-node": "ff12ef5fed88f1cd1d1449c6878a5acc0f7efe098abad7315791d59b5c5cc79c", + "release-2.0.3-node-k6": "ff12ef5fed88f1cd1d1449c6878a5acc0f7efe098abad7315791d59b5c5cc79c" + }, + "igneum/exec/src/pgas.rs": { + "release-2.0.2-node": "0b7dd5080f8a60c77eaab5579797350ac916fc38fa4ce78fcc8618aa7c617c00", + "release-2.0.3-node-k6": "0b7dd5080f8a60c77eaab5579797350ac916fc38fa4ce78fcc8618aa7c617c00" + }, + "kaspad/src/daemon.rs": { + "release-2.0.2-node": "ffa0b97f2ebc9c537380059e7fee511c95406c4333bdf55e80f360241b136ae4", + "release-2.0.3-node-k6": "68c1e5aec8eb7be0fc22da348d1e7ee9dcebb5c4db9ef43434030151fa3e1008" + } + }, + "note": "every cited node file is byte-identical at both revisions except kaspad/src/daemon.rs, whose override refusal is the same rule at both" + }, + "pool": { + "pool/src/config.rs": "0001c219ba629c388eaf916b8d5d4c4da0a854eff6048b27758cab734e797b1d", + "pool/src/payout.rs": "839619378b7e9b0b7eda22ce900aa2ada6410aa90e3d7f1f000a02f0b8449c1f", + "pool/src/pplns.rs": "fa1f6f5627374f1b8ba06238a30e30beacb35bf505a21f2cf7f4bb3539485942" + }, + "citations": [ + { + "id": "year", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 32, + "anchor": "pub const YEAR_SECONDS: u64 = 31_557_600;" + }, + { + "id": "current", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 103, + "anchor": "pub const CURRENT: EmissionSchedule" + }, + { + "id": "launch_rate", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 104, + "anchor": "launch_rate: 3_168_808_781" + }, + { + "id": "ramp_seconds", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 105, + "anchor": "ramp_seconds: 2_592_000" + }, + { + "id": "ramp_start", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 106, + "anchor": "ramp_start_percent: 10" + }, + { + "id": "step_seconds", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 107, + "anchor": "step_seconds: 2 * YEAR_SECONDS" + }, + { + "id": "decay", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 108, + "anchor": "step_decay_q32: 1 << 31" + }, + { + "id": "tail_cap", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 109, + "anchor": "tail: Tail::Cap" + }, + { + "id": "testnet1", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 116, + "anchor": "pub const TESTNET_1: EmissionSchedule" + }, + { + "id": "testnet1_tail", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 122, + "anchor": "tail: Tail::Percent { bps_per_year: 100 }" + }, + { + "id": "decay_rule", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 225, + "anchor": "rate = rate.saturating_mul(s.step_decay_q32 as u128) >> 32;" + }, + { + "id": "ramp_fn", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 316, + "anchor": "pub fn ramp(&self, full: u128, seconds: u64) -> u128" + }, + { + "id": "block_subsidy", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 329, + "anchor": "pub fn block_subsidy(&self, daa_score: u64, bps: u64) -> u128" + }, + { + "id": "per_block_div", + "repo": "node", + "path": "consensus/core/src/emission.rs", + "line": 331, + "anchor": "let per_block = self.rate_at(seconds) / bps as u128;" + }, + { + "id": "cap_const", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 37, + "anchor": "pub const SUPPLY_CAP_SOMPI: u64 = 4_000_000_000 * SOMPI_PER_KASPA;" + }, + { + "id": "pool_share", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 44, + "anchor": "pub const PROVING_POOL_SHARE_PERCENT: u64 = 20;" + }, + { + "id": "silent_split", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 110, + "anchor": "pub fn silent_split_units(" + }, + { + "id": "cap_units", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 154, + "anchor": "pub const fn supply_cap_units(decimals: u8) -> u128" + }, + { + "id": "pool_units", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 201, + "anchor": "pub fn proving_pool_share_units(" + }, + { + "id": "producer_units", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 206, + "anchor": "pub fn producer_share_units(" + }, + { + "id": "pool_script", + "repo": "node", + "path": "consensus/core/src/igneum.rs", + "line": 213, + "anchor": "pub fn proving_pool_script_public_key()" + }, + { + "id": "evm_decimals", + "repo": "node", + "path": "consensus/core/src/unit.rs", + "line": 38, + "anchor": "pub const EVM_DECIMALS: u8 = 18;" + }, + { + "id": "mainnet_deflationary", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1783, + "anchor": "deflationary_phase_daa_score: 15778800 - 259200," + }, + { + "id": "mainnet_pre_deflationary", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1784, + "anchor": "pre_deflationary_phase_base_subsidy: 50000000000," + }, + { + "id": "mainnet_bps", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1789, + "anchor": "blockrate: BlockrateParams::new::<10>()," + }, + { + "id": "mainnet_bonus_off", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1809, + "anchor": "signing_bonus_activation_daa: u64::MAX," + }, + { + "id": "mainnet_bonus_bps", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1810, + "anchor": "signing_bonus_bps: 1_000," + }, + { + "id": "mainnet_n8_off", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1812, + "anchor": "subsidy_per_block_activation_daa: u64::MAX," + }, + { + "id": "mainnet_decimals", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1816, + "anchor": "base_unit_decimals: crate::unit::EVM_DECIMALS" + }, + { + "id": "mainnet_pool_split_off", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1851, + "anchor": "pool_split_activation_daa: u64::MAX," + }, + { + "id": "mainnet_emission", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1858, + "anchor": "emission: EmissionSchedule::CURRENT.rescaled(crate::unit::DEVNET_DECIMALS, crate::unit::EVM_DECIMALS)," + }, + { + "id": "testnet_bonus_on", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1955, + "anchor": "signing_bonus_activation_daa: 0," + }, + { + "id": "testnet_n8_on", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 1960, + "anchor": "subsidy_per_block_activation_daa: 0," + }, + { + "id": "testnet_emission", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 2014, + "anchor": "emission: EmissionSchedule::TESTNET_1.rescaled(" + }, + { + "id": "devnet_bps", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 2208, + "anchor": "blockrate: BlockrateParams::new::<1>()," + }, + { + "id": "devnet_n8_off", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 2232, + "anchor": "subsidy_per_block_activation_daa: u64::MAX," + }, + { + "id": "devnet_pool_split_off", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 2270, + "anchor": "pool_split_activation_daa: u64::MAX," + }, + { + "id": "devnet4_fn", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 2342, + "anchor": "pub fn devnet4_params() -> Params {" + }, + { + "id": "devnet4_emission", + "repo": "node", + "path": "consensus/core/src/config/params.rs", + "line": 2372, + "anchor": "emission: EmissionSchedule::CURRENT.rescaled(crate::unit::DEVNET_DECIMALS, crate::unit::EVM_DECIMALS)," + }, + { + "id": "mainnet_genesis_daa", + "repo": "node", + "path": "consensus/core/src/config/genesis.rs", + "line": 92, + "anchor": "daa_score: 1312860, // Checkpoint DAA score" + }, + { + "id": "genesis_no_outputs", + "repo": "node", + "path": "consensus/core/src/config/genesis.rs", + "line": 26, + "anchor": "Transaction::new(0, Vec::new(), Vec::new(), 0, SUBNETWORK_ID_COINBASE" + }, + { + "id": "cb_header", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 31, + "anchor": "Kaspa's pre-deflationary phase, 426-month table and Crescendo `bps_history` rescaling are gone." + }, + { + "id": "cb_split", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 106, + "anchor": "fn producer_and_pool(&self, daa_score: u64, reward: &BlockRewardData)" + }, + { + "id": "cb_reds", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 152, + "anchor": "for red in ghostdag_data.mergeset_reds.iter()" + }, + { + "id": "cb_pool_output", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 163, + "anchor": "outputs.push(TransactionOutput::new_units(proving_pool_reward, igneum::proving_pool_script_public_key()));" + }, + { + "id": "cb_calc", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 262, + "anchor": "pub fn calc_block_subsidy(&self, daa_score: u64) -> Amount" + }, + { + "id": "cb_calc_body", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 265, + "anchor": "Amount(self.emission.block_subsidy(daa_score, self.bps))" + }, + { + "id": "utxo_retired", + "repo": "node", + "path": "consensus/src/pipeline/body_processor/body_validation_in_isolation.rs", + "line": 54, + "anchor": "RuleError::UtxoTransactionsRetired" + }, + { + "id": "exec_blue_only", + "repo": "node", + "path": "igneum/exec/src/executor.rs", + "line": 181, + "anchor": "for b in blocks.iter().filter(|b| b.is_blue)" + }, + { + "id": "exec_split", + "repo": "node", + "path": "igneum/exec/src/executor.rs", + "line": 189, + "anchor": "silent_split_units(subsidy, b.silent, env.signing_bonus_bps)" + }, + { + "id": "exec_miner_credit", + "repo": "node", + "path": "igneum/exec/src/executor.rs", + "line": 208, + "anchor": "db.add_balance(b.miner, producer_wei);" + }, + { + "id": "exec_pool_credit", + "repo": "node", + "path": "igneum/exec/src/executor.rs", + "line": 211, + "anchor": "db.add_balance(PROVING_POOL_ADDRESS, pool_wei);" + }, + { + "id": "exec_tip_miner", + "repo": "node", + "path": "igneum/exec/src/executor.rs", + "line": 362, + "anchor": "let tip_total = (gas_used as u128) * (price - env.base_fee_exec);" + }, + { + "id": "exec_dev_shares", + "repo": "node", + "path": "igneum/exec/src/pgas.rs", + "line": 290, + "anchor": "pub fn developer_shares(" + }, + { + "id": "pool_address", + "repo": "node", + "path": "igneum/exec/src/config.rs", + "line": 59, + "anchor": "pub const PROVING_POOL_ADDRESS: Address" + }, + { + "id": "override_refused", + "repo": "node", + "path": "kaspad/src/daemon.rs", + "line": { + "release-2.0.2-node": 320, + "release-2.0.3-node-k6": 323 + }, + "anchor": "if network.params_compiled() {" + }, + { + "id": "params_compiled", + "repo": "node", + "path": "consensus/core/src/network.rs", + "line": 266, + "anchor": "NetworkType::Mainnet | NetworkType::Testnet => true," + }, + { + "id": "lp_cap", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 578, + "anchor": "Hard cap of 4 billion IGN, approached and never reached, because emission starts at 1 billion a year and halves every two years for ever." + }, + { + "id": "lp_caption", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 602, + "anchor": "3,938M IGN in the first 12 years of a 4,000M cap." + }, + { + "id": "lp_proving_pool", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 609, + "anchor": "is burned there" + }, + { + "id": "lp_tail", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 630, + "anchor": "One rule sits beside the cap. If external proving revenue is under one fifth of the block subsidy over any 90-day window after year 5, the question of a tail reward goes to the miners' signalling vote. The protocol never changes emission by itself." + }, + { + "id": "lp_no_tail", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 630, + "anchor": "The cap stays at 4 billion. There is no tail emission." + }, + { + "id": "lp_block_rate", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 542, + "anchor": "one block a second at launch with scheduled steps to four and ten" + }, + { + "id": "lp_unit", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 568, + "anchor": "the base unit is Open" + }, + { + "id": "lp_emission_route", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 620, + "anchor": "80% the block's miner, 20% the proving pool for the provers of that block" + }, + { + "id": "lp_tip_route", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 622, + "anchor": "80% the block's miner and provers; 20% the apps whose code ran, per call frame" + }, + { + "id": "lp_external_job", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 624, + "anchor": "90% the provers who delivered" + }, + { + "id": "lp_dev_fee", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 625, + "anchor": "1 block template in 100 requested with the dev address" + }, + { + "id": "lp_pool_custody", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 647, + "anchor": "The reference pool never holds a member" + }, + { + "id": "lp_per_block_figure", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 668, + "anchor": "31.688 IGN a block, 86,400 blocks a day" + }, + { + "id": "lp_mainnet_launch", + "repo": "igneum", + "path": "site/litepaper.html", + "line": 775, + "anchor": "Genesis with no premine, 30-day ramp." + }, + { + "id": "pool_payout_doc", + "repo": "igneum", + "path": "pool/src/payout.rs", + "line": 3, + "anchor": "so the pool receives" + }, + { + "id": "pool_fee_default", + "repo": "igneum", + "path": "pool/src/config.rs", + "line": 58, + "anchor": "fee_percent: 1.0," + }, + { + "id": "pool_distribute", + "repo": "igneum", + "path": "pool/src/pplns.rs", + "line": 76, + "anchor": "pub fn distribute(reward_wei: u128, fee_percent: f64, payees: &[Payee])" + } + ], + "code_dump": { + "path": "tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl", + "sha256": "3e4b5ac3c2f43057a13ace7d781ad714e385a94105623c0e0dac4adc417d8056", + "program": "tools/token-value/tv-01/code-dump/tv01_schedule.rs", + "run": "build-9, release-2.0.3-node-k6 bf60948a: cargo run --release -p kaspa-consensus-core --example tv01_schedule (the program copied into consensus/core/examples of a scratch tree; the node fork is unchanged)", + "agreement": "every field of 129 decay phases, both ramps and both totals equal to this generator's integers" + } + }, + "generator": { + "path": "tools/token-value/tv-01/gen-spec.py", + "command": "python3 tools/token-value/tv-01/gen-spec.py --node-git --igneum-root . --igneum-rev 6824d49cc32def4463b643293a5622545618f7a9 --code-dump tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl --out docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json --md docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md" + } +} diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md new file mode 100644 index 000000000..bc691b8cb --- /dev/null +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md @@ -0,0 +1,170 @@ +# TV-01: the monetary contract as one supply spec + +Igneum 2.0, Token Value Phase 0, item TV-01. 9 October 2026. + +Gate TV-01: **NOT RUN**. This is a Phase 0 document. It activates nothing and changes no node, chain or served page. The verdict stays NOT RUN until the independent panel reads it. Four rows are BLOCKED on questions for the founder (C04, C08, C09, C11), each with a default. + +The machine-readable spec is `supply-spec.json` in this directory. This page says the same in words. Every figure on it comes from `tools/token-value/tv-01/gen-spec.py`, and the consensus code's own functions agree with every one (the code dump below). `tools/token-value/tv-01/check-spec.py` checks the json and sums it to the cap in exact integers. + +## The D01 resolution + +The founder ratified D01 on 9 October 2026, 09:2x UK: capped issuance, subject to TV-04. + +The spec holds the cap as the invariant: **4,000,000,000 IGN, which is 4 x 10^27 base units**. There is no tail. There is no vote on emission, and no price or revenue trigger. There is no emergency mint. + +D01 supersedes this sentence of the litepaper (site/litepaper.html line 630, igneum master 6824d49c): + +> "One rule sits beside the cap. If external proving revenue is under one fifth of the block subsidy over any 90-day window after year 5, the question of a tail reward goes to the miners' signalling vote. The protocol never changes emission by itself." + +The code already matches D01: + +- Mainnet carries `EmissionSchedule::CURRENT` (params.rs:1858), and that schedule's tail is `Tail::Cap` (emission.rs:109). +- The schedule is a compiled genesis parameter. The daemon refuses an override file on mainnet and the testnet (daemon.rs, `params_compiled`, network.rs:266). +- No signal path reads or writes the emission schedule. + +"Subject to TV-04" means one thing. If TV-04's security-budget analysis shows capped security cannot pass, the volume's rule applies: delay the launch, or openly choose another monetary design. An undisclosed tail is never the answer. The site lane owns the change to the litepaper sentence. This landing does not edit `site/`. + +## The contract + +| Item | Value | Source (igneum-node, identical at release-2.0.2-node a284380b and release-2.0.3-node-k6 bf60948a) | +|---|---|---| +| Coin | IGN | | +| Base unit | 10^-18 IGN; one consensus base unit is one EVM wei | unit.rs:38; params.rs:1816 | +| Cap | 4,000,000,000 IGN = 4,000,000,000,000,000,000,000,000,000 base units | igneum.rs:37, :154 | +| Time key | seconds = floor(DAA score / blocks per second) | emission.rs:329 to 331; coinbase.rs:265 | +| Launch rate | 31,688,087,810,000,000,000 base units a DAA second (floor(10^17 / 31,557,600) at 8 decimals, times 10^10) | emission.rs:104; params.rs:1858 | +| Launch ramp | linear from 10% at genesis to 100% at 2,592,000 DAA seconds (30 days) | emission.rs:105 to 106, :316 | +| Decay | every 63,115,200 DAA seconds (two years of 365.25 days): rate[k+1] = floor(rate[k] x 2^31 / 2^32), a halving | emission.rs:107 to 108, :225 | +| Per-block subsidy | per_block(d) = ramp(floor(rate[floor(s / 63,115,200)] / bps), s), s = floor(d / bps) | emission.rs:329 to 333 | +| Tail | cap: the curve runs to zero and stays there | emission.rs:109 | +| Block rate | mainnet as compiled: 10 blocks a second from genesis; Devnet 4: 1 (C04 BLOCKED) | params.rs:1789, :2208 | +| Genesis | DAA score 0 (C09 BLOCKED: the compiled mainnet genesis still carries Kaspa's 1,312,860) | genesis.rs:92 | + +### How the cap is enforced + +No running counter enforces the cap. The schedule does: + +- Every block's coinbase must carry exactly the scheduled subsidy (coinbase.rs:262). +- The EVM state credits by the same rule (executor.rs:181 to 211). +- The cap constant itself is only compared in tests (igneum.rs:154). + +The schedule sums to the cap minus an exact shortfall, so issuance never reaches the cap. + +## The schedule + +`supply-spec.json` lists every phase with: + +- its start and end DAA score +- its per-block subsidy as an integer +- its rule name +- its source line + +There are two profiles, and their per-second curves are identical: + +- **mainnet as compiled**: 10 blocks a second, 64 phases. +- **Devnet 4**: 1 block a second, the litepaper's launch rate, 67 phases. + +The phases in order: + +1. **Ramp** (DAA 0 to 25,920,000 at 10 blocks a second). The per-block subsidy is the ramp formula, and the phase sum is exact. +2. **Full rate after the ramp**, to the first halving. +3. **One constant phase per halving**, until the last one-base-unit subsidy. +4. **Terminal**: zero from that point on, for ever. + + + +| Figure | Mainnet as compiled (10 blocks/s) | Devnet 4 (1 block/s) | +|---|---|---| +| Cap | 4,000,000,000 IGN (4000000000000000000000000000 base units) | the same | +| Total scheduled issuance | 3,963,038,999.610200467816224 IGN | 3,963,038,999.610200483289816 IGN | +| Shortfall under the cap (exact) | 36961000389799532183776000 base units (36,961,000.389799532183776 IGN) | 36961000389799516710184000 base units (36,961,000.389799516710184 IGN) | +| Ramp phase | DAA 0 to 25,920,000, 45,174,523.72229648548848 IGN | DAA 0 to 2,592,000, 45,174,523.722296485498848 IGN | +| First block | 0.3168808781 IGN | 3.168808781 IGN | +| Full-rate block (after the ramp) | 3.168808781 IGN | 31.68808781 IGN | +| Phases (ramp, constant, terminal) | 64 | 67 | +| Last non-zero subsidy | 1 base unit a block, DAA 38,500,272,000 to 39,131,424,000 | 1 base unit a block, DAA 4,039,372,800 to 4,102,488,000 | +| Terminal (zero for ever) from | DAA 39,131,424,000 (decay step 62, year 125) | DAA 4,102,488,000 (decay step 65, year 131) | +| Scheduled supply at the end of year 1 | 963,038,999.99163248548848 IGN | 963,038,999.991632485498848 IGN | +| Scheduled supply at the end of year 2 | 1,963,038,999.86448848548848 IGN | 1,963,038,999.864488485498848 IGN | +| Scheduled supply at the end of year 5 | 3,213,038,999.70555848548848 IGN | 3,213,038,999.705558485498848 IGN | +| Scheduled supply at the end of year 10 | 3,838,038,999.62609348548848 IGN | 3,838,038,999.626093485498848 IGN | +| Scheduled supply at the end of year 12 | 3,900,538,999.61814698548848 IGN | 3,900,538,999.618146985498848 IGN | +| Scheduled supply at the end of year 20 | 3,959,132,749.61069714173848 IGN | 3,959,132,749.610697141748848 IGN | +| Scheduled supply at the end of year 50 | 3,963,038,880.400910946483264 IGN | 3,963,038,880.400910949333816 IGN | +| Scheduled supply at the end of year 100 | 3,963,038,999.610196921373136 IGN | 3,963,038,999.6101969312294752 IGN | +| A 1-to-10 blocks/s step at DAA 31,557,600 under the current code | total 4,863,038,999.495770867826592 IGN, 863,038,999.495770867826592 IGN over the cap | | +| The compiled mainnet genesis (DAA 1,312,860) | first ramp block at 14.55%; 510,841.7849105123888436 IGN never scheduled | | + +Code agreement: every field of 129 decay phases, both ramps and both totals equal to this generator's integers (dump sha256 `3e4b5ac3c2f43057a13ace7d781ad714e385a94105623c0e0dac4adc417d8056`). + + + +What the figures mean: + +- **For a miner**: a full-rate block pays 3.168808781 IGN at 10 blocks a second, or 31.68808781 IGN at 1. The emission per second is the same either way, so a card's expected income does not depend on the block rate. In the first two years, 80% of each block goes to the block's producer and 20% to the proving escrow. +- **For a holder**: 963.04 million IGN are issued in year 1 and 1,963.04 million by the end of year 2. The total approaches 3,963,038,999.61 IGN and never reaches the cap. The 36,961,000.39 IGN shortfall is the launch ramp's withheld coins plus the floor of the per-second rate. Nobody holds them. +- **For security**: the subsidy halves every two years. It is one base unit a block by year 123 (10 blocks a second) and zero from the start of year 125. From then on, blocks are paid by fees alone. That is TV-04's question, and nothing in this spec answers it. + +## The coinbase split the node enforces + +| Rule | Effect | Source | +|---|---|---| +| Producer and proving pool | producer = subsidy - floor(subsidy x 20 / 100), pool = floor(subsidy x 20 / 100); fees go to the producer | igneum.rs:44, :201 to 208; coinbase.rs:106 | +| Signing bonus | a silent producer's share loses 10% (1,000 bps) to the pool; nothing destroyed; mainnet: off | igneum.rs:110; params.rs:1809 to 1810 | +| Native ledger (EVM state) | 80% to the block's `IGNA` address (or its `IGNW` split once the pool switch is on), 20% to the escrow 0x0000000000000000000000000000000000000220; blue blocks only | executor.rs:181 to 211; config.rs:59 | +| Shadow ledger (UTXO coinbase) | producer outputs plus one OP_RETURN output tagged `igneum-proving-pool-v0`; no UTXO transaction but the coinbase is valid, so these outputs are never spent | coinbase.rs:126 to 168; body_validation_in_isolation.rs:54 | +| Pool | none in consensus; the reference pool keeps `fee_percent` (default 1.0) of the 80% it receives and pays members PPLNS from its address | pool/src/payout.rs:1 to 6; pool/src/config.rs:58; pool/src/pplns.rs:76 | +| Dev fee | none in consensus; the miner software requests 1 template in 100 with the dev address (off with `--dev-fee 0`); it moves those blocks' producer share and creates nothing | litepaper line 625 | +| Treasury, foundation, team, stake | none | coinbase.rs:126 to 168; executor.rs:181 to 211 | +| Genesis | the genesis coinbase has no outputs; no premine | genesis.rs:26 | +| Burns | execution base fee and proving charge burned; 20% of the tip goes to registered payees, and unregistered parts are burned | executor.rs:319 to 371; pgas.rs:290 | + +## Supply accounting (VR-02, for TV-02) + +- **Issued**: the EVM state's subsidy credits (producer plus escrow), for blue blocks only. The schedule's sum, at one subsidy per DAA score, is the upper bound. +- **Burned**: base fees, proving charges and unregistered developer shares. The UTXO OP_RETURN output is **not** a burn (C06). +- **Locked**: the proving escrow's balance, meaning credited but not yet paid against a valid proof. Unclaimed credit is stranded today, as the litepaper says at line 609. +- **Not supply**: the UTXO coinbase outputs, which are an unspendable shadow record, and any bridged or wrapped IGN on another chain. +- **Replay model**: one scheduled subsidy per DAA score. A block is paid at its own DAA score. Parallel blocks can share a score, and a score can have no block. So measured issuance differs from the model only at phase boundaries, and only by the blocks merged across them. TV-02 reconciles the measured ledgers to this model in exact integers. + +## Conflicts between the litepaper and the code + +The litepaper is site/litepaper.html at igneum master 6824d49c (sha256 `35ac65e1...e85a94`). The code is igneum-node at both releases. Every quote below is checked exact by the generator. + +| # | The litepaper's words | The code's line and value | The spec takes, and why | Status | +|---|---|---|---|---| +| C01 | line 630, the tail-vote sentence quoted above | emission.rs:109 `Tail::Cap`; params.rs:1858; the override refused on mainnet; no vote path | The cap; the sentence is SUPERSEDED. D01, VR-01. | RESOLVED by D01, subject to TV-04 | +| C02 | line 578 "emission starts at 1 billion a year"; line 602 "3,938M IGN in the first 12 years of a 4,000M cap." | emission.rs:104 to 106: year 1 issues 963,038,999.99 IGN; years 1 to 12 issue 3,900,538,999.62 IGN | The code. The chart ignores the ramp's withheld 36.96M IGN. | RESOLVED; text correction owed (site lane) | +| C03 | line 578 "halves every two years for ever" | emission.rs:225, :331: the per-block subsidy floors to 0 at DAA 39,131,424,000 (start of year 125, 10 blocks/s) or DAA 4,102,488,000 (start of year 131, 1 block/s) | The code: a finite schedule ending in a terminal zero phase | RESOLVED; text correction owed | +| C04 | line 542 "one block a second at launch with scheduled steps to four and ten"; line 668 "31.688 IGN a block, 86,400 blocks a day" | params.rs:1789: mainnet runs 10 blocks/s from genesis. coinbase.rs:31, :265: the subsidy keys time on one block rate with no history, so a 1-to-10 step one year in would issue 4,863,038,999.50 IGN, which is 863,038,999.50 over the cap (check-spec KF4) | Both profiles, identical per second. No live block-rate step is cap-safe under today's code. | **BLOCKED** | +| C05 | line 568 "the base unit is Open" | unit.rs:38; params.rs:1816: 18 decimals | 18 decimals (O-2.6, decided 6 October 2026) | RESOLVED; text correction owed | +| C06 | line 609: the 20% "goes to an unspendable script ... and is burned there. Provers are paid from a separate escrow in the execution state" | coinbase.rs:163 (OP_RETURN on the UTXO side); body_validation_in_isolation.rs:54 (UTXO never spent); executor.rs:181 to 211 (the EVM state credits 80% and 20%) | Native supply is the EVM state. The UTXO 20% is not a burn, and the escrow balance is issued and locked. | RESOLVED for the spec; TV-02 reconciles both ledgers | +| C07 | line 620 "80% the block's miner, 20% the proving pool for the provers of that block" | coinbase.rs:152 to 159 (the UTXO side pays DAA red blocks); executor.rs:181 (the EVM credits blue only) | Native issuance is the blue blocks' subsidies; the schedule sum is the upper bound | RESOLVED; TV-02 measures the red share | +| C08 | silent | params.rs:1812 and :2232: `subsidy_per_block_activation_daa` is never on mainnet and Devnet 4 (testnet-1: 0). The EVM credits a merged block the merging chain block's subsidy, while the UTXO side pays the block's own. | Each block's own DAA score (ledger N8) is normative | **BLOCKED** | +| C09 | line 775 "Genesis with no premine, 30-day ramp." | genesis.rs:92: the mainnet GENESIS carries DAA 1,312,860 and Kaspa's 2021 timestamp. The first block would pay 14.55% of full rate, not 10%. 510,841.78 IGN would never be scheduled, and the schedule would run 1.52 days early. | Genesis at DAA score 0, as Devnet 4 and testnet-1 have it | **BLOCKED** | +| C10 | line 622 "80% the block's miner and provers; 20% the apps whose code ran, per call frame" | executor.rs:362 to 371: the tip less the developer share goes to the miner alone | The code. Fees move supply and never create it. | RESOLVED; text correction owed (drop "and provers") | +| C11 | line 647 "The reference pool never holds a member’s balance. Payouts come straight from the coinbase split" | pool/src/payout.rs:1 to 6 (the pool's address receives and pays); config.rs:58 (1% fee); params.rs:1851 (`pool_split_activation_daa` never) | The code: a custodial PPLNS pool with no supply effect | **BLOCKED** | +| C12 | line 630 "The cap stays at 4 billion. There is no tail emission." | emission.rs:116 to 122; params.rs:2014: the held testnet-1 object carries a 1% a year tail | Not the contract. The contract is `CURRENT` with `Tail::Cap`. | RESOLVED by D01 | +| C13 | silent | params.rs:1783 to 1784: Kaspa's `deflationary_phase_daa_score` and `pre_deflationary_phase_base_subsidy` sit in the mainnet object. Only the digest reads them (coinbase.rs:31). | Ignored: not part of the contract | RESOLVED; the node lane may annotate or remove them at the genesis cut | + +### The four BLOCKED rows: the founder's questions + +| Row | Question for the founder | Default if unanswered | Earliest clock | +|---|---|---|---| +| C04 | Does mainnet launch at the compiled 10 blocks a second, with the litepaper's 1-to-4-to-10 step plan removed? Or at 1 with steps? If steps, the node lane adds a block-rate-history subsidy rule before genesis, and TV-02 replays a step. | The spec freezes the compiled 10 blocks a second, and the step plan is treated as text to correct | the founder's answer | +| C08 | Should `subsidy_per_block_activation_daa` be 0 in the mainnet object, as testnet-1 has it, so both ledgers credit each block its own subsidy? (The node lane owns the change.) | N8 stays normative, and TV-02 reports the chain-block reading as a measured divergence | the founder's answer | +| C09 | Is the mainnet genesis cut at DAA score 0? | The spec freezes DAA score 0, and the genesis cut is checked against it | the founder's answer | +| C11 | Should the `IGNW` coinbase split be active at mainnet genesis (`pool_split_activation_daa` = 0), so the litepaper's custody sentence holds? Or should the sentence change to describe the custodial PPLNS pool and its 1% fee? | The sentence is corrected to match the code | the founder's answer | + +## Reproduce + +``` +python3 tools/token-value/tv-01/gen-spec.py --node-git \ + --igneum-root . --igneum-rev 6824d49cc32def4463b643293a5622545618f7a9 \ + --code-dump tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl \ + --out docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json \ + --md docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md +python3 tools/token-value/tv-01/check-spec.py +``` + +The generator refuses to write if any cited line has lost its anchor text at either node revision, or if any litepaper quote is not exact. The code dump comes from `tools/token-value/tv-01/code-dump/tv01_schedule.rs`. That program was copied into `consensus/core/examples/` of a scratch tree of bf60948a on build-9 and run there with `cargo run --release -p kaspa-consensus-core --example tv01_schedule`. It calls `Params::emission`, `EmissionTable::block_subsidy` and `Params::bps` for mainnet, Devnet 4 and testnet-1. The node fork itself is unchanged. diff --git a/tools/ci/batches/tv-01-20261009-01.json b/tools/ci/batches/tv-01-20261009-01.json new file mode 100644 index 000000000..b925f42c3 --- /dev/null +++ b/tools/ci/batches/tv-01-20261009-01.json @@ -0,0 +1,19 @@ +{ + "run_id": "tv-01-20261009-01", + "manifest_sha": "6824d49cc", + "evidence_dir": "docs/plans/igneum-2.0-master/token-value/phase0/tv-01", + "cells": [ + { + "cell": "token-value:tv-01", + "cases": [ + "TV-01" + ], + "status": "NOT RUN", + "in_progress": true, + "method": "static", + "evidence": "docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json; docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md; tools/token-value/tv-01/check-spec.py; tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl", + "note": "Phase 0 supply spec (TV-01): the cap 4,000,000,000 IGN held as the invariant under D01 (capped issuance, subject to TV-04), the litepaper's year-five tail-vote sentence superseded; two schedule profiles generated in exact integers and equal on every field to the consensus code's own dump on bf60948a (release-2.0.2-node a284380b byte-identical for every cited file); total + shortfall = cap exactly; 13 litepaper-versus-code rows, 4 BLOCKED on the founder's questions (C04 block rate, C08 per-block crediting, C09 mainnet genesis clock, C11 pool custody). A document and its checks, not a verdict; the panel reads it.", + "claim_impact": "none: nothing activates; the litepaper sentences named in the conflicts table are the site lane's to correct" + } + ] +} diff --git a/tools/token-value/tv-01/check-spec.py b/tools/token-value/tv-01/check-spec.py new file mode 100644 index 000000000..ef2839612 --- /dev/null +++ b/tools/token-value/tv-01/check-spec.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""TV-01 checker: reads supply-spec.json, checks it is well-formed, and sums every schedule to the cap in exact integers. + + python3 tools/token-value/tv-01/check-spec.py [docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json] + +Order of work (the known-failed cases first, the standing rule that a check is trusted only after it has fired): + 1. four known-failed specs, built in memory from the real one, must each be REFUSED for the stated reason: + KF1 a tail (the terminal phase pays 1 base unit a block for ever): unbounded, the overshoot is infinite; + KF2 an emergency mint (the first full-rate phase doubled, every sum made self-consistent): overshoot over the cap; + KF3 the declared shortfall one base unit off: the exact reconciliation fails; + KF4 a block-rate step from 1 to 10 blocks a second one year in, keyed as the code keys time today: overshoot; + 2. the real spec must be well-formed and reconcile exactly: total + shortfall = cap, total <= cap, the phases + regenerated from the spec's own rules equal the listed phases, tail = cap, the tail-vote text SUPERSEDED. +Exit 0 only when every known-failed case was refused and the real spec checked clean. The output never says PASS: the +gate TV-01 reads NOT RUN until the independent panel reads the evidence. Standalone Python 3, deterministic. +""" +import copy +import json +import sys + +DEFAULT = "docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json" +TOP = ["schema", "gate", "verdict", "decision", "invariant", "units", "cap", "rules", "activation_clocks", "schedules", + "coinbase_split", "fees", "supply_accounting", "superseded", "conflicts", "sources", "generator"] +CONFLICT_KEYS = ["id", "topic", "litepaper", "code", "spec_takes", "why", "status"] + + +class Refused(Exception): + pass + + +def need(cond, why): + if not cond: + raise Refused(why) + + +def amount(x, where): + need(isinstance(x, str) and x.isdigit() and (x == "0" or not x.startswith("0")), f"{where}: amount {x!r} is not a decimal string of base units") + return int(x) + + +# ---- the schedule regenerated from the spec's own rules (a third implementation beside the code and the generator) -- +def rules_of(spec): + r = spec["rules"] + return {"L": amount(r["launch_rate_per_second"], "rules.launch_rate_per_second"), "R": r["ramp_seconds"], + "P": r["ramp_start_percent"], "S": r["step_seconds"], "Q": r["step_decay_q32"]} + + +def rate(rr, k, cache={}): + key = (rr["L"], rr["Q"], k) + if key not in cache: + x = rr["L"] + for _ in range(k): + x = (x * rr["Q"]) >> 32 + cache[key] = x + return cache[key] + + +def per_block(rr, daa, bps): + s = daa // bps + full = rate(rr, s // rr["S"]) // bps + if s >= rr["R"]: + return full + return full * (rr["P"] * rr["R"] + (100 - rr["P"]) * s) // (100 * rr["R"]) + + +def ramp_sum(rr, bps): + full = rr["L"] // bps + R, P = rr["R"], rr["P"] + return sum(full * (P * R + (100 - P) * s) // (100 * R) for s in range(R)) * bps + + +def supply_to(rr, bps, daa): + """Scheduled issuance over DAA scores [0, daa) at one subsidy per DAA score.""" + total, R = 0, rr["R"] * bps + full = rr["L"] // bps + secs = min(daa, R) // bps + total += sum(full * (rr["P"] * rr["R"] + (100 - rr["P"]) * s) // (100 * rr["R"]) for s in range(secs)) * bps + total += (min(daa, R) - secs * bps) * per_block(rr, secs * bps, bps) + d = R + while d < daa: + k = (d // bps) // rr["S"] + end = min((k + 1) * rr["S"] * bps, daa) + pb = per_block(rr, d, bps) + if pb == 0: + break + total += pb * (end - d) + d = end + return total + + +# ---- the checks ---------------------------------------------------------------------------------------------- +def check(spec, regen=True): + """Returns a list of report lines; raises Refused on the first fault.""" + out = [] + for k in TOP: + need(k in spec, f"top-level field {k!r} missing") + need(spec["gate"] == "TV-01", "gate is not TV-01") + need(spec["verdict"] in ("NOT RUN", "BLOCKED", "FAIL"), f"verdict {spec['verdict']!r}: a lane never writes PASS on its own result") + need(spec["decision"].get("id") == "D01", "decision is not D01") + need(spec["rules"].get("tail") == "cap", f"tail is {spec['rules'].get('tail')!r}: under D01 the only tail is the cap") + cap = amount(spec["cap"]["base_units"], "cap.base_units") + unit = amount(spec["units"]["base_units_per_coin"], "units.base_units_per_coin") + need(unit == 10 ** spec["units"]["decimals"], "base_units_per_coin is not 10^decimals") + need(cap == int(spec["cap"]["coins"]) * unit, "cap.base_units is not cap.coins x base_units_per_coin") + sup = spec["superseded"] + need(any(s.get("status") == "SUPERSEDED" and "tail reward" in s.get("text", "") and s.get("by", "").startswith("D01") for s in sup), + "the litepaper's tail-vote sentence is not named SUPERSEDED by D01") + ids = set() + for c in spec["conflicts"]: + for k in CONFLICT_KEYS: + need(k in c, f"conflict {c.get('id')}: field {k!r} missing") + need(c["id"] not in ids, f"conflict id {c['id']} repeated") + ids.add(c["id"]) + need(c["status"].startswith("RESOLVED") or c["status"] == "BLOCKED", f"conflict {c['id']}: status {c['status']!r}") + if c["status"] == "BLOCKED": + need(c.get("question", "").startswith("For the founder"), f"conflict {c['id']}: BLOCKED without the founder's question") + rr = rules_of(spec) + need(rr["Q"] < 1 << 32 and rr["Q"] > 0, "step_decay_q32 outside 1..2^32: the curve would never fall") + for sc in spec["schedules"]: + name, bps, ph = sc["profile"], sc["bps"], sc["phases"] + need(isinstance(bps, int) and bps > 0, f"{name}: bps {bps!r}") + need(sc["genesis_daa_score"] == 0, f"{name}: genesis DAA score is not 0") + need(len(ph) == sc["phase_count"] and len(ph) >= 2, f"{name}: phase_count disagrees with the list") + need(ph[0]["start_daa_score"] == 0, f"{name}: the first phase does not start at DAA 0") + total, prev_pb = 0, None + for i, p in enumerate(ph): + need(p["index"] == i, f"{name}: phase {i} carries index {p['index']}") + need(p["source"], f"{name} phase {i}: no source line") + if i: + need(p["start_daa_score"] == ph[i - 1]["end_daa_score"], f"{name}: phase {i} does not start where phase {i - 1} ends (a gap or an overlap)") + s = amount(p["phase_sum"], f"{name} phase {i}.phase_sum") + if p["kind"] == "ramp": + need(i == 0, f"{name}: a ramp phase at index {i}") + rs = ramp_sum(rr, bps) + need(s == rs, f"{name}: the ramp's declared sum {s} is not its formula's {rs} (off by {s - rs})") + need(amount(p["ramp"]["full_per_block"], "ramp.full_per_block") == rr["L"] // bps, f"{name}: ramp full_per_block is not launch_rate / bps") + elif p["kind"] == "constant": + pb = amount(p["per_block_subsidy"], f"{name} phase {i}.per_block_subsidy") + need(pb > 0, f"{name}: constant phase {i} pays 0 (that is the terminal)") + need(p["end_daa_score"] > p["start_daa_score"], f"{name}: phase {i} is empty") + need(s == pb * (p["end_daa_score"] - p["start_daa_score"]), f"{name}: phase {i} sum is not per_block x length") + need(prev_pb is None or pb <= prev_pb, f"{name}: phase {i} pays more than phase {i - 1} (issuance rises)") + prev_pb = pb + elif p["kind"] == "terminal": + need(i == len(ph) - 1, f"{name}: a terminal phase before the end") + need(p["end_daa_score"] is None, f"{name}: the terminal phase has an end") + tb = amount(p["per_block_subsidy"], f"{name} terminal per_block_subsidy") + need(tb == 0, f"{name}: the terminal phase pays {tb} base units a block for ever: issuance is unbounded, the overshoot over the cap is infinite") + else: + raise Refused(f"{name}: phase {i} kind {p['kind']!r}") + total += s + need(ph[-1]["kind"] == "terminal", f"{name}: the schedule has no terminal phase (issuance is not shown to end)") + need(total == amount(sc["total_issuance"], f"{name}.total_issuance"), f"{name}: the phases sum to {total}, the spec says {sc['total_issuance']}") + if total > cap: + raise Refused(f"{name}: overshoot {total - cap} base units ({(total - cap) / unit:,.6f} IGN) over the cap") + short = cap - total + declared = amount(sc["cap_shortfall"], f"{name}.cap_shortfall") + need(short == declared, f"{name}: total + declared shortfall = {total + declared}, not the cap (off by {declared - short} base units)") + if regen: + for p in ph: + if p["kind"] == "ramp": + continue + a = p["start_daa_score"] + pb = per_block(rr, a, bps) + need(str(pb) == p["per_block_subsidy"], f"{name}: phase {p['index']} lists {p['per_block_subsidy']}, the rules give {pb}") + if p["end_daa_score"] is not None: + need(per_block(rr, p["end_daa_score"] - 1, bps) == pb, f"{name}: phase {p['index']} is not constant under the rules") + nb = per_block(rr, p["end_daa_score"], bps) + need(nb != pb, f"{name}: phase {p['index']} ends where the rules do not change") + out.append(f"{name}: {len(ph)} phases, contiguous from DAA 0, terminal zero from DAA {ph[-1]['start_daa_score']:,}; " + f"total {total} + shortfall {short} = cap {cap} exactly (shortfall {short / unit:,.6f} IGN)") + return out + + +def bps_step_case(spec): + """KF4: the schedule as the code would pay it if the block rate stepped from 1 to 10 a second at DAA one year in.""" + rr = rules_of(spec) + cap = int(spec["cap"]["base_units"]) + y = spec["rules"]["year_seconds"] + step = y * 1 + s10 = int(next(s for s in spec["schedules"] if s["bps"] == 10)["total_issuance"]) + issued = supply_to(rr, 1, step) + (s10 - supply_to(rr, 10, step)) + need(issued <= cap, f"a 1-to-10 block-rate step at DAA {step:,} keyed as daa / bps: issuance {issued}, overshoot {issued - cap} base units over the cap") + return issued + + +def main(): + path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT + spec = json.load(open(path)) + fails = 0 + + def known_failed(label, mutate, run=None): + nonlocal fails + s = copy.deepcopy(spec) + mutate(s) + try: + (run or (lambda x: check(x, regen=False)))(s) + except Refused as e: + print(f"known-failed {label}: refused as expected: {e}") + return + print(f"known-failed {label}: NOT refused (the checker is broken)") + fails += 1 + + def kf1(s): + sc = s["schedules"][0] + sc["phases"][-1]["per_block_subsidy"] = "1" + + def kf2(s): + sc = s["schedules"][0] + p = sc["phases"][1] + old = int(p["phase_sum"]) + pb = int(p["per_block_subsidy"]) * 2 + p["per_block_subsidy"] = str(pb) + p["phase_sum"] = str(pb * (p["end_daa_score"] - p["start_daa_score"])) + sc["total_issuance"] = str(int(sc["total_issuance"]) - old + int(p["phase_sum"])) + sc["cap_shortfall"] = str(int(s["cap"]["base_units"]) - int(sc["total_issuance"])) # may go negative: str(-x) fails too + + def kf2_run(s): + sc = s["schedules"][0] + if sc["cap_shortfall"].startswith("-"): + sc["cap_shortfall"] = "0" # make the field well-formed so the overshoot itself is what refuses it + return check(s, regen=False) + + def kf3(s): + sc = s["schedules"][0] + sc["cap_shortfall"] = str(int(sc["cap_shortfall"]) + 1) + + known_failed("KF1 (a tail: the terminal pays 1 base unit a block for ever)", kf1) + known_failed("KF2 (an emergency mint: the first full-rate phase doubled, sums self-consistent)", kf2, kf2_run) + known_failed("KF3 (the declared shortfall one base unit off)", kf3) + known_failed("KF4 (a 1-to-10 block-rate step one year in, time keyed as daa / bps)", lambda s: None, bps_step_case) + + try: + lines = check(spec) + except Refused as e: + print(f"spec {path}: REFUSED: {e}") + return 1 + for l in lines: + print(l) + blocked = [c["id"] for c in spec["conflicts"] if c["status"] == "BLOCKED"] + print(f"conflicts: {len(spec['conflicts'])} rows, BLOCKED: {', '.join(blocked) or 'none'}; gate TV-01 verdict as written: {spec['verdict']}") + if fails: + print(f"checker: {fails} known-failed case(s) were not refused; the result above is not trusted") + return 2 + print("check: well-formed and exact (a check result, not a gate verdict)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl b/tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl new file mode 100644 index 000000000..05d2b9638 --- /dev/null +++ b/tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl @@ -0,0 +1,139 @@ +{"network":"mainnet","bps":10,"decimals":18,"launch_rate":"31688087810000000000","ramp_seconds":2592000,"ramp_start_percent":10,"step_seconds":63115200,"step_decay_q32":2147483648,"tail":"cap","steps":66,"genesis_daa_score":1312860} +{"network":"mainnet","phase":"ramp","start_daa":0,"end_daa":25920000,"first_block":"316880878100000000","last_block":"3168807680719173263","sum":"45174523722296485488480000"} +{"network":"mainnet","phase":"step","k":0,"start_daa":25920000,"end_daa":631152000,"per_block":"3168808781000000000","rate_per_second":"31688087810000000000","sum":"1917864476142192000000000000"} +{"network":"mainnet","phase":"step","k":1,"start_daa":631152000,"end_daa":1262304000,"per_block":"1584404390500000000","rate_per_second":"15844043905000000000","sum":"999999999872856000000000000"} +{"network":"mainnet","phase":"step","k":2,"start_daa":1262304000,"end_daa":1893456000,"per_block":"792202195250000000","rate_per_second":"7922021952500000000","sum":"499999999936428000000000000"} +{"network":"mainnet","phase":"step","k":3,"start_daa":1893456000,"end_daa":2524608000,"per_block":"396101097625000000","rate_per_second":"3961010976250000000","sum":"249999999968214000000000000"} +{"network":"mainnet","phase":"step","k":4,"start_daa":2524608000,"end_daa":3155760000,"per_block":"198050548812500000","rate_per_second":"1980505488125000000","sum":"124999999984107000000000000"} +{"network":"mainnet","phase":"step","k":5,"start_daa":3155760000,"end_daa":3786912000,"per_block":"99025274406250000","rate_per_second":"990252744062500000","sum":"62499999992053500000000000"} +{"network":"mainnet","phase":"step","k":6,"start_daa":3786912000,"end_daa":4418064000,"per_block":"49512637203125000","rate_per_second":"495126372031250000","sum":"31249999996026750000000000"} +{"network":"mainnet","phase":"step","k":7,"start_daa":4418064000,"end_daa":5049216000,"per_block":"24756318601562500","rate_per_second":"247563186015625000","sum":"15624999998013375000000000"} +{"network":"mainnet","phase":"step","k":8,"start_daa":5049216000,"end_daa":5680368000,"per_block":"12378159300781250","rate_per_second":"123781593007812500","sum":"7812499999006687500000000"} +{"network":"mainnet","phase":"step","k":9,"start_daa":5680368000,"end_daa":6311520000,"per_block":"6189079650390625","rate_per_second":"61890796503906250","sum":"3906249999503343750000000"} +{"network":"mainnet","phase":"step","k":10,"start_daa":6311520000,"end_daa":6942672000,"per_block":"3094539825195312","rate_per_second":"30945398251953125","sum":"1953124999751671559424000"} +{"network":"mainnet","phase":"step","k":11,"start_daa":6942672000,"end_daa":7573824000,"per_block":"1547269912597656","rate_per_second":"15472699125976562","sum":"976562499875835779712000"} +{"network":"mainnet","phase":"step","k":12,"start_daa":7573824000,"end_daa":8204976000,"per_block":"773634956298828","rate_per_second":"7736349562988281","sum":"488281249937917889856000"} +{"network":"mainnet","phase":"step","k":13,"start_daa":8204976000,"end_daa":8836128000,"per_block":"386817478149414","rate_per_second":"3868174781494140","sum":"244140624968958944928000"} +{"network":"mainnet","phase":"step","k":14,"start_daa":8836128000,"end_daa":9467280000,"per_block":"193408739074707","rate_per_second":"1934087390747070","sum":"122070312484479472464000"} +{"network":"mainnet","phase":"step","k":15,"start_daa":9467280000,"end_daa":10098432000,"per_block":"96704369537353","rate_per_second":"967043695373535","sum":"61035156242239420656000"} +{"network":"mainnet","phase":"step","k":16,"start_daa":10098432000,"end_daa":10729584000,"per_block":"48352184768676","rate_per_second":"483521847686767","sum":"30517578121119394752000"} +{"network":"mainnet","phase":"step","k":17,"start_daa":10729584000,"end_daa":11360736000,"per_block":"24176092384338","rate_per_second":"241760923843383","sum":"15258789060559697376000"} +{"network":"mainnet","phase":"step","k":18,"start_daa":11360736000,"end_daa":11991888000,"per_block":"12088046192169","rate_per_second":"120880461921691","sum":"7629394530279848688000"} +{"network":"mainnet","phase":"step","k":19,"start_daa":11991888000,"end_daa":12623040000,"per_block":"6044023096084","rate_per_second":"60440230960845","sum":"3814697265139608768000"} +{"network":"mainnet","phase":"step","k":20,"start_daa":12623040000,"end_daa":13254192000,"per_block":"3022011548042","rate_per_second":"30220115480422","sum":"1907348632569804384000"} +{"network":"mainnet","phase":"step","k":21,"start_daa":13254192000,"end_daa":13885344000,"per_block":"1511005774021","rate_per_second":"15110057740211","sum":"953674316284902192000"} +{"network":"mainnet","phase":"step","k":22,"start_daa":13885344000,"end_daa":14516496000,"per_block":"755502887010","rate_per_second":"7555028870105","sum":"476837158142135520000"} +{"network":"mainnet","phase":"step","k":23,"start_daa":14516496000,"end_daa":15147648000,"per_block":"377751443505","rate_per_second":"3777514435052","sum":"238418579071067760000"} +{"network":"mainnet","phase":"step","k":24,"start_daa":15147648000,"end_daa":15778800000,"per_block":"188875721752","rate_per_second":"1888757217526","sum":"119209289535218304000"} +{"network":"mainnet","phase":"step","k":25,"start_daa":15778800000,"end_daa":16409952000,"per_block":"94437860876","rate_per_second":"944378608763","sum":"59604644767609152000"} +{"network":"mainnet","phase":"step","k":26,"start_daa":16409952000,"end_daa":17041104000,"per_block":"47218930438","rate_per_second":"472189304381","sum":"29802322383804576000"} +{"network":"mainnet","phase":"step","k":27,"start_daa":17041104000,"end_daa":17672256000,"per_block":"23609465219","rate_per_second":"236094652190","sum":"14901161191902288000"} +{"network":"mainnet","phase":"step","k":28,"start_daa":17672256000,"end_daa":18303408000,"per_block":"11804732609","rate_per_second":"118047326095","sum":"7450580595635568000"} +{"network":"mainnet","phase":"step","k":29,"start_daa":18303408000,"end_daa":18934560000,"per_block":"5902366304","rate_per_second":"59023663047","sum":"3725290297502208000"} +{"network":"mainnet","phase":"step","k":30,"start_daa":18934560000,"end_daa":19565712000,"per_block":"2951183152","rate_per_second":"29511831523","sum":"1862645148751104000"} +{"network":"mainnet","phase":"step","k":31,"start_daa":19565712000,"end_daa":20196864000,"per_block":"1475591576","rate_per_second":"14755915761","sum":"931322574375552000"} +{"network":"mainnet","phase":"step","k":32,"start_daa":20196864000,"end_daa":20828016000,"per_block":"737795788","rate_per_second":"7377957880","sum":"465661287187776000"} +{"network":"mainnet","phase":"step","k":33,"start_daa":20828016000,"end_daa":21459168000,"per_block":"368897894","rate_per_second":"3688978940","sum":"232830643593888000"} +{"network":"mainnet","phase":"step","k":34,"start_daa":21459168000,"end_daa":22090320000,"per_block":"184448947","rate_per_second":"1844489470","sum":"116415321796944000"} +{"network":"mainnet","phase":"step","k":35,"start_daa":22090320000,"end_daa":22721472000,"per_block":"92224473","rate_per_second":"922244735","sum":"58207660582896000"} +{"network":"mainnet","phase":"step","k":36,"start_daa":22721472000,"end_daa":23352624000,"per_block":"46112236","rate_per_second":"461122367","sum":"29103829975872000"} +{"network":"mainnet","phase":"step","k":37,"start_daa":23352624000,"end_daa":23983776000,"per_block":"23056118","rate_per_second":"230561183","sum":"14551914987936000"} +{"network":"mainnet","phase":"step","k":38,"start_daa":23983776000,"end_daa":24614928000,"per_block":"11528059","rate_per_second":"115280591","sum":"7275957493968000"} +{"network":"mainnet","phase":"step","k":39,"start_daa":24614928000,"end_daa":25246080000,"per_block":"5764029","rate_per_second":"57640295","sum":"3637978431408000"} +{"network":"mainnet","phase":"step","k":40,"start_daa":25246080000,"end_daa":25877232000,"per_block":"2882014","rate_per_second":"28820147","sum":"1818988900128000"} +{"network":"mainnet","phase":"step","k":41,"start_daa":25877232000,"end_daa":26508384000,"per_block":"1441007","rate_per_second":"14410073","sum":"909494450064000"} +{"network":"mainnet","phase":"step","k":42,"start_daa":26508384000,"end_daa":27139536000,"per_block":"720503","rate_per_second":"7205036","sum":"454746909456000"} +{"network":"mainnet","phase":"step","k":43,"start_daa":27139536000,"end_daa":27770688000,"per_block":"360251","rate_per_second":"3602518","sum":"227373139152000"} +{"network":"mainnet","phase":"step","k":44,"start_daa":27770688000,"end_daa":28401840000,"per_block":"180125","rate_per_second":"1801259","sum":"113686254000000"} +{"network":"mainnet","phase":"step","k":45,"start_daa":28401840000,"end_daa":29032992000,"per_block":"90062","rate_per_second":"900629","sum":"56842811424000"} +{"network":"mainnet","phase":"step","k":46,"start_daa":29032992000,"end_daa":29664144000,"per_block":"45031","rate_per_second":"450314","sum":"28421405712000"} +{"network":"mainnet","phase":"step","k":47,"start_daa":29664144000,"end_daa":30295296000,"per_block":"22515","rate_per_second":"225157","sum":"14210387280000"} +{"network":"mainnet","phase":"step","k":48,"start_daa":30295296000,"end_daa":30926448000,"per_block":"11257","rate_per_second":"112578","sum":"7104878064000"} +{"network":"mainnet","phase":"step","k":49,"start_daa":30926448000,"end_daa":31557600000,"per_block":"5628","rate_per_second":"56289","sum":"3552123456000"} +{"network":"mainnet","phase":"step","k":50,"start_daa":31557600000,"end_daa":32188752000,"per_block":"2814","rate_per_second":"28144","sum":"1776061728000"} +{"network":"mainnet","phase":"step","k":51,"start_daa":32188752000,"end_daa":32819904000,"per_block":"1407","rate_per_second":"14072","sum":"888030864000"} +{"network":"mainnet","phase":"step","k":52,"start_daa":32819904000,"end_daa":33451056000,"per_block":"703","rate_per_second":"7036","sum":"443699856000"} +{"network":"mainnet","phase":"step","k":53,"start_daa":33451056000,"end_daa":34082208000,"per_block":"351","rate_per_second":"3518","sum":"221534352000"} +{"network":"mainnet","phase":"step","k":54,"start_daa":34082208000,"end_daa":34713360000,"per_block":"175","rate_per_second":"1759","sum":"110451600000"} +{"network":"mainnet","phase":"step","k":55,"start_daa":34713360000,"end_daa":35344512000,"per_block":"87","rate_per_second":"879","sum":"54910224000"} +{"network":"mainnet","phase":"step","k":56,"start_daa":35344512000,"end_daa":35975664000,"per_block":"43","rate_per_second":"439","sum":"27139536000"} +{"network":"mainnet","phase":"step","k":57,"start_daa":35975664000,"end_daa":36606816000,"per_block":"21","rate_per_second":"219","sum":"13254192000"} +{"network":"mainnet","phase":"step","k":58,"start_daa":36606816000,"end_daa":37237968000,"per_block":"10","rate_per_second":"109","sum":"6311520000"} +{"network":"mainnet","phase":"step","k":59,"start_daa":37237968000,"end_daa":37869120000,"per_block":"5","rate_per_second":"54","sum":"3155760000"} +{"network":"mainnet","phase":"step","k":60,"start_daa":37869120000,"end_daa":38500272000,"per_block":"2","rate_per_second":"27","sum":"1262304000"} +{"network":"mainnet","phase":"step","k":61,"start_daa":38500272000,"end_daa":39131424000,"per_block":"1","rate_per_second":"13","sum":"631152000"} +{"network":"mainnet","phase":"step","k":62,"start_daa":39131424000,"end_daa":39762576000,"per_block":"0","rate_per_second":"6","sum":"0"} +{"network":"mainnet","phase":"terminal","start_daa":39131424000,"per_block":"0"} +{"network":"mainnet","total":"3963038999610200467816224000","cap":"4000000000000000000000000000","shortfall":"36961000389799532183776000"} +{"network":"devnet-4","bps":1,"decimals":18,"launch_rate":"31688087810000000000","ramp_seconds":2592000,"ramp_start_percent":10,"step_seconds":63115200,"step_decay_q32":2147483648,"tail":"cap","steps":66,"genesis_daa_score":0} +{"network":"devnet-4","phase":"ramp","start_daa":0,"end_daa":2592000,"first_block":"3168808781000000000","last_block":"31688076807191732638","sum":"45174523722296485498848000"} +{"network":"devnet-4","phase":"step","k":0,"start_daa":2592000,"end_daa":63115200,"per_block":"31688087810000000000","rate_per_second":"31688087810000000000","sum":"1917864476142192000000000000"} +{"network":"devnet-4","phase":"step","k":1,"start_daa":63115200,"end_daa":126230400,"per_block":"15844043905000000000","rate_per_second":"15844043905000000000","sum":"999999999872856000000000000"} +{"network":"devnet-4","phase":"step","k":2,"start_daa":126230400,"end_daa":189345600,"per_block":"7922021952500000000","rate_per_second":"7922021952500000000","sum":"499999999936428000000000000"} +{"network":"devnet-4","phase":"step","k":3,"start_daa":189345600,"end_daa":252460800,"per_block":"3961010976250000000","rate_per_second":"3961010976250000000","sum":"249999999968214000000000000"} +{"network":"devnet-4","phase":"step","k":4,"start_daa":252460800,"end_daa":315576000,"per_block":"1980505488125000000","rate_per_second":"1980505488125000000","sum":"124999999984107000000000000"} +{"network":"devnet-4","phase":"step","k":5,"start_daa":315576000,"end_daa":378691200,"per_block":"990252744062500000","rate_per_second":"990252744062500000","sum":"62499999992053500000000000"} +{"network":"devnet-4","phase":"step","k":6,"start_daa":378691200,"end_daa":441806400,"per_block":"495126372031250000","rate_per_second":"495126372031250000","sum":"31249999996026750000000000"} +{"network":"devnet-4","phase":"step","k":7,"start_daa":441806400,"end_daa":504921600,"per_block":"247563186015625000","rate_per_second":"247563186015625000","sum":"15624999998013375000000000"} +{"network":"devnet-4","phase":"step","k":8,"start_daa":504921600,"end_daa":568036800,"per_block":"123781593007812500","rate_per_second":"123781593007812500","sum":"7812499999006687500000000"} +{"network":"devnet-4","phase":"step","k":9,"start_daa":568036800,"end_daa":631152000,"per_block":"61890796503906250","rate_per_second":"61890796503906250","sum":"3906249999503343750000000"} +{"network":"devnet-4","phase":"step","k":10,"start_daa":631152000,"end_daa":694267200,"per_block":"30945398251953125","rate_per_second":"30945398251953125","sum":"1953124999751671875000000"} +{"network":"devnet-4","phase":"step","k":11,"start_daa":694267200,"end_daa":757382400,"per_block":"15472699125976562","rate_per_second":"15472699125976562","sum":"976562499875835905942400"} +{"network":"devnet-4","phase":"step","k":12,"start_daa":757382400,"end_daa":820497600,"per_block":"7736349562988281","rate_per_second":"7736349562988281","sum":"488281249937917952971200"} +{"network":"devnet-4","phase":"step","k":13,"start_daa":820497600,"end_daa":883612800,"per_block":"3868174781494140","rate_per_second":"3868174781494140","sum":"244140624968958944928000"} +{"network":"devnet-4","phase":"step","k":14,"start_daa":883612800,"end_daa":946728000,"per_block":"1934087390747070","rate_per_second":"1934087390747070","sum":"122070312484479472464000"} +{"network":"devnet-4","phase":"step","k":15,"start_daa":946728000,"end_daa":1009843200,"per_block":"967043695373535","rate_per_second":"967043695373535","sum":"61035156242239736232000"} +{"network":"devnet-4","phase":"step","k":16,"start_daa":1009843200,"end_daa":1072958400,"per_block":"483521847686767","rate_per_second":"483521847686767","sum":"30517578121119836558400"} +{"network":"devnet-4","phase":"step","k":17,"start_daa":1072958400,"end_daa":1136073600,"per_block":"241760923843383","rate_per_second":"241760923843383","sum":"15258789060559886721600"} +{"network":"devnet-4","phase":"step","k":18,"start_daa":1136073600,"end_daa":1199188800,"per_block":"120880461921691","rate_per_second":"120880461921691","sum":"7629394530279911803200"} +{"network":"devnet-4","phase":"step","k":19,"start_daa":1199188800,"end_daa":1262304000,"per_block":"60440230960845","rate_per_second":"60440230960845","sum":"3814697265139924344000"} +{"network":"devnet-4","phase":"step","k":20,"start_daa":1262304000,"end_daa":1325419200,"per_block":"30220115480422","rate_per_second":"30220115480422","sum":"1907348632569930614400"} +{"network":"devnet-4","phase":"step","k":21,"start_daa":1325419200,"end_daa":1388534400,"per_block":"15110057740211","rate_per_second":"15110057740211","sum":"953674316284965307200"} +{"network":"devnet-4","phase":"step","k":22,"start_daa":1388534400,"end_daa":1451649600,"per_block":"7555028870105","rate_per_second":"7555028870105","sum":"476837158142451096000"} +{"network":"devnet-4","phase":"step","k":23,"start_daa":1451649600,"end_daa":1514764800,"per_block":"3777514435052","rate_per_second":"3777514435052","sum":"238418579071193990400"} +{"network":"devnet-4","phase":"step","k":24,"start_daa":1514764800,"end_daa":1577880000,"per_block":"1888757217526","rate_per_second":"1888757217526","sum":"119209289535596995200"} +{"network":"devnet-4","phase":"step","k":25,"start_daa":1577880000,"end_daa":1640995200,"per_block":"944378608763","rate_per_second":"944378608763","sum":"59604644767798497600"} +{"network":"devnet-4","phase":"step","k":26,"start_daa":1640995200,"end_daa":1704110400,"per_block":"472189304381","rate_per_second":"472189304381","sum":"29802322383867691200"} +{"network":"devnet-4","phase":"step","k":27,"start_daa":1704110400,"end_daa":1767225600,"per_block":"236094652190","rate_per_second":"236094652190","sum":"14901161191902288000"} +{"network":"devnet-4","phase":"step","k":28,"start_daa":1767225600,"end_daa":1830340800,"per_block":"118047326095","rate_per_second":"118047326095","sum":"7450580595951144000"} +{"network":"devnet-4","phase":"step","k":29,"start_daa":1830340800,"end_daa":1893456000,"per_block":"59023663047","rate_per_second":"59023663047","sum":"3725290297944014400"} +{"network":"devnet-4","phase":"step","k":30,"start_daa":1893456000,"end_daa":1956571200,"per_block":"29511831523","rate_per_second":"29511831523","sum":"1862645148940449600"} +{"network":"devnet-4","phase":"step","k":31,"start_daa":1956571200,"end_daa":2019686400,"per_block":"14755915761","rate_per_second":"14755915761","sum":"931322574438667200"} +{"network":"devnet-4","phase":"step","k":32,"start_daa":2019686400,"end_daa":2082801600,"per_block":"7377957880","rate_per_second":"7377957880","sum":"465661287187776000"} +{"network":"devnet-4","phase":"step","k":33,"start_daa":2082801600,"end_daa":2145916800,"per_block":"3688978940","rate_per_second":"3688978940","sum":"232830643593888000"} +{"network":"devnet-4","phase":"step","k":34,"start_daa":2145916800,"end_daa":2209032000,"per_block":"1844489470","rate_per_second":"1844489470","sum":"116415321796944000"} +{"network":"devnet-4","phase":"step","k":35,"start_daa":2209032000,"end_daa":2272147200,"per_block":"922244735","rate_per_second":"922244735","sum":"58207660898472000"} +{"network":"devnet-4","phase":"step","k":36,"start_daa":2272147200,"end_daa":2335262400,"per_block":"461122367","rate_per_second":"461122367","sum":"29103830417678400"} +{"network":"devnet-4","phase":"step","k":37,"start_daa":2335262400,"end_daa":2398377600,"per_block":"230561183","rate_per_second":"230561183","sum":"14551915177281600"} +{"network":"devnet-4","phase":"step","k":38,"start_daa":2398377600,"end_daa":2461492800,"per_block":"115280591","rate_per_second":"115280591","sum":"7275957557083200"} +{"network":"devnet-4","phase":"step","k":39,"start_daa":2461492800,"end_daa":2524608000,"per_block":"57640295","rate_per_second":"57640295","sum":"3637978746984000"} +{"network":"devnet-4","phase":"step","k":40,"start_daa":2524608000,"end_daa":2587723200,"per_block":"28820147","rate_per_second":"28820147","sum":"1818989341934400"} +{"network":"devnet-4","phase":"step","k":41,"start_daa":2587723200,"end_daa":2650838400,"per_block":"14410073","rate_per_second":"14410073","sum":"909494639409600"} +{"network":"devnet-4","phase":"step","k":42,"start_daa":2650838400,"end_daa":2713953600,"per_block":"7205036","rate_per_second":"7205036","sum":"454747288147200"} +{"network":"devnet-4","phase":"step","k":43,"start_daa":2713953600,"end_daa":2777068800,"per_block":"3602518","rate_per_second":"3602518","sum":"227373644073600"} +{"network":"devnet-4","phase":"step","k":44,"start_daa":2777068800,"end_daa":2840184000,"per_block":"1801259","rate_per_second":"1801259","sum":"113686822036800"} +{"network":"devnet-4","phase":"step","k":45,"start_daa":2840184000,"end_daa":2903299200,"per_block":"900629","rate_per_second":"900629","sum":"56843379460800"} +{"network":"devnet-4","phase":"step","k":46,"start_daa":2903299200,"end_daa":2966414400,"per_block":"450314","rate_per_second":"450314","sum":"28421658172800"} +{"network":"devnet-4","phase":"step","k":47,"start_daa":2966414400,"end_daa":3029529600,"per_block":"225157","rate_per_second":"225157","sum":"14210829086400"} +{"network":"devnet-4","phase":"step","k":48,"start_daa":3029529600,"end_daa":3092644800,"per_block":"112578","rate_per_second":"112578","sum":"7105382985600"} +{"network":"devnet-4","phase":"step","k":49,"start_daa":3092644800,"end_daa":3155760000,"per_block":"56289","rate_per_second":"56289","sum":"3552691492800"} +{"network":"devnet-4","phase":"step","k":50,"start_daa":3155760000,"end_daa":3218875200,"per_block":"28144","rate_per_second":"28144","sum":"1776314188800"} +{"network":"devnet-4","phase":"step","k":51,"start_daa":3218875200,"end_daa":3281990400,"per_block":"14072","rate_per_second":"14072","sum":"888157094400"} +{"network":"devnet-4","phase":"step","k":52,"start_daa":3281990400,"end_daa":3345105600,"per_block":"7036","rate_per_second":"7036","sum":"444078547200"} +{"network":"devnet-4","phase":"step","k":53,"start_daa":3345105600,"end_daa":3408220800,"per_block":"3518","rate_per_second":"3518","sum":"222039273600"} +{"network":"devnet-4","phase":"step","k":54,"start_daa":3408220800,"end_daa":3471336000,"per_block":"1759","rate_per_second":"1759","sum":"111019636800"} +{"network":"devnet-4","phase":"step","k":55,"start_daa":3471336000,"end_daa":3534451200,"per_block":"879","rate_per_second":"879","sum":"55478260800"} +{"network":"devnet-4","phase":"step","k":56,"start_daa":3534451200,"end_daa":3597566400,"per_block":"439","rate_per_second":"439","sum":"27707572800"} +{"network":"devnet-4","phase":"step","k":57,"start_daa":3597566400,"end_daa":3660681600,"per_block":"219","rate_per_second":"219","sum":"13822228800"} +{"network":"devnet-4","phase":"step","k":58,"start_daa":3660681600,"end_daa":3723796800,"per_block":"109","rate_per_second":"109","sum":"6879556800"} +{"network":"devnet-4","phase":"step","k":59,"start_daa":3723796800,"end_daa":3786912000,"per_block":"54","rate_per_second":"54","sum":"3408220800"} +{"network":"devnet-4","phase":"step","k":60,"start_daa":3786912000,"end_daa":3850027200,"per_block":"27","rate_per_second":"27","sum":"1704110400"} +{"network":"devnet-4","phase":"step","k":61,"start_daa":3850027200,"end_daa":3913142400,"per_block":"13","rate_per_second":"13","sum":"820497600"} +{"network":"devnet-4","phase":"step","k":62,"start_daa":3913142400,"end_daa":3976257600,"per_block":"6","rate_per_second":"6","sum":"378691200"} +{"network":"devnet-4","phase":"step","k":63,"start_daa":3976257600,"end_daa":4039372800,"per_block":"3","rate_per_second":"3","sum":"189345600"} +{"network":"devnet-4","phase":"step","k":64,"start_daa":4039372800,"end_daa":4102488000,"per_block":"1","rate_per_second":"1","sum":"63115200"} +{"network":"devnet-4","phase":"step","k":65,"start_daa":4102488000,"end_daa":4165603200,"per_block":"0","rate_per_second":"0","sum":"0"} +{"network":"devnet-4","phase":"terminal","start_daa":4102488000,"per_block":"0"} +{"network":"devnet-4","total":"3963038999610200483289816000","cap":"4000000000000000000000000000","shortfall":"36961000389799516710184000"} +{"network":"testnet-1","bps":1,"decimals":18,"launch_rate":"100000000000000000000","ramp_seconds":7776000,"ramp_start_percent":10,"step_seconds":2629800,"step_decay_q32":4172697914,"tail":"percent:100","steps":125,"genesis_daa_score":0} +{"network":"testnet-1","note":"not a cap schedule; phases not dumped"} diff --git a/tools/token-value/tv-01/code-dump/tv01_schedule.rs b/tools/token-value/tv-01/code-dump/tv01_schedule.rs new file mode 100644 index 000000000..e4ab77be7 --- /dev/null +++ b/tools/token-value/tv-01/code-dump/tv01_schedule.rs @@ -0,0 +1,60 @@ +//! TV-01 (token value phase 0, 9 October 2026): the per-block subsidy schedule of each compiled network, read from the +//! consensus code's own functions (`Params::emission`, `EmissionTable::block_subsidy`, `Params::bps`), as JSON lines. +//! Lane scratch file on build-9 only; not part of the node fork. +use kaspa_consensus_core::config::params::{devnet4_params, Params, MAINNET_PARAMS, TESTNET_PARAMS}; +use kaspa_consensus_core::emission::Tail; + +fn dump(name: &str, p: &Params) { + let t = p.emission.table(); + let s = p.emission; + let bps = p.bps(); + let tail = match s.tail { Tail::Cap => "cap".to_string(), Tail::Fixed { rate } => format!("fixed:{rate}"), Tail::Percent { bps_per_year } => format!("percent:{bps_per_year}") }; + println!( + "{{\"network\":\"{name}\",\"bps\":{bps},\"decimals\":{},\"launch_rate\":\"{}\",\"ramp_seconds\":{},\"ramp_start_percent\":{},\"step_seconds\":{},\"step_decay_q32\":{},\"tail\":\"{tail}\",\"steps\":{},\"genesis_daa_score\":{}}}", + p.base_unit_decimals, s.launch_rate, s.ramp_seconds, s.ramp_start_percent, s.step_seconds, s.step_decay_q32, t.step_rate.len(), p.genesis.daa_score + ); + if s.tail != Tail::Cap { + println!("{{\"network\":\"{name}\",\"note\":\"not a cap schedule; phases not dumped\"}}"); + return; + } + // the ramp: every block of DAA second sec pays block_subsidy(sec * bps); sum per DAA score + let mut ramp_sum: u128 = 0; + for sec in 0..s.ramp_seconds { + let a = t.block_subsidy(sec * bps, bps); + let b = t.block_subsidy(sec * bps + bps - 1, bps); + assert_eq!(a, b, "constant inside a DAA second"); + ramp_sum += a * bps as u128; + } + let first = t.block_subsidy(0, bps); + let last_ramp = t.block_subsidy(s.ramp_seconds * bps - 1, bps); + println!("{{\"network\":\"{name}\",\"phase\":\"ramp\",\"start_daa\":0,\"end_daa\":{},\"first_block\":\"{first}\",\"last_block\":\"{last_ramp}\",\"sum\":\"{ramp_sum}\"}}", s.ramp_seconds * bps); + let mut total = ramp_sum; + let mut k: u64 = 0; + loop { + let start = if k == 0 { s.ramp_seconds * bps } else { k * s.step_seconds * bps }; + let end = (k + 1) * s.step_seconds * bps; + let per = t.block_subsidy(start, bps); + let per_end = t.block_subsidy(end - 1, bps); + assert_eq!(per, per_end, "constant inside step {k}"); + let n = (end - start) as u128; + total += per * n; + println!("{{\"network\":\"{name}\",\"phase\":\"step\",\"k\":{k},\"start_daa\":{start},\"end_daa\":{end},\"per_block\":\"{per}\",\"rate_per_second\":\"{}\",\"sum\":\"{}\"}}", t.rate_at(start / bps), per * n); + if per == 0 { + // the terminal: zero at every later DAA score the table can reach + for probe in [end, end * 2, u64::MAX / bps * bps - 1, u64::MAX] { + assert_eq!(t.block_subsidy(probe, bps), 0); + } + println!("{{\"network\":\"{name}\",\"phase\":\"terminal\",\"start_daa\":{start},\"per_block\":\"0\"}}"); + break; + } + k += 1; + } + let cap = kaspa_consensus_core::igneum::supply_cap_units(p.base_unit_decimals); + println!("{{\"network\":\"{name}\",\"total\":\"{total}\",\"cap\":\"{cap}\",\"shortfall\":\"{}\"}}", cap - total); +} + +fn main() { + dump("mainnet", &MAINNET_PARAMS); + dump("devnet-4", &devnet4_params()); + dump("testnet-1", &TESTNET_PARAMS); +} diff --git a/tools/token-value/tv-01/gen-spec.py b/tools/token-value/tv-01/gen-spec.py new file mode 100644 index 000000000..c3ec7b7ae --- /dev/null +++ b/tools/token-value/tv-01/gen-spec.py @@ -0,0 +1,573 @@ +#!/usr/bin/env python3 +"""TV-01 (Igneum 2.0 Token Value, Phase 0): generates the machine-readable supply spec. + +Reads nothing it does not cite. The schedule constants are transcribed from the node fork's consensus crate and every +transcription is anchored: for each cited line the generator reads the file at each named revision (git show) and +refuses to write the spec when the line no longer carries its anchor text. The schedule is then computed here in exact +integers (Python ints), and, when --code-dump is given, compared field by field with the dump the consensus code's own +functions printed on the build box (tools/token-value/tv-01/code-dump/tv01_schedule.rs), so every figure in the spec is +one the code agrees with. + + python3 tools/token-value/tv-01/gen-spec.py \ + --node-git \ + --igneum-root . --igneum-rev 6824d49cc32def4463b643293a5622545618f7a9 \ + --code-dump tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl \ + --out docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json \ + --md docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md + +Deterministic: the same inputs give the same bytes. Nothing here activates anything. +""" +import argparse +import hashlib +import json +import subprocess +import sys + +NODE_REVS = { + "release-2.0.2-node": "a284380bdd62d5d0d972cb38f2cbd1daeedda69d", + "release-2.0.3-node-k6": "bf60948a01467bb54eaab9e50d32b91058f11242", +} + +# ---- The constants, as the code writes them (each one anchored below) ------------------------------------------- +YEAR_SECONDS = 31_557_600 # emission.rs:32 +LAUNCH_RATE_8 = 3_168_808_781 # emission.rs:104 (10^17 / 31,557,600 floored) +RAMP_SECONDS = 2_592_000 # emission.rs:105 +RAMP_START_PERCENT = 10 # emission.rs:106 +STEP_SECONDS = 2 * YEAR_SECONDS # emission.rs:107 +STEP_DECAY_Q32 = 1 << 31 # emission.rs:108 +DECIMALS = 18 # unit.rs:38, params.rs:1816 +UNIT = 10 ** DECIMALS +LAUNCH_RATE = LAUNCH_RATE_8 * 10 ** (DECIMALS - 8) # emission.rs rescaled(8, 18); params.rs:1858 +CAP = 4_000_000_000 * UNIT # igneum.rs:154 supply_cap_units(18) +PROVING_POOL_SHARE_PERCENT = 20 # igneum.rs:44 +SIGNING_BONUS_BPS = 1_000 # params.rs:1810 +DEVELOPER_SHARE_PERCENT = 20 # exec config (pgas.rs:290 reads it) +MAINNET_BPS = 10 # params.rs:1789 BlockrateParams::new::<10>() +DEVNET4_BPS = 1 # params.rs:2208 (DEVNET_PARAMS, inherited by devnet3/devnet4) +MAINNET_GENESIS_DAA_COMPILED = 1_312_860 # genesis.rs:92 + +# (id, repo, path, line, anchor text the line must contain) +CITATIONS = [ + ("year", "node", "consensus/core/src/emission.rs", 32, "pub const YEAR_SECONDS: u64 = 31_557_600;"), + ("current", "node", "consensus/core/src/emission.rs", 103, "pub const CURRENT: EmissionSchedule"), + ("launch_rate", "node", "consensus/core/src/emission.rs", 104, "launch_rate: 3_168_808_781"), + ("ramp_seconds", "node", "consensus/core/src/emission.rs", 105, "ramp_seconds: 2_592_000"), + ("ramp_start", "node", "consensus/core/src/emission.rs", 106, "ramp_start_percent: 10"), + ("step_seconds", "node", "consensus/core/src/emission.rs", 107, "step_seconds: 2 * YEAR_SECONDS"), + ("decay", "node", "consensus/core/src/emission.rs", 108, "step_decay_q32: 1 << 31"), + ("tail_cap", "node", "consensus/core/src/emission.rs", 109, "tail: Tail::Cap"), + ("testnet1", "node", "consensus/core/src/emission.rs", 116, "pub const TESTNET_1: EmissionSchedule"), + ("testnet1_tail", "node", "consensus/core/src/emission.rs", 122, "tail: Tail::Percent { bps_per_year: 100 }"), + ("decay_rule", "node", "consensus/core/src/emission.rs", 225, "rate = rate.saturating_mul(s.step_decay_q32 as u128) >> 32;"), + ("ramp_fn", "node", "consensus/core/src/emission.rs", 316, "pub fn ramp(&self, full: u128, seconds: u64) -> u128"), + ("block_subsidy", "node", "consensus/core/src/emission.rs", 329, "pub fn block_subsidy(&self, daa_score: u64, bps: u64) -> u128"), + ("per_block_div", "node", "consensus/core/src/emission.rs", 331, "let per_block = self.rate_at(seconds) / bps as u128;"), + ("cap_const", "node", "consensus/core/src/igneum.rs", 37, "pub const SUPPLY_CAP_SOMPI: u64 = 4_000_000_000 * SOMPI_PER_KASPA;"), + ("pool_share", "node", "consensus/core/src/igneum.rs", 44, "pub const PROVING_POOL_SHARE_PERCENT: u64 = 20;"), + ("silent_split", "node", "consensus/core/src/igneum.rs", 110, "pub fn silent_split_units("), + ("cap_units", "node", "consensus/core/src/igneum.rs", 154, "pub const fn supply_cap_units(decimals: u8) -> u128"), + ("pool_units", "node", "consensus/core/src/igneum.rs", 201, "pub fn proving_pool_share_units("), + ("producer_units", "node", "consensus/core/src/igneum.rs", 206, "pub fn producer_share_units("), + ("pool_script", "node", "consensus/core/src/igneum.rs", 213, "pub fn proving_pool_script_public_key()"), + ("evm_decimals", "node", "consensus/core/src/unit.rs", 38, "pub const EVM_DECIMALS: u8 = 18;"), + ("mainnet_deflationary", "node", "consensus/core/src/config/params.rs", 1783, "deflationary_phase_daa_score: 15778800 - 259200,"), + ("mainnet_pre_deflationary", "node", "consensus/core/src/config/params.rs", 1784, "pre_deflationary_phase_base_subsidy: 50000000000,"), + ("mainnet_bps", "node", "consensus/core/src/config/params.rs", 1789, "blockrate: BlockrateParams::new::<10>(),"), + ("mainnet_bonus_off", "node", "consensus/core/src/config/params.rs", 1809, "signing_bonus_activation_daa: u64::MAX,"), + ("mainnet_bonus_bps", "node", "consensus/core/src/config/params.rs", 1810, "signing_bonus_bps: 1_000,"), + ("mainnet_n8_off", "node", "consensus/core/src/config/params.rs", 1812, "subsidy_per_block_activation_daa: u64::MAX,"), + ("mainnet_decimals", "node", "consensus/core/src/config/params.rs", 1816, "base_unit_decimals: crate::unit::EVM_DECIMALS"), + ("mainnet_pool_split_off", "node", "consensus/core/src/config/params.rs", 1851, "pool_split_activation_daa: u64::MAX,"), + ("mainnet_emission", "node", "consensus/core/src/config/params.rs", 1858, "emission: EmissionSchedule::CURRENT.rescaled(crate::unit::DEVNET_DECIMALS, crate::unit::EVM_DECIMALS),"), + ("testnet_bonus_on", "node", "consensus/core/src/config/params.rs", 1955, "signing_bonus_activation_daa: 0,"), + ("testnet_n8_on", "node", "consensus/core/src/config/params.rs", 1960, "subsidy_per_block_activation_daa: 0,"), + ("testnet_emission", "node", "consensus/core/src/config/params.rs", 2014, "emission: EmissionSchedule::TESTNET_1.rescaled("), + ("devnet_bps", "node", "consensus/core/src/config/params.rs", 2208, "blockrate: BlockrateParams::new::<1>(),"), + ("devnet_n8_off", "node", "consensus/core/src/config/params.rs", 2232, "subsidy_per_block_activation_daa: u64::MAX,"), + ("devnet_pool_split_off", "node", "consensus/core/src/config/params.rs", 2270, "pool_split_activation_daa: u64::MAX,"), + ("devnet4_fn", "node", "consensus/core/src/config/params.rs", 2342, "pub fn devnet4_params() -> Params {"), + ("devnet4_emission", "node", "consensus/core/src/config/params.rs", 2372, "emission: EmissionSchedule::CURRENT.rescaled(crate::unit::DEVNET_DECIMALS, crate::unit::EVM_DECIMALS),"), + ("mainnet_genesis_daa", "node", "consensus/core/src/config/genesis.rs", 92, "daa_score: 1312860, // Checkpoint DAA score"), + ("genesis_no_outputs", "node", "consensus/core/src/config/genesis.rs", 26, "Transaction::new(0, Vec::new(), Vec::new(), 0, SUBNETWORK_ID_COINBASE"), + ("cb_header", "node", "consensus/src/processes/coinbase.rs", 31, "Kaspa's pre-deflationary phase, 426-month table and Crescendo `bps_history` rescaling are gone."), + ("cb_split", "node", "consensus/src/processes/coinbase.rs", 106, "fn producer_and_pool(&self, daa_score: u64, reward: &BlockRewardData)"), + ("cb_reds", "node", "consensus/src/processes/coinbase.rs", 152, "for red in ghostdag_data.mergeset_reds.iter()"), + ("cb_pool_output", "node", "consensus/src/processes/coinbase.rs", 163, "outputs.push(TransactionOutput::new_units(proving_pool_reward, igneum::proving_pool_script_public_key()));"), + ("cb_calc", "node", "consensus/src/processes/coinbase.rs", 262, "pub fn calc_block_subsidy(&self, daa_score: u64) -> Amount"), + ("cb_calc_body", "node", "consensus/src/processes/coinbase.rs", 265, "Amount(self.emission.block_subsidy(daa_score, self.bps))"), + ("utxo_retired", "node", "consensus/src/pipeline/body_processor/body_validation_in_isolation.rs", 54, "RuleError::UtxoTransactionsRetired"), + ("exec_blue_only", "node", "igneum/exec/src/executor.rs", 181, "for b in blocks.iter().filter(|b| b.is_blue)"), + ("exec_split", "node", "igneum/exec/src/executor.rs", 189, "silent_split_units(subsidy, b.silent, env.signing_bonus_bps)"), + ("exec_miner_credit", "node", "igneum/exec/src/executor.rs", 208, "db.add_balance(b.miner, producer_wei);"), + ("exec_pool_credit", "node", "igneum/exec/src/executor.rs", 211, "db.add_balance(PROVING_POOL_ADDRESS, pool_wei);"), + ("exec_tip_miner", "node", "igneum/exec/src/executor.rs", 362, "let tip_total = (gas_used as u128) * (price - env.base_fee_exec);"), + ("exec_dev_shares", "node", "igneum/exec/src/pgas.rs", 290, "pub fn developer_shares("), + ("pool_address", "node", "igneum/exec/src/config.rs", 59, "pub const PROVING_POOL_ADDRESS: Address"), + ("override_refused", "node", "kaspad/src/daemon.rs", None, "if network.params_compiled() {"), + ("params_compiled", "node", "consensus/core/src/network.rs", 266, "NetworkType::Mainnet | NetworkType::Testnet => true,"), + ("lp_cap", "igneum", "site/litepaper.html", 578, "Hard cap of 4 billion IGN, approached and never reached, because emission starts at 1 billion a year and halves every two years for ever."), + ("lp_caption", "igneum", "site/litepaper.html", 602, "3,938M IGN in the first 12 years of a 4,000M cap."), + ("lp_proving_pool", "igneum", "site/litepaper.html", 609, "is burned there"), + ("lp_tail", "igneum", "site/litepaper.html", 630, "One rule sits beside the cap. If external proving revenue is under one fifth of the block subsidy over any 90-day window after year 5, the question of a tail reward goes to the miners' signalling vote. The protocol never changes emission by itself."), + ("lp_no_tail", "igneum", "site/litepaper.html", 630, "The cap stays at 4 billion. There is no tail emission."), + ("lp_block_rate", "igneum", "site/litepaper.html", 542, "one block a second at launch with scheduled steps to four and ten"), + ("lp_unit", "igneum", "site/litepaper.html", 568, "the base unit is Open"), + ("lp_emission_route", "igneum", "site/litepaper.html", 620, "80% the block's miner, 20% the proving pool for the provers of that block"), + ("lp_tip_route", "igneum", "site/litepaper.html", 622, "80% the block's miner and provers; 20% the apps whose code ran, per call frame"), + ("lp_external_job", "igneum", "site/litepaper.html", 624, "90% the provers who delivered"), + ("lp_dev_fee", "igneum", "site/litepaper.html", 625, "1 block template in 100 requested with the dev address"), + ("lp_pool_custody", "igneum", "site/litepaper.html", 647, "The reference pool never holds a member"), + ("lp_per_block_figure", "igneum", "site/litepaper.html", 668, "31.688 IGN a block, 86,400 blocks a day"), + ("lp_mainnet_launch", "igneum", "site/litepaper.html", 775, "Genesis with no premine, 30-day ramp."), + ("pool_payout_doc", "igneum", "pool/src/payout.rs", 3, "so the pool receives"), + ("pool_fee_default", "igneum", "pool/src/config.rs", 58, "fee_percent: 1.0,"), + ("pool_distribute", "igneum", "pool/src/pplns.rs", 76, "pub fn distribute(reward_wei: u128, fee_percent: f64, payees: &[Payee])"), +] + + +def sh(cmd, cwd): + return subprocess.run(cmd, cwd=cwd, check=True, capture_output=True).stdout + + +def read_node(node_git, rev, path): + return sh(["git", "show", f"{rev}:{path}"], node_git) + + +def read_igneum(root, rev, path): + return sh(["git", "show", f"{rev}:{path}"], root) + + +def anchor(citations, node_git, igneum_root, igneum_rev): + """Every cited line must carry its anchor at every named revision; returns the resolved citation table and the + sha256 of every cited file per revision.""" + out, files = [], {} + for cid, repo, path, line, text in citations: + revs = NODE_REVS.items() if repo == "node" else [("igneum master", igneum_rev)] + lines_at = {} + for name, rev in revs: + blob = read_node(node_git, rev, path) if repo == "node" else read_igneum(igneum_root, rev, path) + files.setdefault(f"{repo}:{path}", {})[name] = hashlib.sha256(blob).hexdigest() + src = blob.decode("utf-8").split("\n") + if line is None: + hits = [i + 1 for i, l in enumerate(src) if text in l] + if len(hits) != 1: + sys.exit(f"anchor {cid}: {path} at {name} has {len(hits)} lines carrying {text!r}") + lines_at[name] = hits[0] + else: + if line > len(src) or text not in src[line - 1]: + sys.exit(f"anchor {cid}: {path}:{line} at {name} no longer carries {text!r}") + lines_at[name] = line + ref_lines = sorted(set(lines_at.values())) + out.append({"id": cid, "repo": repo, "path": path, "line": ref_lines[0] if len(ref_lines) == 1 else lines_at, + "anchor": text}) + return out, files + + +# ---- The schedule, in exact integers --------------------------------------------------------------------------- +def step_rate(k): + r = LAUNCH_RATE + for _ in range(k): + r = (r * STEP_DECAY_Q32) >> 32 + return r + + +def ramp(full, seconds): + if seconds >= RAMP_SECONDS: + return full + num = RAMP_START_PERCENT * RAMP_SECONDS + (100 - RAMP_START_PERCENT) * seconds + return full * num // (100 * RAMP_SECONDS) + + +def block_subsidy(daa, bps): + seconds = daa // bps + return ramp(step_rate(seconds // STEP_SECONDS) // bps, seconds) + + +def ramp_sum(bps, upto_seconds=RAMP_SECONDS): + full = LAUNCH_RATE // bps + return sum(ramp(full, s) for s in range(min(upto_seconds, RAMP_SECONDS))) * bps + + +def schedule(bps): + phases = [] + rs = ramp_sum(bps) + phases.append({ + "index": 0, "kind": "ramp", "rule": "launch-ramp", + "start_daa_score": 0, "end_daa_score": RAMP_SECONDS * bps, + "per_block_subsidy": None, + "ramp": {"full_per_block": str(LAUNCH_RATE // bps), "start_percent": RAMP_START_PERCENT, + "ramp_seconds": RAMP_SECONDS, "first_block": str(block_subsidy(0, bps)), + "last_block": str(block_subsidy(RAMP_SECONDS * bps - 1, bps)), + "formula": "per_block(d) = floor(full_per_block x (start_percent x ramp_seconds + (100 - start_percent) x s) / (100 x ramp_seconds)), s = floor(d / bps)"}, + "phase_sum": str(rs), + "source": ["consensus/core/src/emission.rs:316", "consensus/core/src/emission.rs:329"], + }) + k = 0 + while True: + start = RAMP_SECONDS * bps if k == 0 else k * STEP_SECONDS * bps + end = (k + 1) * STEP_SECONDS * bps + per = block_subsidy(start, bps) + assert per == block_subsidy(end - 1, bps) + if per == 0: + phases.append({ + "index": len(phases), "kind": "terminal", "rule": "terminal-zero", + "start_daa_score": start, "end_daa_score": None, "per_block_subsidy": "0", + "decay_step": k, "rate_per_second": str(step_rate(k)), "phase_sum": "0", + "source": ["consensus/core/src/emission.rs:109", "consensus/core/src/emission.rs:225", "consensus/core/src/emission.rs:331"], + }) + break + phases.append({ + "index": len(phases), "kind": "constant", + "rule": "full-rate-after-ramp" if k == 0 else f"halving-{k}", + "start_daa_score": start, "end_daa_score": end, "per_block_subsidy": str(per), + "decay_step": k, "rate_per_second": str(step_rate(k)), "phase_sum": str(per * (end - start)), + "source": ["consensus/core/src/emission.rs:107", "consensus/core/src/emission.rs:225", "consensus/core/src/emission.rs:331"], + }) + k += 1 + total = sum(int(p["phase_sum"]) for p in phases) + return phases, total + + +def supply_at(phases, daa, bps): + """Scheduled issuance over DAA scores [0, daa): one subsidy per DAA score (the replay model).""" + s = 0 + for p in phases: + a, b = p["start_daa_score"], p["end_daa_score"] + if daa <= a: + break + if p["kind"] == "ramp": + hi = min(daa, b) + full_secs, rem = divmod(hi, bps) + s += ramp_sum(bps, full_secs) + rem * block_subsidy(full_secs * bps, bps) + elif p["kind"] == "constant": + s += int(p["per_block_subsidy"]) * (min(daa, b) - a) + return s + + +def bps_step_overshoot(step_daa, from_bps, to_bps, total_to): + """The code keys time as daa / bps with ONE bps per network (coinbase.rs:31, :265). If the block rate stepped on a + live chain at `step_daa` with no history rule, every block from the step on would be paid as if the clock read + daa / to_bps: issuance = sum_{d < step} sub_from(d) + sum_{d >= step} sub_to(d).""" + ph_from, _ = schedule(from_bps) + ph_to, _ = schedule(to_bps) + issued = supply_at(ph_from, step_daa, from_bps) + (total_to - supply_at(ph_to, step_daa, to_bps)) + return issued + + +def coins(x): + q, r = divmod(x, UNIT) + return f"{q:,}.{r:018d}".rstrip("0").rstrip(".") + + +# ---- The conflicts and the texts ------------------------------------------------------------------------------ +TAIL_SENTENCE = ("One rule sits beside the cap. If external proving revenue is under one fifth of the block subsidy over " + "any 90-day window after year 5, the question of a tail reward goes to the miners' signalling vote. " + "The protocol never changes emission by itself.") + + +def conflicts(fig): + m, d = fig["mainnet"], fig["devnet-4"] + return [ + {"id": "C01", "topic": "cap versus the year-five tail vote", + "litepaper": {"line": 630, "words": TAIL_SENTENCE}, + "code": {"where": "consensus/core/src/emission.rs:109 (Tail::Cap in EmissionSchedule::CURRENT); consensus/core/src/config/params.rs:1858 (mainnet carries CURRENT); kaspad/src/daemon.rs (params_compiled refuses an override file on mainnet and testnet); no signal path reads or writes the emission schedule", + "value": "tail = cap; the schedule is a compiled genesis parameter; no vote, no revenue trigger, no tail"}, + "spec_takes": "the cap as the invariant; the tail-vote sentence is SUPERSEDED", + "why": "D01 (capped issuance, subject to TV-04), ratified by the founder 9 October 2026 09:2x UK; VR-01 (no unexplained tail escape, no price- or revenue-triggered issuance); the code already enforces only the capped schedule. If TV-04 shows capped security cannot pass, the volume's rule applies: delay launch or openly choose another design, never an undisclosed tail.", + "status": "RESOLVED (by D01, subject to TV-04)", + "follow_up": "the litepaper sentence is removed or replaced by the site lane; this landing does not edit site/"}, + {"id": "C02", "topic": "year-one emission and the twelve-year figure", + "litepaper": {"line": 578, "words": "emission starts at 1 billion a year", "line_2": 602, "words_2": "3,938M IGN in the first 12 years of a 4,000M cap."}, + "code": {"where": "consensus/core/src/emission.rs:104 to 106 (launch rate floored, the 30-day ramp from 10%)", + "value": f"year 1 issues {coins(m['year_1'])} IGN at 10 blocks/s ({coins(d['year_1'])} at 1 block/s); the first 12 years issue {coins(m['year_12'])} IGN"}, + "spec_takes": "the code's figures", + "why": "the litepaper chart and caption ignore the launch ramp's withheld 36,961,000 IGN and the floor of the per-second rate; the spec carries what nodes pay", + "status": "RESOLVED (text correction owed by the site lane)"}, + {"id": "C03", "topic": "terminal behaviour", + "litepaper": {"line": 578, "words": "halves every two years for ever"}, + "code": {"where": "consensus/core/src/emission.rs:225 (rate[k+1] = rate[k] x 2^31 >> 32, floored), :331 (per block = rate / bps, floored)", + "value": f"the per-block subsidy floors to zero at decay step {m['terminal_step']} (DAA score {m['terminal_daa']:,}, the start of year {m['terminal_year'] + 1}) at 10 blocks/s and at step {d['terminal_step']} (DAA score {d['terminal_daa']:,}, the start of year {d['terminal_year'] + 1}) at 1 block/s; zero for ever after"}, + "spec_takes": "the code: a finite schedule ending in a terminal zero phase", + "why": "integer halving ends; the last subsidy is 1 base unit (10^-18 IGN) per block; after it the security budget is fees alone (TV-04's question)", + "status": "RESOLVED (text correction owed: 'until the subsidy floors to zero')"}, + {"id": "C04", "topic": "block rate at launch and its steps", + "litepaper": {"line": 542, "words": "one block a second at launch with scheduled steps to four and ten", "line_2": 668, "words_2": "31.688 IGN a block, 86,400 blocks a day"}, + "code": {"where": "consensus/core/src/config/params.rs:1789 (mainnet BlockrateParams::new::<10>()); consensus/src/processes/coinbase.rs:31 (the bps_history rescaling is gone) and :265 (subsidy keyed on daa / ONE bps)", + "value": f"mainnet as compiled runs 10 blocks/s from genesis, 3.1688087810 IGN a block; the subsidy has no block-rate history, so a step from 1 to 10 blocks/s at DAA score {fig['bps_step']['step_daa']:,} (one year in) would issue {coins(fig['bps_step']['issued'])} IGN in all, an overshoot of {coins(fig['bps_step']['issued'] - CAP)} IGN over the cap"}, + "spec_takes": "both profiles (10 blocks/s as compiled; 1 block/s as Devnet 4), identical per second; NO live block-rate step is cap-safe under the current code", + "why": "a block-rate step re-keys the schedule clock (seconds = daa / bps) and re-pays elapsed time", + "status": "BLOCKED", + "question": "For the founder: does mainnet launch at the compiled 10 blocks a second (the litepaper's 1-to-4-to-10 step plan removed), or at 1 with steps (then the node lane adds a block-rate-history subsidy rule before genesis, and TV-02 replays a step)? Default if unanswered: the spec freezes the compiled 10 blocks a second and the step plan is treated as text to correct."}, + {"id": "C05", "topic": "unit", + "litepaper": {"line": 568, "words": "the base unit is Open"}, + "code": {"where": "consensus/core/src/unit.rs:38; consensus/core/src/config/params.rs:1816", + "value": "18 decimals on mainnet, testnet and Devnet 4: 1 IGN = 10^18 base units; one base unit is one EVM wei"}, + "spec_takes": "18 decimals", + "why": "decided 6 October 2026 (O-2.6) and compiled; the cap is 4 x 10^27 base units", + "status": "RESOLVED (text correction owed)"}, + {"id": "C06", "topic": "where the 20% proving share goes, and which ledger is native supply", + "litepaper": {"line": 609, "words": "the coinbase's 20% output goes to an unspendable script tagged igneum-proving-pool-v0 and is burned there. Provers are paid from a separate escrow in the execution state"}, + "code": {"where": "consensus/src/processes/coinbase.rs:163 (UTXO pool output to the OP_RETURN script); consensus/src/pipeline/body_processor/body_validation_in_isolation.rs:54 (no UTXO transaction but the coinbase: UTXO outputs can never be spent); igneum/exec/src/executor.rs:181 to 211 (the EVM state credits 80% to the producer and 20% to the escrow 0x...0220 for every blue block)", + "value": "two ledgers record each subsidy: the UTXO coinbase (unspendable by rule) and the EVM state (spendable)"}, + "spec_takes": "native supply is the EVM state's credit; the UTXO coinbase is a non-spendable shadow record and its OP_RETURN 20% is NOT a burn of native supply; the escrow balance is issued and locked until paid per proof", + "why": "counting both ledgers doubles supply; counting the OP_RETURN as burned and the escrow as issued would understate and overstate at once (VR-02)", + "status": "RESOLVED (for the spec; TV-02 reconciles both ledgers to this rule)"}, + {"id": "C07", "topic": "red blocks", + "litepaper": {"line": 620, "words": "80% the block's miner, 20% the proving pool for the provers of that block"}, + "code": {"where": "consensus/src/processes/coinbase.rs:152 to 159 (the UTXO coinbase pays a DAA red block's subsidy to the merging miner and the pool); igneum/exec/src/executor.rs:181 (the EVM credits blue blocks only)", + "value": "a red block's subsidy exists on the shadow ledger and not in native supply"}, + "spec_takes": "native issuance = the blue blocks' subsidies; the per-DAA-score schedule sum is the upper bound", + "why": "the EVM ledger is native supply (C06); the bound keeps the cap invariant whatever the red share", + "status": "RESOLVED (TV-02 measures the red share; no text change needed beyond 'blue block')"}, + {"id": "C08", "topic": "which DAA score a merged block is credited at", + "litepaper": {"line": None, "words": "(silent)"}, + "code": {"where": "consensus/core/src/config/params.rs:1812 (mainnet subsidy_per_block_activation_daa = never), :2232 (Devnet 4 inherits never), :1960 (testnet-1: 0)", + "value": "on mainnet and Devnet 4 the EVM credits a merged block the merging chain block's subsidy while the UTXO coinbase pays the block's own: one ramp second high per merged block, a whole halving step at a boundary"}, + "spec_takes": "each block's own DAA score (ledger N8) as the normative rule", + "why": "exact integer agreement between ledgers and replays (TV-02) needs one rule; N8 is the one the testnet object already carries", + "status": "BLOCKED", + "question": "For the founder (the node lane owns the change): set subsidy_per_block_activation_daa = 0 in the mainnet object, as testnet-1 has it? Default if unanswered: the spec keeps N8 as normative and TV-02 reports the chain-block reading as a measured divergence."}, + {"id": "C09", "topic": "the mainnet genesis clock", + "litepaper": {"line": 775, "words": "Genesis with no premine, 30-day ramp."}, + "code": {"where": "consensus/core/src/config/genesis.rs:92 (mainnet GENESIS daa_score 1312860, Kaspa's 2021 timestamp); the subsidy reads the absolute DAA score (emission.rs:329)", + "value": f"as compiled, the first mainnet block would sit 131,286 DAA seconds into the ramp (paying {fig['genesis_offset']['first_ramp_percent']} of full rate, not 10%) and the whole schedule would run 1.52 days early; issuance would be {coins(fig['genesis_offset']['issuance_lost'])} IGN lower"}, + "spec_takes": "genesis at DAA score 0 (as Devnet 4 and testnet-1); the activation clocks below are counted from it", + "why": "the mainnet genesis is a Kaspa placeholder not yet cut; every Igneum network so far starts at DAA score 0", + "status": "BLOCKED", + "question": "For the founder: confirm the mainnet genesis is cut at DAA score 0. Default if unanswered: the spec freezes DAA score 0 and the genesis cut is checked against it."}, + {"id": "C10", "topic": "priority fee to provers", + "litepaper": {"line": 622, "words": "80% the block's miner and provers; 20% the apps whose code ran, per call frame"}, + "code": {"where": "igneum/exec/src/executor.rs:362 to 371 (the tip less the developer share is credited to the block's miner alone); igneum/exec/src/pgas.rs:290 (20% developer share, unregistered parts burned)", + "value": "80% to the block's miner, none to provers; 20% to registered payees or burned"}, + "spec_takes": "the code", + "why": "fees move existing supply and burn some; they never create supply", + "status": "RESOLVED (text correction owed: drop 'and provers')"}, + {"id": "C11", "topic": "the reference pool's custody and fee", + "litepaper": {"line": 647, "words": "The reference pool never holds a member’s balance. Payouts come straight from the coinbase split"}, + "code": {"where": "pool/src/payout.rs:1 to 6 (the pool's address receives the 80% and pays members by transfers); pool/src/config.rs:58 (fee_percent 1.0); pool/src/pplns.rs:76; consensus/core/src/config/params.rs:1851 (mainnet pool_split_activation_daa = never: the IGNW coinbase split is off)", + "value": "the reference pool holds members' balances between rounds and keeps a 1% fee; the consensus split that would make the sentence true is off on mainnet and Devnet 4"}, + "spec_takes": "the code (custodial PPLNS, 1% default fee, no supply effect)", + "why": "the payout route moves issued supply; it creates none", + "status": "BLOCKED", + "question": "For the founder: activate the IGNW coinbase split at mainnet genesis (pool_split_activation_daa = 0) so the litepaper sentence holds, or change the sentence to the custodial PPLNS pool with its 1% fee? Default if unanswered: the sentence is corrected to the code."}, + {"id": "C12", "topic": "a tail schedule compiled into testnet-1", + "litepaper": {"line": 630, "words": "The cap stays at 4 billion. There is no tail emission."}, + "code": {"where": "consensus/core/src/emission.rs:116 to 122 (TESTNET_1: 100 IGN a block, monthly glide, a 1% a year tail); consensus/core/src/config/params.rs:2014 (testnet-1 carries it)", + "value": "the held testnet-1 object issues without a cap"}, + "spec_takes": "not the monetary contract: the contract is EmissionSchedule::CURRENT with Tail::Cap", + "why": "D01; the testnet is held and superseded by 2.0; a revived testnet carries CURRENT or is labelled a different supply proposition", + "status": "RESOLVED (by D01)"}, + {"id": "C13", "topic": "dead Kaspa subsidy fields in the mainnet object", + "litepaper": {"line": None, "words": "(silent)"}, + "code": {"where": "consensus/core/src/config/params.rs:1783 to 1784 (deflationary_phase_daa_score 15,519,600; pre_deflationary_phase_base_subsidy 50,000,000,000)", + "value": "read only into the consensus digest; the subsidy never reads them (coinbase.rs:31)"}, + "spec_takes": "ignored: they are not part of the monetary contract", + "why": "a reader grepping 'deflationary' would otherwise find a second schedule that does not exist", + "status": "RESOLVED (the node lane may annotate or remove them at the genesis cut)"}, + ] + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--node-git", required=True) + ap.add_argument("--igneum-root", required=True) + ap.add_argument("--igneum-rev", default="HEAD") + ap.add_argument("--code-dump") + ap.add_argument("--out", required=True) + ap.add_argument("--md") + a = ap.parse_args() + + igneum_rev = sh(["git", "rev-parse", a.igneum_rev], a.igneum_root).decode().strip() + cites, files = anchor(CITATIONS, a.node_git, a.igneum_root, igneum_rev) + + profiles, fig = [], {} + for name, network, bps in [("mainnet", "mainnet (MAINNET_PARAMS as compiled)", MAINNET_BPS), ("devnet-4", "Devnet 4 (devnet4_params, 1 block a second, the litepaper's launch rate)", DEVNET4_BPS)]: + phases, total = schedule(bps) + term = phases[-1] + f = { + "year_1": supply_at(phases, YEAR_SECONDS * bps, bps), + "year_12": supply_at(phases, 12 * YEAR_SECONDS * bps, bps), + "terminal_step": term["decay_step"], "terminal_daa": term["start_daa_score"], + "terminal_year": term["start_daa_score"] // bps // YEAR_SECONDS, + "total": total, + } + fig[name] = f + years = {} + for y in [1, 2, 5, 10, 12, 20, 50, 100]: + years[str(y)] = str(supply_at(phases, y * YEAR_SECONDS * bps, bps)) + last_nonzero = phases[-2] + profiles.append({ + "profile": name, "network": network, "bps": bps, "decimals": DECIMALS, + "genesis_daa_score": 0, + "time_key": "seconds = floor(daa_score / bps); the per-block subsidy is constant inside a DAA second", + "phases": phases, + "phase_count": len(phases), + "last_nonzero_phase": {"index": last_nonzero["index"], "per_block_subsidy": last_nonzero["per_block_subsidy"], + "start_daa_score": last_nonzero["start_daa_score"], "end_daa_score": last_nonzero["end_daa_score"]}, + "terminal": {"start_daa_score": term["start_daa_score"], "decay_step": term["decay_step"], + "start_year": f["terminal_year"], "behaviour": "per-block subsidy 0 at every later DAA score; the cap is never reached; blocks are paid by fees alone"}, + "total_issuance": str(total), + "cap_shortfall": str(CAP - total), + "scheduled_supply_at_year_end": years, + }) + + # the block-rate step demonstration (C04) and the compiled genesis offset (C09) + step_daa = YEAR_SECONDS * DEVNET4_BPS + fig["bps_step"] = {"step_daa": step_daa, "issued": bps_step_overshoot(step_daa, 1, 10, fig["mainnet"]["total"])} + ph10, _ = schedule(MAINNET_BPS) + off = MAINNET_GENESIS_DAA_COMPILED + s0 = off // MAINNET_BPS + num = RAMP_START_PERCENT * RAMP_SECONDS + (100 - RAMP_START_PERCENT) * s0 + fig["genesis_offset"] = {"issuance_lost": supply_at(ph10, off, MAINNET_BPS), + "first_ramp_percent": f"{num * 100 // (100 * RAMP_SECONDS)}.{(num * 10000 // (100 * RAMP_SECONDS)) % 100:02d}%"} + + dump_check = None + if a.code_dump: + raw = open(a.code_dump, "rb").read() + rows = [json.loads(l) for l in raw.decode().splitlines() if l.strip()] + n = 0 + for prof in profiles: + net = prof["profile"] + head = next(r for r in rows if r.get("network") == net and "bps" in r) + assert head["bps"] == prof["bps"] and head["decimals"] == DECIMALS and int(head["launch_rate"]) == LAUNCH_RATE + assert head["ramp_seconds"] == RAMP_SECONDS and head["step_seconds"] == STEP_SECONDS and head["step_decay_q32"] == STEP_DECAY_Q32 and head["tail"] == "cap" + rp = next(r for r in rows if r.get("network") == net and r.get("phase") == "ramp") + p0 = prof["phases"][0] + assert int(rp["sum"]) == int(p0["phase_sum"]) and rp["first_block"] == p0["ramp"]["first_block"] and rp["last_block"] == p0["ramp"]["last_block"] and rp["end_daa"] == p0["end_daa_score"] + steps = [r for r in rows if r.get("network") == net and r.get("phase") == "step"] + for r, p in zip(steps, prof["phases"][1:]): + assert r["start_daa"] == p["start_daa_score"], (net, r, p) + assert r["per_block"] == p["per_block_subsidy"], (net, r, p) + assert int(r["rate_per_second"]) == int(p["rate_per_second"]), (net, r, p) + if p["kind"] == "constant": + assert r["end_daa"] == p["end_daa_score"] and r["sum"] == p["phase_sum"], (net, r, p) + n += 1 + assert len(steps) == len(prof["phases"]) - 1, (net, len(steps), len(prof["phases"])) + tot = next(r for r in rows if r.get("network") == net and "total" in r) + assert int(tot["total"]) == int(prof["total_issuance"]) and int(tot["cap"]) == CAP and int(tot["shortfall"]) == int(prof["cap_shortfall"]) + assert head["genesis_daa_score"] == (MAINNET_GENESIS_DAA_COMPILED if net == "mainnet" else 0) + t1 = next(r for r in rows if r.get("network") == "testnet-1" and "bps" in r) + assert t1["tail"] == "percent:100" + dump_check = {"path": "tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl", "sha256": hashlib.sha256(raw).hexdigest(), + "program": "tools/token-value/tv-01/code-dump/tv01_schedule.rs", + "run": "build-9, release-2.0.3-node-k6 bf60948a: cargo run --release -p kaspa-consensus-core --example tv01_schedule (the program copied into consensus/core/examples of a scratch tree; the node fork is unchanged)", + "agreement": f"every field of {n} decay phases, both ramps and both totals equal to this generator's integers"} + + spec = { + "schema": { + "name": "igneum-supply-spec", "version": "1.0.0", + "integers": "every amount is a decimal string of base units (amounts reach 4 x 10^27, past JSON's exact range); DAA scores are JSON integers (all under 2^53)", + "phase_fields": {"index": "0-based, contiguous", "kind": "ramp | constant | terminal", "rule": "the rule's name", "start_daa_score": "inclusive", "end_daa_score": "exclusive; null for the terminal phase", "per_block_subsidy": "base units per block at every DAA score of the phase (null for the ramp, whose per-block value is its formula)", "phase_sum": "base units the phase issues at one subsidy per DAA score", "source": "file:line in the node fork at both named revisions"}, + "checker": "tools/token-value/tv-01/check-spec.py", + }, + "gate": "TV-01", "verdict": "NOT RUN", + "status": "Phase 0 document. Activates nothing; changes no node, no chain and no served page. The verdict stays NOT RUN until the independent panel reads it.", + "decision": {"id": "D01", "title": "Cap and tail", "ruling": "capped issuance, subject to TV-04 (the founder's ratification, 9 October 2026, 09:2x UK)", + "effect": "the cap is the invariant; the litepaper's year-five tail-vote sentence is superseded (C01)"}, + "invariant": {"rule": "sum over all blocks of the per-block subsidy <= cap, with no tail, no vote, no price or revenue trigger and no emergency mint", + "how_enforced": "by the schedule, not by a running counter: every block's coinbase must carry exactly the scheduled subsidy (coinbase.rs:262) and the EVM state credits by the same rule (executor.rs:181 to 211); the cap constant itself is only compared in tests (igneum.rs:154). The schedule sums to the cap minus the shortfall below, so the cap is never reached.", + "change_policy": "the schedule is a compiled genesis parameter on mainnet; the daemon refuses an override file there (kaspad/src/daemon.rs, network.rs:266); a change is a new release with a new consensus digest and an explicit incompatible-policy process (VR-06)"}, + "units": {"coin": "IGN", "base_unit": "wei (one consensus base unit is one EVM wei)", "decimals": DECIMALS, "base_units_per_coin": str(UNIT), + "source": ["consensus/core/src/unit.rs:38", "consensus/core/src/config/params.rs:1816"]}, + "cap": {"base_units": str(CAP), "coins": "4000000000", "kind": "invariant", + "source": ["consensus/core/src/igneum.rs:37", "consensus/core/src/igneum.rs:154"]}, + "rules": { + "launch_rate_per_second": str(LAUNCH_RATE), "launch_rate_note": "floor(10^9 x 10^8 / 31,557,600) = 3,168,808,781 at 8 decimals, times 10^10: 31.68808781 IGN a DAA second", + "year_seconds": YEAR_SECONDS, "ramp_seconds": RAMP_SECONDS, "ramp_start_percent": RAMP_START_PERCENT, + "step_seconds": STEP_SECONDS, "step_decay_q32": STEP_DECAY_Q32, "decay_rule": "rate[k+1] = floor(rate[k] x step_decay_q32 / 2^32) (a halving every two years)", + "per_block": "per_block(d) = ramp(floor(rate[floor(s / step_seconds)] / bps), s), s = floor(d / bps)", + "tail": "cap", + "source": ["consensus/core/src/emission.rs:103 to 110", "consensus/core/src/emission.rs:225", "consensus/core/src/emission.rs:316 to 333", "consensus/core/src/config/params.rs:1858"], + }, + "activation_clocks": { + "genesis": "DAA score 0 (C09: the compiled mainnet genesis still carries Kaspa's 1,312,860; BLOCKED)", + "ramp_end": {"mainnet": RAMP_SECONDS * MAINNET_BPS, "devnet-4": RAMP_SECONDS * DEVNET4_BPS, "seconds": RAMP_SECONDS}, + "halvings": "every 63,115,200 DAA seconds (2 years of 365.25 days): DAA score k x 631,152,000 at 10 blocks/s, k x 63,115,200 at 1 block/s", + "terminal": {"mainnet": fig["mainnet"]["terminal_daa"], "devnet-4": fig["devnet-4"]["terminal_daa"]}, + "signing_bonus": "mainnet never (params.rs:1809); testnet-1 from 0", + "pool_split": "mainnet never (params.rs:1851)", + "per_block_rule_N8": "mainnet never (params.rs:1812; C08 BLOCKED); testnet-1 from 0", + }, + "schedules": profiles, + "coinbase_split": [ + {"rule": "producer and proving pool", "producer": "subsidy - floor(subsidy x 20 / 100)", "proving_pool": "floor(subsidy x 20 / 100)", "source": ["consensus/core/src/igneum.rs:44", "consensus/core/src/igneum.rs:201 to 208"]}, + {"rule": "signing bonus", "effect": "a silent producer's share loses floor(share x 1,000 / 10,000) to the proving pool; nothing destroyed; fees untouched", "mainnet": "off (never)", "source": ["consensus/core/src/igneum.rs:110", "consensus/core/src/config/params.rs:1809 to 1810"]}, + {"rule": "native ledger", "effect": "the EVM state credits the producer share to the block's IGNA address (or by its IGNW split once pool_split is active) and the pool share to escrow 0x0000000000000000000000000000000000000220, blue blocks only", "source": ["igneum/exec/src/executor.rs:181 to 211", "igneum/exec/src/config.rs:59"]}, + {"rule": "shadow ledger", "effect": "the UTXO coinbase pays the producer outputs and one OP_RETURN pool output tagged igneum-proving-pool-v0; no UTXO transaction but the coinbase is valid, so none of it is ever spent", "source": ["consensus/src/processes/coinbase.rs:126 to 168", "consensus/src/pipeline/body_processor/body_validation_in_isolation.rs:54"]}, + {"rule": "pool", "effect": "none in consensus; the reference pool keeps fee_percent (default 1.0) of the 80% it receives and pays members PPLNS from its address", "source": ["pool/src/payout.rs:1 to 6", "pool/src/config.rs:58", "pool/src/pplns.rs:76"]}, + {"rule": "dev fee", "effect": "none in consensus; the miner software requests 1 template in 100 with the dev address (off with --dev-fee 0); it moves the producer share of those blocks, creates nothing", "source": ["site/litepaper.html:625"]}, + {"rule": "treasury, foundation, team, stake", "effect": "none: no output, credit or address in consensus", "source": ["consensus/src/processes/coinbase.rs:126 to 168", "igneum/exec/src/executor.rs:181 to 211"]}, + {"rule": "genesis", "effect": "the genesis coinbase has no outputs; its payload subsidy field is data; no premine", "source": ["consensus/core/src/config/genesis.rs:26"]}, + ], + "fees": [ + {"route": "execution base fee", "effect": "burned (not credited)", "source": "igneum/exec/src/executor.rs:319 to 321"}, + {"route": "proving charge", "effect": "burned", "source": "igneum/exec/src/executor.rs:319 to 321"}, + {"route": "priority fee", "effect": "80% to the block's miner; 20% to the payees registered for the frames' code by own gas, unregistered parts burned", "source": "igneum/exec/src/executor.rs:362 to 371; igneum/exec/src/pgas.rs:290"}, + {"route": "external job after the proof bridge", "effect": "not in code (Designed, phase two): 90% provers, 10% burned per the litepaper", "source": "site/litepaper.html:624"}, + ], + "supply_accounting": { + "issued": "the EVM state's subsidy credits (producer plus escrow), blue blocks only; bounded above by the schedule's per-DAA-score sum", + "burned": "execution base fees, proving charges and unregistered developer shares (EVM side); the UTXO OP_RETURN output is NOT counted as a burn (C06)", + "locked": "the proving escrow's balance: credited, not yet paid against a valid proof; unclaimed credit is stranded today (litepaper line 609, no rule returns it)", + "not_supply": "UTXO coinbase outputs (unspendable shadow record); any bridged or wrapped IGN on another chain (VR-02)", + "replay_model": "one scheduled subsidy per DAA score (the schedules above); a block is paid at its own DAA score, parallel blocks may share a score and a score may have no block, so the measured issuance differs from the model only near phase boundaries and only by the blocks merged across them; TV-02 reconciles the measured ledgers to this model in exact integers", + }, + "superseded": [{"text": TAIL_SENTENCE, "where": "site/litepaper.html:630", "by": "D01 (capped issuance, subject to TV-04)", "status": "SUPERSEDED"}], + "conflicts": conflicts(fig), + "sources": { + "volume": {"path": "docs/plans/igneum-2.0-master/token-value/rules-and-gates.json", "sha256": hashlib.sha256(read_igneum(a.igneum_root, igneum_rev, "docs/plans/igneum-2.0-master/token-value/rules-and-gates.json")).hexdigest()}, + "litepaper": {"path": "site/litepaper.html", "igneum_commit": igneum_rev, "sha256": files["igneum:site/litepaper.html"]["igneum master"]}, + "node": {"repo": "igneum-node (build-1:/srv/igneum-node.git)", "revisions": NODE_REVS, + "files": {k.split(":", 1)[1]: v for k, v in sorted(files.items()) if k.startswith("node:")}, + "note": "every cited node file is byte-identical at both revisions except kaspad/src/daemon.rs, whose override refusal is the same rule at both"}, + "pool": {k.split(":", 1)[1]: v["igneum master"] for k, v in sorted(files.items()) if k.startswith("igneum:pool/")}, + "citations": cites, + "code_dump": dump_check, + }, + "generator": {"path": "tools/token-value/tv-01/gen-spec.py", + "command": "python3 tools/token-value/tv-01/gen-spec.py --node-git --igneum-root . --igneum-rev " + igneum_rev + " --code-dump tools/token-value/tv-01/code-dump/schedule-bf60948a.jsonl --out docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json --md docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md"}, + } + # every litepaper quote in the conflicts table is exact (tags stripped, entities decoded) or nothing is written + import html as _html, re as _re + lp = read_igneum(a.igneum_root, igneum_rev, "site/litepaper.html").decode("utf-8").split("\n") + for c in spec["conflicts"]: + for lk, wk in (("line", "words"), ("line_2", "words_2")): + ln = c["litepaper"].get(lk) + if ln and c["litepaper"][wk] not in _html.unescape(_re.sub("<[^>]+>", "", lp[ln - 1])): + sys.exit(f"{c['id']}: the litepaper's line {ln} does not carry the quoted words exactly") + with open(a.out, "w", encoding="utf-8") as fh: + json.dump(spec, fh, indent=1, ensure_ascii=False) + fh.write("\n") + + if a.md: + m, d = profiles[0], profiles[1] + L = ["", ""] + L.append("| Figure | Mainnet as compiled (10 blocks/s) | Devnet 4 (1 block/s) |") + L.append("|---|---|---|") + L.append(f"| Cap | {coins(CAP)} IGN ({CAP} base units) | the same |") + L.append(f"| Total scheduled issuance | {coins(int(m['total_issuance']))} IGN | {coins(int(d['total_issuance']))} IGN |") + L.append(f"| Shortfall under the cap (exact) | {m['cap_shortfall']} base units ({coins(int(m['cap_shortfall']))} IGN) | {d['cap_shortfall']} base units ({coins(int(d['cap_shortfall']))} IGN) |") + L.append(f"| Ramp phase | DAA 0 to {m['phases'][0]['end_daa_score']:,}, {coins(int(m['phases'][0]['phase_sum']))} IGN | DAA 0 to {d['phases'][0]['end_daa_score']:,}, {coins(int(d['phases'][0]['phase_sum']))} IGN |") + L.append(f"| First block | {coins(int(m['phases'][0]['ramp']['first_block']))} IGN | {coins(int(d['phases'][0]['ramp']['first_block']))} IGN |") + L.append(f"| Full-rate block (after the ramp) | {coins(int(m['phases'][1]['per_block_subsidy']))} IGN | {coins(int(d['phases'][1]['per_block_subsidy']))} IGN |") + L.append(f"| Phases (ramp, constant, terminal) | {m['phase_count']} | {d['phase_count']} |") + L.append(f"| Last non-zero subsidy | {m['last_nonzero_phase']['per_block_subsidy']} base unit a block, DAA {m['last_nonzero_phase']['start_daa_score']:,} to {m['last_nonzero_phase']['end_daa_score']:,} | {d['last_nonzero_phase']['per_block_subsidy']} base unit a block, DAA {d['last_nonzero_phase']['start_daa_score']:,} to {d['last_nonzero_phase']['end_daa_score']:,} |") + L.append(f"| Terminal (zero for ever) from | DAA {m['terminal']['start_daa_score']:,} (decay step {m['terminal']['decay_step']}, year {m['terminal']['start_year'] + 1}) | DAA {d['terminal']['start_daa_score']:,} (decay step {d['terminal']['decay_step']}, year {d['terminal']['start_year'] + 1}) |") + for y in ["1", "2", "5", "10", "12", "20", "50", "100"]: + L.append(f"| Scheduled supply at the end of year {y} | {coins(int(m['scheduled_supply_at_year_end'][y]))} IGN | {coins(int(d['scheduled_supply_at_year_end'][y]))} IGN |") + L.append(f"| A 1-to-10 blocks/s step at DAA {fig['bps_step']['step_daa']:,} under the current code | total {coins(fig['bps_step']['issued'])} IGN, {coins(fig['bps_step']['issued'] - CAP)} IGN over the cap | |") + L.append(f"| The compiled mainnet genesis (DAA 1,312,860) | first ramp block at {fig['genesis_offset']['first_ramp_percent']}; {coins(fig['genesis_offset']['issuance_lost'])} IGN never scheduled | |") + L.append("") + if dump_check: + L.append(f"Code agreement: {dump_check['agreement']} (dump sha256 `{dump_check['sha256']}`).") + L.append("") + L.append("") + txt = open(a.md).read() + b, e = txt.index("") + len("") + with open(a.md, "w") as fh: + fh.write(txt[:b] + "\n".join(L) + txt[e:]) + print(f"wrote {a.out}: mainnet total {fig['mainnet']['total']}, shortfall {CAP - fig['mainnet']['total']}; devnet-4 total {fig['devnet-4']['total']}; code dump {'agrees' if dump_check else 'not given'}") + + +if __name__ == "__main__": + main() From b6bd4b193f9037f78b658ca5c12210f534199b5d Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:33:38 +0000 Subject: [PATCH 03/13] TV-02: the supply replays agree to the integer on three network objects with the genesis rule written in; the words alone diverge at genesis A (Rust, the node's issuance code at bf60948a, build-5) and B (Python from the rules in words, build-6), no shared code: with rule set v0.1 EQUAL and byte-identical at all 591 rows (every transition's before/at/after, the terminal and one step past) on devnet-4 (1 bps, 18 decimals), mainnet (10 bps, 18) and devnet (8), and on both hand-written reorg fixtures, the no-undo ledger refused; 46,010,980,803 blocks replayed one by one in A, the node's two issuance paths agreeing on every one. With the words alone (v0) the first row differs: genesis issues nothing in the node (window.rs 176-179, coinbase.rs 139), so block h carries DAA score h - 1; the terminal total is unchanged, 3,963,038,999.610200483289816 IGN on devnet-4, 36,961,000.39 IGN under the cap. README with the method, the run commands, the result rows and the open rows (TV-01 spec re-run, the genesis and per-block-order rules for the spec, paid/burned/locked accounting); A's crate tests (3 green on build-5); the registry batch with TV-02 NOT RUN. Co-Authored-By: Claude Opus 5.5 --- .../token-value/phase0/tv-02/README.md | 193 ++ tools/ci/batches/tv-02-20261009-01.json | 19 + tools/token-value/tv-02/compare-all.sh | 17 + .../token-value/tv-02/replay-node/Cargo.lock | 2697 +++++++++++++++++ .../token-value/tv-02/replay-node/src/main.rs | 55 +- .../tv-02/replay-spec/replay_spec.py | 11 +- tools/token-value/tv-02/table.py | 31 + 7 files changed, 3019 insertions(+), 4 deletions(-) create mode 100644 docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md create mode 100644 tools/ci/batches/tv-02-20261009-01.json create mode 100755 tools/token-value/tv-02/compare-all.sh create mode 100644 tools/token-value/tv-02/replay-node/Cargo.lock create mode 100644 tools/token-value/tv-02/table.py diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md b/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md new file mode 100644 index 000000000..e4e6de326 --- /dev/null +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md @@ -0,0 +1,193 @@ +# TV-02: two independent supply replays, exact integer agreement + +Token Value Phase 0, gate TV-02 ("Reproduce complete supply accounting"), 9 October 2026. Verdict: **NOT RUN**. The +independent panel reads this evidence; this lane writes no PASS on its own result. + +## Result in one paragraph + +Two implementations with no shared code replay the issued supply from genesis to the cap, block by block, on three +network objects of the node at bf60948a: the Igneum 2.0 devnet object (igneum-devnet-4: 1 block a second, 18 +decimals), the mainnet object (10 blocks a second, 18 decimals) and the 8-decimal devnet object. With the rule set +v0.1 they agree to the integer at every one of 591 rows: every transition's block before, at and after (the ramp's +end and every halving step until the subsidy is 0), the terminal block and one full step past it. Their CSVs are +byte-identical. Both reorg fixtures agree with each other and with integers written by hand before either replay ran, +and the known-failed no-undo ledger is refused. Implementation B written from the published words alone (rule set +v0) diverges at the first row, genesis. The cause is one rule the words never state: genesis issues nothing, so the +block at height h carries DAA score h - 1. The total at the terminal is unchanged by it. No cap breach, no issuance +after the terminal block, and the node's two issuance code paths agree on all 46,010,980,803 blocks replayed. + +| network object | blocks a second | decimals | terminal block (last with a subsidy) | issued supply at the terminal, base units | IGN | under the 4,000,000,000 cap by | +|---|---|---|---|---|---|---| +| igneum-devnet-4 | 1 | 18 | height 4,102,488,000 (step 65, year 130) | 3963038999610200483289816000 | 3,963,038,999.610200483289816 | 36,961,000.389799516710184 IGN | +| mainnet | 10 | 18 | height 39,131,424,000 (step 62, year 124) | 3963038999610200467816224000 | 3,963,038,999.610200467816224 | 36,961,000.389799532183776 IGN | +| devnet (8 decimals) | 1 | 8 | height 2,019,686,400 (step 32, year 64) | 396303898886765648 | 3,963,038,988.86765648 | 36,961,011.13234352 IGN | + +What the gap under the cap is made of (devnet-4, evaluated with bc on build-5 from the closed forms, a third +computation beside A and B): the launch ramp withholds 36,960,999.881223514501152 IGN (2,592,000 x the full rate +minus the ramp's sum) and the floors of the launch rate and the 65 halvings withhold 0.508576002209032 IGN; the two +sum to A's and B's gap exactly. + +What it means for a holder: the supply that will ever exist on the 2.0 devnet and mainnet objects is 3,963,038,999.61 +IGN, reached at year 130 (devnet-4) or 124 (mainnet), with nothing minted after it and no path to the 4,000,000,000 +figure; the first two years (DAA 0 to 63,115,199) issue 1,963,038,999.86 IGN (49.5 percent of the final supply). A miner sees the per-block +subsidy of the table below; a pool sees the same numbers per block found. Neither changes with this document. + +## Sources, by sha + +| source | identity | +|---|---| +| the node's issuance code | igneum-node bf60948a (release-2.0.3-node-k6); the six files that decide issuance are byte-identical in release-2.0.2-node a284380b: consensus/core/src/emission.rs, consensus/core/src/igneum.rs, consensus/src/processes/coinbase.rs, consensus/core/src/config/params.rs, consensus/src/processes/window.rs, consensus/src/processes/difficulty.rs | +| the volume's rules | docs/plans/igneum-2.0-master/token-value/rules-and-gates.json, sha256 10ce307329e1663d... (TV-02, VR-02, D01) | +| the published words | site/litepaper.html (sha256 35ac65e151339089..., blob db1b8bf7b1d2) and site/economics.html (sha256 449c20b85cc4eede..., blob b15926199f06), both at master 6824d49cc32d | +| the TV-01 supply spec | not landed at the time of this run (docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json, a first version due 13:00 UK). B ran on the two interim rule sets below; the re-run on the spec is the open row R1 | +| B's rules v0 (words only) | tools/token-value/tv-02/replay-spec/rules-v0-litepaper.json, sha256 67707b095584d8bb8bc99e799875b8d167512085385ea6883bc12575d98fbb33 | +| B's rules v0.1 (v0 plus the genesis rule) | tools/token-value/tv-02/replay-spec/rules-v0.1-genesis-rule.json, sha256 d72d5578dfe4e0c860b45fdb37a749dd7f1b0365175a38349dd413b1e93eb0b9; the genesis rule from the node lane's written answer (9 October 2026, 09:25 UK), cited by file and line | + +## Method + +**The model.** A straight chain. Genesis (height 0, DAA score 0) issues nothing: it never enters the DAA window +(window.rs 176-179 puts it in mergeset_non_daa) and the coinbase pays only blues outside that set (coinbase.rs 139). +A block's DAA score is its selected parent's plus its mergeset's DAA blocks (difficulty.rs 31-32), so block 1 has DAA +score 0 and block h >= 1 has DAA score h - 1. Each block issues the schedule's subsidy at its DAA score: the +per-second rate of its step (launch rate 31,688,087,810,000,000,000 base units at 18 decimals, halved and floored +every 63,115,200 DAA seconds), divided by the blocks a second (floor), then the 30-day ramp from 10 percent (floor). +"cumulative" at height h is the issued supply of heights 0..h, exact, in base units. + +**Implementation A** (tools/token-value/tv-02/replay-node, Rust, on build-5). Calls the node's own code through +kaspa-consensus-core by path (vendor/igneum-node at bf60948a, read-only, no node file changed): the network objects +(devnet4_params(), MAINNET_PARAMS, DEVNET_PARAMS) and `EmissionTable::block_subsidy`, the value +`CoinbaseManager::calc_block_subsidy` returns and body validation compares every coinbase against. Every block from +DAA 0 to one step past the terminal is computed one by one (46,010,980,803 DAA scores over the three objects, 32 threads, +38 s wall in all) and checked against the node's second issuance path `igneum::block_subsidy_units` (0 mismatches) +and against the shape rule (non-decreasing through the ramp, non-increasing after it; 0 violations). Crate tests +(cargo test --release on build-5, 3 green): the no-undo ledger double-counts the orphan (the known-failed case first), +the hand-written boundary subsidies, the two node paths at every row. + +**Implementation B** (tools/token-value/tv-02/replay-spec/replay_spec.py, Python 3, on build-6). Written from a rules +file in words, shares no code with A and uses another method: inside the ramp it sums DAA seconds and multiplies by +the blocks in each second; after the ramp it multiplies each step's subsidy by the step's block count, counting only +a partial second or step block by block. Its self-test (the known-failed grouping that drops a partial second is +caught; the grouped sum equals a block-by-block sum on three small networks) ran before any replay. + +**The row set** (written into both independently): genesis; DAA 0, 1, 2; at 10 blocks a second the last block of +second 0 and the first two of second 1; the first block of every ramp day 1 to 29 and the ramp's midpoint; for every +transition (the ramp's end, then step k = 1, 2, ... up to the first step whose blocks pay 0) the blocks at DAA x - 1, +x and x + 1; the terminal block; one full step past the terminal. 232 rows on devnet-4, 226 on mainnet, 133 on devnet. + +**The reorg fixtures** (tools/token-value/tv-02/fixtures/, committed in 4e206eaf8 before either replay ran). A common +ancestor, an orphan branch of k blocks, rolled back, replaced by k + 1, at a transition boundary on devnet-4. Every +expected integer was written by hand from the closed forms (the ramp's sum has an exact closed form at 18 decimals: +schedule(d) = F/10 + d q + floor(d/9) with q = 11,002,808,267,361) and its digits evaluated with bc on build-5. The +known-failed ledger (a rollback that forgets to subtract) is part of every fixture and must be refused. + +| fixture | ancestor | orphan (k) | replacement (k+1) | final, hand-written | known-failed no-undo, hand-written | +|---|---|---|---|---|---| +| reorg-ramp-end-k1 | height 2,592,000 (the last ramp block), 45174523722296485498848000 | 1 block | 2 blocks | 45174587098472105498848000 (= the straight chain at height 2,592,002) | 45174618786559915498848000 (one full block too many) | +| reorg-first-step-k2 | height 63,115,199, 1963038968176400675498848000 | 2 blocks (full, then the first halved) | 3 blocks | 1963039031552576295498848000 (= the straight chain at height 63,115,202) | 1963039079084708010498848000 | + +**The comparison** (tools/token-value/tv-02/compare.py). Reads A's and B's CSVs (height, daa_score, subsidy, +cumulative) and reports EQUAL or the first differing row with both values, the columns that differ, the heights only +one side has, and the cumulative offsets on shared heights; for a reorg it checks A against B row by row, both +against the fixture's hand-written integers, the final against the straight chain at the same height, and that the +no-undo ledger is refused. Its self-test runs first in every comparison (a corrupted cumulative, a dropped row and a +no-undo ledger are each reported; equal inputs read EQUAL). + +## Results + +Run 9 October 2026, 08:22 to 08:35 UTC. A on build-5, B on build-6, the comparison on build-5 over B's outputs +copied from build-6. A rebuilt from this landing's source re-ran byte-identical (the second run under rerun-final/). + +| comparison | rules (B) | result | first difference | +|---|---|---|---| +| straight, devnet-4, 232 rows | v0.1 | EQUAL, byte-identical CSVs (sha256 6ede4f4a2fe3c551...) | none, through the ramp's end and steps 1 to 65, the terminal and one step past | +| straight, mainnet, 226 rows | v0.1 | EQUAL, byte-identical (sha256 3cf7f426d062722d...) | none, through steps 1 to 62 | +| straight, devnet, 133 rows | v0.1 | EQUAL, byte-identical (sha256 5679a4353380491f...) | none, through steps 1 to 32 | +| reorg-ramp-end-k1 | v0.1 | EQUAL, every row, and both equal the hand-written integers; no-undo refused | none | +| reorg-first-step-k2 | v0.1 | EQUAL, every row, and both equal the hand-written integers; no-undo refused | none | +| straight, all three objects | v0 (words only) | DIFFER | row 1, genesis: A (0, 0, subsidy 0, cumulative 0), B (0, 0, 3168808781000000000, 3168808781000000000) on devnet-4; 316880878100000000 on mainnet; 316880878 on devnet. Every later row is one block apart in height; the terminal total is identical | +| both reorg fixtures | v0 (words only) | DIFFER from A and from the fixture, by the same one-block shift | row 1, the ancestor: A DAA 2,591,999, cumulative 45174523722296485498848000; B DAA 2,592,000, cumulative 45174555410384295498848000 | + +The rule that explains the v0 divergence: the published words (litepaper, economics page) never say what genesis +issues or which DAA score block h carries. The node's rule is that genesis never enters the DAA window +(consensus/src/processes/window.rs 176-179), is skipped by the coinbase (coinbase.rs 139) and is never rewarded, so +block 1 carries DAA score 0 (difficulty.rs 31-32). Written in as v0.1, A and B agree everywhere. The words need the +rule (row R2). + +Devnet-4, selected rows (the full tables: build-5/table-devnet4.md, table-mainnet.md, table-devnet.md, generated by +tools/token-value/tv-02/table.py from A's CSV, which is byte-identical to B's): + +| height | DAA score | row | subsidy (base units) | issued supply (base units) | issued supply (IGN) | +|---|---|---|---|---|---| +| 0 | 0 | genesis (issues nothing) | 0 | 0 | 0 | +| 1 | 0 | DAA 0, the ramp at 10 percent | 3168808781000000000 | 3168808781000000000 | 3.168808781 | +| 2,592,001 | 2,592,000 | at ramp end | 31688087810000000000 | 45174555410384295498848000 | 45,174,555.410384295498848 | +| 63,115,201 | 63,115,200 | at step 1 | 15844043905000000000 | 1963039015708532390498848000 | 1,963,039,015.708532390498848 | +| 126,230,401 | 126,230,400 | at step 2 | 7922021952500000000 | 2963039007659366437998848000 | 2,963,039,007.659366437998848 | +| 189,345,601 | 189,345,600 | at step 3 | 3961010976250000000 | 3463039003634783461748848000 | 3,463,039,003.634783461748848 | +| 252,460,801 | 252,460,800 | at step 4 | 1980505488125000000 | 3713039001622491973623848000 | 3,713,039,001.622491973623848 | +| 504,921,601 | 504,921,600 | at step 8 | 123781593007812500 | 3947413999735968703506660500 | 3,947,413,999.7359687035066605 | +| 1,009,843,201 | 1,009,843,200 | at step 16 | 483521847686767 | 3962977964454441767484072367 | 3,962,977,964.454441767484072367 | +| 2,019,686,401 | 2,019,686,400 | at step 32 | 7377957880 | 3963038998678877917554525880 | 3,963,038,998.67887791755452588 | +| 3,029,529,601 | 3,029,529,600 | at step 48 | 112578 | 3963038999610186273155109378 | 3,963,038,999.610186273155109378 | +| 4,039,372,801 | 4,039,372,800 | at step 64 | 1 | 3963038999610200483226700801 | 3,963,038,999.610200483226700801 | +| 4,102,488,000 | 4,102,487,999 | terminal (last block with a subsidy) | 1 | 3963038999610200483289816000 | 3,963,038,999.610200483289816 | +| 4,102,488,001 | 4,102,488,000 | at step 65 | 0 | 3963038999610200483289816000 | 3,963,038,999.610200483289816 | +| 4,165,603,201 | 4,165,603,200 | one step past the terminal | 0 | 3963038999610200483289816000 | 3,963,038,999.610200483289816 | + +The height-1 row matches the team-reported first-block credit on the economics page (3.168808781 IGN at 18 decimals, +read back over the EVM RPC on 8 October 2026); this replay did not read the chain. + +## Open rows + +| id | row | state | waits on, earliest clock | +|---|---|---|---| +| R1 | Re-run B on the TV-01 supply spec and record its sha here | BLOCKED | the spec's first version, due 13:00 UK today; the re-run takes under a minute on build-6 | +| R2 | The published words (and the TV-01 spec) must state the genesis rule: genesis issues nothing, block h carries DAA score h - 1. Without it a reader reproduces the supply one block early (the v0 divergence above) | BLOCKED | TV-01's spec text | +| R3 | The words must state the per-block order at more than one block a second: the per-second rate divided by the blocks a second and floored, then the ramp (B assumed it; it matches the node). The other order changes the mainnet ramp's sum | BLOCKED | TV-01's spec text | +| R4 | The mainnet object's 10 blocks a second against the litepaper's "1 / s blocks, rising to 10 (designed)": the supply at 10 differs from 1 by 15,473,592,000 base units (0.000000015 IGN) and ends six years earlier (year 124, not 130); which rate mainnet launches at is a spec question | BLOCKED | TV-01, D01 | +| R5 | The 8-decimal and 18-decimal schedules are not the same coin amounts after the first halving (3,168,808,781 is odd, so the 8-decimal halving floors and the 18-decimal one does not): the 18-decimal supply is 10.74 IGN larger. igneum.rs's comment that the 18-decimal numbers are "the 8-decimal ones times 10^10" holds for the launch rate only. Informational; no network mixes the two | NOT RUN | none | +| R6 | The testnet-1 object carries a percent tail (`EmissionSchedule::TESTNET_1`, no cap). D01 ratified capped issuance; that object is outside it and was not replayed | BLOCKED | TV-01, D01 | +| R7 | DEVNET4_GENESIS carries the 8-byte subsidy layout on an 18-decimal network: read at 16 bytes its subsidy field is 134825277635862783248051596202800963840 base units (A's genesis-payload output). The window rule keeps it from ever being paid (the node lane, 09:25 UK); any other reader of that field (an explorer, an executor path) must apply the same rule. The node lane takes the B10 test case for devnet-3 and devnet-4 as a 2.0.3 test item | NOT RUN | the node lane's 2.0.3 test | +| R8 | The rest of the gate's procedure: paid (the 80/20 split is by subtraction, so producer plus pool equals the subsidy per block by construction), burned (the pool's OP_RETURN script on devnet v0), locked, outstanding, red and non-DAA blocks in a DAG, the execution layer's ledger, duplicate records, wrappers. This replay covers issued supply on a straight chain and its reorg only | NOT RUN | a later TV-02 item | +| R9 | Independence: A and B share no code, language or method, but one lane wrote both and B's rules were written after reading the node; the panel should weigh that | NOT RUN | the independent panel | + +## Run commands + +On build-5 (A), with the igneum repo at this landing and the node fork checked out at bf60948a as vendor/igneum-node: + +``` +tools/token-value/tv-02/run-a.sh /srv/artefacts/token-value/tv-02 build +tools/token-value/tv-02/run-a.sh /srv/artefacts/token-value/tv-02 genesis +tools/token-value/tv-02/run-a.sh /srv/artefacts/token-value/tv-02 straight devnet4 # and mainnet, devnet +tools/token-value/tv-02/run-a.sh /srv/artefacts/token-value/tv-02 reorg tools/token-value/tv-02/fixtures/reorg-first-step-k2.json +(cd tools/token-value/tv-02/replay-node && cargo test --release) +``` + +On build-6 (B), from tools/token-value/tv-02 alone (no node code on the box): + +``` +./run-b.sh /srv/artefacts/token-value/tv-02 replay-spec/rules-v0.1-genesis-rule.json selftest +./run-b.sh /srv/artefacts/token-value/tv-02 replay-spec/rules-v0.1-genesis-rule.json straight devnet4 +./run-b.sh /srv/artefacts/token-value/tv-02 replay-spec/rules-v0.1-genesis-rule.json reorg fixtures/reorg-ramp-end-k1.json +``` + +The comparison, on build-5 with B's outputs copied to /from-build-6: + +``` +tools/token-value/tv-02/compare-all.sh /srv/artefacts/token-value/tv-02 rules-v0.1-genesis-rule +python3 tools/token-value/tv-02/table.py a-devnet4.csv a-devnet4-labels.csv 18 +``` + +Each run writes its pid file first (run-a.pid, run-b.pid, compare.pid in the out dir) and removes it on exit. + +## Evidence + +| what | where | +|---|---| +| A's outputs, summaries, genesis-payload read, crate test log, build log | build-5:/srv/artefacts/token-value/tv-02/ (a-*.csv, a-*-summary.json, a-genesis-payload.json, a-cargo-test.log, build-a.log, a-run.log); the rebuild from this landing under rerun-final/ | +| B's outputs, both rule sets, self-test log | build-6:/srv/artefacts/token-value/tv-02/ (b-rules-v0.1-genesis-rule-*, b-rules-v0-litepaper-*, b-selftest.log, b-run.log) | +| the comparison logs and tables | build-5:/srv/artefacts/token-value/tv-02/compare-rules-v0.1-genesis-rule.log (sha256 81545cc811ff208b...), compare-rules-v0-litepaper.log (sha256 f743ecf3a323afba...), table-*.md | +| all of the above, copied | build-1:/srv/artefacts/token-value/tv-02/build-5/ and build-1:/srv/artefacts/token-value/tv-02/build-6/ | +| the binary that ran | tv02-replay-node sha256 ef3af79157947584... (source at 4e206eaf8); rebuilt from this landing 25ff78695bd9d972..., outputs byte-identical | +| B's code that ran | replay_spec.py sha256 acd68e1b8f9f4d1a591791caa408ba539680125db1af763fe0a7fb7e1e25eaf4 (this landing's file) | +| the registry batch | tools/ci/batches/tv-02-20261009-01.json (TV-02 NOT RUN, for the CI steward to record) | diff --git a/tools/ci/batches/tv-02-20261009-01.json b/tools/ci/batches/tv-02-20261009-01.json new file mode 100644 index 000000000..ab84702b9 --- /dev/null +++ b/tools/ci/batches/tv-02-20261009-01.json @@ -0,0 +1,19 @@ +{ + "run_id": "tv-02-20261009-01", + "manifest_sha": "bf60948a", + "evidence_dir": "build-1:/srv/artefacts/token-value/tv-02", + "cells": [ + { + "cell": "tv-02:supply-replay", + "cases": [ + "TV-02" + ], + "status": "NOT RUN", + "in_progress": true, + "method": "native", + "evidence": "docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md; build-1:/srv/artefacts/token-value/tv-02/build-5/compare-rules-v0.1-genesis-rule.log; build-1:/srv/artefacts/token-value/tv-02/build-5/compare-rules-v0-litepaper.log; build-1:/srv/artefacts/token-value/tv-02/build-5/a-*.csv; build-1:/srv/artefacts/token-value/tv-02/build-6/b-*.csv", + "note": "two supply replays, A (Rust on the node's issuance code at bf60948a, build-5) and B (Python from the rules in words, build-6), no shared code: with rule set v0.1 (the words plus the genesis rule, window.rs 176-179) EQUAL to the integer, byte-identical CSVs, at every transition's before/at/after, the terminal and one step past, on the devnet-4 (1 bps, 18 decimals), mainnet (10 bps, 18) and devnet (8) objects, and on two hand-written reorg fixtures with the no-undo ledger refused; with the words alone (v0) DIFFER at genesis (A 0, B 3168808781000000000 on devnet-4), a one-block shift with the terminal total unchanged (3963038999610200483289816000 base units on devnet-4); the TV-01 spec re-run, paid/burned/locked accounting, DAG reds and independent signoff are open rows; NOT RUN until the independent panel reads it", + "claim_impact": "none: no served figure moves; the supply ever issued on the 2.0 objects is 3,963,038,999.61 IGN under the 4,000,000,000 cap, with nothing after the terminal block" + } + ] +} diff --git a/tools/token-value/tv-02/compare-all.sh b/tools/token-value/tv-02/compare-all.sh new file mode 100755 index 000000000..02c1c6c9c --- /dev/null +++ b/tools/token-value/tv-02/compare-all.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# TV-02: every comparison of A's outputs with B's, on A's box (build-5) with B's outputs copied from build-6 into +# /from-build-6. Usage: compare-all.sh ; writes /compare-.log +set -u +D="$1"; O="$2"; TAG="$3" +PID="$O/compare.pid"; echo "$$ $(date -u +%FT%TZ) tv02-compare $TAG" > "$PID"; trap 'rm -f "$PID"' EXIT +B="$O/from-build-6"; LOG="$O/compare-$TAG.log"; : > "$LOG" +python3 "$D/compare.py" selftest >> "$LOG" 2>&1 +for N in devnet4 mainnet devnet; do + echo "== straight $N" >> "$LOG" + python3 "$D/compare.py" straight "$O/a-$N.csv" "$B/b-$TAG-$N.csv" >> "$LOG" 2>&1; echo "exit $?" >> "$LOG" +done +for F in reorg-ramp-end-k1 reorg-first-step-k2; do + echo "== reorg $F" >> "$LOG" + python3 "$D/compare.py" reorg "$O/a-reorg-$F.csv" "$B/b-$TAG-reorg-$F.csv" "$D/fixtures/$F.json" "$O/a-devnet4.csv" >> "$LOG" 2>&1; echo "exit $?" >> "$LOG" +done +cat "$LOG" diff --git a/tools/token-value/tv-02/replay-node/Cargo.lock b/tools/token-value/tv-02/replay-node/Cargo.lock new file mode 100644 index 000000000..94142c266 --- /dev/null +++ b/tools/token-value/tv-02/replay-node/Cargo.lock @@ -0,0 +1,2697 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom 0.2.15", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq 0.3.1", +] + +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq 0.4.2", + "cpufeatures", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "blst" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20659f9bbee16cbbd2f7393e40ab6309f5a98f76a2eb57a995ec508b72387fe" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.110", + "syn_derive", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 1.0.64", +] + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.110", + "unicode-xid", +] + +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" +dependencies = [ + "serde", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hexplay" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da1f4f846e8dcc1b5225caf702924816cabd855e4b46115c334ba09d5254a21" +dependencies = [ + "atty", + "termcolor", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kaspa-addresses" +version = "2.0.3" +dependencies = [ + "borsh", + "js-sys", + "serde", + "smallvec", + "thiserror 2.0.18", + "wasm-bindgen", + "workflow-log", + "workflow-wasm", +] + +[[package]] +name = "kaspa-consensus-core" +version = "2.0.3" +dependencies = [ + "arc-swap", + "async-trait", + "bitflags", + "blst", + "borsh", + "cfg-if", + "faster-hex", + "futures-util", + "getrandom 0.2.15", + "itertools 0.13.0", + "js-sys", + "kaspa-addresses", + "kaspa-core", + "kaspa-hashes", + "kaspa-math", + "kaspa-merkle", + "kaspa-muhash", + "kaspa-smt", + "kaspa-txscript-errors", + "kaspa-utils", + "rand 0.8.6", + "secp256k1", + "serde", + "serde-value", + "serde-wasm-bindgen", + "serde_json", + "sha2", + "sha3", + "smallvec", + "thiserror 2.0.18", + "wasm-bindgen", + "workflow-core", + "workflow-log", + "workflow-serializer", + "workflow-wasm", +] + +[[package]] +name = "kaspa-core" +version = "2.0.3" +dependencies = [ + "anyhow", + "cfg-if", + "ctrlc", + "downcast", + "futures-util", + "log", + "log4rs", + "num_cpus", + "thiserror 2.0.18", + "tokio", + "triggered", + "wasm-bindgen", + "workflow-log", +] + +[[package]] +name = "kaspa-hashes" +version = "2.0.3" +dependencies = [ + "blake2b_simd", + "blake3", + "borsh", + "cc", + "faster-hex", + "kaspa-utils", + "keccak", + "serde", + "sha2", + "sha2-const-stable", + "wasm-bindgen", + "zerocopy 0.8.42", +] + +[[package]] +name = "kaspa-math" +version = "2.0.3" +dependencies = [ + "borsh", + "faster-hex", + "js-sys", + "kaspa-utils", + "malachite-base", + "malachite-nz", + "serde", + "serde-wasm-bindgen", + "thiserror 2.0.18", + "wasm-bindgen", + "workflow-core", + "workflow-log", + "workflow-wasm", +] + +[[package]] +name = "kaspa-merkle" +version = "2.0.3" +dependencies = [ + "kaspa-hashes", +] + +[[package]] +name = "kaspa-muhash" +version = "2.0.3" +dependencies = [ + "kaspa-hashes", + "kaspa-math", + "rand_chacha 0.3.1", + "serde", +] + +[[package]] +name = "kaspa-smt" +version = "2.0.3" +dependencies = [ + "blake3", + "kaspa-hashes", + "thiserror 2.0.18", + "zerocopy 0.8.42", +] + +[[package]] +name = "kaspa-txscript-errors" +version = "2.0.3" +dependencies = [ + "borsh", + "kaspa-hashes", + "secp256k1", + "thiserror 2.0.18", +] + +[[package]] +name = "kaspa-utils" +version = "2.0.3" +dependencies = [ + "async-channel 2.3.1", + "borsh", + "cfg-if", + "faster-hex", + "ipnet", + "itertools 0.13.0", + "log", + "once_cell", + "parking_lot", + "serde", + "sha2", + "smallvec", + "thiserror 2.0.18", + "uuid", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "serde", + "value-bag", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e947bb896e702c711fccc2bf02ab2abb6072910693818d1d6b07ee2b9dfd86c" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derive_more", + "flate2", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "mock_instant", + "parking_lot", + "rand 0.9.4", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror 2.0.18", + "thread-id", + "typemap-ors", + "unicode-segmentation", + "winapi", +] + +[[package]] +name = "malachite-base" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b6f86fdbb1eb9955946be91775239dfcb0acdb1a51bb07d5fc9b8c854f5ccd" +dependencies = [ + "hashbrown 0.16.1", + "itertools 0.14.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-nz" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0197a2f5cfee19d59178e282985c6ca79a9233e26a2adcf40acb693896aa09f6" +dependencies = [ + "itertools 0.14.0", + "libm", + "malachite-base", + "wide", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mock_instant" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "parse-variants" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99088a2b0695df5940d7b5a3b4c4460b765053cf5ecd6d46da43812d3fad7f13" +dependencies = [ + "parse-variants-derive", +] + +[[package]] +name = "parse-variants-derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70df726c43c645ef1dde24c7ae14692036ebe5457c92c5f0ec4cfceb99634ff6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 1.0.64", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rlimit" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" +dependencies = [ + "libc", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safe_arch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7caad094bd561859bcd467734a720c3c1f5d1f338995351fefe2190c45efed" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "rand 0.8.6", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "thread-id" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2010d27add3f3240c1fef7959f46c814487b216baee662af53be645ba7831c07" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" +dependencies = [ + "bytes", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" + +[[package]] +name = "triggered" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce148eae0d1a376c1b94ae651fc3261d9cb8294788b962b7382066376503a2d1" + +[[package]] +name = "tv02-replay-node" +version = "0.1.0" +dependencies = [ + "kaspa-consensus-core", + "serde_json", +] + +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "rand 0.9.4", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[package]] +name = "vergen" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +dependencies = [ + "anyhow", + "cargo_metadata", + "cfg-if", + "regex", + "rustc_version", + "rustversion", + "time", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.110", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wide" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9479f84a757f819cfab37295955906479181395de83add28f74975fde083141" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "workflow-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d67bbe225ea90aa6979167f28935275506696ac867661e218893d3a42e1666" +dependencies = [ + "async-channel 2.3.1", + "async-std", + "borsh", + "bs58", + "cfg-if", + "chrono", + "dirs", + "faster-hex", + "futures", + "getrandom 0.2.15", + "instant", + "js-sys", + "rand 0.8.6", + "rlimit", + "serde", + "serde-wasm-bindgen", + "thiserror 1.0.64", + "tokio", + "triggered", + "vergen", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "workflow-core-macros", + "workflow-log", +] + +[[package]] +name = "workflow-core-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65659ed208b0066a9344142218abda353eb6c6cc1fc3ae4808b750c560de004b" +dependencies = [ + "convert_case", + "parse-variants", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "sha2", + "syn 1.0.109", + "workflow-macro-tools", +] + +[[package]] +name = "workflow-log" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64bf52c539193f219b7a79eb0c7c5f6c222ccf9b95c5e0bd59e924feb762256f" +dependencies = [ + "cfg-if", + "console", + "downcast", + "hexplay", + "lazy_static", + "log", + "termcolor", + "wasm-bindgen", +] + +[[package]] +name = "workflow-macro-tools" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "085d3045d5ca780fb589d230030e34fec962b3638d6c69806a72a7d7d1affea4" +dependencies = [ + "convert_case", + "parse-variants", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "workflow-panic-hook" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c76ca8b459e4f0c949f06ce2d45565a6769748e83ca7064d36671bbd67b4da" +dependencies = [ + "cfg-if", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "workflow-serializer" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64679db6856852a472caff4ce869e3ecebe291fbccc9406e9643eb5951a0904a" +dependencies = [ + "ahash", + "borsh", + "serde", +] + +[[package]] +name = "workflow-wasm" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e5fbf266e0fffb5c24d6103735eb2b94bb31f93b664b91eaaf63b4f959804" +dependencies = [ + "cfg-if", + "faster-hex", + "futures", + "js-sys", + "serde", + "serde-wasm-bindgen", + "thiserror 1.0.64", + "wasm-bindgen", + "wasm-bindgen-futures", + "workflow-core", + "workflow-log", + "workflow-panic-hook", + "workflow-wasm-macros", +] + +[[package]] +name = "workflow-wasm-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40237c65ecff78dbfedb13985e33f802a31f6f7de72dff12a6674fcdcf601822" +dependencies = [ + "js-sys", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +dependencies = [ + "zerocopy-derive 0.8.42", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/tools/token-value/tv-02/replay-node/src/main.rs b/tools/token-value/tv-02/replay-node/src/main.rs index 000000282..5086fa306 100644 --- a/tools/token-value/tv-02/replay-node/src/main.rs +++ b/tools/token-value/tv-02/replay-node/src/main.rs @@ -74,7 +74,7 @@ fn rows(net: &Net) -> (Vec, BTreeMap, u64) { let s = &net.params.emission; let bps = net.bps; let mut labels: BTreeMap = BTreeMap::new(); - let mut add = |h: u64, l: String, labels: &mut BTreeMap| { + let add = |h: u64, l: String, labels: &mut BTreeMap| { labels.entry(h).and_modify(|e| { e.push_str("; "); e.push_str(&l) }).or_insert(l); }; add(0, "daa 0 (block 1)".into(), &mut labels); @@ -375,3 +375,56 @@ fn main() { } } } + +#[cfg(test)] +mod tests { + use super::*; + + const F: u128 = 31_688_087_810_000_000_000; + const H: u128 = 15_844_043_905_000_000_000; + const RAMP_SUM: u128 = 45_174_523_722_296_485_498_848_000; // the fixture's hand-written closed form + + /// The known-failed case first: a ledger that forgets to subtract the orphan on rollback ends above the straight + /// chain, by exactly the orphan's subsidy; the ledger here subtracts it and lands on the straight chain. + #[test] + fn a_rollback_that_forgets_the_orphan_double_counts_it() { + let net = Net::new("devnet4"); + let r = 2_592_000u64; + let (cum, _) = cumulative_at(&net, &[r - 1, r + 1]); + assert_eq!(cum[0], RAMP_SUM, "the ramp's closed form, written by hand"); + let orphan = net.subsidy(r); + let naive = cum[0] + orphan + net.subsidy(r) + net.subsidy(r + 1); + assert_ne!(naive, cum[1], "the no-undo ledger must not equal the straight chain"); + assert_eq!(naive - cum[1], orphan); + assert_eq!(cum[0] + net.subsidy(r) + net.subsidy(r + 1), cum[1]); + } + + /// The hand-written boundary values of the fixtures: the last ramp block, the full rate, the first halving at 18 + /// decimals (F is even there, so floor(F / 2) loses nothing, unlike 3,168,808,781 >> 1 at 8 decimals). + #[test] + fn boundary_subsidies_are_the_hand_written_ones() { + let net = Net::new("devnet4"); + assert_eq!(net.subsidy(0), F / 10); + assert_eq!(net.subsidy(2_591_999), 31_688_076_807_191_732_638); + assert_eq!(net.subsidy(2_592_000), F); + assert_eq!(net.subsidy(63_115_199), F); + assert_eq!(net.subsidy(63_115_200), H); + let dev = Net::new("devnet"); + assert_eq!(dev.subsidy(63_115_200), 1_584_404_390); + assert_ne!(dev.subsidy(63_115_200) * 10_000_000_000, H, "the 8-decimal halving floors; the 18-decimal one does not"); + } + + /// The node's two issuance paths agree at every transition's before/at/after on the three networks. + #[test] + fn the_node_paths_agree_at_every_row() { + for n in ["devnet4", "mainnet", "devnet"] { + let net = Net::new(n); + let (daas, _, zero) = rows(&net); + for d in daas { + assert_eq!(net.subsidy(d), net.subsidy_units(d), "{n} daa {d}"); + } + assert_eq!(net.subsidy(zero), 0); + assert!(net.subsidy(zero - 1) > 0); + } + } +} diff --git a/tools/token-value/tv-02/replay-spec/replay_spec.py b/tools/token-value/tv-02/replay-spec/replay_spec.py index 9e5a22aee..50cccb37c 100755 --- a/tools/token-value/tv-02/replay-spec/replay_spec.py +++ b/tools/token-value/tv-02/replay-spec/replay_spec.py @@ -122,9 +122,14 @@ class Schedule: return 0 last_s = h // self.bps total = 0 - # the ramp, second by second - for s in range(0, min(self.ramp_s, last_s + 1)): - total += self.blocks_in_seconds(s, s + 1, h) * self.subsidy_of_second(s) + # the ramp, second by second (the whole ramp summed once and kept, when h lies past it) + if last_s >= self.ramp_s: + if getattr(self, "_ramp_total", None) is None: + self._ramp_total = sum(self.bps * self.subsidy_of_second(s) for s in range(self.ramp_s)) + total += self._ramp_total + else: + for s in range(0, last_s + 1): + total += self.blocks_in_seconds(s, s + 1, h) * self.subsidy_of_second(s) # after the ramp, step by step k = self.ramp_s // self.step while True: diff --git a/tools/token-value/tv-02/table.py b/tools/token-value/tv-02/table.py new file mode 100644 index 000000000..87956e6ec --- /dev/null +++ b/tools/token-value/tv-02/table.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""TV-02: the README's result tables, generated from a replay CSV and its labels (A's and B's CSVs are byte-identical +when the comparison reads EQUAL, so either serves). Prints Markdown rows: the transitions' at-rows (the first block +of each new subsidy), the ramp's end and the terminal. + + table.py +""" +import csv +import sys + + +def ign(units, decimals): + q, r = divmod(units, 10 ** decimals) + return "{:,}".format(q) + ("." + str(r).rjust(decimals, "0").rstrip("0") if r else "") + + +def main(path, labels_path, decimals): + rows = {int(r["height"]): r for r in csv.DictReader(open(path))} + labels = {int(r["height"]): r["label"] for r in csv.DictReader(open(labels_path))} + print("| height | DAA score | rows' label | subsidy (base units) | issued supply (base units) | issued supply (IGN) |") + print("|---|---|---|---|---|---|") + for h in sorted(rows): + l = labels.get(h, "") + if not (h == 0 or "at ramp end" in l or "at step" in l or "terminal" in l or "past the terminal" in l): + continue + r = rows[h] + print("| {:,} | {:,} | {} | {} | {} | {} |".format(h, int(r["daa_score"]), l, r["subsidy"], r["cumulative"], ign(int(r["cumulative"]), decimals))) + + +if __name__ == "__main__": + main(sys.argv[1], sys.argv[2], int(sys.argv[3])) From 7e7ccc190f0782d74757c8744d876661a1adbd84 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:36:38 +0000 Subject: [PATCH 04/13] TV-01: the genesis rule and the terminal fields TV-02 reads; C10 resolved by O-5.7; the registry batch kept beside the spec Genesis is never rewarded (window.rs:177, coinbase.rs:139, genesis.rs:26), so block height h carries DAA score h - 1; each profile now carries transition_count, last_paying_daa_score, last_paying_block_height and terminal_supply (Devnet 4: 66 transitions, last paying block at height 4,102,488,000, terminal supply 3,963,038,999.610200483289816 IGN), and the checker refuses a spec whose terminal fields disagree. C10 (tip to provers) is resolved by spec 05 section 5.2's closed O-5.7. The batch moved out of tools/ci/batches/ because the test map has no token-value cell for the replay; the CI steward records it. Co-Authored-By: Claude Opus 5.5 --- .../token-value/phase0/tv-01/README.md | 1 + .../registry-batch-tv-01-20261009-01.json | 0 .../token-value/phase0/tv-01/supply-spec.json | 58 ++++++++++++++++++- .../token-value/phase0/tv-01/supply-spec.md | 3 +- tools/token-value/tv-01/check-spec.py | 5 ++ tools/token-value/tv-01/gen-spec.py | 20 +++++-- 6 files changed, 79 insertions(+), 8 deletions(-) rename tools/ci/batches/tv-01-20261009-01.json => docs/plans/igneum-2.0-master/token-value/phase0/tv-01/registry-batch-tv-01-20261009-01.json (100%) diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md index 1512dd89e..2fd632b9d 100644 --- a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/README.md @@ -10,6 +10,7 @@ Gate TV-01 ("Resolve and freeze the monetary contract", rules-and-gates.json sha | `supply-spec.md` | the same in words, with the conflicts table, the D01 resolution and the founder's four questions | | `../../../../../../tools/token-value/tv-01/gen-spec.py` | the generator; every figure in both files is its output | | `../../../../../../tools/token-value/tv-01/check-spec.py` | the checker: four known-failed cases first, then the spec summed to the cap in exact integers | +| `registry-batch-tv-01-20261009-01.json` | the registry batch for the CI steward (TV-01 NOT RUN with the evidence paths); kept here, not under tools/ci/batches/, because the map has no token-value cell until the steward adds one | | `../../../../../../tools/token-value/tv-01/code-dump/` | the consensus code's own schedule (`tv01_schedule.rs`, run on build-9 against bf60948a) and its output, `schedule-bf60948a.jsonl` (sha256 `3e4b5ac3c2f43057a13ace7d781ad714e385a94105623c0e0dac4adc417d8056`) | ## Sources diff --git a/tools/ci/batches/tv-01-20261009-01.json b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/registry-batch-tv-01-20261009-01.json similarity index 100% rename from tools/ci/batches/tv-01-20261009-01.json rename to docs/plans/igneum-2.0-master/token-value/phase0/tv-01/registry-batch-tv-01-20261009-01.json diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json index 7a4fadbd5..d85ba2831 100644 --- a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.json @@ -59,6 +59,16 @@ "decay_rule": "rate[k+1] = floor(rate[k] x step_decay_q32 / 2^32) (a halving every two years)", "per_block": "per_block(d) = ramp(floor(rate[floor(s / step_seconds)] / bps), s), s = floor(d / bps)", "tail": "cap", + "genesis_rule": { + "rule": "genesis-unrewarded", + "text": "the genesis block is never rewarded: it never enters the DAA window (window.rs:177), the coinbase pays only the mergeset blues inside the DAA window (coinbase.rs:139), and the genesis coinbase has no outputs (genesis.rs:26). A block's DAA score counts its DAA-window mergeset, so on a chain the block at height h >= 1 carries DAA score h - 1: DAA score 0 is the first paid block and every schedule below is indexed by DAA score, not height", + "height_of_daa_score": "height = DAA score + 1 (a chain of blocks; in the DAG several blocks may share a DAA score)", + "source": [ + "consensus/src/processes/window.rs:177", + "consensus/src/processes/coinbase.rs:139", + "consensus/core/src/config/genesis.rs:26" + ] + }, "source": [ "consensus/core/src/emission.rs:103 to 110", "consensus/core/src/emission.rs:225", @@ -1128,8 +1138,13 @@ "start_daa_score": 38500272000, "end_daa_score": 39131424000 }, + "transition_count": 63, + "last_paying_daa_score": 39131423999, + "last_paying_block_height": 39131424000, + "terminal_supply": "3963038999610200467816224000", "terminal": { "start_daa_score": 39131424000, + "first_unpaid_block_height": 39131424001, "decay_step": 62, "start_year": 124, "behaviour": "per-block subsidy 0 at every later DAA score; the cap is never reached; blocks are paid by fees alone" @@ -2240,8 +2255,13 @@ "start_daa_score": 4039372800, "end_daa_score": 4102488000 }, + "transition_count": 66, + "last_paying_daa_score": 4102487999, + "last_paying_block_height": 4102488000, + "terminal_supply": "3963038999610200483289816000", "terminal": { "start_daa_score": 4102488000, + "first_unpaid_block_height": 4102488001, "decay_step": 65, "start_year": 130, "behaviour": "per-block subsidy 0 at every later DAA score; the cap is never reached; blocks are paid by fees alone" @@ -2519,9 +2539,9 @@ "where": "igneum/exec/src/executor.rs:362 to 371 (the tip less the developer share is credited to the block's miner alone); igneum/exec/src/pgas.rs:290 (20% developer share, unregistered parts burned)", "value": "80% to the block's miner, none to provers; 20% to registered payees or burned" }, - "spec_takes": "the code", - "why": "fees move existing supply and burn some; they never create supply", - "status": "RESOLVED (text correction owed: drop 'and provers')" + "spec_takes": "the code, which is the design's own closed decision", + "why": "docs/spec/05-fees-and-economics.md line 24 (section 5.2): 'No part of the tip reaches the provers: O-5.7 is closed at zero (8 October 2026, docs/design/proving-payment.md); the provers' user-funded payment is the proving base fee'. The code (executor.rs:362 to 371, revm's beneficiary credit) matches it. The litepaper line 622 and the spec's own summary rows (05 line 79, line 98) still say 'producer and provers' and are stale. Fees move existing supply and burn some; they never create supply.", + "status": "RESOLVED (by O-5.7, closed at zero 8 October 2026; text corrections owed: litepaper line 622, spec 05 lines 79 and 98)" }, { "id": "C11", @@ -2619,6 +2639,10 @@ "release-2.0.2-node": "ef25b2412507d608df08fd22895e74262489f1fa460e6fe26f89877fb5cc6c63", "release-2.0.3-node-k6": "ef25b2412507d608df08fd22895e74262489f1fa460e6fe26f89877fb5cc6c63" }, + "consensus/src/processes/window.rs": { + "release-2.0.2-node": "fae87c4cd23ede2486650d8b87699e65c274f3b31b469f6c8ac08dc224b0b2d5", + "release-2.0.3-node-k6": "fae87c4cd23ede2486650d8b87699e65c274f3b31b469f6c8ac08dc224b0b2d5" + }, "igneum/exec/src/config.rs": { "release-2.0.2-node": "fdc4ba56109ddab4dadfe3d184247cf2cda8774dc5542337acaa38c8da716aaf", "release-2.0.3-node-k6": "fdc4ba56109ddab4dadfe3d184247cf2cda8774dc5542337acaa38c8da716aaf" @@ -2973,6 +2997,20 @@ "line": 265, "anchor": "Amount(self.emission.block_subsidy(daa_score, self.bps))" }, + { + "id": "genesis_not_in_window", + "repo": "node", + "path": "consensus/src/processes/window.rs", + "line": 177, + "anchor": "Special case: Genesis does not enter the DAA window" + }, + { + "id": "cb_daa_blues_only", + "repo": "node", + "path": "consensus/src/processes/coinbase.rs", + "line": 139, + "anchor": "for blue in ghostdag_data.mergeset_blues.iter().filter(|h| !mergeset_non_daa.contains(h))" + }, { "id": "utxo_retired", "repo": "node", @@ -3144,6 +3182,20 @@ "line": 775, "anchor": "Genesis with no premine, 30-day ramp." }, + { + "id": "spec_tip_no_provers", + "repo": "igneum", + "path": "docs/spec/05-fees-and-economics.md", + "line": 24, + "anchor": "No part of the tip reaches the provers: O-5.7 is closed at zero (8 October 2026" + }, + { + "id": "spec_tip_table_stale", + "repo": "igneum", + "path": "docs/spec/05-fees-and-economics.md", + "line": 79, + "anchor": "80 / 20 (producer and provers / developer)" + }, { "id": "pool_payout_doc", "repo": "igneum", diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md index bc691b8cb..89f31c028 100644 --- a/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-01/supply-spec.md @@ -39,6 +39,7 @@ The code already matches D01: | Tail | cap: the curve runs to zero and stays there | emission.rs:109 | | Block rate | mainnet as compiled: 10 blocks a second from genesis; Devnet 4: 1 (C04 BLOCKED) | params.rs:1789, :2208 | | Genesis | DAA score 0 (C09 BLOCKED: the compiled mainnet genesis still carries Kaspa's 1,312,860) | genesis.rs:92 | +| Genesis is never rewarded | genesis never enters the DAA window and the coinbase pays only DAA-window blues; the genesis coinbase has no outputs. On a chain, block height h >= 1 carries DAA score h - 1, so DAA score 0 is the first paid block and the last paying block is at height = terminal DAA score (mainnet 39,131,424,000; Devnet 4 4,102,488,000) | window.rs:177; coinbase.rs:139; genesis.rs:26 | ### How the cap is enforced @@ -142,7 +143,7 @@ The litepaper is site/litepaper.html at igneum master 6824d49c (sha256 `35ac65e1 | C07 | line 620 "80% the block's miner, 20% the proving pool for the provers of that block" | coinbase.rs:152 to 159 (the UTXO side pays DAA red blocks); executor.rs:181 (the EVM credits blue only) | Native issuance is the blue blocks' subsidies; the schedule sum is the upper bound | RESOLVED; TV-02 measures the red share | | C08 | silent | params.rs:1812 and :2232: `subsidy_per_block_activation_daa` is never on mainnet and Devnet 4 (testnet-1: 0). The EVM credits a merged block the merging chain block's subsidy, while the UTXO side pays the block's own. | Each block's own DAA score (ledger N8) is normative | **BLOCKED** | | C09 | line 775 "Genesis with no premine, 30-day ramp." | genesis.rs:92: the mainnet GENESIS carries DAA 1,312,860 and Kaspa's 2021 timestamp. The first block would pay 14.55% of full rate, not 10%. 510,841.78 IGN would never be scheduled, and the schedule would run 1.52 days early. | Genesis at DAA score 0, as Devnet 4 and testnet-1 have it | **BLOCKED** | -| C10 | line 622 "80% the block's miner and provers; 20% the apps whose code ran, per call frame" | executor.rs:362 to 371: the tip less the developer share goes to the miner alone | The code. Fees move supply and never create it. | RESOLVED; text correction owed (drop "and provers") | +| C10 | line 622 "80% the block's miner and provers; 20% the apps whose code ran, per call frame" | executor.rs:362 to 371: the tip less the developer share goes to the miner alone. docs/spec/05-fees-and-economics.md line 24 says the same: "No part of the tip reaches the provers: O-5.7 is closed at zero (8 October 2026 ...)". | The code, which is the design's closed decision O-5.7. Fees move supply and never create it. | RESOLVED by O-5.7; text corrections owed (litepaper line 622; spec 05 lines 79 and 98 still say "producer and provers") | | C11 | line 647 "The reference pool never holds a member’s balance. Payouts come straight from the coinbase split" | pool/src/payout.rs:1 to 6 (the pool's address receives and pays); config.rs:58 (1% fee); params.rs:1851 (`pool_split_activation_daa` never) | The code: a custodial PPLNS pool with no supply effect | **BLOCKED** | | C12 | line 630 "The cap stays at 4 billion. There is no tail emission." | emission.rs:116 to 122; params.rs:2014: the held testnet-1 object carries a 1% a year tail | Not the contract. The contract is `CURRENT` with `Tail::Cap`. | RESOLVED by D01 | | C13 | silent | params.rs:1783 to 1784: Kaspa's `deflationary_phase_daa_score` and `pre_deflationary_phase_base_subsidy` sit in the mainnet object. Only the digest reads them (coinbase.rs:31). | Ignored: not part of the contract | RESOLVED; the node lane may annotate or remove them at the genesis cut | diff --git a/tools/token-value/tv-01/check-spec.py b/tools/token-value/tv-01/check-spec.py index ef2839612..79a31d36f 100644 --- a/tools/token-value/tv-01/check-spec.py +++ b/tools/token-value/tv-01/check-spec.py @@ -97,6 +97,7 @@ def check(spec, regen=True): need(spec["gate"] == "TV-01", "gate is not TV-01") need(spec["verdict"] in ("NOT RUN", "BLOCKED", "FAIL"), f"verdict {spec['verdict']!r}: a lane never writes PASS on its own result") need(spec["decision"].get("id") == "D01", "decision is not D01") + need(spec["rules"].get("genesis_rule", {}).get("rule") == "genesis-unrewarded", "the genesis rule (genesis never rewarded) is missing") need(spec["rules"].get("tail") == "cap", f"tail is {spec['rules'].get('tail')!r}: under D01 the only tail is the cap") cap = amount(spec["cap"]["base_units"], "cap.base_units") unit = amount(spec["units"]["base_units_per_coin"], "units.base_units_per_coin") @@ -150,6 +151,10 @@ def check(spec, regen=True): raise Refused(f"{name}: phase {i} kind {p['kind']!r}") total += s need(ph[-1]["kind"] == "terminal", f"{name}: the schedule has no terminal phase (issuance is not shown to end)") + need(sc["transition_count"] == len(ph) - 1, f"{name}: transition_count is not phases - 1") + need(sc["last_paying_daa_score"] == ph[-1]["start_daa_score"] - 1 and sc["last_paying_block_height"] == ph[-1]["start_daa_score"], + f"{name}: the last paying DAA score or height disagrees with the terminal phase (genesis is unrewarded: height = DAA score + 1)") + need(sc["terminal_supply"] == sc["total_issuance"], f"{name}: terminal_supply is not the total issuance") need(total == amount(sc["total_issuance"], f"{name}.total_issuance"), f"{name}: the phases sum to {total}, the spec says {sc['total_issuance']}") if total > cap: raise Refused(f"{name}: overshoot {total - cap} base units ({(total - cap) / unit:,.6f} IGN) over the cap") diff --git a/tools/token-value/tv-01/gen-spec.py b/tools/token-value/tv-01/gen-spec.py index c3ec7b7ae..610264944 100644 --- a/tools/token-value/tv-01/gen-spec.py +++ b/tools/token-value/tv-01/gen-spec.py @@ -95,6 +95,8 @@ CITATIONS = [ ("cb_pool_output", "node", "consensus/src/processes/coinbase.rs", 163, "outputs.push(TransactionOutput::new_units(proving_pool_reward, igneum::proving_pool_script_public_key()));"), ("cb_calc", "node", "consensus/src/processes/coinbase.rs", 262, "pub fn calc_block_subsidy(&self, daa_score: u64) -> Amount"), ("cb_calc_body", "node", "consensus/src/processes/coinbase.rs", 265, "Amount(self.emission.block_subsidy(daa_score, self.bps))"), + ("genesis_not_in_window", "node", "consensus/src/processes/window.rs", 177, "Special case: Genesis does not enter the DAA window"), + ("cb_daa_blues_only", "node", "consensus/src/processes/coinbase.rs", 139, "for blue in ghostdag_data.mergeset_blues.iter().filter(|h| !mergeset_non_daa.contains(h))"), ("utxo_retired", "node", "consensus/src/pipeline/body_processor/body_validation_in_isolation.rs", 54, "RuleError::UtxoTransactionsRetired"), ("exec_blue_only", "node", "igneum/exec/src/executor.rs", 181, "for b in blocks.iter().filter(|b| b.is_blue)"), ("exec_split", "node", "igneum/exec/src/executor.rs", 189, "silent_split_units(subsidy, b.silent, env.signing_bonus_bps)"), @@ -119,6 +121,8 @@ CITATIONS = [ ("lp_pool_custody", "igneum", "site/litepaper.html", 647, "The reference pool never holds a member"), ("lp_per_block_figure", "igneum", "site/litepaper.html", 668, "31.688 IGN a block, 86,400 blocks a day"), ("lp_mainnet_launch", "igneum", "site/litepaper.html", 775, "Genesis with no premine, 30-day ramp."), + ("spec_tip_no_provers", "igneum", "docs/spec/05-fees-and-economics.md", 24, "No part of the tip reaches the provers: O-5.7 is closed at zero (8 October 2026"), + ("spec_tip_table_stale", "igneum", "docs/spec/05-fees-and-economics.md", 79, "80 / 20 (producer and provers / developer)"), ("pool_payout_doc", "igneum", "pool/src/payout.rs", 3, "so the pool receives"), ("pool_fee_default", "igneum", "pool/src/config.rs", 58, "fee_percent: 1.0,"), ("pool_distribute", "igneum", "pool/src/pplns.rs", 76, "pub fn distribute(reward_wei: u128, fee_percent: f64, payees: &[Payee])"), @@ -339,9 +343,9 @@ def conflicts(fig): "litepaper": {"line": 622, "words": "80% the block's miner and provers; 20% the apps whose code ran, per call frame"}, "code": {"where": "igneum/exec/src/executor.rs:362 to 371 (the tip less the developer share is credited to the block's miner alone); igneum/exec/src/pgas.rs:290 (20% developer share, unregistered parts burned)", "value": "80% to the block's miner, none to provers; 20% to registered payees or burned"}, - "spec_takes": "the code", - "why": "fees move existing supply and burn some; they never create supply", - "status": "RESOLVED (text correction owed: drop 'and provers')"}, + "spec_takes": "the code, which is the design's own closed decision", + "why": "docs/spec/05-fees-and-economics.md line 24 (section 5.2): 'No part of the tip reaches the provers: O-5.7 is closed at zero (8 October 2026, docs/design/proving-payment.md); the provers' user-funded payment is the proving base fee'. The code (executor.rs:362 to 371, revm's beneficiary credit) matches it. The litepaper line 622 and the spec's own summary rows (05 line 79, line 98) still say 'producer and provers' and are stale. Fees move existing supply and burn some; they never create supply.", + "status": "RESOLVED (by O-5.7, closed at zero 8 October 2026; text corrections owed: litepaper line 622, spec 05 lines 79 and 98)"}, {"id": "C11", "topic": "the reference pool's custody and fee", "litepaper": {"line": 647, "words": "The reference pool never holds a member’s balance. Payouts come straight from the coinbase split"}, "code": {"where": "pool/src/payout.rs:1 to 6 (the pool's address receives the 80% and pays members by transfers); pool/src/config.rs:58 (fee_percent 1.0); pool/src/pplns.rs:76; consensus/core/src/config/params.rs:1851 (mainnet pool_split_activation_daa = never: the IGNW coinbase split is off)", @@ -404,7 +408,11 @@ def main(): "phase_count": len(phases), "last_nonzero_phase": {"index": last_nonzero["index"], "per_block_subsidy": last_nonzero["per_block_subsidy"], "start_daa_score": last_nonzero["start_daa_score"], "end_daa_score": last_nonzero["end_daa_score"]}, - "terminal": {"start_daa_score": term["start_daa_score"], "decay_step": term["decay_step"], + "transition_count": len(phases) - 1, + "last_paying_daa_score": term["start_daa_score"] - 1, + "last_paying_block_height": term["start_daa_score"], + "terminal_supply": str(total), + "terminal": {"start_daa_score": term["start_daa_score"], "first_unpaid_block_height": term["start_daa_score"] + 1, "decay_step": term["decay_step"], "start_year": f["terminal_year"], "behaviour": "per-block subsidy 0 at every later DAA score; the cap is never reached; blocks are paid by fees alone"}, "total_issuance": str(total), "cap_shortfall": str(CAP - total), @@ -477,6 +485,10 @@ def main(): "step_seconds": STEP_SECONDS, "step_decay_q32": STEP_DECAY_Q32, "decay_rule": "rate[k+1] = floor(rate[k] x step_decay_q32 / 2^32) (a halving every two years)", "per_block": "per_block(d) = ramp(floor(rate[floor(s / step_seconds)] / bps), s), s = floor(d / bps)", "tail": "cap", + "genesis_rule": {"rule": "genesis-unrewarded", + "text": "the genesis block is never rewarded: it never enters the DAA window (window.rs:177), the coinbase pays only the mergeset blues inside the DAA window (coinbase.rs:139), and the genesis coinbase has no outputs (genesis.rs:26). A block's DAA score counts its DAA-window mergeset, so on a chain the block at height h >= 1 carries DAA score h - 1: DAA score 0 is the first paid block and every schedule below is indexed by DAA score, not height", + "height_of_daa_score": "height = DAA score + 1 (a chain of blocks; in the DAG several blocks may share a DAA score)", + "source": ["consensus/src/processes/window.rs:177", "consensus/src/processes/coinbase.rs:139", "consensus/core/src/config/genesis.rs:26"]}, "source": ["consensus/core/src/emission.rs:103 to 110", "consensus/core/src/emission.rs:225", "consensus/core/src/emission.rs:316 to 333", "consensus/core/src/config/params.rs:1858"], }, "activation_clocks": { From bb508a9a1901ed0ea232c652b963b53de687ce0b Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:48:29 +0000 Subject: [PATCH 05/13] TV-02: the registry batch moves beside the README (registry-batch-tv-02.json, the proposed cell native:tv-02) for the CI steward to record, as TV-04's did; the merge's replay refused a cell not yet on the map Co-Authored-By: Claude Opus 5.5 --- .../token-value/phase0/tv-02/README.md | 2 +- .../phase0/tv-02/registry-batch-tv-02.json | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) rename tools/ci/batches/tv-02-20261009-01.json => docs/plans/igneum-2.0-master/token-value/phase0/tv-02/registry-batch-tv-02.json (50%) diff --git a/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md b/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md index e4e6de326..75572dc6c 100644 --- a/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md @@ -190,4 +190,4 @@ Each run writes its pid file first (run-a.pid, run-b.pid, compare.pid in the out | all of the above, copied | build-1:/srv/artefacts/token-value/tv-02/build-5/ and build-1:/srv/artefacts/token-value/tv-02/build-6/ | | the binary that ran | tv02-replay-node sha256 ef3af79157947584... (source at 4e206eaf8); rebuilt from this landing 25ff78695bd9d972..., outputs byte-identical | | B's code that ran | replay_spec.py sha256 acd68e1b8f9f4d1a591791caa408ba539680125db1af763fe0a7fb7e1e25eaf4 (this landing's file) | -| the registry batch | tools/ci/batches/tv-02-20261009-01.json (TV-02 NOT RUN, for the CI steward to record) | +| the registry batch | docs/plans/igneum-2.0-master/token-value/phase0/tv-02/registry-batch-tv-02.json (TV-02 NOT RUN, the proposed cell native:tv-02; the CI steward records it and names the cell on the map) | diff --git a/tools/ci/batches/tv-02-20261009-01.json b/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/registry-batch-tv-02.json similarity index 50% rename from tools/ci/batches/tv-02-20261009-01.json rename to docs/plans/igneum-2.0-master/token-value/phase0/tv-02/registry-batch-tv-02.json index ab84702b9..2f5eda4b5 100644 --- a/tools/ci/batches/tv-02-20261009-01.json +++ b/docs/plans/igneum-2.0-master/token-value/phase0/tv-02/registry-batch-tv-02.json @@ -1,10 +1,22 @@ { "run_id": "tv-02-20261009-01", - "manifest_sha": "bf60948a", + "manifest_sha": "af2c23bc", + "method": "native", "evidence_dir": "build-1:/srv/artefacts/token-value/tv-02", + "release_identity": { + "commit": "af2c23bc (branch tv-02-supply-replay: the two implementations, the fixtures from 4e206eaf8, the compare harness)", + "lockfile": "tools/token-value/tv-02/replay-node/Cargo.lock", + "binary": "tv02-replay-node sha256 ef3af79157947584... (the run), 25ff78695bd9d972... (rebuilt from af2c23bc, outputs byte-identical)", + "network_object": "the compiled objects of igneum-node bf60948a read by A (devnet4_params, MAINNET_PARAMS, DEVNET_PARAMS); no chain state read or written", + "activation": "none (Phase 0 lands documents and tests only)", + "profile_hashes": "igneum-node bf60948a (emission.rs, igneum.rs, coinbase.rs, params.rs, window.rs, difficulty.rs byte-identical in release-2.0.2-node a284380b); rules-and-gates.json 10ce3073...; B rules v0 67707b09..., v0.1 d72d5578...; replay_spec.py acd68e1b..." + }, + "claim_impact": "none: no served figure moves; the supply ever issued on the 2.0 objects is 3,963,038,999.61 IGN under the 4,000,000,000 cap, with nothing after the terminal block", + "reviewer": "", + "note": "for the CI steward: the proposed cell native:tv-02 (cases TV-02, coverage partial: issued supply on a straight chain and its reorg; paid, burned, locked, outstanding, DAG reds and duplicate records open) is not yet on the map; this file is the batch to record.", "cells": [ { - "cell": "tv-02:supply-replay", + "cell": "native:tv-02", "cases": [ "TV-02" ], @@ -12,8 +24,7 @@ "in_progress": true, "method": "native", "evidence": "docs/plans/igneum-2.0-master/token-value/phase0/tv-02/README.md; build-1:/srv/artefacts/token-value/tv-02/build-5/compare-rules-v0.1-genesis-rule.log; build-1:/srv/artefacts/token-value/tv-02/build-5/compare-rules-v0-litepaper.log; build-1:/srv/artefacts/token-value/tv-02/build-5/a-*.csv; build-1:/srv/artefacts/token-value/tv-02/build-6/b-*.csv", - "note": "two supply replays, A (Rust on the node's issuance code at bf60948a, build-5) and B (Python from the rules in words, build-6), no shared code: with rule set v0.1 (the words plus the genesis rule, window.rs 176-179) EQUAL to the integer, byte-identical CSVs, at every transition's before/at/after, the terminal and one step past, on the devnet-4 (1 bps, 18 decimals), mainnet (10 bps, 18) and devnet (8) objects, and on two hand-written reorg fixtures with the no-undo ledger refused; with the words alone (v0) DIFFER at genesis (A 0, B 3168808781000000000 on devnet-4), a one-block shift with the terminal total unchanged (3963038999610200483289816000 base units on devnet-4); the TV-01 spec re-run, paid/burned/locked accounting, DAG reds and independent signoff are open rows; NOT RUN until the independent panel reads it", - "claim_impact": "none: no served figure moves; the supply ever issued on the 2.0 objects is 3,963,038,999.61 IGN under the 4,000,000,000 cap, with nothing after the terminal block" + "note": "two supply replays, A (Rust on the node's issuance code at bf60948a, build-5) and B (Python from the rules in words, build-6), no shared code: with rule set v0.1 (the words plus the genesis rule, window.rs 176-179) EQUAL to the integer, byte-identical CSVs, at every transition's before/at/after, the terminal and one step past, on the devnet-4 (1 bps, 18 decimals), mainnet (10 bps, 18) and devnet (8) objects, and on two hand-written reorg fixtures with the no-undo ledger refused; with the words alone (v0) DIFFER at genesis (A 0, B 3168808781000000000 on devnet-4), a one-block shift with the terminal total unchanged (3963038999610200483289816000 base units on devnet-4); the TV-01 spec re-run, paid/burned/locked accounting, DAG reds and independent signoff are open rows; NOT RUN until the independent panel reads it" } ] } From 48fd4e048cf00ebafdcd0e4490628ce41cf273c9 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:53:10 +0000 Subject: [PATCH 06/13] workers page and the build queue (the coordinator, 9 October 2026 09:5x UK): a rig card reads "agent not reporting since