Merge remote-tracking branch 'build/ca3-v4-amend' into class-v5

# Conflicts:
#	igneum-pow/src/generator.rs
This commit is contained in:
igneum-labs 2026-10-07 17:53:53 +00:00
commit 3a7ba78349
437 changed files with 42104 additions and 21020 deletions

45
.github/workflows/ci-red.yml vendored Normal file
View file

@ -0,0 +1,45 @@
# The red watcher as its own workflow, on workflow_run, so the copy on master watches EVERY branch's ci run whatever
# ci.yml that branch carries: GitHub runs a workflow_run workflow from the default branch only, and the branch's own
# ci.yml never enters it (7 October 2026: the inline `red` job of ci.yml was conditioned on master and release-*, and
# a feature branch would have waited for a merge of master before its reds were posted at all).
#
# One line per failed run (tools/ci/red-watch.mjs record, idempotent per run attempt) to /srv/ci-red/red.jsonl on the
# box; the box's igneum-ci-red.timer posts each new line once to the hidden updates channel, naming the branch, the
# commit, the red check and the pushing author. Runs on the box's own runner (not a GitHub-hosted machine: the billing
# block of 6 October 2026, 18:37Z to 20:10Z, failed every hosted job at start and nobody was told). Never blocks a
# release: it reads the run, writes one line, and ends.
name: ci-red
on:
workflow_run:
workflows: [ci]
types: [completed]
jobs:
red:
name: red watcher (every branch; one line per failed run, with the branch, commit, red check and pushing author, to the updates channel and the box file)
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
# the label ci-red is on igneum-build-1 only (added through the runners API on 7 October 2026; the default of
# RUNNER_LABELS in provision.sh carries it): the record file and the poster (igneum-ci-red.timer, the webhook file)
# live on that box, and the pool label igneum-build-1 is shared with igneum-build-2 since the same day
runs-on: [self-hosted, linux, x64, ci-red]
timeout-minutes: 5
permissions:
actions: read # the failed run's jobs API (the first real red run, 21:19Z on 6 October: the default token answered 403 and the line carried no step)
contents: read
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: tools/ci
- name: record the failed run (one line, the branch, the commit, the failed jobs and their first failed step from the run's own API, the pushing author)
env:
GITHUB_TOKEN: ${{ github.token }}
RED_WATCH_RUN_ID: ${{ github.event.workflow_run.id }}
RED_WATCH_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
RED_WATCH_WORKFLOW: ${{ github.event.workflow_run.name }}
RED_WATCH_BRANCH: ${{ github.event.workflow_run.head_branch }}
RED_WATCH_SHA: ${{ github.event.workflow_run.head_sha }}
RED_WATCH_EVENT: ${{ github.event.workflow_run.event }}
RED_WATCH_URL: ${{ github.event.workflow_run.html_url }}
RED_WATCH_ACTOR: ${{ github.event.workflow_run.actor.login }}
RED_WATCH_TITLE: ${{ github.event.workflow_run.head_commit.message }}
RED_WATCH_AUTHOR: ${{ github.event.workflow_run.head_commit.author.name }}
run: node tools/ci/red-watch.mjs record --file /srv/ci-red/red.jsonl

View file

@ -8,12 +8,16 @@
# local gate and CI cannot drift (6 October 2026: 131 red `ci` runs in three days, 92 of them on master, every one a
# tree check that would have failed on the pushing machine in under 25 s; docs/analysis/ci-failures-2026-10-06.md).
#
# Where it runs: `pow` and `sims` go to the box's runner (igneum-build-1, rustc pinned, sccache read-only, 48 jobs)
# Where it runs: `pow` and `sims` go to the self-hosted pool (label igneum-build-1: the runners on igneum-build-1 and, since
# 7 October 2026, igneum-build-2, which carries that label too; rustc pinned, sccache read-only) and only when the push
# touched code (the `changes` job; a docs-only push skips them)
# when the repository variable IGNEUM_CI_RUNNER is `box`, else to ubuntu-latest (docs/plans/ci-self-hosted.md; GitHub
# has no fallback in runs-on, the variable is the switch). The `site` job stays on GitHub's machines. The `red` job
# runs on the box after any failed master or release-* run and records the failure for the watcher
# (tools/ci/red-watch.mjs; infra/build-server/ci-red): one line per run to the hidden updates channel and to
# /srv/ci-red/red.jsonl, so nobody opens the Actions page to learn master is red.
# has no fallback in runs-on, the variable is the switch). The `site` job stays on GitHub's machines. The red watcher
# is its own workflow, .github/workflows/ci-red.yml (workflow_run, so the copy on master watches every branch's run
# whatever ci.yml that branch carries): one line per failed run, naming the branch, the commit, the red check and the
# pushing author, to the hidden updates channel and to /srv/ci-red/red.jsonl (tools/ci/red-watch.mjs;
# infra/build-server/ci-red), so nobody opens the Actions page to learn a branch is red (the inline `red` job here
# watched master and release-* only until 7 October 2026, when eight red runs on ca3-v4-node went unseen).
#
# What does not run, on purpose: the node fork (vendor/igneum-node*, a rusty-kaspa fork of about 500 crates with
# rocksdb, blst and the execution layer) is gitignored here and too big for the free runners today (a cold build is
@ -24,8 +28,39 @@ on:
push:
pull_request:
jobs:
changes:
# What the push touched (tools/ci/docs-only-check.sh): a push of documents only (docs/, site/, *.md) skips the two
# compile-or-compute jobs below, which read none of those paths, so the self-hosted queue carries only runs that can
# change their result (7 October 2026: 31 runs queued on one runner, most of them status-document pushes). The tree
# gate (the `site` job) runs on ubuntu-latest for every push. A pull request, a new branch or a force push answers
# code=true (no `before` to compare from), as does any error reading the compare API: when in doubt, run.
name: what the push touched (docs-only runs skip the Rust and simulator jobs)
runs-on: ubuntu-latest
outputs:
code: ${{ steps.classify.outputs.code }}
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: tools/ci
- id: classify
env:
GH_TOKEN: ${{ github.token }}
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.sha }}
REPO: ${{ github.repository }}
EVENT: ${{ github.event_name }}
run: |
if [ "$EVENT" != push ] || [ -z "$BEFORE" ] || [ "$BEFORE" = 0000000000000000000000000000000000000000 ]; then
echo "code=true" >> "$GITHUB_OUTPUT"; echo "no base to compare from ($EVENT): the compile jobs run"; exit 0
fi
files="$(gh api "repos/$REPO/compare/$BEFORE...$AFTER" --paginate --jq '.files[].filename' 2>/dev/null || true)"
line="$(printf '%s\n' "$files" | bash tools/ci/docs-only-check.sh)"
echo "$line" >> "$GITHUB_OUTPUT"
echo "$line: $(printf '%s\n' "$files" | grep -c .) changed path(s) between ${BEFORE:0:8} and ${AFTER:0:8}"
pow:
name: igneum-pow tests, igneum-census build
needs: changes
if: ${{ needs.changes.outputs.code == 'true' }}
runs-on: ${{ vars.IGNEUM_CI_RUNNER == 'box' && fromJSON('["self-hosted", "linux", "x64", "igneum-build-1"]') || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
@ -41,6 +76,10 @@ jobs:
run: cargo build --release
sims:
name: simulators, quick modes
needs: changes
# master and release-* pushes, and pull requests into them, only (main, 7 October 2026: every code push cost two box jobs and the
# queue read 22); a feature-branch code push runs the igneum-pow tests alone. tools/ci/sims-branch-check.sh holds this rule.
if: ${{ needs.changes.outputs.code == 'true' && ((github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))) || (github.event_name == 'pull_request' && (github.base_ref == 'master' || startsWith(github.base_ref, 'release-')))) }}
runs-on: ${{ vars.IGNEUM_CI_RUNNER == 'box' && fromJSON('["self-hosted", "linux", "x64", "igneum-build-1"]') || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
@ -70,32 +109,13 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: a headless Chromium for the text-overlap sweep (Playwright outside the tree; the gate finds it through IGNEUM_PLAYWRIGHT_DIR)
run: |
mkdir -p /tmp/pw && cd /tmp/pw && npm init -y >/dev/null && npm i --no-audit --no-fund playwright@1.56 | tail -1
npx playwright install --with-deps chromium | tail -1
echo "IGNEUM_PLAYWRIGHT_DIR=/tmp/pw" >> "$GITHUB_ENV"
- name: the tree gate, tools/ci/pre-push.sh --ci (the same script the pre-push hook runs; one line per check, a red check prints its output)
run: bash tools/ci/pre-push.sh --ci
- name: public stats API answers with the documented fields (the live site; master only, the endpoints exist there after the merge)
if: github.ref == 'refs/heads/master'
run: node tools/ci/public-api-check.mjs https://igneum.network
red:
# Runs only when a master or release-* run has a failed job, on the box's own runner (not a GitHub-hosted machine:
# the billing block of 6 October 2026, 18:37Z to 20:10Z, failed every hosted job at start and nobody was told).
# tools/ci/red-watch.mjs record appends ONE line for this run to /srv/ci-red/red.jsonl (idempotent per run attempt);
# the box's igneum-ci-red.timer posts each new line once to the hidden updates channel. Never blocks a release:
# it reads the run, writes one line, and ends.
name: red watcher (master and release-* only; one line per failed run to the updates channel and the box file)
needs: [pow, sims, site]
if: ${{ failure() && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-')) }}
runs-on: [self-hosted, linux, x64, igneum-build-1]
timeout-minutes: 5
permissions:
actions: read # the run's jobs API (the first real red run, 21:19Z: the default token answered 403 and the line carried no step)
contents: read
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: tools/ci
- name: record this run (one line, the failed jobs and their first failed step, from the run's own API)
env:
GITHUB_TOKEN: ${{ github.token }}
RED_WATCH_TITLE: ${{ github.event.head_commit.message }}
run: node tools/ci/red-watch.mjs record --file /srv/ci-red/red.jsonl

View file

@ -0,0 +1,65 @@
// Igneum vendor and OS marks (gpu-logos, 7 October 2026): the strings the miner app ships in app/igneum-app/ui/app.js
// (View.MARKS and View.VENDORS), exported verbatim for the site and anything else that names the hardware.
// view.test.mjs fails when this file and app.js drift apart, so edit app.js first and regenerate this file with
// `node brand/marks/regen.mjs` (or copy the strings by hand; the test says which one moved).
//
// Each glyph: a hand-drawn simplified monochrome mark of the vendor's public geometry (never a copied logo file,
// never a raster), 24 x 24 viewBox at 22 px, under 460 bytes, fill or stroke through currentColor so the element's
// colour tints it. Nominative use that names the hardware; the ember accent is for state and never tints a brand.
//
// The treatment in the app (app.css, the block at the end): a 44 x 44 well, radius 12, background the vendor colour
// at .14 alpha (dark) or .10 (light), a 1 px ring in the vendor colour at .45 alpha, the glyph in the full colour;
// hover and focus-within add a 3 px halo of the well colour; nothing animates. The light hex of every vendor reads at
// 3:1 or better on its well over white (nvidia 3.87, amd 5.01, intel 4.29, apple 7.52, gpu 4.65).
//
// Class names in the app: .badge.<vendor> (nvidia | amd | intel | apple | gpu), .badge.mini for a 26 px inline mark,
// .gen for the series line under the name. Tokens: --mark-<vendor>, --mark-<vendor>-well, --mark-<vendor>-ring.
export const VENDORS = {
"nvidia": {
"label": "NVIDIA",
"dark": "#8BE37A",
"light": "#2F8A22"
},
"amd": {
"label": "AMD Radeon",
"dark": "#FF5A5A",
"light": "#C41E2A"
},
"intel": {
"label": "Intel",
"dark": "#7CC4FF",
"light": "#1C6FD6"
},
"apple": {
"label": "Apple",
"dark": "#E6E3DD",
"light": "#4A4A50"
},
"gpu": {
"label": "GPU",
"dark": "#9A9A9E",
"light": "#6B6B70"
}
};
export const WELL_ALPHA = { dark: 0.14, light: 0.1 };
export const MARKS = {
"nvidia": "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M2.6 12C5 7.9 8.3 5.8 12 5.8s7 2.1 9.4 6.2c-2.4 4.1-5.7 6.2-9.4 6.2S5 16.1 2.6 12z\"/><path d=\"M15.6 12A3.6 3.6 0 1 0 12 15.6\"/><circle cx=\"12\" cy=\"12\" r=\"1.2\" fill=\"currentColor\" stroke=\"none\"/></svg>",
"amd": "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" focusable=\"false\" fill=\"currentColor\"><path d=\"M8 3h13v13l-4-4V7h-5z\"/><path d=\"M3 8l4 4v5h5l4 4H3z\"/></svg>",
"intel": "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"><path d=\"M7.4 7.6C4.9 8.8 3.4 10.5 3.4 12.3c0 3.4 4.3 5.9 9.8 5.9 4.5 0 7.6-1.6 7.6-3.9 0-1.4-1.3-2.6-3.5-3.3\"/><path d=\"M11.2 9.6v6.2\"/><circle cx=\"11.2\" cy=\"6.6\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\"/></svg>",
"apple": "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" focusable=\"false\" fill=\"currentColor\"><path d=\"M16.4 12.6c0-2.5 2-3.6 2.1-3.7-1.2-1.7-3-1.9-3.6-2-1.5-.2-3 .9-3.8.9-.8 0-2-.9-3.3-.8-1.7 0-3.2 1-4.1 2.5-1.8 3-.5 7.6 1.3 10.1.9 1.2 1.9 2.6 3.2 2.5 1.3 0 1.8-.8 3.3-.8 1.6 0 2 .8 3.3.8 1.4 0 2.3-1.2 3.1-2.5 1-1.4 1.4-2.8 1.4-2.9 0 0-2.7-1-2.9-4.1zM13.9 5.3c.7-.8 1.2-2 1-3.2-1 0-2.2.7-2.9 1.5-.6.7-1.2 1.9-1 3 1.1.1 2.2-.5 2.9-1.3z\"/></svg>",
"gpu": "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\"><rect x=\"5.5\" y=\"5.5\" width=\"13\" height=\"13\" rx=\"2.2\"/><rect x=\"9.5\" y=\"9.5\" width=\"5\" height=\"5\" rx=\"1\"/><path d=\"M9 2.5v3M12 2.5v3M15 2.5v3M9 18.5v3M12 18.5v3M15 18.5v3M2.5 9h3M2.5 12h3M2.5 15h3M18.5 9h3M18.5 12h3M18.5 15h3\"/></svg>"
};
// OS marks in the same treatment: Apple is the vendor glyph; Windows is the four slanted panes
export const OS_MARKS = {
macos: MARKS.apple,
windows: "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" aria-hidden=\"true\" focusable=\"false\" fill=\"currentColor\"><path d=\"M3 5.6l7.3-1v7.1H3zM11.4 4.4L21 3v8.7h-9.6zM3 12.3h7.3v7.1L3 18.4zM11.4 12.3H21V21l-9.6-1.4z\"/></svg>"
};
export const OS_COLOURS = { macos: VENDORS.apple, windows: { label: 'Windows', dark: '#7CC4FF', light: '#1C6FD6' } };
// the CSS tokens for both themes, as the app declares them
export function tokensCss() {
const line = (theme) => Object.entries(VENDORS).map(([v, c]) => { const h = c[theme], r = parseInt(h.slice(1, 3), 16), g = parseInt(h.slice(3, 5), 16), b = parseInt(h.slice(5, 7), 16), a = String(WELL_ALPHA[theme]).replace(/^0/, ''); return `--mark-${v}:${h};--mark-${v}-well:rgba(${r},${g},${b},${a});--mark-${v}-ring:rgba(${r},${g},${b},.45)`; }).join(';');
return `:root{${line('dark')}}\n@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){${line('light')}}}\n:root[data-theme="light"]{${line('light')}}`;
}
// the well: <div class="badge nvidia"><svg…></svg></div>
export function markHtml(vendor, size) { const v = MARKS[vendor] ? vendor : 'gpu'; return '<div class="badge ' + (size ? size + ' ' : '') + v + '" data-mark="' + v + '" title="' + VENDORS[v].label + '">' + MARKS[v] + '</div>'; }

View file

@ -0,0 +1,86 @@
# The era VDF: built, measured and gated (7 October 2026)
Era VDF lane, 7 October 2026, from the attack pass's F7 row (`docs/analysis/attack-pass/f7-era.md`, sub-row a: the node's era seed was a plain chain block hash, grindable with one block of hash at no delay, and the 1-hour VDF of spec 04 section 4.4 did not exist in the node). Repository branch `era-vdf` (this record, the spec text, the harness `tools/era-vdf/`, the fast-time fields); node fork branch `era-vdf-node` on the 0.3.19 line (`release-0.3.19-node` dc141409). Every number below names its log on igneum-build-1 under `/srv/builds/igneum-wt-era-vdf/ev-*/`.
## 1. What was built
| Piece | Where | What |
|---|---|---|
| The integer | `consensus/core/src/era_vdf/bigint.rs` | a fixed-width signed integer (40 limbs, 2,560 bits) on the stack: add, sub, mul, shifts, Knuth division with floor, truncated, exact and Euclidean remainders, the extended gcd and the partial extended gcd with Lehmer's word steps (chiavdf `xgcd_partial.c`), modpow, sqrt and the fourth root, Miller-Rabin with the first 30 primes as bases; every operation checked against `num-bigint` on 20,000 random operands of the class group's sizes, the known-failed shapes first |
| The class group | `era_vdf/classgroup.rs` | `proto-vdf/src/classgroup.rs` (3 October 2026) on the fixed-width integer: NUDUPL and NUCOMP ported line by line from chiavdf's `qfb_nudupl` and `qfb_nucomp`, the plain duplication and Cohen 5.4.7 kept as the oracles the tests hold them to on random forms at 256, 512 and 1,024 bits; serialization as sign byte plus fixed width, 258 bytes a form |
| Wesolowski | `era_vdf/wesolowski.rs` | eval with serialized checkpoints (at most 2^16, 17 MB), the 12-bit-digit block prover bucketed per residue class and parallel over them, the naive prover as the oracle, verify; T + 1, another y, another pi and another input refused |
| The hash chain | `era_vdf/hashchain.rs` | scheme 1: T sequential SHA-256 applications from a tagged start; verification by recomputation; one step short refused |
| The scheme byte and the seed | `era_vdf/mod.rs` | `vdf_scheme` 0 and 1, `EraVdfProof` and its wire form, `era_vdf_input` (the chain's BLAKE2b keyed `IgneumEraVdfInput` over `chain_id || n || the day's blue hashes`), `era_seed_of` = SHA-256 of the scheme byte, the input, T and y |
| The switch | `consensus/core/src/config/params.rs`, `igneum.rs` | `pow_era_blocks` and `pow_era_lead` as override fields (the constants everywhere; in the digest when they differ), `era_vdf_activation_daa` (never), `vdf_scheme` (0), `era_vdf_t` (the reference T); the three in the digest once the activation is set (the 0.3.15 rule); installed with the PoW schedule |
| The node side | `consensus/src/processes/era_vdf.rs`, `model/stores/era_vdf.rs` | the cut rule (the chain block below the cut, memoised and re-validated by reachability), the day-of-blues input (memoised per cut block), the evaluator thread started by the virtual processor a quarter of the lead past the cut, the record store (one row per era), the header processor's wait when a header arrives before the record, the template's `era_seed` None while evaluating, `submit` for a record from outside (verified against this chain's input) |
| The template and the miner | `PowEpochInfo`, `RpcPowEpochInfo`, `rpc.proto` fields 37 to 44, `igneum-miner` | the era schedule, the VDF's state, scheme, T and input in every template; the miner holds while the node reports no era seed ("era VDF: the node is still evaluating"); `igneum-miner vdf bench|eval|verify` with the node's own code |
| The harness | `tools/era-vdf/reroll.mjs` | the F7 re-roll harness against the REAL era cut (era 120 DAA, lead 20 on the merged fast-time file; ports 30100 and up, suffix 1010), `--vdf off` the stand-in, `--vdf on` the VDF at a fast T, the adversary running the node's evaluator over its candidate before publishing |
## 2. The parameters
Measured 7 October 2026 on igneum-build-2 (AMD EPYC 9454P, 96 threads, Ubuntu 24.04), one core under `/srv/builds/_bin/lease cores 31` at nice 10 while the box ran other lanes' suites (load 25 to 75), with the node's own code (`igneum-miner vdf bench`, logs `ev-vdf-bench3.log`, `ev-vdf-bench5.log` in this lane's scratch) and chiavdf 7e62ce14 built on the box against GMP 6.3.0 (`ev-chiavdf`).
| Parameter | Value | Label |
|---|---|---|
| Group | class group, 1,024-bit prime discriminant `D = -HashPrime("igneum-era-discriminant" \|\| input)`, `\|D\| = 7 mod 8` | Implemented (spec 4.2) |
| Generator, Fiat-Shamir prime, proof plan | `(2, 1, (1 - D) / 8)`; 256 bits; 12-bit digits, at most 2^16 serialized checkpoints (17 MB) | Implemented |
| Proof on the wire | 529 bytes: scheme (1), T (8), two 258-byte forms with 2-byte lengths; `y` and `pi` 258 bytes each | Measured |
| Scheme byte | `vdf_scheme` 0 = class group, 1 = hash chain; genesis 0 everywhere | Implemented |
| Reference rate, scheme 0 | 30,589 and 40,117 squarings/s in two 10-s runs on the box core (the spread is the box's load); 30,000 is the reference | Measured |
| `T_era`, scheme 0 | 3,600 x 30,000 = 108,000,000 squarings (`ERA_VDF_T_CLASS_GROUP`): 60 min at the reference, 45 at the faster run | Measured, set |
| Prove, scheme 0 | eval + prove 11.6 s at T 401,167 (eval 10.0 s): the single-thread block prover is about 14 percent of the evaluation, parallel over residue classes in the node (up to 8) | Measured |
| Verify, scheme 0 | 21.9 and 22.6 ms with the group held (mean of 20); 184 ms with the discriminant derived, the derivation being 161 to 167 ms, once per era | Measured (section 5 for the gate) |
| Reference rate, scheme 1 | 16.2 and 17.0 million SHA-256/s (SHA-NI); 16,000,000 is the reference; `T_era` = 57,600,000,000 hashes (`ERA_VDF_T_HASH_CHAIN`) | Measured, set |
| Verify, scheme 1 | recomputation: 10.1 s for T 170 million, the full hour at `T_era` | Measured |
| Discriminant search | 161 to 167 ms per era (Miller-Rabin with the first 30 primes on the fixed-width integer) | Measured |
| chiavdf on the same core | 208.8 K squarings/s (`vdf_bench square`, NUDUPL over GMP, 1,000,000 iterations); the AVX-512 IFMA path (`square_asm`) gave 127.3 K at 20,000 iterations and stalled at 300,000 and above in this build (built outside its Makefile's `FAST_MACHINE` flags), so the IFMA number is not established here | Measured; the asm path unestablished |
| Delay on the fastest prover measured | 108,000,000 / 208,800 = 517 s against the 1-s block interval (517x) and the 2-s publish window (259x); a prover 10x chiavdf's GMP path (the ceiling Chia's and the EF's hardware efforts aimed at, approximate, from memory) would still take 52 s, 26x the window | Computed from the measurements |
| The gate "at least 60x one block interval on the fastest known prover" | 517x on chiavdf's GMP path, the fastest evaluator measured on this hardware; PASS as measured, with the IFMA path unestablished (above) and the 10x hardware ceiling still 52x | PASS (measured), caveat recorded |
The node's own evaluator is 5.2 to 6.8x slower than chiavdf's GMP path on the same core. That ratio only moves the honest side: `T_era` is set from the node's rate, so an honest node finishes in the hour; the attacker's margin is the delay at the fastest prover, above.
## 3. The gate: the re-roll harness with the VDF off and on
`tools/era-vdf/reroll.mjs` on igneum-build-2 (the box under other lanes' suites, nice 10; logs and per-cut JSON under `/srv/builds/igneum-wt-era-vdf/ev-harness-out/reroll-vdf-{on,off}-5.json`), three nodes of the fork at this record's commit on the fast-time file with `skip_proof_of_work`, era 120 DAA and lead 20 (cuts at `S = 120 n - 20`, one every two minutes), ports 30100 and up, suffix 1010; two honest virtual miners share 1 block/s on nodes 0 and 1; the adversary on node 2 holds a block A built on the tip at `S - 1` and tries to make it the era's cut block. With the VDF on, the adversary runs the node's own evaluator (`igneum-miner vdf eval`, the network's T) over its candidate before publishing; T is set from a 3-s bench at the start so the delay is about 5 s on one core of this box, five times the block interval.
| Run | Switch | Cuts | A accepted | A became the cut block | Known-draw re-rolls (the seed the adversary knew before publishing is the era's seed) | Adversary's evaluation | Nodes agree on the era seed | Gate | Harness |
|---|---|---|---|---|---|---|---|---|---|
| vdf-off-5 (the stand-in, 15:08 to 15:22 UTC) | `era_vdf_activation_daa` never | 6 (eras 2 to 7) | 6 of 6 | 6 of 6 | 6 of 6: the seed is `hash(A)` every time | none needed: the draw of hash(A) is known the instant A is built | 3 of 3 on every era | FAIL (the known-pass fires) | SOUND |
| vdf-on-5 (the era VDF, 14:54 to 15:08 UTC) | activation 0, scheme 0, T 189,650 (5 s on this core) | 6 (eras 2 to 7) | 6 of 6 | 0 of 6 | 0 of 6 | 5.38 to 6.64 s, during which the honest chain advanced 3 to 10 blocks; A arrived behind them and never became the cut block | 3 of 3 on every era, the record ready (state 3) at every era start | PASS (silent) | SOUND |
What the two runs say. Under the stand-in a miner with one block of hash at the right second owns the era draw outright on this network: holding the block at `S - 1` and publishing it the moment the chain reaches `S - 1` makes it the cut block in every one of six cuts (the attack lane's epoch-cut run saw 1 of 6, with the honest block often landing first; here the adversary is faster to the second), and its draw is known the instant the block is built. Under the VDF the same adversary cannot know any candidate's draw before T steps have run; while it ran them the honest chain moved 3 to 10 blocks, so its block arrived behind the cut and the seed came from the delay over the day of blues ending at the honest cut block, the same on all three nodes. The second half of the written argument of F7 (a) holds in the node, not only on paper: the re-roll needs the draw inside the window, and the window is 1 s against a delay of 5 s here and 517 s at the fastest prover measured on the production T (section 2).
The known-pass and the known-fail ran on the same binaries, file and ports, the VDF switch the only difference. A first VDF-off run with a lookup defect in the harness (the adversary's block not found, so the verdict read "held") was discarded once the node's own log showed the adversary's hash as the cut block in 5 of 5 cuts; the harness now reads A from that log line. Two runs that overlapped on the box through a stale node of an earlier run were discarded as well (their nodes disagreed because they were two networks); the two runs above ran alone.
## 4. The cut rule and the certified checkpoint (for the finality lane)
The node names `C_era(n)` as the last selected-chain block below the cut on the header's own chain (the block the stand-in used), which is the checkpoint block the lead rule names under the O-4.3 decision of 3 October 2026 (certified or not). Three facts decide it:
1. Determinism. A header's validity must be a function of its own past. "The certificate carried by a block in the header's past, for the highest-index checkpoint with DAA score at most the cut" is such a function, but a certificate that lands after the era starts flips the reading between headers of one era (a header before the carrier reads the fallback, a header after it reads the certificate), so the certified binding needs a second rule: the carrier must sit at most half a lead above the cut (3,600 DAA s, the merge depth) and be a chain ancestor of the header; under that rule every honest header of the era reads the same certificate once the network merged the carrier, and a header on a chain that never merged it reads the fallback, consistently with its own past. The chain-block reading needs no second rule.
2. Liveness. A finality pause across the cut (a third of weight leaving in an hour is a 30-day pause under rule v3) leaves the certified binding without a checkpoint for the era; the chain-block reading always has one, which is the reason O-4.3 was decided the way it was for the epoch.
3. The defence. The grinding defence is the delay: no candidate's draw is knowable for T steps, whichever block is the cut. The binding moves which block a withholder would have to be the author of, not whether withholding pays; both readings leave the withholder with a coin flip it cannot see.
The change, if the finality lane wants the certified binding: `EraVdfManager::cut_block` (one function; the input, the delay and the seed are unchanged), plus the second rule above and a test with a certificate carried late.
## 5. The verify gate on a 2019-class core
The gate was "the VDF verifies in under 10 ms on a 2019-class core". Measured: 21.9 and 22.6 ms with the group held on the box core (above), which the F6 row's calibration puts at about 1.19x on an i7-9700K (the O-1.14 run: 6.0 ms on the 2019 core against 5.06 ms on the box proxy), so about 26 ms on a 2019 core, labelled a proxy: no 2019 host was rented this lane (Vast rentals are a purchase; not made without the project lead's word). NOT MET, by 2.2x on the box and about 2.6x on the proxy.
Where the time goes and what closes it: a verification is two 256-bit exponentiations, about 770 group operations at 28 µs each; the operation is NUDUPL on the fixed-width integer, whose cost is the extended gcd (Lehmer rounds on 8-limb numbers) and the reduction. Three rounds of this lane moved it from 345 µs (a Lehmer convention defect that fell back to plain division every round) to 28 µs (the convention, i64 word division, 34 limbs, the x86-64 128-by-64 division); the next 2.2x is chiavdf's Pulmark reducer (reduce only when `a` exceeds 8 limbs, O-4.6) and a limb-level NUDUPL that keeps the partial gcd's intermediates in words, or GMP through `rug` behind a feature on the x86-64 Linux and Windows builds (chiavdf's 208 K/s is 6x this evaluator, which would put the verification near 4 ms as the prototype measured), with the fixed-width path the fallback for wasm and macOS. Owed, not blocking: the verification runs once per era (180 days) on a node that imports a record rather than evaluating; every mining node evaluates and never verifies.
## 6. Consequences per tier (the standing rule of 5 October 2026)
| Tier | What the era VDF costs | What it means |
|---|---|---|
| A home miner, any card (8, 12, 16, 24 or 32 GB), any vendor, Windows, Linux or macOS | one CPU core for about 60 min once per 180 days at the reference rate (a 2019-class desktop core about 72 min by the F6 calibration), 17 MB of host RAM for the prover's checkpoints during it, 0 bytes on the card; the node starts it a quarter of the lead past the cut and holds the record from then | nothing changes on the card or in the hash rate; the 2-hour lead covers a core half the reference speed; a node that was off across the cut evaluates on arrival and its miner holds until the record lands (the miner says so every 10 s) |
| A rig (one node, several cards) | the same one core on the rig's host, once per era | nothing per card |
| A pool user | the pool's node evaluates; the member's miner takes `era_seed` from the template as today | nothing |
| A light client or a syncing node | verifies an imported record in 22 ms (26 ms on a 2019 core, proxy) plus the 165-ms discriminant derivation, once per era; under scheme 1 it recomputes the hour | the 10-ms gate is missed (section 5); operationally one verification per 180 days |
| The protocol | the era draw's input is unknowable for 517 s on the fastest prover measured, against a 1-s block interval: the stand-in's one-block grind is closed (section 3) | the freeze of the draw procedure and the C_era cut rule no longer waits on the VDF's existence; it waits on the two decisions of `ledger-decisions.md` |
## 7. What is owed
- The P2P relay of an era record to a syncing peer and the RPC import (spec 4.5, O-4.10), before era 1 of any network with the switch set.
- The external review of the class-group port (O-4.1): the port is a second implementation checked against the textbook algorithms and `num-bigint`, not a review.
- The attack pass's F7 status row (branch `attack-pass`, `docs/analysis/attack-pass-2026-10.md`) reads INCOMPLETE pending this lane; the line for it, from section 3: "F7 (a): the era VDF is in the node (fork `era-vdf-node`); the re-roll harness against the real era cut fires with it off (6 of 6 cuts, the seed the adversary's block) and is silent with it on (0 of 6 across six cuts, the adversary's 5-s evaluation against a 1-s block interval, three nodes agreeing on every era seed); PASS, the delay 517 s on the fastest prover measured at the production T."
- The decisions of `docs/plans/ledger-decisions.md` (the activation per network, the cut's binding).

View file

@ -2521,6 +2521,25 @@ same once; a pool user nothing; a fleet operator gets a node that keeps its only
every peer (13,354 lines of work it did not need in seven minutes). Owed: the fleet agent's synced-node reading; a receiver-side limit on
certificates per index per minute as a second belt once the seed is fixed; the formatter's reflow of `finality.rs` (taken out of the commit).
## 6 October 2026, Counter ASIC 3.0: the class v4 rehearsal
A fleet of real GPU boxes ran a fleet-only chain from the devnet's genesis state and crossed a class v4 activation by miner signal, in the shape the live devnet will see. Numbers only; the per-box record is `docs/plans/counter-asic-3-gate/class-v4-20261006-rehearsal-PASS.json`.
| Number | Value |
|---|---|
| Nodes on the chain | 16 (15 mining, 1 seed) plus 37 more miners joining after the flip; 1 box left on the old binary as the stale case |
| Object | 17 fields, epochs of 600 DAA, the signal window 600 DAA, the threshold 9,500 bps, the floor at DAA 2,400; one consensus digest on every node |
| First block | 18:41:10 UTC |
| The flip | DAA 1,202, 19:00:5x UTC, by miner signal at epoch 2: 10,000 bps over the window, 599 of 599 blue blocks, the identical line on all 52 signalling nodes |
| Program ids | one id per epoch on every box for epochs 2, 3 and 4, each equal to the CPU verifier's class v4 id for that seed and era and different from the class v3 id of the same seed; the pre-flip epoch's id is a class v3 id |
| Blocks rejected for proof of work | 0 on every node over the whole run; every miner's CPU re-check mismatched 0 |
| The old-binary box | refused at every connect by consensus digest (11 refusals, 22 mismatch lines), never a peer; given the full object it exits at parse |
| The floor | crossed at DAA 2,400 with v4 already in force; the chain mined through it at about 1.6 blocks/s; one chain, no fork at the 19:34:54 UTC sweep (heights 2,502 to 2,509, blue 2,432 to 2,440) |
| Verifier against the GPU on the first v4 pack | 1,024 of 1,024 nonces at target ff..ff found by the GPU worker and re-derived by the CPU verifier, one digest on both sides |
| The stall | 90 s at the flip and then a stop: the shipped worker of the previous release refuses a generator-4 pack by design; the fleet resumed on the release tree's generator-4 worker 15 minutes later |
What it means per tier: a class change on this chain is decided by the miners' own signal and lands at an epoch boundary with no human release; an old node cannot join the new chain and an old worker cannot mine the new class, so a home miner, a rig and a pool update the node and the worker together before the signal window closes, which the one-click app does in one update; a chip built for the old class mines nothing from the flip. The live cut carries the one lesson: the new object is published only once every node and every worker runs the release that reads it.
## 6 October 2026, 16:01Z: Ember run 6 on PC 1 (ember-tune-pc1-6, 0.3.13 + kit-6 = 564bdea, elevated, one click)
The helper registered inside the run but on the scratch copy (fixed: task_exe, the `reregister` verb; see the plan's
@ -2633,3 +2652,32 @@ in the same shape and reports a box behind its wanted binary.
| Rig | the same, and a rig that leaves is itself a weight removal: at 459 MH/s on tonight's devnet it is about 20 percent of the weight, over the hour's budget by itself |
| Pool | a pool node is one voter carrying its members' whole weight; a pool restart is the largest single removal on the network and must be sliced like the fleet's |
| The network | finality by miner weight is only as steady as the miners' uptime; until public hash dwarfs the fleet, the fleet's supervisor is a consensus component |
## 7 October 2026, the first 16 GB card: an RTX 5060 Ti in a Thunderbolt enclosure on PC 2 (branch bench-5060ti)
Machine: PC 2 (`1ccfe586`, Windows 11), an ASUS Dual GeForce RTX 5060 Ti (16 GB GDDR7, Blackwell sm_120, PnP `PCI\VEN_10DE&DEV_2D04&SUBSYS_8A111043`) in a Razer Core X V2 Thunderbolt enclosure ("USB4 Router (2.0), Razer - Core X V2", bus `0B:00.0`), beside the RTX 5090 on its own supply; NVIDIA driver 610.47 (WDDM 32.0.16.1047, the 5090's driver, nothing installed for the new card); the installed app 0.3.19 and its own `igneum-worker-cuda.exe` (NVRTC 12.8). Jobs `fetch-5060ti-packs-20261007` (the kit: `tools/bench-5060ti/make-kit.sh`, the class v4 pack at sub-version 1 and the v3 control, sha256 `fd8393ed...`, 105,892 bytes) and `run-5060ti-bench-20261007-b` (`tools/bench-5060ti/pc2-5060ti-bench.ps1`, 14:44:19Z, ran 14:45:05 to 14:58:11Z, exit 0; the 5060 Ti alone through the runner's `--cards-off`, the 5090 mining throughout; run `-a` died in 1 s on an argument-binding fault in the nvidia-smi query and is void). PC 2 lost power twice that day, so the job WRITES NO POWER LIMIT: it reads `power.limit` against `power.default_limit` (180 W = 180 W, range 150 to 198 W) and the row says `limit_is_stock=yes`. Read back with `node tools/jobs.mjs run-5060ti-bench-20261007-b`.
**Detection** (the app's first poll after the restart, run `win-1ccfe586-20261007-143611`, 14:36:16Z): `GPUs: NVIDIA GeForce RTX 5090 (CUDA); NVIDIA GeForce RTX 5060 Ti (CUDA); AMD Radeon(TM) Graphics (OpenCL, gfx1036)` and `cards: NVIDIA GeForce RTX 5090 [discrete, off] | NVIDIA GeForce RTX 5060 Ti [discrete, off] | ...`; the app started a miner on it by itself (`nvidia-1ccfe586-2`, `--device 1`, 8 identities, the default). The kind reads `discrete`, not `external`: the app does not know it is an eGPU. nvidia-smi in the job: index 1, 16,311 MiB, PCIe link gen 4 x4 current against gen 4 x16 maximum (the Thunderbolt link: a quarter of the slot's lanes), 43 C idle. The freeze lane's cause class for the 15:10 UK hang on the first boot with the card: not the card (no TDR, no Thunderbolt or PCIe link event; Kernel-Power 41 + 6008, no bugcheck, the power shape again).
**G1 and the window** (the installed CUDA worker, `--bench --batch-log2 24 --block-warps 1`, the card alone, `CUDA_VISIBLE_DEVICES` on its UUID so every row names the device; nvidia-smi every 2 s on the card, the loaded samples at utilisation 90 percent and over):
| Pack | Dispatches of 2^24 | Self-test | Fingerprint 2^24 at base 0 | MH/s |
|---|---|---|---|---|
| mx8-devnet-epoch0 (the class v3 control) | 5 | PASS | 90f794dd556f7a3b (= the control everywhere) | 30.895 |
| v4-devnet-epoch0 (class v4, sub-version 1, program id 1a4230699a6b9c60) | 5 | PASS | 867dbc45cfb36b4d (= Metal, Apple OpenCL, the RTX 5090) | 30.879 |
| v4-devnet-epoch0, the 10-minute window at the stock limit | 1,105 (602 s) | PASS | 867dbc45cfb36b4d | 30.882 |
| Row | Value |
|---|---|
| NVIDIA RTX 5060 Ti 16 GB, class v4, CUDA (NVRTC), driver 610.47, PCIe 4.0 x4 through the enclosure | 30.9 MH/s over 10 minutes on the card alone |
| Watts at the stock limit (180 W default, unchanged) | 114.8 W mean, 115 W p50 over the window; 0.269 MH/W; SM 2,753 MHz, memory 13,801 MHz, 60 C maximum |
| The class v4 shadow against the control | 0.1 percent (the 5090 paid 0.2, the 9070 XT 3, the B580 0.1) |
| The efficient point | OWED to the app's Ember Tune: nothing set by the job; PC 2's Power Helper refused every request since the restart ("the helper did not run sequence 0 within 15 s", 14:39Z), so no ladder ran on either card |
| Prove beside the miner (16 GB tier) | BLOCKED, not measured: the shipped WSL2 host (sha `71bc2438...`) carries no `IGNEUM_CUDA_DEVICE` selector, so aimed at anything it proves on CUDA device 0 (the 5090) through the app's own socket `/tmp/sp1-cuda-0.sock`; the selector lives in the prover-floor host (`proof_system.rs`, branch prover-floor) and is the owed cut. The job's inventory: the floor server IS on PC 2 (`/opt/igneum-floor/bin/sp1-gpu-server`, 6.8.1 build `e911facb...`, 166,665,880 bytes) beside the stock one (`~/.sp1/bin`, `c2642ad1...`), WSL sees the card as CUDA device 1 |
| Card-picker entry (`site/yourcard.js`) | `['NVIDIA RTX 5060 Ti', 30.9]`, added; the public table row in `site/miner-bench.json` |
Against the 5090 on the same PC (122 MH/s at 308 W, 0.396 MH/W): 25.3 percent of its hash at 37 percent of its draw, 68 percent of its hash per watt. The dependent-read ceiling was not probed (the memprobe step is not in this job); at 128 loads a hash 30.9 MH/s is 3.95 G dependent reads a second, between the 9070 XT (2.4 to 2.7 G) and the 5090 (16 to 18 G).
Consequences per tier (the rule of 5 October 2026): a 5060 Ti owner (16 GB, Windows) mines at 30.9 MH/s and 115 W from the box with nothing to set: about 5,100 blocks a day at the 522 MH/s the devnet showed at 14:44Z (one every 17 s, approximate: the network rate moves), about a quarter of a 5090 owner's 20,200, for 2.76 kWh a day (£0.79 at 28.5 p against the 5090's £2.11); through a Thunderbolt enclosure the x4 link costs nothing measurable (the hash is bound by the card's own memory latency, not the link; the 5090's PCIe-slot rows are the comparison), so a laptop with a Thunderbolt 4 port and this enclosure is a 31 MH/s miner. The 8 GB 5060 Ti: the same hash is the expectation (the 1 GiB dataset fits), a line owed. Proving on the 16 GB tier: the fleet's 4060 Ti 16 GB row (9.0 GB peak beside the miner on the patched server) says this card would mine and prove with about 7 GB spare, approximate until the host with the device selector ships; today the app's prover default leaves it off ("a full shard needs a 24 GB card") and the measured read is owed to the prover-floor host cut. Linux and HiveOS take the same CUDA worker (owed a line). What the lane does next: the prover-floor host's selector into the shipped WSL2 bundle, then the prove-beside read on this card; the Power Helper fault on PC 2 to the Ember lane (no efficient point on any PC 2 card until it answers).
Found on the way: inside a PowerShell `@( ... )` the comma binds before `+`, so `'--query-gpu=' + $f, '--format=csv'` is one argument (run a, void in 1 s; the query string is built first now); a bare string inside a function that also returns a value is swallowed into the caller's variable (the sampler line; `[Console]::Out.WriteLine` now); the app's `kind` for a Thunderbolt card reads `discrete` (a word for the Cards page to earn: `external`, which the state already names).

View file

@ -14,6 +14,7 @@ eight fields, UK time in the footer with UTC in brackets, never a mention, never
| Weekly numbers | #numbers | Monday 09:00 | the reddit kit's template (docs/community/reddit/posts/02-weekly-numbers-template.md) as six fields with the last-week column, a link to the ledger |
| Release | #announcements | on a publish, by the shipper | version, three download links with full sha256, node commit, consensus digest, up to five "what changed" lines read from the release plan |
| Incident open / resolve | #incidents | by hand, or by the watcher | UTC time, what happened, who is affected per tier, what is being done; then cause, the rule or fix, duration |
| Network feed | #network-feed | every hour, when `DISCORD_WEBHOOK_FEED` is set | height and blocks/s over the hour, hash rate and difficulty, keys active and voters, the last lock; the daily hash-origin field once per report date from `/api/live state.hash_origin`; milestones once each (every 100,000 blocks, 10,000 locks, 10,000 paid shards; the first crossing of a vote-key count and of a hash-rate step). Added 7 October 2026; docs/community/discord/structure.md section 4.1 |
Every number comes from the public API (`/api/stats`, `/api/live?window=300`, `/api/supply`; docs/api/public-stats.md). The
Devnet 2 line reads the fleet file and uses its numbers and the gate word only; the seed address and the state text never pass.
@ -35,6 +36,7 @@ The watcher's texts are fixed sentences in the script, each a stated rule, and a
```
node tools/community/discord-hooks.mjs pulse | digest | weekly [--live] [--force]
node tools/community/discord-hooks.mjs feed [--live] [--via updates] the hourly feed; --via updates posts it through DISCORD_WEBHOOK_UPDATES until #network-feed has a webhook
node tools/community/discord-hooks.mjs release 0.3.14 --windows <url> --windows-sha <hex> --mac <url> --mac-sha <hex> --hive <url> --hive-sha <hex> \
--node-commit 4c6b129d --digest "b18ed271 (thirteen fields, unchanged)" --plan docs/plans/release-0.3.14.md --section "1. Why" [--changed "line"]... [--live]
node tools/community/discord-hooks.mjs incident open --what "..." --affected "..." --doing "..." [--at 2026-10-06T15:44:00Z] [--id inc-...] [--live]
@ -65,7 +67,9 @@ parenthetical dropped, 160 characters at most; a line that trips the guard is dr
## Credentials and deployment
`~/.config/igneum/discord`, mode 600, KEY=VALUE lines: `DISCORD_WEBHOOK_NUMBERS`, `DISCORD_WEBHOOK_ANNOUNCEMENTS`,
`DISCORD_WEBHOOK_INCIDENTS`. Never in the repository, never printed; `check` prints which keys are set.
`DISCORD_WEBHOOK_INCIDENTS`; optional `DISCORD_WEBHOOK_FEED` (no key, no feed, one "feed off" note per tick) and
`DISCORD_WEBHOOK_UPDATES` (the hidden updates channel, the `--via updates` route). Never in the repository, never printed;
`check` prints which keys are set.
The scheduler runs on igneum-build-1 because the Mac sleeps: `infra/build-server/discord-hooks/{igneum-discord-hooks.service,
igneum-discord-hooks.timer, install.sh}`, a tick every minute. `install.sh` copies the script to `/srv/discord-hooks/bin`, the

View file

@ -0,0 +1,68 @@
# Discord server assets and boost perks
Server: Igneum (id 1557085229330464808), vanity https://discord.gg/igneum, boosted to level 3 on 7 October 2026 (20 boosts: 14 on the three levels, 3 on the Server Tag add-on, 3 on the Enhanced Role Styles add-on; 0 spare).
Every file in `assets/` is built from the brand in this repo: the ember mark from `site/index.html`, the colour tokens from `site/site.css` (obsidian #0C0C0E, ember #F2541B, ember-hi #FF6A2B, molten #FFB35C, bone #F4F1EC, ink-2 #C9C7C2, ash #9A9A9E), the fonts in `site/fonts/` (Unbounded for the wordmark, IBM Plex Sans and Mono for the rest) and the DAG step recording `docs/plans/site-ui-3-shots/after-v2/home-steps-1440-dark.webm`.
Rebuild everything from the repo root:
```
python3 docs/community/discord/make-assets.py [path to a frame of the recording for the invite backdrop]
sh docs/community/discord/make-banner-gif.sh
```
The guide banner (1920x480) was rendered with the same helpers; see the commit that added it for the snippet.
## Files
| File | Size | Purpose | Where it is set |
| --- | --- | --- | --- |
| `server-banner-960x540.gif` | 5.9 MB | Animated server banner: nine seconds of the live DAG scene between two dark bands that carry the lockup and the tagline (level 3 perk, limit 10 MB) | Server Settings, Boost Perks, Server Banner Background |
| `server-banner-960x540.png` | 42 KB | Static fallback for the same slot | Not uploaded (the GIF is live) |
| `banner-overlay-960x540.png` | 16 KB | Transparent overlay ffmpeg composites onto the recording for the GIF | Build input only |
| `invite-background-1920x1080.png` | 368 KB | Invite embed and invite page background: lockup and tagline over a blurred, darkened DAG frame | Server Settings, Boost Perks, Server Invite Background |
| `guide-banner-1920x480.png` | 69 KB | Server Guide header (4:1) | Server Settings, Onboarding, Server Guide, Server Guide Banner |
| `role-*.png` | 2 to 5 KB each, 64x64 | Role icons, one mark variant per role (limit 256 KB) | Server Settings, Roles, each role's Display tab |
| `emoji-*.png` | 2 to 8 KB each, 128x128 | The ten custom emoji | Server Settings, Emoji |
| `sticker-*.png` | 9 to 24 KB each, 320x320 | The three stickers (limit 512 KB) | Server Settings, Stickers |
| `make-assets.py` | | Generator for every PNG above | |
| `make-banner-gif.sh` | | ffmpeg recipe for the animated banner | |
## Role ladder (7 October 2026)
| Role | Colour | Style | Icon | Hoisted | Permissions |
| --- | --- | --- | --- | --- | --- |
| Founder | unchanged | solid | `role-founder.png` (molten mark on obsidian) | unchanged | untouched |
| Core | unchanged | solid | `role-core.png` (ember-hi mark on obsidian) | unchanged | untouched |
| Pool operator | #FF6A2B | gradient ember to molten | `role-pool-operator.png` (mark in a molten ring) | yes | none |
| Node runner | #F4F1EC | gradient ember to molten | `role-node-runner.png` (bone mark) | yes | none |
| Verified miner | #F2541B | gradient ember to molten | `role-verified-miner.png` (ember mark with a tick) | yes | none |
| Miner | #F2541B | gradient ember to molten | `role-miner.png` (ember mark) | yes | unchanged |
| Early miner | #FFB35C | solid | `role-early-miner.png` (obsidian mark on molten) | no | none |
| Prover | #FFB35C | gradient ember to molten | `role-prover.png` (molten mark) | no | unchanged |
| Builder | #E8E4DC | solid | `role-builder.png` (bone mark on graphite) | no | Embed Links, Attach Files |
| Researcher | #9A9A9E | solid | `role-researcher.png` (ash mark) | no | Embed Links, Attach Files |
| Community | #C9C7C2 | solid | `role-community.png` (outlined mark) | no | none |
| Bot | #9A9A9E | solid | `role-bot.png` (ash mark on row) | no | unchanged |
"None" means the role grants nothing of its own; members fall back to @everyone. Early miner is for the first 1,000 miners. Gradient roles use the Enhanced Role Styles add-on with start #F2541B and end #FFB35C. Holographic was tried on Founder and left off: it is a fixed pink and blue shimmer with no colour control and does not read as the brand.
List order (set by drag on 7 October 2026): Founder, Core, Pool operator, Node runner, Verified miner, Prover, Miner, Early miner, Builder, Researcher, Community, Bot, Server Booster.
## Emoji and stickers
Emoji names: `ign_ember`, `ign_block`, `ign_shard`, `ign_lock`, `ign_gpu`, `ign_proven`, `ign_hash`, `ign_letter`, `ign_flame`, `ign_ladder`.
Stickers: Ember (related emoji fire), Proven (white_check_mark), GPU (desktop_computer). Three of the five free slots are used.
## Onboarding
Server Guide: welcome sign plus five to-dos (start-here, mining, ledger, announcements, read the rules) and the guide banner. Pre-join question "What do you mine with?" with five answers: NVIDIA, AMD and Apple grant Miner and #mining; "I run a node" grants Node runner and #devnet; "I build" grants Builder and #proving. Multiple answers allowed, not required.
## Server Tag
IGNM, enabled 7 October 2026 on the 3-boost add-on. Badge: Fire (Discord offers only its own pixel-art badge set, no custom upload, so the ember mark cannot be the badge; Fire is the nearest). Badge colour: custom, primary #F2541B. Members adopt the tag from Server Settings, Server Tag ("Adopt Tag") or from their own profile; the founder's account has not adopted it yet.
## Not set, and why
- Server Profile banner: that field only offers colour presets; it stays on "Server Icon Colour", which derives from the ember icon.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,379 @@
#!/usr/bin/env python3
"""Build the Discord boost assets from the brand (mark, fonts, tokens in site/site.css).
Run from the repo root: python3 docs/community/discord/make-assets.py
Writes into docs/community/discord/assets/. The animated banner is built by ffmpeg from
docs/plans/site-ui-3-shots/after-v2/home-steps-1440-dark.webm (see make-banner-gif.sh).
"""
import math
import os
import sys
from PIL import Image, ImageDraw, ImageFont, ImageFilter
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
OUT = os.path.join(ROOT, "docs", "community", "discord", "assets")
FONTS = os.path.join(ROOT, "site", "fonts")
os.makedirs(OUT, exist_ok=True)
# site.css tokens (dark theme)
OBSIDIAN = (12, 12, 14)
GRAPHITE = (22, 22, 26)
ROW = (17, 17, 20)
LINE = (42, 42, 48)
LINE2 = (58, 58, 66)
EMBER = (242, 84, 27)
EMBER_HI = (255, 106, 43)
MOLTEN = (255, 179, 92)
BONE = (244, 241, 236)
INK2 = (201, 199, 194)
ASH = (154, 154, 158)
# The ember mark, from site/index.html (viewBox 100 units)
OUTER = [(50, 4), (74, 34), (67, 58), (80, 54), (61, 96), (39, 96), (20, 54), (33, 58), (26, 34)]
INNER = [(50, 42), (59, 58), (50, 82), (41, 58)]
SS = 4 # supersample
def font(name, size):
return ImageFont.truetype(os.path.join(FONTS, name + ".woff2"), size)
def rgba(color, a=255):
return tuple(color) + (a,)
def mark(size, color=EMBER, cut=None, box=None):
"""Return an RGBA image of the ember mark. `cut` fills the inner diamond (None = transparent)."""
W = size * SS
im = Image.new("RGBA", (W, W), (0, 0, 0, 0))
d = ImageDraw.Draw(im)
if box is not None:
d.rounded_rectangle([0, 0, W - 1, W - 1], radius=int(W * 0.16), fill=rgba(box))
pad = 0.12 if box is None else 0.19
s = W * (1 - 2 * pad) / 100.0
o = W * pad
pts = [(o + x * s, o + y * s) for x, y in OUTER]
d.polygon(pts, fill=rgba(color))
inner = [(o + x * s, o + y * s) for x, y in INNER]
if cut is None:
hole = Image.new("L", (W, W), 0)
ImageDraw.Draw(hole).polygon(inner, fill=255)
alpha = im.getchannel("A")
alpha = Image.composite(Image.new("L", (W, W), 0), alpha, hole)
im.putalpha(alpha)
else:
d.polygon(inner, fill=rgba(cut))
return im.resize((size, size), Image.LANCZOS)
def text_size(d, txt, f):
l, t, r, b = d.textbbox((0, 0), txt, font=f)
return r - l, b - t, l, t
def lockup(width, mark_px, word_px, word_color=BONE, mark_color=EMBER, gap=None):
"""Mark + IGNEUM wordmark on a transparent strip, returned as RGBA with the strip height."""
f = font("unbounded-900", word_px)
probe = ImageDraw.Draw(Image.new("RGBA", (10, 10)))
tw, th, tl, tt = text_size(probe, "IGNEUM", f)
gap = gap or int(mark_px * 0.32)
H = max(mark_px, th + 8)
im = Image.new("RGBA", (mark_px + gap + tw + 4, H), (0, 0, 0, 0))
m = mark(mark_px, mark_color)
im.alpha_composite(m, (0, (H - mark_px) // 2))
d = ImageDraw.Draw(im)
d.text((mark_px + gap - tl, (H - th) // 2 - tt), "IGNEUM", font=f, fill=rgba(word_color))
return im
def glow(base, mark_px, cx, cy, color=EMBER, spread=1.6, alpha=70):
g = Image.new("RGBA", base.size, (0, 0, 0, 0))
r = int(mark_px * spread / 2)
ImageDraw.Draw(g).ellipse([cx - r, cy - r, cx + r, cy + r], fill=rgba(color, alpha))
g = g.filter(ImageFilter.GaussianBlur(mark_px * 0.45))
base.alpha_composite(g)
# ---------------------------------------------------------------- banners
def banner_static(W, H, name, mark_px, word_px, tag_px, sub_px, backdrop=None):
im = Image.new("RGBA", (W, H), rgba(OBSIDIAN))
if backdrop is not None:
bd = Image.open(backdrop).convert("RGB")
# crop the recording frame to 16:9 and darken it
bw, bh = bd.size
ch = int(bw * H / W)
bd = bd.crop((0, 60, bw, 60 + ch)).resize((W, H), Image.LANCZOS)
bd = bd.filter(ImageFilter.GaussianBlur(W * 0.004))
bd = Image.blend(Image.new("RGB", (W, H), OBSIDIAN), bd, 0.34)
im.paste(bd, (0, 0))
scrim = Image.new("RGBA", (W, H), (0, 0, 0, 0))
sd = ImageDraw.Draw(scrim)
for y in range(H):
a = int(255 * (0.25 + 0.55 * (y / H) ** 1.4))
sd.line([(0, y), (W, y)], fill=rgba(OBSIDIAN, min(255, a)))
im.alpha_composite(scrim)
lk = lockup(W, mark_px, word_px)
cx = (W - lk.width) // 2
cy = int(H * 0.40) - lk.height // 2
glow(im, mark_px, cx + mark_px // 2, cy + lk.height // 2)
im.alpha_composite(lk, (cx, cy))
d = ImageDraw.Draw(im)
f_tag = font("unbounded-700", tag_px)
tw, th, tl, tt = text_size(d, "Mined by GPUs. Proven by fire.", f_tag)
ty = cy + lk.height + int(H * 0.06)
d.text(((W - tw) // 2 - tl, ty - tt), "Mined by GPUs. Proven by fire.", font=f_tag, fill=rgba(MOLTEN))
f_sub = font("plex-sans-500", sub_px)
sub = "The GPU-mined layer 1 · igneum.network"
sw, sh, sl, st = text_size(d, sub, f_sub)
d.text(((W - sw) // 2 - sl, ty + th + int(H * 0.035) - st), sub, font=f_sub, fill=rgba(INK2))
path = os.path.join(OUT, name)
im.convert("RGB").save(path, optimize=True)
return path
def banner_overlay(W, H, name, mark_px, word_px, band):
"""Transparent overlay for the animated banner: the graph sits between two dark bands
(ffmpeg pads it); the lockup goes in the top band, the tagline in the bottom band."""
im = Image.new("RGBA", (W, H), (0, 0, 0, 0))
d = ImageDraw.Draw(im)
lk = lockup(W, mark_px, word_px)
x = int(W * 0.035)
im.alpha_composite(lk, (x, (band - lk.height) // 2))
f_tag = font("unbounded-700", int(word_px * 0.62))
tw, th, tl, tt = text_size(d, "Mined by GPUs. Proven by fire.", f_tag)
d.text((x - tl, H - band + (band - th) // 2 - tt), "Mined by GPUs. Proven by fire.", font=f_tag, fill=rgba(MOLTEN))
f_sub = font("plex-sans-500", int(word_px * 0.5))
sw, sh, sl, st = text_size(d, "igneum.network", f_sub)
d.text((W - x - sw - sl, H - band + (band - sh) // 2 - st), "igneum.network", font=f_sub, fill=rgba(INK2))
path = os.path.join(OUT, name)
im.save(path, optimize=True)
return path
# ---------------------------------------------------------------- role icons
def role_icons():
out = {}
spec = {
"founder": dict(color=MOLTEN, box=OBSIDIAN),
"core": dict(color=EMBER_HI, box=OBSIDIAN),
"miner": dict(color=EMBER),
"prover": dict(color=MOLTEN),
"pool-operator": dict(color=EMBER_HI, ring=True),
"node-runner": dict(color=BONE),
"verified-miner": dict(color=EMBER, tick=True),
"early-miner": dict(color=OBSIDIAN, box=MOLTEN),
"builder": dict(color=BONE, box=GRAPHITE),
"researcher": dict(color=ASH),
"community": dict(color=INK2, outline=True),
"bot": dict(color=ASH, box=ROW),
}
for name, s in spec.items():
size = 64
W = size * SS
if s.get("outline"):
im = Image.new("RGBA", (W, W), (0, 0, 0, 0))
d = ImageDraw.Draw(im)
pad = 0.12
sc = W * (1 - 2 * pad) / 100.0
o = W * pad
pts = [(o + x * sc, o + y * sc) for x, y in OUTER]
d.line(pts + [pts[0]], fill=rgba(s["color"]), width=int(W * 0.055), joint="curve")
inner = [(o + x * sc, o + y * sc) for x, y in INNER]
d.polygon(inner, fill=rgba(s["color"]))
im = im.resize((size, size), Image.LANCZOS)
else:
im = mark(size, s["color"], cut=(s["box"] if s.get("box") else None), box=s.get("box"))
if s.get("ring"):
big = im.resize((W, W), Image.LANCZOS)
d = ImageDraw.Draw(big)
d.ellipse([W * 0.02, W * 0.02, W * 0.98, W * 0.98], outline=rgba(MOLTEN), width=int(W * 0.05))
im = big.resize((size, size), Image.LANCZOS)
if s.get("tick"):
big = im.resize((W, W), Image.LANCZOS)
d = ImageDraw.Draw(big)
r = W * 0.19
cx, cy = W * 0.80, W * 0.80
d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=rgba(MOLTEN))
d.line([(cx - r * 0.5, cy), (cx - r * 0.1, cy + r * 0.42), (cx + r * 0.55, cy - r * 0.42)],
fill=rgba(OBSIDIAN), width=int(W * 0.045), joint="curve")
im = big.resize((size, size), Image.LANCZOS)
path = os.path.join(OUT, "role-%s.png" % name)
im.save(path, optimize=True)
out[name] = path
return out
# ---------------------------------------------------------------- emoji
def emoji_canvas(size=128):
W = size * SS
return Image.new("RGBA", (W, W), (0, 0, 0, 0)), W
def finish(im, size, path):
im.resize((size, size), Image.LANCZOS).save(path, optimize=True)
return path
def emoji_set():
size = 128
paths = {}
# 1 the ember
paths["igneum"] = mark(size, EMBER)
paths["igneum"].save(os.path.join(OUT, "emoji-igneum.png"), optimize=True)
paths["igneum"] = os.path.join(OUT, "emoji-igneum.png")
# 2 a block (the DAG square: rounded outline, ember, dark fill)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
m = W * 0.12
d.rounded_rectangle([m, m, W - m, W - m], radius=int(W * 0.14), fill=rgba(GRAPHITE),
outline=rgba(EMBER), width=int(W * 0.085))
paths["block"] = finish(im, size, os.path.join(OUT, "emoji-block.png"))
# 3 a shard (a tall molten crystal with one lit facet)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
p = [(0.50, 0.06), (0.78, 0.40), (0.62, 0.94), (0.38, 0.94), (0.22, 0.40)]
d.polygon([(x * W, y * W) for x, y in p], fill=rgba(MOLTEN))
d.polygon([(x * W, y * W) for x, y in [(0.50, 0.06), (0.78, 0.40), (0.62, 0.94), (0.50, 0.40)]], fill=rgba(EMBER))
paths["shard"] = finish(im, size, os.path.join(OUT, "emoji-shard.png"))
# 4 a lock (ember body, bone shackle, dark keyhole)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
sw = int(W * 0.09)
d.arc([W * 0.26, W * 0.06, W * 0.74, W * 0.60], 180, 360, fill=rgba(BONE), width=sw)
d.line([(W * 0.26 + sw / 2, W * 0.33), (W * 0.26 + sw / 2, W * 0.50)], fill=rgba(BONE), width=sw)
d.line([(W * 0.74 - sw / 2, W * 0.33), (W * 0.74 - sw / 2, W * 0.50)], fill=rgba(BONE), width=sw)
d.rounded_rectangle([W * 0.14, W * 0.46, W * 0.86, W * 0.94], radius=int(W * 0.10), fill=rgba(EMBER))
d.ellipse([W * 0.43, W * 0.60, W * 0.57, W * 0.74], fill=rgba(OBSIDIAN))
d.rounded_rectangle([W * 0.465, W * 0.68, W * 0.535, W * 0.84], radius=int(W * 0.03), fill=rgba(OBSIDIAN))
paths["lock"] = finish(im, size, os.path.join(OUT, "emoji-lock.png"))
# 5 a GPU (graphite card, two ember fans, bone bracket)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
d.rounded_rectangle([W * 0.06, W * 0.24, W * 0.94, W * 0.78], radius=int(W * 0.08), fill=rgba(GRAPHITE),
outline=rgba(LINE2), width=int(W * 0.03))
d.rectangle([W * 0.06, W * 0.78, W * 0.16, W * 0.90], fill=rgba(BONE))
d.rectangle([W * 0.20, W * 0.74, W * 0.80, W * 0.80], fill=rgba(LINE2))
for cx in (0.34, 0.66):
r = W * 0.17
d.ellipse([cx * W - r, W * 0.51 - r, cx * W + r, W * 0.51 + r], outline=rgba(EMBER), width=int(W * 0.045))
for k in range(6):
a = k * math.pi / 3
d.line([(cx * W, W * 0.51), (cx * W + math.cos(a) * r * 0.85, W * 0.51 + math.sin(a) * r * 0.85)],
fill=rgba(EMBER), width=int(W * 0.035))
d.ellipse([cx * W - r * 0.22, W * 0.51 - r * 0.22, cx * W + r * 0.22, W * 0.51 + r * 0.22], fill=rgba(MOLTEN))
paths["gpu"] = finish(im, size, os.path.join(OUT, "emoji-gpu.png"))
# 6 the proof tick (ember rounded square, bone tick)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
d.rounded_rectangle([W * 0.08, W * 0.08, W * 0.92, W * 0.92], radius=int(W * 0.18), fill=rgba(EMBER))
d.line([(W * 0.28, W * 0.52), (W * 0.44, W * 0.68), (W * 0.74, W * 0.34)], fill=rgba(BONE), width=int(W * 0.10),
joint="curve")
paths["proven"] = finish(im, size, os.path.join(OUT, "emoji-proven.png"))
# 7 the hash glyph (Plex Mono)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
f = font("plex-mono-500", int(W * 0.92))
tw, th, tl, tt = text_size(d, "#", f)
d.text(((W - tw) / 2 - tl, (W - th) / 2 - tt), "#", font=f, fill=rgba(EMBER))
paths["hash"] = finish(im, size, os.path.join(OUT, "emoji-hash.png"))
# 8 the wordmark letter (bone I on an ember square)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
d.rounded_rectangle([W * 0.08, W * 0.08, W * 0.92, W * 0.92], radius=int(W * 0.18), fill=rgba(OBSIDIAN))
f = font("unbounded-900", int(W * 0.62))
tw, th, tl, tt = text_size(d, "I", f)
d.text(((W - tw) / 2 - tl, (W - th) / 2 - tt), "I", font=f, fill=rgba(EMBER))
paths["letter"] = finish(im, size, os.path.join(OUT, "emoji-letter.png"))
# 9 a flame (ember outer, molten core)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
outer = [(0.50, 0.04), (0.64, 0.22), (0.70, 0.40), (0.82, 0.30), (0.88, 0.58), (0.80, 0.84), (0.62, 0.96),
(0.38, 0.96), (0.20, 0.84), (0.12, 0.58), (0.20, 0.36), (0.32, 0.44), (0.34, 0.24)]
d.polygon([(x * W, y * W) for x, y in outer], fill=rgba(EMBER))
core = [(0.50, 0.46), (0.62, 0.60), (0.66, 0.78), (0.58, 0.92), (0.42, 0.92), (0.34, 0.78), (0.38, 0.60)]
d.polygon([(x * W, y * W) for x, y in core], fill=rgba(MOLTEN))
paths["flame"] = finish(im, size, os.path.join(OUT, "emoji-flame.png"))
# 10 the ladder (two ember rails, bone rungs)
im, W = emoji_canvas()
d = ImageDraw.Draw(im)
rw = int(W * 0.09)
d.line([(W * 0.28, W * 0.06), (W * 0.28, W * 0.94)], fill=rgba(EMBER), width=rw)
d.line([(W * 0.72, W * 0.06), (W * 0.72, W * 0.94)], fill=rgba(EMBER), width=rw)
for k in range(5):
y = W * (0.16 + k * 0.17)
d.line([(W * 0.28, y), (W * 0.72, y)], fill=rgba(BONE), width=int(W * 0.07))
paths["ladder"] = finish(im, size, os.path.join(OUT, "emoji-ladder.png"))
return paths
# ---------------------------------------------------------------- stickers (320x320)
def stickers():
size = 320
paths = {}
# 1 the ember, large
m = mark(size, EMBER)
p = os.path.join(OUT, "sticker-ember.png")
m.save(p, optimize=True)
paths["ember"] = p
# 2 "Proven by fire." badge
im = Image.new("RGBA", (size * SS, size * SS), (0, 0, 0, 0))
W = size * SS
d = ImageDraw.Draw(im)
d.rounded_rectangle([W * 0.03, W * 0.30, W * 0.97, W * 0.70], radius=int(W * 0.10), fill=rgba(OBSIDIAN),
outline=rgba(EMBER), width=int(W * 0.02))
f1 = font("unbounded-900", int(W * 0.115))
f2 = font("unbounded-700", int(W * 0.062))
tw, th, tl, tt = text_size(d, "PROVEN", f1)
d.text(((W - tw) / 2 - tl, W * 0.37 - tt), "PROVEN", font=f1, fill=rgba(BONE))
tw2, th2, tl2, tt2 = text_size(d, "by fire.", f2)
d.text(((W - tw2) / 2 - tl2, W * 0.37 + th + W * 0.04 - tt2), "by fire.", font=f2, fill=rgba(MOLTEN))
p = os.path.join(OUT, "sticker-proven.png")
im.resize((size, size), Image.LANCZOS).save(p, optimize=True)
paths["proven"] = p
# 3 GPU with the ember rising out of it
im = Image.new("RGBA", (W, W), (0, 0, 0, 0))
d = ImageDraw.Draw(im)
em = mark(int(size * 0.55), EMBER).resize((int(W * 0.55), int(W * 0.55)), Image.LANCZOS)
im.alpha_composite(em, (int(W * 0.225), int(W * 0.02)))
d.rounded_rectangle([W * 0.06, W * 0.52, W * 0.94, W * 0.86], radius=int(W * 0.07), fill=rgba(GRAPHITE),
outline=rgba(LINE2), width=int(W * 0.02))
d.rectangle([W * 0.06, W * 0.86, W * 0.16, W * 0.95], fill=rgba(BONE))
for cx in (0.34, 0.66):
r = W * 0.12
cy = W * 0.69
d.ellipse([cx * W - r, cy - r, cx * W + r, cy + r], outline=rgba(EMBER), width=int(W * 0.03))
for k in range(6):
a = k * math.pi / 3
d.line([(cx * W, cy), (cx * W + math.cos(a) * r * 0.85, cy + math.sin(a) * r * 0.85)],
fill=rgba(EMBER), width=int(W * 0.025))
d.ellipse([cx * W - r * 0.22, cy - r * 0.22, cx * W + r * 0.22, cy + r * 0.22], fill=rgba(MOLTEN))
p = os.path.join(OUT, "sticker-gpu.png")
im.resize((size, size), Image.LANCZOS).save(p, optimize=True)
paths["gpu"] = p
return paths
if __name__ == "__main__":
frame = sys.argv[1] if len(sys.argv) > 1 else None
print(banner_static(960, 540, "server-banner-960x540.png", 150, 96, 30, 20))
print(banner_static(1920, 1080, "invite-background-1920x1080.png", 300, 192, 60, 40, backdrop=frame))
print(banner_overlay(960, 540, "banner-overlay-960x540.png", 46, 30, 87))
for k, v in role_icons().items():
print(k, v, os.path.getsize(v))
for k, v in emoji_set().items():
print(k, v, os.path.getsize(v))
for k, v in stickers().items():
print(k, v, os.path.getsize(v))

View file

@ -0,0 +1,11 @@
#!/bin/sh
# Animated server banner (level 3 perk): nine seconds of the DAG step recording (from 1 s, after the hero fold), graph only,
# padded into two dark bands that carry the lockup and the tagline (banner-overlay-960x540.png).
# Run from the repo root after make-assets.py. Output stays under Discord's 10 MB banner limit.
set -e
A=docs/community/discord/assets
SRC=docs/plans/site-ui-3-shots/after-v2/home-steps-1440-dark.webm
ffmpeg -v error -y -ss 1 -t 9 -i "$SRC" -i "$A/banner-overlay-960x540.png" -filter_complex \
"[0:v]crop=1340:510:50:118,pad=1340:754:0:122:color=#0C0C0E,scale=960:540,fps=12[b];[b][1:v]overlay=0:0,split[a][c];[a]palettegen=max_colors=160:stats_mode=diff[p];[c][p]paletteuse=dither=bayer:bayer_scale=4:diff_mode=rectangle" \
"$A/server-banner-960x540.gif"
ls -la "$A/server-banner-960x540.gif"

View file

@ -0,0 +1,333 @@
# Discord server structure: the build sheet and the changelog
Server Igneum (id 1557085229330464808, https://discord.gg/igneum). This file is the structure half of the server; the
cosmetics half (banner, roles ladder, emoji, stickers, server guide, pre-join question, server tag, vanity) is in
README.md. Every item below is a row with a "set / read back" state. A row is set only when it has been read back
from the server after the change. Nothing is posted that names a number our files do not hold.
The founder, 7 October 2026, 12:0x UK: "is discord fully setup and optimised to house an amazing community?" Cosmetics yes;
structure no. This sheet is the structure.
## Status, 7 October 2026, 13:1x UK
| Item | State |
|---|---|
| Chrome profile | confirmed: the signed-in Google account on the tab is the igneum.network login, the Igneum profile; the Discord account is igneum_network (Founder) |
| Discord session | expired at 11:1x UK (nothing typed); the founder signed in at 12:5x UK and the sheet was built in one pass from 12:5x to 13:1x UK |
| Channels | 24 channels in 4 categories, every topic set, 19 pinned first posts, slowmode 10 s on every talk and support channel, threads on, read-only where the sheet says (section 1, every row "set, read back") |
| Moderation | 7 AutoMod rules live (section 2), verification Medium, explicit filter on all members, raid protection 3 of 3, DM protection 5 of 5, prune restricted to admins, #mod-log private with every alert, rules screen on with the four lines |
| Onboarding | pre-join answers re-pointed at the new map, 5 server-guide to-dos, welcome sign rewritten, safety notifications to #mod-log |
| Network feed | LIVE from the box every hour: `DISCORD_WEBHOOK_FEED` created (webhook "Igneum feed" on #network-feed, 13:0x UK), written to `~/.config/igneum/discord`, `install.sh` run with the ruling flag at 13:05 UK; the box's first hourly post `feed:2026-10-07:13` landed at 13:06 UK (message 1557363371605622818) and the next tick was "0 due". The Mac's proof posts: `feed:2026-10-07:11` through the updates webhook (11:30 UK) and `feed:2026-10-07:12-proof` through the new webhook (13:05 UK, message 1557363266689171547) |
| Not done, owed | 2FA for moderation (the founder account's 2FA is off: Discord greys the switch); the office-hour event (Discord has no undated event; the template is section 5, created the day a time is picked). Done at 13:1x UK: post 1's "How this channel works" paragraph edited through the announcements webhook (PATCH 200, edited 12:11:37 UTC); the release webhook moved to #releases |
## 1. Channel map
Order top to bottom as the sidebar shows it. "Threads" means Create Public Threads allowed for @everyone and the
channel's own threads kept (auto-archive 3 days). Slowmode is 10 s where set. A pinned post is the channel's first
message, posted from the founder's account (the webhook posts only the bot's numbers), then pinned.
### What changes from the current server
| Today | Becomes | Why |
|---|---|---|
| #numbers | #network-feed, read-only, under START HERE's bot row moved to its own place below MINE | one bot channel; the pulse, the digest, the weekly, the hash-origin report and the hourly feed all land here. `DISCORD_WEBHOOK_NUMBERS` keeps working (the webhook follows the channel through a rename); `DISCORD_WEBHOOK_FEED` is set to the same channel's URL (a second webhook named "Igneum feed", or the same URL copied) |
| #first-blocks | #first-block | the brief's name; one block per post, so the singular reads right. ladder.md says #first-blocks; this sheet is the later word |
| #ask | removed | the four support channels and #mining take questions; the pinned FAQ answers the common ones; #ask's messages are read once for anything worth pinning before deletion |
| #wallet | removed | wallet questions go to the support channel of the OS; the wallet page is linked from every support pin |
| #incidents | kept, under START HERE after #releases | the watcher already posts there; cause and fix in public is the ledger's rule |
| #ledger | kept, under BUILD last | criticism with a ledger id and a date, as announcement 1 says |
| #discord-updates | kept, hidden (Founder and Core only) | the CI red watcher and the feed's proof post use its webhook |
| #mining, #devnet, #proving, #start-here, #announcements, #rules | kept, topics and pins set below | |
### START HERE (read-only for @everyone: Send Messages off on the category; Core and Founder post)
| Channel | Topic | Pinned first post | Settings | Set / read back |
|---|---|---|---|---|
| #start-here | What Igneum is, what to do first, the ladder. | the welcome post (section 3.3) | read-only; the server guide's first to-do | set, read back |
| #rules | Three rules. Read once. | the rules post (section 2.6, the same words as the rules screen) | read-only | set, read back |
| #announcements | Releases and chain events only. Announcement channel (followable). | announcement 1 is already here (announcement-01.md); edit its "How this channel works" line to: "Releases in #releases, numbers every hour in #network-feed from the labelled bot. Incidents, with cause and fix, in #incidents. Questions in #mining or the support channel for your OS, criticism in #ledger, where it gets a ledger id and a date." | read-only; Announcement channel type | set, read back |
| #releases | One post per release: version, downloads, sha256, what changed. The app updates itself. (The "Igneum" release webhook was moved here from #announcements at 13:13 UK; read back from Discord: channel 1557346271726141471.) | "Every release lands here from the bot: the version, the three downloads with their sha256, the node commit, up to five lines on what changed. The app updates itself over the air at a safe moment; a fresh install is at igneum.network/miner. A release is posted after it has crossed the staging chain, never before." | read-only; `DISCORD_WEBHOOK_ANNOUNCEMENTS` moves here (Channel settings, Integrations, Webhooks, edit the channel of the "Igneum" webhook); #announcements keeps human posts only | set, read back |
| #incidents | What broke, who it affects, what is being done. Then the cause and the fix. | "An incident is posted when it opens and when it resolves: the UTC time, what happened, who is affected per tier, what is being done; then the cause, the fix and how long it lasted. Three are automatic from the public API: finality paused over five minutes, proving over fifteen minutes behind, the live numbers stale over three minutes. Everything else a person wrote." | read-only | set, read back |
### MINE
| Channel | Topic | Pinned first post | Settings | Set / read back |
|---|---|---|---|---|
| #mining | Mining talk: cards, rates, settings, what your card is doing. | "Mining questions go here. Say the card, the OS and the app version; the Cards page shows all three. Every rate the project publishes has a row in the bench table (igneum.network/miners) with the command and the hardware. Nothing here is estimated earnings. No price talk: there is no market and nothing is for sale." | slowmode 10 s; threads on | set, read back |
| #rig-photos | Your rig, your card, your first block card. Photos only, a line of text. | "Post the rig. One photo or the saved block card, one line: the cards, the OS, the rate the app shows. No serial numbers, no machine names, no payout addresses in the shot. Threads for the replies, so the photos stay a wall." | threads on; slowmode 10 s; Attach Files on for @everyone in this channel only | set, read back |
| #first-block | Every first block, posted by the bot. Your reply under it. | "When a key finds its first blue block the bot posts it here: the short key id and the block link, and the card if the miner switched 'Make my page public' on in the app. Reply in the thread under it. The next rungs are in #start-here." | threads on; Send Messages off for @everyone, Send Messages in Threads on; the ladder bot posts (section 4.2) | set, read back |
| #benchmarks | Your card's rate and watts, the command, the app version. The bench table is built from rows like these. | "A benchmark is a row: card, driver, OS, app version, MH/s, W, and the command or the screen it came from. The project's own rows are at igneum.network/miners with the hardware named. Say the watts; a row without them is not used." | slowmode 10 s; threads on | set, read back |
| #support-windows | Windows: install, SmartScreen, the firewall prompt, drivers. | the FAQ post (section 1.5) with the Windows line first: "SmartScreen shows 'Windows protected your PC' on a new build: More info, then Run anyway. The firewall prompt comes 20 to 50 seconds in; it is the app opening its port." | slowmode 10 s; threads on by default | set, read back |
| #support-mac | macOS: Gatekeeper, Open Anyway, Apple silicon. | the FAQ post with the Mac line first: "macOS refuses an app it has not seen: System Settings, Privacy and Security, Open Anyway. Apple silicon mines; it proves on the CPU, slowly." | slowmode 10 s; threads on by default | set, read back |
| #support-linux-hiveos | Linux and HiveOS: the tarball, the flight sheet, drivers. | the FAQ post with the Linux line first: "The Linux and HiveOS tarball and the flight sheet are at igneum.network/miner. Say the distribution and the driver version with every question." | slowmode 10 s; threads on by default | set, read back |
| #pools | Pools: the project's pool-0 when it opens, and every outside pool with a signed statement. | "Until the public testnet every machine mines solo on its own keys, and a card runs several. Pools come with the testnet; the project runs pool-0 at 1 percent, the same as the optional software fee, and an outside pool is listed here once it publishes a signed key list. A pool never holds your vote weight: the member's own vote key is in every header." | slowmode 10 s; threads on | set, read back |
### #network-feed (its own row under MINE; the bot's only voice)
| Channel | Topic | Pinned first post | Settings | Set / read back |
|---|---|---|---|---|
| #network-feed | Numbers from the public API, every hour. Read-only. | "Every hour: height, hash rate, keys and the last lock, from igneum.network/api/live. Four times a day the fuller pulse, at 09:00 UK the daily digest, Monday the weekly numbers, daily the hash-origin report (who found the blocks). Milestones once each. The bot never replies; questions go to #mining." | Send Messages off for @everyone and every role; webhooks "Igneum" (numbers) and "Igneum feed" (feed) | set, read back; two feed posts in the channel at 13:05 and 13:06 UK |
### BUILD
| Channel | Topic | Pinned first post | Settings | Set / read back |
|---|---|---|---|---|
| #devnet | The devnet: resets, activations, what the node is doing. | "The devnet has mined since 3 October 2026. Coins on it have no value and the chain may be reset. A consensus change flips when 95 percent of mining weight signals it, with a floor height as the backstop; it is announced here first. The staging chain crosses first, every time." | slowmode 10 s; threads on | set, read back |
| #proving | Proving: shards, the proof pool, what your card can prove. | "Every block is proven in shards by miners' cards and paid from the block. The Prove page of the app says in one sentence what your card can do: the full shard needs a 24 GB NVIDIA card, a 32 GB card mines and proves at once, Apple silicon proves on the CPU. Proof lag and shards paid are in #network-feed." | slowmode 10 s; threads on | set, read back |
| #node-runners | Running a node: sync, peers, the RPC, the snapshot. | "A node runner's channel. Say the version, the height and the peer count; the node prints all three. A node that pauses finality reports it itself. Snapshots are refused below the node's tip; a reorg never resets execution. Node problems that look like consensus go to #devnet." | slowmode 10 s; threads on; the pre-join "I run a node" lands here | set, read back |
| #dev | Building on or with Igneum: the client, the API, the explorer, tools. | "For people writing code. The public API is igneum.network/api/stats, /api/live and /api/supply, every field named. The repository opens with the public testnet. A claim about another chain cites the repo and file or is labelled approximate." | slowmode 10 s; threads on; the pre-join "I build" lands here | set, read back |
| #spec | The spec, line by line. An issue on the spec is the way to report a flaw. | "The litepaper is at igneum.network/litepaper and the spec pages under it. Quote the line you mean. A flaw goes to hello@igneum.network or an issue on the spec; it gets a ledger id and a date in #ledger." | slowmode 10 s; threads on | set, read back |
| #ledger | Every criticism, with a ledger id and a date, and what was done. | "Criticism lands here and at igneum.network/ledger with an id and a date. Nothing is deleted; a resolved item says how. The founder is one person, pseudonymous, building with AI systems; say so if that is your criticism, it has an entry." | slowmode 10 s; threads on | set, read back |
### COMMUNITY
| Channel | Topic | Pinned first post | Settings | Set / read back |
|---|---|---|---|---|
| #general | Everything else about Igneum. | "General talk. The three rules: be kind, no seed phrases ever, nobody from Igneum will DM you first. No price talk: there is no market and nothing is for sale. Regional threads open here when a language has ten people asking for one." | slowmode 10 s; threads on | set, read back |
| #off-topic | Not Igneum. GPUs, games, the weather. | "Anything but Igneum and anything but prices. Same three rules." | slowmode 10 s | set, read back |
### Hidden (Founder and Core)
| Channel | Topic | Settings | Set / read back |
|---|---|---|---|
| #mod-log | AutoMod alerts and moderation notes. | private; every AutoMod rule's alert channel | set, read back |
| #discord-updates | CI red runs, the feed's proof posts, tooling notes. | private; unchanged; holds the `DISCORD_WEBHOOK_UPDATES` webhook | exists |
Regional threads: not now. A regional thread opens in #general when ten members ask for one language; it is a thread,
not a channel, until it carries a week of talk (the no-empty-channels rule applies to channels, not threads).
### 1.5 The support FAQ pin (the same post in the three support channels, the OS line first)
From the miner page's "Before you start" section, verbatim where it is quoted:
```
Before you start. The questions worth asking.
Does this website use my GPU to mine?
No. Mining happens only in the app you install, and only when you press Start.
Does my card mine and prove?
Every card mines. Proving the full shard needs a 24 GB NVIDIA card; a 32 GB card does both at once. Apple silicon proves on the CPU, slowly. The Prove page of the app says in one sentence what your card can do.
Is the devnet paying real money?
No. Devnet coins have no value and the chain may reset. The app's pounds row reads 0.00 on devnet and says why.
Is there a fee?
Not in the protocol: no dev fund, no fee to any team. The app takes an optional 1% software fee, the norm for GPU miners, and one flag turns it off.
Can I run it on a rig or in a pool?
The Linux and HiveOS tarball is on the miner page with the flight sheet. Pools are part of the public testnet; until then every machine mines solo on its own keys, and a card runs several.
Where do the numbers come from?
Every rate has a row in the bench table and an entry in the engineering log with the command and the hardware. Nothing is estimated earnings.
Ask here with the card, the OS and the app version. Nobody from Igneum will DM you first. igneum.network/miner
```
## 2. Moderation
| Setting | Value | Where | Set / read back |
|---|---|---|---|
| Verification level | Medium (was already Medium) | Safety Setup, DM and Spam Protection | read back |
| Explicit image filter | Filter messages from all members (was already on) | Safety Setup, AutoMod, Sensitive content filters | read back |
| 2FA requirement for moderation | off: the switch is greyed until the founder account enables 2FA | Server Settings, Safety Setup, Permissions | OWED (needs the founder) |
| @everyone role | Mention @everyone, @here and All Roles: off. Also off: Manage Messages, Manage Threads, Create Private Threads, Use External Emoji stays on, Create Invite on | Server Settings, Roles, Default Permissions | set, read back |
| Bot role | View Channels, Send Messages, Embed Links, Attach Files, Read Message History. Nothing else (no Manage, no Mention Everyone, no Administrator). Webhooks are not members and carry no role; this role is for the ladder bot's user only until that bot gets its own role (section 4.2) | Server Settings, Roles, Bot | set, read back |
| #mod-log | private channel, Founder and Core; the alert channel of every AutoMod rule | Channel create | set, read back |
| Rules screen | Server Rules on (Access tab), four lines: the three rules and the no-price plus report-a-flaw line | Server Settings, Access, Server Rules | set, read back |
| DM spam | DM and Spam Protection 5 of 5 on; Raid Protection and CAPTCHA 3 of 3 on; activity alerts to #discord-updates; member prune restricted to admins (set today) | Safety Setup | read back |
### 2.1 to 2.5 AutoMod rules, as built (Server Settings, Safety Setup, AutoMod; every alert to #mod-log; Core exempt; #mod-log and #discord-updates exempt where a channel field exists)
Discord's keyword rule takes words and wildcards; the regex field was not needed. Seven rules are live:
| # | Rule (Discord name) | Trigger | Action | Read back |
|---|---|---|---|---|
| 1 | Invite and spam links (14 words) | `*discord.com/invite/*, *discord.gg/*, *discordapp.com/invite/*, *discord.com/invite*, *bit.ly/*, *tinyurl.com/*, *cutt.ly/*, *t.me/*, *wa.me/*, *.xyz/*, *.top/*, *.click/*, *.buzz/*, *.icu/*` (the old "Igneum words and invites" rule, renamed and rewritten; its price words moved to rule 3) | block, alert #mod-log | enabled |
| 2 | Seed phrases and wallet scams (19 words) | `seed phrase, seed phrases, recovery phrase, secret phrase, 12 words, 24 words, private key, send to, send me, dm me, message me first, validate your wallet, sync your wallet, connect your wallet, claim your, airdrop, giveaway, support ticket, open a ticket` | block, alert, time out 10 min | enabled |
| 3 | Price pumping (alert only) (22 words) | `100x, 1000x, to the moon, wen moon, pump, pumping, buy now, buy the dip, price prediction, price target, listing soon, when binance, wen binance, wen lambo, presale, ico, ido, wen listing, wtb, wts, for sale, otc` | alert only, no block (a miner asking is a conversation; a mod answers with the pin) | enabled |
| 4 | Impersonation of the team (10 words) | `igneum team, igneum support, official igneum, igneum admin, igneum mod, igneum staff, from igneum, igneum official, igneum helpdesk, igneum customer service` | block, alert | enabled |
| 5 | Block Words in Member Profile Names | `*igneum*, *admin*, *moderator*, *support*, *official*, *helpdesk*, *team*` in display names | block member interactions, alert | enabled |
| 6 | Block Commonly Flagged Words (Discord preset) | Severe Profanity, Insults and Slurs, Sexual Content, all three lists | block, alert | enabled |
| 7 | Block Suspected Spam Content (Discord preset) | Discord's spam model | block, alert | enabled |
| 8 | Block Mention Spam (Discord preset) | was already on | block | enabled |
Owed: the test from a non-Core account (one blocked message per rule in #mod-log, one legitimate message passing: a payout address, a sha256, "2x per joule"). The founder's account is Core-exempt and the server has one member, so the test waits for a second account.
### 2.6 The rules text (the rules screen and #rules, the same words)
```
Three rules.
1. Be kind. Argue the claim.
2. No seed phrases ever. Not yours, not anyone's, not in a DM, not "to check".
3. Nobody from Igneum will DM you first. Anyone who does is not from Igneum.
No price talk: there is no market and nothing is for sale.
Report a flaw: hello@igneum.network or an issue on the spec. Nobody from Igneum will ask for your seed.
```
## 3. Onboarding
### 3.1 Server guide to-dos (Server Settings, Onboarding, Server Guide), aligned with the map
| # | To-do | Channel |
|---|---|---|
| 1 | Read what Igneum is and the ladder | #start-here |
| 2 | Read the three rules | #rules |
| 3 | Pick your lane: Miner, Node runner, Builder (the question you answered on joining; change it in Channels and Roles) | #mining |
| 4 | Post your first block when it lands | #first-block |
| 5 | Follow releases | #releases |
As built: to-do 1 "Read what Igneum is and the ladder" in #start-here (visit), 2 "Say hello in mining: your card, the OS, the app version" in #mining (message), 3 "Post your first block when it lands" in #first-block (visit), 4 "Follow releases: version, downloads, what changed" in #releases (visit), 5 "Read the rules" (Discord's built-in). Welcome sign: "Welcome. Three rules in #rules: be kind, no seed phrases ever, nobody from Igneum will DM you first. #start-here has the ladder: your first block lands in #first-block. Nothing is for sale and devnet coins have no value." Author igneum_network. The guide banner is the one in README.md.
### 3.2 The pre-join question (Server Settings, Onboarding, Default Channels and Questions)
Question "What do you mine with?" stays. Answers and what each hands out:
| Answer | Role | Channels joined |
|---|---|---|
| NVIDIA | Miner | #mining, #first-block, #rig-photos, #support-windows, #support-linux-hiveos |
| AMD | Miner | #mining, #first-block, #rig-photos, #support-windows, #support-linux-hiveos |
| Apple | Miner | #mining, #first-block, #rig-photos, #support-mac |
| I run a node | Node runner | #node-runners, #devnet |
| I build | Builder | #dev, #spec, #proving |
Multiple answers allowed, not required. As built (13:0x UK): the five answers re-pointed exactly as the table above; Discord reports 22 of 22 public channels assignable and no public channel missing from Questions and Default Channels (22 default channels, unchanged).
### 3.3 The welcome post in #start-here (pinned, from the founder's account)
```
Welcome to Igneum.
A proof-of-work chain built for graphics cards. The miners also prove every block and are paid for it from the block. No premine, no stake, no fee to any team in the protocol. One founder, pseudonymous, building with AI systems; every criticism we know of is at igneum.network/ledger.
Start here
1. Install the miner: igneum.network/miner. Windows, macOS, Linux and HiveOS.
2. Press Start. The app says what your card can do.
3. Your first block lands in #first-block. Reply under it.
The ladder. The chain computes it, nobody hands it out.
First block: one blue block with your key. The bot posts it in #first-block.
Your key has a vote: 100 blocks in the 30-day window (the dust line). The Voter role.
Your signature is in a checkpoint: the first lock carrying your vote.
Full window: 30 of 30 days with blocks. The Window role.
Rank: your place by weight among all keys, on igneum.network/live.
Your card proved a shard: a paid shard record. The Prover role.
Questions in #mining or the support channel for your OS. Numbers every hour in #network-feed. Rules in #rules; there are three.
Devnet coins have no value and the chain may be reset. Nothing is for sale.
```
The rung names and rules are reinvent.md section 3.7 and ladder.md; on the devnet the dust line is the chain's
`params.dust` (5 today), and the bot's rung 1 post says the number it read, never the constant.
## 4. Bots
Two bots. Neither replies, neither mentions, both post from the public API only, both go through the forbidden-string
guard in `tools/community/discord-hooks.mjs` (the founder's name, hosts, machine ids, paths, IPs, 32-hex tokens,
webhook URLs, any mention).
### 4.1 The network feed bot (webhook; BUILT and LIVE from the box since 13:06 UK, 7 October 2026)
What it is: `tools/community/discord-hooks.mjs feed`, added 7 October 2026 beside pulse, digest and weekly. A webhook
needs no bot user, no token and no Discord permission beyond the webhook itself (Channel settings, Integrations,
Webhooks, "Igneum feed" on #network-feed, or the existing "Igneum" webhook's URL copied into the key).
| Post | When | Exact shape |
|---|---|---|
| Network feed | every hour, on the London clock, through `tick` | title "Network feed" linking igneum.network/live; line 1 "igneum-devnet. Devnet coins have no value. https://igneum.network/live"; fields Height (`state.height`, blocks/s over the hour from `block_count`), Hash rate (`state.hashes_per_second_estimate`, difficulty), Keys (`state.miners_10m` "active in 10 min (a card runs several)", `finality.weights.voters` "voters in the 30-day window"), Last lock (the newest locked checkpoint: index, share of weight, age, voters); footer the UK time with UTC in brackets and "every hour from /api/live; this channel is read-only, questions go to #mining" |
| Hash origin, daily | in the first feed of the day that sees a new `state.hash_origin.date` (the launch pack's job writes it at 08:30 UTC) | one extra field "Hash origin, <date>": "N keys found a block, M above dust, ten largest X% of blocks, project fleet Y%, P attested pools. Full report in #numbers." A field the report lacks is left out, never estimated |
| Milestone | once each, when a feed crosses it | title "Milestone": every 100,000 chain blocks ("Chain block 100,000. igneum.network/block/100000"), every 10,000 locks, every 10,000 paid shards, the first crossing of 100, 250, 500, 1,000, 2,500, 5,000, 10,000 vote keys active at once, the first crossing of 1, 10, 100 GH/s and 1, 10, 100 TH/s. The first feed seeds and posts none |
Read back today (7 October 2026, 11:30 UK, dry run against the live API, then one live post through the updates
webhook as the proof of the path): "Height 162,099, 1.47 blocks/s last 60 s; Hash rate 444.7 MH/s, difficulty
210,368,365; Keys 17 active in 10 min, 16 voters in the 30-day window; Last lock checkpoint 8,723 at 72.1% of weight,
16 s ago, 16 voters". 413 characters. Tests: 37 pass (`node --test tools/community/discord-hooks.test.mjs`).
What the numbers mean per tier (the consequences rule): 17 keys and 444.7 MH/s is the project's own machines on the
devnet, so the feed names no outside miner yet; a home miner with one 8 GB card at the measured rates sees a first
block inside the hour at this size, and the Keys line is the one that tells them when that stops being true (the
pool is the answer from about 1 TH/s, reinvent.md 3.5).
Turned on 7 October 2026, 13:05 UK: the webhook "Igneum feed" was created on #network-feed (Channel settings,
Integrations, Webhooks), its URL copied straight from Discord's button into `~/.config/igneum/discord` as
`DISCORD_WEBHOOK_FEED` (the clipboard was cleared after; the URL was never printed), then
`IGNEUM_SECRET_ON_BOX_OK=1 infra/build-server/discord-hooks/install.sh` copied the new script and the file to the box.
The box's tick posted `feed:2026-10-07:13` at 13:06 UK (height 163,299, 310.5 MH/s, 14 keys active, 12 voters, lock
8,910 at 80.3 percent) and the next tick read "0 due". Two webhooks now post to #network-feed: "Igneum" (numbers:
pulse, digest, weekly, hash-origin) and "Igneum feed" (the hour and milestones).
What the numbers mean per tier (the consequences rule): 14 keys and 311 MH/s is the project's own machines on the
devnet, so the feed names no outside miner yet; a home miner with one 8 GB card at the measured untuned rates finds a
first block inside the hour at this size, and the Keys line is the one that tells them when that stops being true
(the pool is the answer from about 1 TH/s, reinvent.md 3.5).
### 4.2 The ladder bot (a bot user; SPECIFIED, not built)
What it posts, from ladder.md (the shapes are fixed there; the words are repeated here so this file stands alone):
| Post | Channel | Trigger | Exact shape |
|---|---|---|---|
| First block (rung 0) | #first-block | a key's `blocks` goes 0 to 1 in `finality.weights.keys[]` and a block in `/api/live blocks[]` names it | "First block: key 6ad0e117" / "Block 1,284,117 · igneum.network/block/<hash>" / "RTX 4070 (the miner chose to show the card)". Without the opt-in the third line is absent. Embed: ember, title "First block", fields Key and Block, UK footer |
| The vote (rung 1) | #first-block | `keys[].voter` goes false to true | "Your key has a vote: 6ad0e117" / "100 blocks in the window (the line is 100) · the key now signs every checkpoint"; the number is `params.dust` as read |
| The ladder, yesterday | #network-feed | 09:00 UK beside the digest | the seven-line summary in ladder.md (first blocks, votes, signatures, full window, rank 1, signing streak, shards); a line whose field the node does not expose is left out |
| Roles | | daily read of `getFinalityWeights` through `/api/live` | Voter granted when a message signed with the vote key names a key whose `voter` is true; revoked at a daily read where `voter` is false. Window when `keys[].daysMined` spans the window (owed from the node; by hand until then). Prover when a paid shard record names the key; never revoked |
Opt-in, per miner: the key id and the block link post for every key (a chain fact); the card model and the "(you)"
link only when the miner switched "Make my page public" on in the app (`settings.profile_public`). The Discord role
needs the miner to prove the key: a `/key <id> <signature>` slash command, the signature over a nonce the bot gives,
verified against `getFinalityWeights`; the app's "prove my key" button is owed (ladder.md).
Permissions the bot user needs, minimal: View Channels; Send Messages and Embed Links in #first-block and #network-feed
only (channel overrides, not server-wide); Read Message History; Manage Roles, with the bot's role placed above Voter,
Window and Prover and below everything else; `applications.commands` scope for the slash command. Not: Administrator,
Mention Everyone, Manage Channels, Manage Webhooks, Moderate Members.
Rungs 2 to 5 and P are never posted singly; they are counted in the daily summary (one a minute at scale).
### 4.3 The two webhooks that exist and where they point
| Key | Channel | Used by |
|---|---|---|
| DISCORD_WEBHOOK_NUMBERS | #network-feed (the channel was renamed; the webhook followed) | pulse, digest, weekly, the hash-origin report |
| DISCORD_WEBHOOK_ANNOUNCEMENTS | #releases (moved 7 Oct 2026, 13:13 UK; the webhook keeps its URL and name "Igneum") | release posts |
| DISCORD_WEBHOOK_INCIDENTS | #incidents | incident open and resolve, the watcher |
| DISCORD_WEBHOOK_UPDATES | #discord-updates (hidden) | the CI red watcher; the feed's proof post today (`feed --via updates`) |
| DISCORD_WEBHOOK_FEED | #network-feed, webhook "Igneum feed" (created 13:0x UK) | the hourly feed and milestones |
## 5. Events
One template, "Devnet office hour", weekly. Discord cannot hold an event without a start time, so nothing is created on the server until the founder picks a day and an hour; the fields below are the template, created in one minute on that day.
| Field | Value |
|---|---|
| Name | Devnet office hour |
| Where | a voice channel "office-hour" under COMMUNITY, created with the first event (not before: no empty channels) |
| Description | "An hour on the devnet, every week. Bring the card, the log line or the question. Numbers from the public API only; nothing is for sale and there is no price." |
| Recurrence | weekly, same day and hour, UK time |
| Needs the founder | the day and the hour |
## 6. What needs the founder
| Item | Why |
|---|---|
| A day and an hour for the office hour | section 5; Discord has no undated event |
| 2FA on the founder account, then the "Require 2FA for moderator actions" switch | Discord greys the switch until the account has 2FA; one click after that |
| The hosting word for the ladder bot | a bot token with Manage Roles is a secret the box would hold; the 6 October exception covers webhook URLs only. Options: the box under a second exception, or a separate small host that holds only the bot token and reads the public API |
| A second account for the AutoMod test | the founder's account is Core-exempt; the seven rules are enabled but untested against a real message |
## Changelog
| When (UK) | What |
|---|---|
| 7 Oct 2026, 11:1x | Sheet opened on branch discord-structure. Chrome profile confirmed as the igneum.network login. Discord session found expired; stopped at the log-in form, nothing typed |
| 7 Oct 2026, 11:2x | `feed` subcommand, milestones, the daily hash-origin field, `--via updates`, `DISCORD_WEBHOOK_FEED` (optional) and the tick wiring added to tools/community/discord-hooks.mjs; six tests added, 37 pass |
| 7 Oct 2026, 11:30 | One live feed posted through the updates webhook: key `feed:2026-10-07:11`, message id 1557339585166581846, 413 characters |
| 7 Oct 2026, 12:5x | Founder signed in. Categories renamed INFO, CHAIN, LEDGER, TALK to START HERE, MINE, BUILD, COMMUNITY. #ask read (no messages beyond the welcome; the r/igneum FAQ line is in the support pins) and renamed to #off-topic; #finality read (no messages) and renamed to #first-block; #proving read (no messages) and renamed to #rig-photos; #devnet (no messages) renamed to #benchmarks; #breaks (no messages) renamed to #spec; #numbers renamed to #network-feed (both webhooks follow). Created #releases, #mod-log (private, Core), #support-windows, #support-mac, #support-linux-hiveos, #pools, #devnet, #proving, #node-runners; #dev moved to BUILD. Every topic and slowmode set; #first-block Send Messages denied, threads allowed; #network-feed Send Messages denied. The 0.3.15 and 0.3.16 posts and the two incidents stay where they were |
| 7 Oct 2026, 12:2x to 12:3x | 19 first posts sent from the founder's account and pinned: start-here (the welcome and ladder), releases, incidents, network-feed, mining, rig-photos, first-block, benchmarks, support-windows, support-mac, support-linux-hiveos (the FAQ, OS line first), pools, devnet, proving, node-runners, dev, spec, ledger, off-topic |
| 7 Oct 2026, 12:4x | AutoMod: seven rules enabled (section 2), prune restricted to admins, safety notifications to #mod-log |
| 7 Oct 2026, 12:5x | Onboarding: the five answers re-pointed, four to-dos rewritten, welcome sign rewritten; Server Rules screen on with four lines; Bot role set to View Channels, Send Messages, Embed Links, Attach Files, Read Message History (read back 13:1x UK: exactly those five on, 46 off) |
| 7 Oct 2026, 13:05 | Webhook "Igneum feed" created on #network-feed, key written, box installed; 13:06 the box's first hourly feed |
| 7 Oct 2026, 13:1x | Post 1's "How this channel works" line edited by webhook PATCH (message 1557154683779547138); the "Igneum" release webhook moved from #announcements to #releases (Channel settings, Integrations, Webhooks, Channel), read back from each webhook's own endpoint: announcements → 1557346271726141471 (#releases), numbers and feed → 1557089967807926322 (#network-feed) |

View file

@ -0,0 +1,29 @@
Latency ladder inactive (no activation in the override file): every class v4 program at rung 0, 27 shadow passes; no ladder bits in the header
Program class signal inactive (the window or the floor is absent from the override file): headers carry block version 2 exactly
Fees on igneum-devnet-973: pgas table v0, B_p 30000000 pgas, S_p 7500000 pgas, floors 1000000000 wei per gas and 1000000000 wei per pgas; calibrated v1 from DAA score never
Consensus params digest: 079d8a7e736abbd4b135eb1d652466ca7906ea1b200e22eac97f795e626356e7 (exchanged in the p2p handshake; a peer with another digest is refused)
2026-10-07 15:14:29.331+02:00 [INFO ] igneumd/2.1.0-dc141409
2026-10-07 15:14:29.331+02:00 [INFO ] Application directory: gf-digest/d-base
2026-10-07 15:14:29.331+02:00 [INFO ] Data directory: gf-digest/d-base/igneum-devnet-973/datadir
2026-10-07 15:14:29.331+02:00 [INFO ] Logs to console only
2026-10-07 15:14:29.350+02:00 [INFO ] Finality v2 (igneum-devnet-973): interval 30 depth 20 window 7200 DAA dust 5 presence 20 aggregators 8 ban 7200 fold 3; rule v3 (frozen table, certificate fold) from checkpoint DAA never; 0 checkpoints known, next index 1, 0 locks, 0 keys; C1 in DAA seconds from DAA never; weight-gated deep fork choice from DAA never; leave rule (W7, delay 3600 DAA) from checkpoint DAA never, 0 leaves
2026-10-07 15:14:29.363+02:00 [INFO ] [igneum-exec] proving v0: payouts from DAA score never, window 7200 DAA, dust 5, verifier Off
2026-10-07 15:14:29.363+02:00 [INFO ] [igneum-exec] proving v1: segment records from DAA score never, 8 blocks a segment, unproven after 600 DAA, aggregator share 1000 bps, shard program id unknown, aggregator id unknown
2026-10-07 15:14:29.364+02:00 [INFO ] [igneum-exec] verifying keys embedded: shard program id 0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a aggregator id 0x474678f35f7545db28055d5e5bbc308231d84a5a072202087a2a8d5b09123896
2026-10-07 15:14:29.370+02:00 [INFO ] template prewarm: on (the cached block template is rebuilt on every virtual change; IGNEUM_TEMPLATE_PREWARM=0 turns it off)
2026-10-07 15:14:29.371+02:00 [INFO ] GRPC Server starting on: 127.0.0.1:29770
2026-10-07 15:14:29.372+02:00 [INFO ] P2P Server starting on: 127.0.0.1:29771
2026-10-07 15:14:29.373+02:00 [INFO ] [igneum-exec] chain follower started
2026-10-07 15:14:29.373+02:00 [INFO ] [igneum-exec] proof verifier: Off (no SP1 verification on this node)
2026-10-07 15:14:29.373+02:00 [INFO ] [igneum-exec] exec sync: sink edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block 0; pruning point edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block Some(0) (DAA 0); retention root edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block Some(0) (DAA 0), its body held
2026-10-07 15:14:29.373+02:00 [INFO ] [igneum-exec] eth_ JSON-RPC listening on 127.0.0.1:26790
2026-10-07 15:14:29.373+02:00 [INFO ] [igneum-exec] exec sync: no snapshot to resume from (no snapshot file); the follower starts at genesis
2026-10-07 15:14:29.373+02:00 [INFO ] [igneum-exec] exec sync: waiting for consensus to sync before the executor starts (the sink is 393269 s old)
2026-10-07 15:14:29.373+02:00 [WARN ] [igneum-exec] cannot bind the eth_ JSON-RPC server on 127.0.0.1:26790: Address already in use (os error 98)
2026-10-07 15:14:29.374+02:00 [INFO ] WRPC Server starting on: 127.0.0.1:29772
2026-10-07 15:14:29.474+02:00 [INFO ] [igneum-exec] chain follower stopped
^SIGTERM - shutting down...
2026-10-07 15:14:54.316+02:00 [INFO ] P2P Server stopped: 127.0.0.1:29771
2026-10-07 15:14:54.316+02:00 [INFO ] WRPC Server stopped on: 127.0.0.1:29772
2026-10-07 15:14:54.317+02:00 [INFO ] GRPC Server stopped on: 127.0.0.1:29770
2026-10-07 15:14:54.824+02:00 [INFO ] igneumd has stopped...

View file

@ -0,0 +1,29 @@
Latency ladder inactive (no activation in the override file): every class v4 program at rung 0, 27 shadow passes; no ladder bits in the header
Program class signal inactive (the window or the floor is absent from the override file): headers carry block version 2 exactly
Fees on igneum-devnet-973: pgas table v0, B_p 30000000 pgas, S_p 7500000 pgas, floors 1000000000 wei per gas and 1000000000 wei per pgas; calibrated v1 from DAA score never
Consensus params digest: 079d8a7e736abbd4b135eb1d652466ca7906ea1b200e22eac97f795e626356e7 (exchanged in the p2p handshake; a peer with another digest is refused)
2026-10-07 15:12:35.092+02:00 [INFO ] igneumd/2.1.0-75810130
2026-10-07 15:12:35.093+02:00 [INFO ] Application directory: gf-digest/d-none
2026-10-07 15:12:35.093+02:00 [INFO ] Data directory: gf-digest/d-none/igneum-devnet-973/datadir
2026-10-07 15:12:35.093+02:00 [INFO ] Logs to console only
2026-10-07 15:12:35.128+02:00 [INFO ] Finality v2 (igneum-devnet-973): interval 30 depth 20 window 7200 DAA dust 5 presence 20 aggregators 8 ban 7200 fold 3; rule v3 (frozen table, certificate fold) from checkpoint DAA never; 0 checkpoints known, next index 1, 0 locks, 0 keys; C1 in DAA seconds from DAA never; weight-gated deep fork choice from DAA never; leave rule (W7, delay 3600 DAA) from checkpoint DAA never, 0 leaves; key succession (W5) from checkpoint DAA never, 0 successions; signature scheme 0 (0 = BLS12-381), any other refused until a class names it
2026-10-07 15:12:35.136+02:00 [INFO ] [igneum-exec] proving v0: payouts from DAA score never, window 7200 DAA, dust 5, verifier Off
2026-10-07 15:12:35.136+02:00 [INFO ] [igneum-exec] proving v1: segment records from DAA score never, 8 blocks a segment, unproven after 600 DAA, aggregator share 1000 bps, shard program id unknown, aggregator id unknown
2026-10-07 15:12:35.136+02:00 [INFO ] [igneum-exec] proof archive at gf-digest/d-none/igneum-devnet-973/datadir/evm/proofs: 0 proofs
2026-10-07 15:12:35.136+02:00 [INFO ] [igneum-exec] verifying keys embedded: shard program id 0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a aggregator id 0x474678f35f7545db28055d5e5bbc308231d84a5a072202087a2a8d5b09123896
2026-10-07 15:12:35.141+02:00 [INFO ] template prewarm: on (the cached block template is rebuilt on every virtual change; IGNEUM_TEMPLATE_PREWARM=0 turns it off)
2026-10-07 15:12:35.141+02:00 [INFO ] GRPC Server starting on: 127.0.0.1:29760
2026-10-07 15:12:35.141+02:00 [INFO ] P2P Server starting on: 127.0.0.1:29761
2026-10-07 15:12:35.141+02:00 [INFO ] [igneum-exec] eth_ JSON-RPC listening on 127.0.0.1:26790
2026-10-07 15:12:35.141+02:00 [INFO ] [igneum-exec] proof verifier: Off (no SP1 verification on this node)
2026-10-07 15:12:35.142+02:00 [INFO ] [igneum-exec] chain follower started
2026-10-07 15:12:35.142+02:00 [INFO ] WRPC Server starting on: 127.0.0.1:29762
2026-10-07 15:12:35.142+02:00 [WARN ] [igneum-exec] cannot bind the eth_ JSON-RPC server on 127.0.0.1:26790: Address already in use (os error 98)
2026-10-07 15:12:35.142+02:00 [INFO ] [igneum-exec] exec sync: sink edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block 0; pruning point edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block Some(0) (DAA 0); retention root edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block Some(0) (DAA 0), its body held
2026-10-07 15:12:35.142+02:00 [INFO ] [igneum-exec] exec sync: no snapshot to resume from (no snapshot file); the follower starts at genesis
2026-10-07 15:12:35.142+02:00 [INFO ] [igneum-exec] chain follower stopped
^SIGTERM - shutting down...
2026-10-07 15:13:00.089+02:00 [INFO ] P2P Server stopped: 127.0.0.1:29761
2026-10-07 15:13:00.089+02:00 [INFO ] GRPC Server stopped on: 127.0.0.1:29760
2026-10-07 15:13:00.089+02:00 [INFO ] WRPC Server stopped on: 127.0.0.1:29762
2026-10-07 15:13:00.597+02:00 [INFO ] igneumd has stopped...

View file

@ -0,0 +1,32 @@
Signature scheme byte from the override file: genesis scheme 0 (0 = BLS12-381); every vote item and key reveal carries its scheme byte on the wire from DAA score 0; any other scheme refused until a class the 95 percent signal moves to names it
Key succession (W5) from the override file: a vote key hands its window weight and its forfeit term to a successor key once, from checkpoint DAA score 0; the successor inherits the window
Latency ladder from the override file: rungs 27, 35, 53, [88], [173], [267] shadow passes, cache [512 MiB] beside them (brackets: inadmissible, never entered), active from epoch 0 (DAA score 0 rounded up to the epoch boundary at 0), one rung per decision at 90 percent of blue blocks in each of 7 consecutive windows of 86400 DAA ending at an epoch's seed block
Latency ladder active: rungs 27, 35, 53, [88], [173], [267] shadow passes, cache [512 MiB] beside them, this node signals none (header version bits 15 and 14; both set asks for the cache rung)
Program class signal inactive (the window or the floor is absent from the override file): headers carry block version 2 exactly
Fees on igneum-devnet-973: pgas table v0, B_p 30000000 pgas, S_p 7500000 pgas, floors 1000000000 wei per gas and 1000000000 wei per pgas; calibrated v1 from DAA score never
Consensus params digest: 60e842a738c7dea04543af93e990fb962618ace0b76b38013a63cb29dd45644a (exchanged in the p2p handshake; a peer with another digest is refused)
2026-10-07 15:13:26.156+02:00 [INFO ] igneumd/2.1.0-75810130
2026-10-07 15:13:26.156+02:00 [INFO ] Application directory: gf-digest/d-testnet-shape
2026-10-07 15:13:26.156+02:00 [INFO ] Data directory: gf-digest/d-testnet-shape/igneum-devnet-973/datadir
2026-10-07 15:13:26.156+02:00 [INFO ] Logs to console only
2026-10-07 15:13:26.172+02:00 [INFO ] Finality v2 (igneum-devnet-973): interval 30 depth 20 window 7200 DAA dust 5 presence 20 aggregators 8 ban 7200 fold 3; rule v3 (frozen table, certificate fold) from checkpoint DAA never; 0 checkpoints known, next index 1, 0 locks, 0 keys; C1 in DAA seconds from DAA never; weight-gated deep fork choice from DAA never; leave rule (W7, delay 3600 DAA) from checkpoint DAA never, 0 leaves; key succession (W5) from checkpoint DAA 0, 0 successions; signature scheme 0 (0 = BLS12-381), any other refused until a class names it
2026-10-07 15:13:26.180+02:00 [INFO ] [igneum-exec] proving v0: payouts from DAA score never, window 7200 DAA, dust 5, verifier Off
2026-10-07 15:13:26.180+02:00 [INFO ] [igneum-exec] proving v1: segment records from DAA score never, 8 blocks a segment, unproven after 600 DAA, aggregator share 1000 bps, shard program id unknown, aggregator id unknown
2026-10-07 15:13:26.180+02:00 [INFO ] [igneum-exec] proof archive at gf-digest/d-testnet-shape/igneum-devnet-973/datadir/evm/proofs: 0 proofs
2026-10-07 15:13:26.181+02:00 [INFO ] [igneum-exec] verifying keys embedded: shard program id 0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a aggregator id 0x474678f35f7545db28055d5e5bbc308231d84a5a072202087a2a8d5b09123896
2026-10-07 15:13:26.185+02:00 [INFO ] template prewarm: on (the cached block template is rebuilt on every virtual change; IGNEUM_TEMPLATE_PREWARM=0 turns it off)
2026-10-07 15:13:26.186+02:00 [INFO ] GRPC Server starting on: 127.0.0.1:29760
2026-10-07 15:13:26.186+02:00 [INFO ] P2P Server starting on: 127.0.0.1:29761
2026-10-07 15:13:26.186+02:00 [INFO ] [igneum-exec] eth_ JSON-RPC listening on 127.0.0.1:26790
2026-10-07 15:13:26.186+02:00 [INFO ] WRPC Server starting on: 127.0.0.1:29762
2026-10-07 15:13:26.186+02:00 [INFO ] [igneum-exec] chain follower started
2026-10-07 15:13:26.186+02:00 [INFO ] [igneum-exec] proof verifier: Off (no SP1 verification on this node)
2026-10-07 15:13:26.186+02:00 [WARN ] [igneum-exec] cannot bind the eth_ JSON-RPC server on 127.0.0.1:26790: Address already in use (os error 98)
2026-10-07 15:13:26.186+02:00 [INFO ] [igneum-exec] exec sync: sink edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block 0; pruning point edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block Some(0) (DAA 0); retention root edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 is chain block Some(0) (DAA 0), its body held
2026-10-07 15:13:26.186+02:00 [INFO ] [igneum-exec] exec sync: no snapshot to resume from (no snapshot file); the follower starts at genesis
2026-10-07 15:13:26.186+02:00 [INFO ] [igneum-exec] chain follower stopped
^SIGTERM - shutting down...
2026-10-07 15:13:51.152+02:00 [INFO ] P2P Server stopped: 127.0.0.1:29761
2026-10-07 15:13:51.152+02:00 [INFO ] WRPC Server stopped on: 127.0.0.1:29762
2026-10-07 15:13:51.152+02:00 [INFO ] GRPC Server stopped on: 127.0.0.1:29760
2026-10-07 15:13:51.660+02:00 [INFO ] igneumd has stopped...

View file

@ -0,0 +1,695 @@
{
"expect": "no-succession",
"pass": false,
"checks": {
"window_filled_before_the_succession": true,
"succession_accepted_on_submit": true,
"carried_once_on_every_node": false,
"successor_inherits_on_every_node": false,
"old_key_handed_over_on_every_node": false,
"nodes_agree_on_the_successor_weight": false,
"refused_old_again_on_n2": false,
"refused_old_again_on_n0": false,
"refused_successor_that_handed_over": false,
"scheme_1_refused_by_every_node": true,
"locks_continue_after_the_fold": true,
"zero_rejected_by_nodes": true,
"sinks_agree": true
},
"old_key": "e4036b1e79c817f2",
"new_key": "2f031ed35a29ced8",
"old_blocks_before": 41,
"succeeded_at_daa": 261,
"carried_seen_at_daa": null,
"new_mined_alone": 112,
"rows": [
{
"new": {
"blocks": 37,
"keyHash": "2f031ed35a29ced8",
"participation": 1,
"pubkey": "80ab38c523736ad8944c7d3375e7563ba3b0d5a3ab149bec568cf207edcc9e1ab1ee2566e06ff422a5ec4c85a4049976",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "",
"voter": true
},
"old": {
"blocks": 0,
"keyHash": "e4036b1e79c817f2",
"participation": 0,
"pubkey": "b0fd8eebbbaad96268e5f7b8a86090ca0c723954a0a5cb81c92b633094c1742b2505c4f7cea646c5c96bb52e57400067",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "6543d321fd61aedb",
"voter": false
}
},
{
"new": {
"blocks": 0,
"keyHash": "2f031ed35a29ced8",
"participation": 0,
"pubkey": "80ab38c523736ad8944c7d3375e7563ba3b0d5a3ab149bec568cf207edcc9e1ab1ee2566e06ff422a5ec4c85a4049976",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "e4036b1e79c817f2",
"voter": false
},
"old": {
"blocks": 0,
"keyHash": "e4036b1e79c817f2",
"participation": 0,
"pubkey": "b0fd8eebbbaad96268e5f7b8a86090ca0c723954a0a5cb81c92b633094c1742b2505c4f7cea646c5c96bb52e57400067",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "6543d321fd61aedb",
"voter": false
}
},
{
"new": {
"blocks": 37,
"keyHash": "2f031ed35a29ced8",
"participation": 1,
"pubkey": "80ab38c523736ad8944c7d3375e7563ba3b0d5a3ab149bec568cf207edcc9e1ab1ee2566e06ff422a5ec4c85a4049976",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "",
"voter": true
},
"old": {
"blocks": 0,
"keyHash": "e4036b1e79c817f2",
"participation": 0,
"pubkey": "b0fd8eebbbaad96268e5f7b8a86090ca0c723954a0a5cb81c92b633094c1742b2505c4f7cea646c5c96bb52e57400067",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "6543d321fd61aedb",
"voter": false
}
}
],
"locks": [
12,
12,
12
],
"locks_at_fold": 9,
"refusals": [
{
"what": "w5-old -> w5-third on n2",
"code": 0,
"line": "1791378723.612 SUCCEED old=e4036b1e79c817f2 new=6543d321fd61aedb daa=502 (accepted: carried in this node's next block)"
},
{
"what": "w5-old -> w5-third on n0",
"code": 0,
"line": "1791378723.629 SUCCEED old=e4036b1e79c817f2 new=6543d321fd61aedb daa=502 (accepted: carried in this node's next block)"
},
{
"what": "w5-new -> w5-old on n1",
"code": 0,
"line": "1791378723.645 SUCCEED old=2f031ed35a29ced8 new=e4036b1e79c817f2 daa=502 (accepted: carried in this node's next block)"
}
],
"probes": [
{
"node": 0,
"code": 0,
"line": "1791378723.659 SCHEME 1 REFUSED key=b875b095c1b5d559 index=16 (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)"
},
{
"node": 1,
"code": 0,
"line": "1791378723.672 SCHEME 1 REFUSED key=b875b095c1b5d559 index=16 (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)"
},
{
"node": 2,
"code": 0,
"line": "1791378723.687 SCHEME 1 REFUSED key=b875b095c1b5d559 index=16 (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)"
}
],
"samples": [
{
"t": 5.7,
"daa": 0,
"phase": "fill",
"weights": [
"?",
"?",
"?"
],
"locks": [
0,
0,
0
]
},
{
"t": 20.7,
"daa": 7,
"phase": "fill",
"weights": [
"?",
"?",
"?"
],
"locks": [
0,
0,
0
]
},
{
"t": 35.7,
"daa": 17,
"phase": "fill",
"weights": [
"?",
"?",
"?"
],
"locks": [
0,
0,
0
]
},
{
"t": 50.7,
"daa": 36,
"phase": "fill",
"weights": [
"?",
"?",
"?"
],
"locks": [
0,
0,
0
]
},
{
"t": 65.7,
"daa": 57,
"phase": "fill",
"weights": [
"29/3",
"29/3",
"29/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 80.8,
"daa": 79,
"phase": "fill",
"weights": [
"29/3",
"29/3",
"29/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 95.8,
"daa": 92,
"phase": "fill",
"weights": [
"59/3",
"59/3",
"59/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 110.8,
"daa": 104,
"phase": "fill",
"weights": [
"59/3",
"59/3",
"59/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 125.8,
"daa": 114,
"phase": "fill",
"weights": [
"89/3",
"89/3",
"89/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 140.8,
"daa": 123,
"phase": "fill",
"weights": [
"89/3",
"89/3",
"89/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 155.8,
"daa": 135,
"phase": "fill",
"weights": [
"89/3",
"89/3",
"89/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 170.9,
"daa": 152,
"phase": "fill",
"weights": [
"119/3",
"119/3",
"119/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 185.9,
"daa": 173,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
1,
1,
1
]
},
{
"t": 200.9,
"daa": 187,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
1,
1,
1
]
},
{
"t": 215.9,
"daa": 203,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
2,
2,
2
]
},
{
"t": 230.9,
"daa": 224,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
2,
2,
2
]
},
{
"t": 246,
"daa": 242,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
3,
3,
3
]
},
{
"t": 261,
"daa": 258,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
3,
3,
3
]
},
{
"t": 276.5,
"daa": 270,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
4,
4,
4
]
},
{
"t": 291.6,
"daa": 285,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
4,
4,
4
]
},
{
"t": 306.6,
"daa": 308,
"phase": "carry",
"weights": [
"118/3",
"118/3",
"118/3"
],
"locks": [
4,
4,
4
]
},
{
"t": 321.7,
"daa": 338,
"phase": "carry",
"weights": [
"120/4",
"120/4",
"120/4"
],
"locks": [
4,
4,
4
]
},
{
"t": 336.7,
"daa": 357,
"phase": "carry",
"weights": [
"120/4",
"120/4",
"120/4"
],
"locks": [
4,
4,
4
]
},
{
"t": 351.8,
"daa": 368,
"phase": "carry",
"weights": [
"120/4",
"120/4",
"120/4"
],
"locks": [
4,
4,
4
]
},
{
"t": 366.9,
"daa": 385,
"phase": "carry",
"weights": [
"120/4",
"120/4",
"120/4"
],
"locks": [
5,
5,
5
]
},
{
"t": 382,
"daa": 406,
"phase": "carry",
"weights": [
"120/4",
"120/4",
"120/4"
],
"locks": [
5,
5,
5
]
},
{
"t": 397,
"daa": 424,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
6,
6,
6
]
},
{
"t": 412.1,
"daa": 441,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
7,
7,
7
]
},
{
"t": 427.2,
"daa": 454,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
7,
7,
7
]
},
{
"t": 442.3,
"daa": 469,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
7,
7,
7
]
},
{
"t": 457.3,
"daa": 485,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
8,
8,
8
]
},
{
"t": 472.4,
"daa": 494,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
8,
8,
8
]
},
{
"t": 487.6,
"daa": 509,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
9,
9,
9
]
},
{
"t": 502.6,
"daa": 526,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
9,
9,
9
]
},
{
"t": 517.6,
"daa": 545,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
10,
10,
10
]
},
{
"t": 532.7,
"daa": 561,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
11,
11,
11
]
},
{
"t": 547.7,
"daa": 578,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
11,
11,
11
]
}
],
"wall_s": 560.2,
"binaries": {
"igneumd": "/srv/builds/igneum-wt-genesis-forward/vendor/igneum-node-gf/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-genesis-forward/vendor/igneum-node-gf/target/release/igneum-miner"
}
}

View file

@ -0,0 +1,455 @@
{
"expect": "succession",
"pass": true,
"checks": {
"window_filled_before_the_succession": true,
"succession_accepted_on_submit": true,
"carried_once_on_every_node": true,
"successor_inherits_on_every_node": true,
"old_key_handed_over_on_every_node": true,
"nodes_agree_on_the_successor_weight": true,
"refused_old_again_on_n2": true,
"refused_old_again_on_n0": true,
"refused_successor_that_handed_over": true,
"scheme_1_refused_by_every_node": true,
"locks_continue_after_the_fold": true,
"zero_rejected_by_nodes": true,
"sinks_agree": true
},
"old_key": "e4036b1e79c817f2",
"new_key": "2f031ed35a29ced8",
"old_blocks_before": 42,
"succeeded_at_daa": 260,
"carried_seen_at_daa": 266,
"new_mined_alone": 29,
"rows": [
{
"new": {
"blocks": 37,
"keyHash": "2f031ed35a29ced8",
"participation": 1,
"pubkey": "80ab38c523736ad8944c7d3375e7563ba3b0d5a3ab149bec568cf207edcc9e1ab1ee2566e06ff422a5ec4c85a4049976",
"strippedUntilDaa": 0,
"succeededFrom": "e4036b1e79c817f2",
"succeededTo": "",
"voter": true
},
"old": {
"blocks": 0,
"keyHash": "e4036b1e79c817f2",
"participation": 0,
"pubkey": "b0fd8eebbbaad96268e5f7b8a86090ca0c723954a0a5cb81c92b633094c1742b2505c4f7cea646c5c96bb52e57400067",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "2f031ed35a29ced8",
"voter": false
}
},
{
"new": {
"blocks": 37,
"keyHash": "2f031ed35a29ced8",
"participation": 1,
"pubkey": "80ab38c523736ad8944c7d3375e7563ba3b0d5a3ab149bec568cf207edcc9e1ab1ee2566e06ff422a5ec4c85a4049976",
"strippedUntilDaa": 0,
"succeededFrom": "e4036b1e79c817f2",
"succeededTo": "",
"voter": true
},
"old": {
"blocks": 0,
"keyHash": "e4036b1e79c817f2",
"participation": 0,
"pubkey": "b0fd8eebbbaad96268e5f7b8a86090ca0c723954a0a5cb81c92b633094c1742b2505c4f7cea646c5c96bb52e57400067",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "2f031ed35a29ced8",
"voter": false
}
},
{
"new": {
"blocks": 37,
"keyHash": "2f031ed35a29ced8",
"participation": 1,
"pubkey": "80ab38c523736ad8944c7d3375e7563ba3b0d5a3ab149bec568cf207edcc9e1ab1ee2566e06ff422a5ec4c85a4049976",
"strippedUntilDaa": 0,
"succeededFrom": "e4036b1e79c817f2",
"succeededTo": "",
"voter": true
},
"old": {
"blocks": 0,
"keyHash": "e4036b1e79c817f2",
"participation": 0,
"pubkey": "b0fd8eebbbaad96268e5f7b8a86090ca0c723954a0a5cb81c92b633094c1742b2505c4f7cea646c5c96bb52e57400067",
"strippedUntilDaa": 0,
"succeededFrom": "",
"succeededTo": "2f031ed35a29ced8",
"voter": false
}
}
],
"locks": [
7,
7,
7
],
"locks_at_fold": 4,
"refusals": [
{
"what": "w5-old -> w5-third on n2",
"code": 3,
"line": "1791379651.057 SUCCEED REFUSED old=e4036b1e79c817f2 new=6543d321fd61aedb daa=290 (refused: key e4036b1e79c817f2 has already handed its weight to 2f031ed35a29ced8; a key hands over once)"
},
{
"what": "w5-old -> w5-third on n0",
"code": 3,
"line": "1791379651.072 SUCCEED REFUSED old=e4036b1e79c817f2 new=6543d321fd61aedb daa=290 (refused: key e4036b1e79c817f2 has already handed its weight to 2f031ed35a29ced8; a key hands over once)"
},
{
"what": "w5-new -> w5-old on n1",
"code": 3,
"line": "1791379651.085 SUCCEED REFUSED old=2f031ed35a29ced8 new=e4036b1e79c817f2 daa=290 (refused: successor e4036b1e79c817f2 has itself handed its weight to 2f031ed35a29ced8; it signs nothing and can inherit nothing)"
}
],
"probes": [
{
"node": 0,
"code": 0,
"line": "1791379651.097 SCHEME 1 REFUSED key=b875b095c1b5d559 index=9 (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)"
},
{
"node": 1,
"code": 0,
"line": "1791379651.110 SCHEME 1 REFUSED key=b875b095c1b5d559 index=9 (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)"
},
{
"node": 2,
"code": 0,
"line": "1791379651.123 SCHEME 1 REFUSED key=b875b095c1b5d559 index=9 (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)"
}
],
"samples": [
{
"t": 5.6,
"daa": 0,
"phase": "fill",
"weights": [
"?",
"?",
"?"
],
"locks": [
0,
0,
0
]
},
{
"t": 20.6,
"daa": 37,
"phase": "fill",
"weights": [
"?",
"?",
"?"
],
"locks": [
0,
0,
0
]
},
{
"t": 35.6,
"daa": 56,
"phase": "fill",
"weights": [
"28/3",
"28/3",
"28/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 50.6,
"daa": 71,
"phase": "fill",
"weights": [
"28/3",
"28/3",
"28/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 65.6,
"daa": 85,
"phase": "fill",
"weights": [
"58/3",
"58/3",
"58/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 80.6,
"daa": 106,
"phase": "fill",
"weights": [
"58/3",
"58/3",
"58/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 95.6,
"daa": 126,
"phase": "fill",
"weights": [
"88/3",
"88/3",
"88/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 110.6,
"daa": 141,
"phase": "fill",
"weights": [
"118/3",
"118/3",
"118/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 125.6,
"daa": 155,
"phase": "fill",
"weights": [
"118/3",
"118/3",
"118/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 140.6,
"daa": 166,
"phase": "fill",
"weights": [
"118/3",
"118/3",
"118/3"
],
"locks": [
0,
0,
0
]
},
{
"t": 155.7,
"daa": 185,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
1,
1,
1
]
},
{
"t": 170.7,
"daa": 202,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
2,
2,
2
]
},
{
"t": 185.7,
"daa": 213,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
2,
2,
2
]
},
{
"t": 200.7,
"daa": 229,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
2,
2,
2
]
},
{
"t": 215.7,
"daa": 244,
"phase": "fill",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
3,
3,
3
]
},
{
"t": 233.2,
"daa": 261,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
4,
4,
4
]
},
{
"t": 248.3,
"daa": 280,
"phase": "carry",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
4,
4,
4
]
},
{
"t": 263.4,
"daa": 300,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
5,
5,
5
]
},
{
"t": 278.4,
"daa": 316,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
5,
5,
5
]
},
{
"t": 293.4,
"daa": 328,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
6,
6,
6
]
},
{
"t": 308.4,
"daa": 343,
"phase": "after",
"weights": [
"120/3",
"120/3",
"120/3"
],
"locks": [
6,
6,
6
]
}
],
"wall_s": 317,
"binaries": {
"igneumd": "/srv/builds/igneum-wt-genesis-forward/vendor/igneum-node-gf/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-genesis-forward/vendor/igneum-node-gf/target/release/igneum-miner"
}
}

View file

@ -0,0 +1,85 @@
# Genesis forward-compatibility: the scheme byte, key succession, the cache rung
7 October 2026, 10:1x UK, the project lead's order to build mission item 8 now (`docs/analysis/mission/mission.md` section 2.8; the research in `docs/analysis/mission/future.md` sections 1.3, 7 and 10 and `docs/design/finality-in-proof.md` section 7). Branch `genesis-forward` on the node fork (from release-0.3.19-node dc141409, the merged line that carries the ladder and the W7 leave item, which ca3-v4-0318 alone does not) and `genesis-forward` on the repo. Three genesis fields, every switch never on the devnet (its digest does not move), all three set at the testnet genesis by the testnet lane, which holds the cut and re-pins. The class-group VDF's quantum fallback is flagged in spec 04 section 4.8, not built.
## 1. The scheme byte
| Item | Place | Value |
|---|---|---|
| The byte | `finality::Vote::sig_scheme`, `finality::KeyReveal::sig_scheme`, `finality::Succession::successor_scheme` | `SIG_SCHEME_BLS12_381` = 0 everywhere today |
| The genesis value | `Params::sig_scheme` (override key `sig_scheme`) | 0 on every network |
| The switch | `Params::sig_scheme_activation_daa` (override key `sig_scheme_activation_daa`; `u64::MAX` = never) | never on devnet, simnet, mainnet; 0 at the testnet genesis |
| The digest | both fields enter once the switch is set (the 0.3.15 rule) | the devnet's digest unchanged; the testnet's moves at the cut |
| The wire, plain | vote item tag 1 (`Vote::LEN` = 280 bytes), evidence tag 3, reveal `IGNK` + 288 hex: scheme 0 implied | byte for byte what every live network carries |
| The wire, explicit | vote item tag 5 = `scheme \|\| vote`, evidence tag 6 = `scheme \|\| first \|\| second`, reveal `IGNS` + 2 hex of the scheme + 288 hex | written by every template once the switch is active (`encode_section_explicit_within`); a vote of any other scheme is always explicit, so a scheme the node does not run is never mistaken for one it does |
| The active scheme | `igneum::active_sig_scheme(genesis, class)` = the scheme the program class at the sink names (`SIG_SCHEME_OF_CLASS`, a genesis table with no row today), else the genesis byte; the finality manager re-reads it at every virtual change | 0 |
| The refusal | `FinalityManager::scheme_refusal`: a reveal is not registered, a vote is not recorded, carried or gossiped, a successor is refused; the RPC answers `refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one` | every node, whatever its switch |
Why a class change names the scheme: the flip is the P2 mechanism (95 percent of mining weight over a window with a floor height, the one path a consensus change takes on this chain), and the aggregated-vote format must ship first (naive ML-DSA-44 votes at 8,192 voters cost 19.4 MB a checkpoint and 57 GB a day, `future.md` 7.3; with 250x SNARK aggregation about 223 MB a day). Adding a row to the table is a code change under the class path; the byte is in every item from genesis so the row costs no fork.
## 2. Key succession, W5
| Item | Place | Value |
|---|---|---|
| The item | `finality::Succession` (tag 7, 297 bytes): `daa`, old key, successor key, successor scheme, the old key's signature, the successor's signature, both over `"igneum-succeed-v1/" \|\| chain_id \|\| 0 \|\| daa \|\| old \|\| new \|\| scheme` under `IGNEUM_SUCCEED_V1_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_` | the successor's signature is its consent and its proof of possession in one |
| The switch | `Params::finality_succession_activation_daa` (override key of the same name) | never everywhere; 0 at the testnet genesis; in the digest once set |
| Submission | `submitFinalitySuccession` (RPC op 158, gRPC 1131/1132, wRPC, `igneum-miner succeed <grpc url> <old label> <new label>`) | carried after the leaves in the templates of the node that holds it; no p2p gossip kind (the successor's own node mines it) |
| The fold | `successions_at` (deterministic like `leaves_at`: the lowest-DAA carrier in C's past dates it) and `fold_successions` inside `voters_at`: the old key's window blocks are credited to the successor as they stand (count and oldest block), the old key leaves the table, a ban or a leave on the old key lands on the successor; the successor's presence counts the old key's carried votes | from the first checkpoint whose past holds the carrier; the window inherited, not a fresh one |
| Once | one record per old key: a second succession from a key that handed over is refused (`already handed its weight to`), a successor that has itself handed over is refused (`has itself handed`), which also refuses every cycle; a chain old to new to newer is legal | a record outlives the weight it moved by one window, then is trimmed |
| After | the old key's votes are refused (`handed its weight to`), never counted, never carried | the old key is dead |
| The report | `getFinalityWeights`: `succeededFrom` on the successor's row, a weightless row for the old key with `succeededTo` | both nodes of the unit test agree on every voter list |
Not in this round: a p2p gossip kind for successions (the leave's shape, protocol version bump); the app's "rotate key" button, which signs the item from the old key's label and restarts the miner under the new label (the `succeed` subcommand is the primitive); the aggregated-vote format.
## 3. The cache rung beside N
| Item | Place | Value |
|---|---|---|
| The rung | `igneum::CacheRung { mib, admissible }` as `LatencyLadder::cache_rung` (override key `latency_ladder_cache_rung`) | `{512, false}` at genesis; a power of two above the 256 MiB genesis cache |
| The signal | `LadderSignal::Cache` = both ladder bits set (header version 0xc000; until today both set was "no signal" and no node ever stamped it, so no block written so far changes meaning); `IGNEUM_LADDER_SIGNAL=cache` | code 3 in `PowEpochInfo::latency_ladder_signal` |
| The rule | `latency_ladder_step_signalled_with_cache`: the cache step moves 0 to 1 when every one of the newest seven windows reaches 90 percent for the cache rung, the rung is admissible and the cool-down holds (the oldest window begins after the last decision, shared with N); never back; never in the same decision as N (one signal per block, exclusive shares) | `LadderState::cache_step` |
| The digest | the rung's size and flag enter with the ladder, once `latency_ladder_activation_daa` is set | the testnet's digest moves at the cut |
| The RPC | `powEpoch.latencyLadderCacheStep`, `nextLatencyLadderCacheStep`, `latencyLadderCacheMib`, `nextLatencyLadderCacheMib`, `latencyLadderCacheBps`, `latencyLadderCacheWeakestBps`, `latencyLadderCacheAdmissible` (proto fields 37 to 43); the daemon's ladder line (`cache [512 MiB]`) | the rung visible on every node |
| The gate | `admissible` in the genesis list, false until measured: the cold verify of one warp with the 512 MiB cache on the reference core with its SMT sibling loaded under 10 ms (the verifier reads the cache, so this is the bound), the day-cache build on a 2019-class core under twice today's, and the 8 GB tier still holding dataset, cache and the prover footprint | the rule never enters an inadmissible rung (tested) |
Why beside N and not a seventh N rung: the six approved rungs and their indices stand (the project lead, 7 October 2026, 09:3x UK); a rung inserted in the list would either sit behind the three inadmissible rungs (unreachable) or shift the approved indices. A second lever on the same signal carrier keeps the list as approved and the cool-down shared.
Owed with the measurement: the engine's consumption of `cache_mib` (`EpochSeeds` carries `shadow_reps` today and no cache size; the pack and the three hosts build a 256 MiB cache), so the flag stays false until the path exists and is measured. Per tier what the rung means: every tier from 8 GB holds a 512 MiB cache; the day-cache build doubles (about 0.7 s on the reference core today, approximate, from the class v4 fill line); the Apple tier's unified memory holds it; a pool user does nothing.
## 4. The class-group VDF under a quantum computer
Flagged in spec 04 section 4.8, not sized: Shor computes the class-group order, which removes the sequentiality assumption of the Wesolowski VDF, so an attacker with a cryptographically relevant quantum computer grinds the hourly seed (a liveness nuisance against the lottery, not a safety break; finality rests on the vote keys of section 1). The fallback is a hash-chain delay behind the same version byte, designed when the scheme flip is scheduled.
## 5. Gates
| Gate | Where | Result |
|---|---|---|
| The digest test | `consensus_digest_covers_every_consensus_field_and_nothing_else` (30 edits; the scheme byte and the cache rung alone move nothing), `override_params_carry_the_genesis_forward_fields_and_the_digest_moves_only_when_set` | section 6 |
| Scheme 1 refused by every node until the signal | unit: `a_vote_reveal_or_successor_of_another_signature_scheme_is_refused_until_a_class_names_it` (RPC, in a block, a reveal, a successor; the explicit template form); fast-time: `probe-scheme` on three nodes | section 6 |
| A succession carried once and refused twice | unit: `a_key_hands_its_window_to_a_successor_once_and_a_second_succession_is_refused` (two nodes); fast-time: `infra/fast-time/key-succession.mjs` (the known-failed case `--expect no-succession` first) | section 6 |
| The ladder rung visible in the RPC | `powEpoch.latencyLadderCache*` on `getBlockTemplate`, the daemon line; unit: `latency_ladder_rule` (an inadmissible cache rung never entered; with the flag, 90 percent in seven windows enters it, N still steps after it, one rung, never back) | section 6 |
| The codec | `scheme_byte_and_succession_items_round_trip_and_an_older_decoder_stops_at_them` | section 6 |
## 6. Results (7 October 2026, 12:5x to 14:3x UK; igneum-build-1 for the gate build, the harness and the digests, igneum-build-2 for the suites)
| Gate | Run | Result |
|---|---|---|
| The digest test | `cargo test --release -p kaspa-consensus-core --lib` on build-2 at 75810130 | 124 passed, 0 failed, 2 ignored (`consensus_digest_covers_every_consensus_field_and_nothing_else` with 30 edits, `override_params_carry_the_genesis_forward_fields_and_the_digest_moves_only_when_set`, `latency_ladder_rule` with the cache rung) |
| The 60x keeper test | `fast_time_60x_file_is_the_devnet_at_60x` on build-2 against the completed file (repo 9c9a1f52) | 1 passed (the file had lacked 17 fields on master, `emission` and `proving_consensus_verify_daa` among them; the box mirrors carry no `infra/`, so the test skips there unless the file is shipped) |
| Scheme 1 refused by every node | unit `a_vote_reveal_or_successor_of_another_signature_scheme_is_refused_until_a_class_names_it`; fast-time `probe-scheme` on three nodes, both harness cases | green in the suite runs; every node: `SCHEME 1 REFUSED ... (refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one)` |
| A succession carried once and refused twice | unit `a_key_hands_its_window_to_a_successor_once_and_a_second_succession_is_refused` (two nodes); fast-time `infra/fast-time/key-succession.mjs` on build-1 (3 nodes, one CPU miner each, override-60x with the three switches at 0) | known-failed case first (`--expect no-succession`, 13:04 to 13:13 UK, `genesis-forward-harness/known-failed-no-succession.json`): FAIL as it must on every carried-once check with the probe, the locks and the sinks holding. Pass case (`--expect succession`, node bdb34f62, 13:23 to 13:28 UK, `pass-succession.json`): PASS, every check holds: w5-old held 42 blocks at DAA 260; the succession accepted on n2 at DAA 261 and carried by a block at DAA 266 on every node; at the fold (DAA 290) every node reads w5-new 37 blocks (7 mined alone) and w5-old 0 with `succeededTo`; w5-old to w5-third refused on n2 and n0 (`already handed`), w5-new to w5-old refused on n1 (`has itself handed`); locks 4 to 7 on every node after the fold; 0 rejected blocks, sinks agree; 317 s wall |
| The cache rung visible in the RPC | `powEpoch.latencyLadderCache*` (proto fields 37 to 43); the daemon's ladder line | the testnet-shaped file prints `rungs 27, 35, 53, [88], [173], [267] shadow passes, cache [512 MiB] beside them` (`digest-testnet-shape.log`) |
| The codec | `scheme_byte_and_succession_items_round_trip_and_an_older_decoder_stops_at_them` | green in the suite runs |
| The digest, cross-binary | igneumd 75810130 against the 0.3.20 base dc141409 (bs0319's build), both with no file, devnet suffix 973 | both `079d8a7e736abbd4` (`digest-no-file.log`, `digest-base-dc141409.log`): the three fields at never move nothing; the ladder alone at 0 reads `772f9f8e3ef59ca1`, the testnet-shaped file (ladder at 0, scheme switch at 0, succession at 0, the cache rung) `60e842a738c7dea0`, on devnet params; the testnet lane's own number comes from `TESTNET_PARAMS` at its cut. The 0.3.18 line's `c562d70e` is behind the decimals, tail-emission and subsidy fields of the 0.3.19 and 0.3.20 lines, not behind these |
| The gate build | `tools/build-remote.sh --priority gate` on build-1 at bdb34f62 | igneumd 57,554,336 B sha256 6d0aa37b..., igneum-miner 10,240,768 B sha256 69fc3c63... (commit string carried) |
| The consensus suite | `cargo test --release -p kaspa-consensus` (all targets) on build-2 at f95178a1, twice | 114 passed, 0 failed, 3 ignored in the lib binary both times, the two moved targets 1 each; `cargo check -p kaspad` clean. Before the fix below the lib binary failed 1 of 114 on one of the two-node tests in three of four runs (the succession test once, the pre-existing F23 test twice), node 1 refusing block 61 at DAA 60 with `UnexpectedDifficulty`, the two nodes' bits about 17,000 apart in the mantissa |
Faults found on the way, both mine: (1) rule v3's frozen table stood at the lock before the carrier, where the old key still weighed and signed nothing, so nothing locked after the fold (fixed in `frozen_table`, 9322cc1d); (2) the template read the explicit-form switch from the process-wide static that only the daemon installs, so `TestConsensus` wrote the plain form (the manager holds the activation now, daa61847).
### 6a. The two-node refusal: found and fixed (f95178a1)
The probe on build-2: F23 alone three times, green each time; the three two-node tests together three times, green each time; so the refusal needed the rest of the lib binary. The cause: `consensus/src/consensus/services.rs` built the difficulty manager with `igneum::pow_epoch_blocks()`, the process-wide static, where every other argument of that constructor comes from `params`; two pruning-proof tests in the same binary (`igneum_m20_tests.rs:27`, `igneum_pow.rs:346`) install a 60-block schedule process-wide, so a `TestConsensus` built inside that window read a 60-block epoch into its difficulty manager while its partner read 3,600, the two disagreed on `reference_window` from DAA 60, and the second node refused block 61, the exact block of every refusal. The fix is the manager's own field, `params.pow_epoch_blocks`. The node's behaviour is unchanged (the daemon installs the static from the same params before building the consensus); the class is the static-at-construction read, the sibling of the signal-table race the node lane moved two installing tests for on 7 October 2026. The test helper now names the node and the block it refuses, which is what found it.
## 7. For the testnet lane
Override keys and testnet values: `sig_scheme: 0`, `sig_scheme_activation_daa: 0`, `finality_succession_activation_daa: 0`, `latency_ladder_cache_rung: {"mib": 512, "admissible": false}` (the six N rungs unchanged). Digest: with the ladder already active from genesis the cache rung's two fields enter after the six rungs' fields; the scheme switch adds two fields, the succession switch one. `print_testnet_object` prints the four keys.

View file

@ -308,7 +308,7 @@ Sweep (5 October 2026, evening): stated. `site/litepaper.html`, Finality ends wi
### F11. VDFs are exotic
"A class-group VDF with Wesolowski proofs in a consensus-critical path, in a project with no cryptographer. Chia needed years and still got timelord ASICs."
Status: Answered by design, with the dependency conceded.
Status: Answered by design, with the dependency conceded. Update 7 October 2026 (era VDF lane): the era VDF is in the node (spec 4.4 Implemented, behind `era_vdf_activation_daa`, never until the project lead sets it per network), on a fixed-width integer with no C library, with the hash-chain fallback behind the genesis scheme byte for the day a class group's order is computable; the attack pass's F7 harness fires against the stand-in (1 of 6 cuts re-rolled at no delay) and is silent against the VDF (0 of 6); the measured rates, prove and verify times and the margin against the fastest known prover (chiavdf's AVX-512 path on the same box) are in `docs/analysis/era-vdf-2026-10-07.md`. The timelord-ASIC point is answered by the margin table of spec 4.6: the delay only has to exceed the 2-s publish window, and it does so by orders of magnitude on the fastest evaluator measured. The external review (O-4.1) is still owed. Was: Answered by design, with the dependency conceded.
Answer: The VDF is used for one thing: making the hourly program unknowable within the roughly two seconds a miner has to decide whether to publish a block, closing a withhold-or-publish grind the review measured at about 130 to 1 for a 30% miner. The VDF input is a certified checkpoint at least one epoch before the epoch starts, so honest nodes have about 50 minutes of slack to evaluate a 10-minute VDF, and an evaluator 300x faster than reference would still be needed to beat the two-second decision window. Chia has run class-group VDFs in production since 2021, with faster hardware evaluators existing and not breaking it (approximate, from memory). The design doc lists the VDF as a new dependency and ships the evaluator in every node. The grinding simulation with and without the VDF is scheduled before gate 3.
@ -2347,6 +2347,30 @@ Evidence: `site/index.html`; `site/litepaper.html`; `tools/ci/ledger-text-check.
- **E5, L9** (the client dev fee), evening: the fee is built, switchable and audited (E18). The homepage's miner section now carries the litepaper's sentence (`site/index.html`, under the download buttons: "The protocol carries no fee. The Igneum Miner software takes an optional 1% dev fee, like other GPU miners, off with one flag"), and the litepaper's "What a miner's hour looks like" states the mechanism, the flag and the audit. The "no value" and "may be reset" devnet wording of L9 is still open.
- **D2** (the app share). The "Why build here" text is applied at `site/litepaper.html:355` and states the number; two gaps noted under E17.
## Node findings (6 October 2026, evening): the 0.3.15 canary and the sync crash, fixed before the cut
Two faults found on the live devnet by the fleet's canary boxes and the hub while 0.3.15 (class v4) was held for its cut. Both are conceded as ours, both are fixed on the fork branch `ca3-v4-order-fix` for release-0.3.15-node (the shipper's tree), both carry a test and a gate that every future node cut runs (`infra/fast-time/node-compat.mjs`).
### N1. A 0.3.15 node on the live file wrote blocks every 0.3.14 node rejected
"p2-3090-1 on 713ef876 with the live thirteen-field file, at every reconnect since its 19:42Z restart: P2P, got reject message: wrong block version: got 1026 but expected 2 from peer 188.245.5.161:26611; blocks 122,630, synced false, peers 0."
Status: Fixed (6 October 2026, 20:0xZ, fork commit 17c60367 on ca3-v4-order-fix, the stamp; and 20:4xZ, f1ea7a38, the receive side, after the clean canary of 21:3x UK showed a 0.3.15 node ACCEPTING and relaying a version-1026 block it would never write, off a poisoned peer, and being disconnected by every 0.3.14 peer in turn; in 0.3.15). Conceded: the node lane's fault, twice.
Answer: the class v4 signal (PROPOSED, `docs/plans/counter-asic-3-node.md` section 6) is the producer's object version in the high byte of the header version; the first 0.3.15 build stamped it from the binary alone, so on the live file (no window, no floor) a new node wrote version 1026 and every 0.3.14 node, whose rule is version 2 or reject, refused its blocks and its relays, although the digest compat rule (the same evening) made the handshake peer. A new miner lost every block; a lagging new node could not re-sync. The fix gates the stamp, the signal read AND the header version rule on publish 2's object (both `program_class_v4_signal_window_daa` and `program_class_v4_activation_daa` set): on the thirteen-field file the header is byte for byte what 0.3.14 writes, and a header carrying the signal bit is refused with 0.3.14's own `WrongBlockVersion(1026, 2)` before the engine and never relayed, so a poisoned peer cannot poison a new node. What no new-node change can do: a 0.3.14 node whose datadir holds version-1026 blocks is disconnected by its 0.3.14 peers by THEIR rule when it relays them, until those blocks leave the relay window; the fleet wipes the poisoned datadirs. Why the gates missed it: the digest compat harness peered the two binaries but neither mined; the new gate mines on both sides, joins a clean node through each, restarts the new node and re-syncs it.
Evidence: unit tests `block_template_uses_current_block_version` (version 2 with no window and no floor, 1026 with both) and `program_class_signal_rule`; the gate `infra/fast-time/node-compat.mjs` on the fixed binary beside 0.3.14 on the live object: one digest on all four nodes, the new miner's 73 blocks accepted by the old hub, counts 169/169/169/169, header versions {0: the genesis, 2: 169}, no reject line, the clean join and the restart re-sync (PASS, 20:06Z; `docs/plans/counter-asic-3-gate/node-compat-20261006-fixed.json`); the same gate on the canary binary 713ef876 reproduces the fault (`HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting`, counts 138 against 101, the restarted node never re-synced: FAIL, `node-compat-20261006-canary-failed-case.json`).
### N2. Any peer could crash any pruned node with a sync request below its retention
"thread 'tokio-rt-worker' panicked at consensus/src/processes/sync/mod.rs:87:62: called Result::unwrap() on an Err value: KeyNotFound(GhostdagCompact/0/edc4fa84...) then Exiting..." (the hub, a pruned 0.3.14 node, 19:57Z, when p2-3090-1, cut off since 19:42Z, began a sync from the genesis against it).
Status: Fixed (6 October 2026, 20:2xZ, fork commit 7961c5f1 on ca3-v4-order-fix; in 0.3.15). Conceded: a remote crash vector in every release from the first pruned devnet node to 0.3.14; security.
Answer: `SyncManager::antipast_hashes_between` (the IBD headers path, `RequestHeaders`) unwrapped the GHOSTDAG reads of the requested low block and of every chain block of the walk; a pruned node holds no GHOSTDAG data below its retention, so a request from the genesis killed the serving node, not the requester. The fix makes the walk fallible: a read below the retention is `SyncManagerError::BlockBelowRetention(hash)` (also in `find_highest_common_chain_block` and the pruning-point locator), the consensus API returns it, and the serving flow answers the peer with the error and disconnects it, the node alive; the peer syncs from a node that holds the history. The hub was restarted by hand at 20:00Z (synced in 25 s).
Evidence: unit test `a_sync_request_below_retention_is_an_error_not_a_panic` (a chain of six headers, the genesis's GHOSTDAG entry deleted as a pruned node holds it, `get_hashes_between(genesis, tip)` returns the error, a request inside the retention still answers); the gate's served-join case (a clean old node syncing from the genesis through the NEW node, which must stay alive and serve it) in `node-compat.mjs`; the box's kaspa-consensus and consensus-core suites on the commit. A truly pruned serving node in a harness needs hours of fast-time chain (pruning depth 13,838 DAA) and is owed; the unit test stands in for it.
## Status updates, 5 October 2026 (ledger sweep, night of 4 to 5 October)
`docs/review/ledger-sweep-2026-10-05.md` holds the runs, the commands and the running table. Every Open, Proposed, Unmeasured or pending entry was read against its experiment line; the status lines above carry the evidence inline, marked "Sweep (5 October 2026)" where a note was added and "Was:" where the status changed. Items owned by the other two night branches (F23, F24, G12, X18, the flood memory growth; the base-fee floor, testnet parameters, G13, G14, public text) were left to them.
@ -2431,6 +2455,52 @@ Answer: Correct as a fault, wrong as a null. The window layer (spec 01 1.13.1 as
The amendment (a0aaca92): in class v4's chain draw a load's source is drawn only from registers whose last writer injects (add, sub, xor, mad, shfl, load) or is a rotate (rotl, rotr), never one last written by `or`, `mul` or `mulhi` (`generator.rs` candidate_from_words_class, keyed on the era-composed V4_CLASS; no attempts lost, rule (a) unchanged; v2, v3 and the generator-2 ladder packs byte-identical). Split protection (the node lane's form): generator stays 4 and a generator-4 program id appends `"sub/" || PROGRAM_SUBVERSION_V4 (= 1) as little-endian u16` inside `program_id()`, so a binary from before the rule and one after it never share a program id for one seed and the node's id check catches a split; packs carry `IGNEUM_PROGRAM_SUBVERSION 1` and `"sub_version": 1`, and packcheck refuses a generator-4 pack whose sub-version is absent or other. The node side (release-0.3.20-node, built to a0aaca92): kaspa-pow's test pins 1a4230699a6b9c60 must-equal and c120d7963abdcd96 must-differ through `generator::program_id` with the suffix, and the amended class signals object byte 5 in the header (CLASS_SIGNAL_V4 = 5; the tally counts byte 5 and above), so a block of the 6 October stream (byte 4) never counts towards the flip and a byte-4 node forks alone at it; object 6 is class v5's. The seven gate packs re-exported (devnet epoch 0 and eras 0 to 5): id 1a4230699a6b9c60 (was c120d7963abdcd96, pinned as the must-differ vector in `tests/recheck.rs`), fingerprints Metal = Apple OpenCL 867dbc45cfb36b4d, 2146ecacc8c75a8e, fe52602393f6d3d4, 3b206471a13912b4, c3f03c4a5d7333aa, f1dfd7209f15bb97, 8c194da64fadf31d; the v3 control 73bcbfe8ccf988f1 / 90f794dd556f7a3b untouched; the zip of the eight packs sha256 889ec99976d2728b4b5035bfa476032e5b6a13b928968fc45236d5f25084aa39.
Evidence: `docs/analysis/ca3-v4-uniform.md` (the model, the census `tools/ca3-v4-uniform`, the chip pricing); F8's logs under `/srv/builds/igneum-wt-attack/target-attack-f8/log/`; the tests limited by the project lead's word to what prevents a split and proves the fix: the vectors (the seven packs' ids and fingerprints above), the `igneum-pow` crate suite on igneum-build-1 (SUITE-LINE), the pairing of the fork's kaspa-pow with this igneum-pow on igneum-build-1 (PAIRING-LINE), and one G1 run on the RTX 5090 (PC 2 job run-ca3-v4-amend-g1-pc2-20261007, 09:41:07 to 09:41:28Z, exit 0, the installed 0.3.17 worker sha256 14b6637e..., NVRTC, beside the app's miner, the prover on, the lock held 09:40:22 to 09:41:51Z): self-test PASS on all eight packs and every 2^24 fingerprint equal to the Mac's (the seven above and the control 90f794dd556f7a3b), NVRTC 188 to 332 ms per pack, the 1 GiB build 38 to 49 ms.
Evidence: `docs/analysis/ca3-v4-uniform.md` (the model, the census `tools/ca3-v4-uniform`, the chip pricing); F8's logs under `/srv/builds/igneum-wt-attack/target-attack-f8/log/`; the tests limited by the project lead's word to what prevents a split and proves the fix: the vectors (the seven packs' ids and fingerprints above), the `igneum-pow` crate suite on igneum-build-1 at 8c728ca3 (`tools/build-remote.sh -- test --release`, rc 0, 77 s, 12:1x UTC: 61 lib + 7 derive + 4 mixer + 19 packs + 2 recheck + 7 scratch = 100 passed, 0 failed, the pinned v2 and v3 packs byte-identical and the v4 must-equal and must-differ ids as pinned), the pairing of the fork's kaspa-pow with this igneum-pow on igneum-build-1 (PAIRING-LINE), and one G1 run on the RTX 5090 (PC 2 job run-ca3-v4-amend-g1-pc2-20261007, 09:41:07 to 09:41:28Z, exit 0, the installed 0.3.17 worker sha256 14b6637e..., NVRTC, beside the app's miner, the prover on, the lock held 09:40:22 to 09:41:51Z): self-test PASS on all eight packs and every 2^24 fingerprint equal to the Mac's (the seven above and the control 90f794dd556f7a3b), NVRTC 188 to 332 ms per pack, the 1 GiB build 38 to 49 ms.
Sub-version 2 (7 October 2026, afternoon; main's ruling B2: 0.3.20 ships sub-version 1 as object byte 5 untouched, and sub-version 2 is object byte 7, built on this branch at 07a809a7): the attack-pass lane's F8 census on sub-version 1 (64 chain-shaped seeds, the window-model control, 2^24 nonces) read 53 of 64 PASS and 11 FAIL at 1.2x, worst p31 at 29.27x, and named three residual classes, all a constant delivered through a writer the one-writer rule admits: saturation or zero preserved through rotl, rotr or a load after a saturated load (p6, p23, p26, p31, p34), zero from mulhi (p45), and the iteration boundary (an `or` at 63 feeding a load at 1, p11); p4, p10 and p25 at 1.28x to 1.57x are the window model's own tail. (An F9 hot-set census read on the same day was withdrawn by its author: its harness drew outside the rule and its metric counted the era's designed windows as hot; F8 is the one re-gate instrument.) Sub-version 2 closes the three: the draw takes a load's source only from a register fresh by dataflow (fresh at the start; a load keeps freshness only from a fresh source; add, sub, xor, mad, shfl from either operand; rotl, rotr from their operand; or, mul, mulhi never), keyed on the class v4 shape on every draw path; rule (a') of the acceptance rule runs that freshness to its fixpoint over the loop (base then shadow block) and rejects a candidate whose load reads an unfresh register in the steady state; rule (c') counts, per load site, the source values equal to 0 or all-ones over the 64 units' 16,384 evaluations and rejects at 164 or more. The devnet epoch-0 seed's attempt 0 is rejected and attempt 1 accepted: id a788661687db4bb3 (c120d7963abdcd96 and 1a4230699a6b9c60 the must-differ pair), fingerprints Metal = Apple OpenCL e370fb2080b7dbb1, b7237555d31fc3cf, b6b167fa15dfe2c9, 28bdf65eff33f2c4, e26d38c46f3f1b16, dd8fdf6ff4f59eed, 8bf40f5cb858d835 (13:01Z), the packs zip sha256 69c36772cd79e44e2ddd589466d9c64a94a13c9e970e9f27bd76feabb9b4581b; the seven 256-block ladder packs of `packs-ca3-shadow` re-exported under the rule (their measured rates stand as the old stream's). Nothing is proposed for sub-version 2 until the attack-pass lane's two gates on 07a809a7 are green (the 64-seed census under 1.2x on every seed, the hot-set census on the chain path); its suite, pairing (after the node lane's re-pin to byte 7) and G1 lines are added below as they land. The static census (`tools/ca3-v4-uniform`, box 2, 13:12Z) over 1,024 chain-shaped seeds plus F8's p1 to p3 at sub-version 2: 0 lossy-sourced load sites of 16,432 (14,329 injecting, 2,103 bijective), 0 programs with an `or`-, `mul`- or `mulhi`-sourced load, the no-era draw path giving the devnet epoch-0 seed the pack's own id a788661687db4bb3 (one stream on every path); the cost of rules (a') and (c'): 1.99 attempts per seed on average against 0.05 before (p2's seed took five), which is 2 ms of generation per rejected attempt on one core, nothing a miner or node notices. The crate suite at 526fa757 on box 2 (`tools/build-remote.sh --box 2 -- test --release`, route line "box 2 for class suite, priority normal", rc 0, 66 s, 13:15Z): 61 lib + 7 derive + 4 mixer + 19 packs + 2 recheck + 7 scratch = 100 passed, 0 failed, the pinned v2 and v3 packs byte-identical and the three v4 ids as pinned (a788661687db4bb3 equal; c120d7963abdcd96 and 1a4230699a6b9c60 differ).
AP-F8-2 (7 October 2026, 13:xx UTC, the attack-pass lane on sub-version 2 at 07a809a7): chain-shaped seed igneum-f9/331672 exhausted the 32 attempts under rule (a') and the generator panicked, which on the chain is an epoch no node can draw, a liveness halt; the measured (a') plus (c') rejection rate of about two thirds per attempt puts the exhaustion probability at about (2/3)^32, 2e-6 per epoch seed (sub-version 1 exhausted 0 of 10^6). Main's ruling: the draw is total and no consensus path panics. Fixed at 8bdcbdd8 with the stream unchanged (re-export diff 0; the id a788661687db4bb3 and the fingerprints stand, so sub-version 2 keeps its number and the running censuses): the attempt cap of the class v4 shape is 256 (`MAX_ATTEMPTS_V4`; v2 and v3 keep 32), which puts the exhaustion probability under 1e-45 at a worst-case draw of about half a second on one core; after the cap the seed takes the last-resort program, deterministic and accepted as drawn, the candidate at attempt 256 with every `or`, `mul` and `mulhi` of the base program and the shadow block rewritten to `xor`, so every register stays fresh from the init words on and rule (a') holds by construction. The spec text for class v4 therefore reads: attempts 0 to 255 under rules (a), (b), (a'), (c) and (c'), then the last-resort program; the probability of reaching it is (r)^256 for a per-attempt rejection rate r, under 1e-45 at the measured r of about 2/3. Tests: `class_v4_draw_is_total_with_the_last_resort` (the last resort on real (a')-rejected candidates, every load fresh after it, no lossy op left, the chain path over 64 seeds without a panic, the cap per class). The 10^6-seed exhaustion count at the fixed commit is the attack-pass lane's measurement (its re-gate string is 8bdcbdd8); the 4,096-seed census with the attempt histogram (box 2, 13:33Z, `tools/ca3-v4-uniform --n 4096 --f8` at 8bdcbdd8): 4,099 chain-shaped programs, 0 lossy-sourced load sites of 65,584 (57,304 injecting, 8,280 bijective), 0 exhaustions, the accepted attempt geometric with ratio 0.674 (1,338 at attempt 0, 921, 620, 408, 274, 189, 127, 75, 55, 40, 16, 11, 12, 6, 5, then one each at 16 and 17; mean 1.98), so the measured per-attempt rejection rate is r = 0.674 and the exhaustion probability is 0.674^32 = 3e-6 under the old cap and 0.674^256 = 1e-44 under the class v4 cap. The crate suite at 8bdcbdd8 on box 2 (route line "box 2 for class suite, priority normal", rc 0, 80 s, 13:31Z): 62 lib + 7 derive + 4 mixer + 19 packs + 2 recheck + 7 scratch = 101 passed, 0 failed, the total-draw test included. G1 for sub-version 2 on a fleet RTX 5090 (main's order; the fleet lane, p1-5090, driver 580.173.02, sm_120, the box's Linux NVRTC worker `igneum-worker-cuda 1.0 (4 October 2026)` sha256 97e036e2..., which carries no program and compiles each pack's own text; the kit zip sha256 asserted before the put; 13:43:22 to 13:44:07Z): `--bench --batches 5 --batch-log2 24 --block-warps 1` on all eight packs, self-test PASS on every pack with the cache FNV 448274a57f508cbc and every 2^24 fingerprint equal to the Mac's (the control 90f794dd556f7a3b and the seven above); the rates (120 to 142 MH/s beside the box's own miner loop) are a reference only. The Windows G1 on PC 2 (job run-ca3-v4-sub2-g1-pc2-20261007b, published 13:46:33Z under the PC 2 lock, ran 13:46:36 to 13:46:50Z, exit 0; app 0.3.19, the installed worker sha256 14b6637e..., the RTX 5090 switched off by the runner's --cards-off before the script and restored on exit, igneum-worker-cuda running 0 before and after, the prover untouched): self-test PASS on all eight packs with the cache FNV 448274a57f508cbc, every 2^24 fingerprint equal to the Mac's and the fleet's (the control 90f794dd556f7a3b and the seven above), NVRTC 197 to 317 ms per pack, the 1 GiB build 22 to 34 ms, 115 to 130 MH/s with the card alone (a reference, 5 batches).
AP-F8-2, the exhaustion half, FIXED-AND-PASSED at 8bdcbdd8 on the attack-pass lane's 10^6 chain-shaped seeds through the chain path (14:03:53Z): 0 exhausted, 0 panics, 4 seeds past attempt 31 (three at 32, one at 35; 4e-6, inside (2/3)^32), max attempt 35, no seed at the last resort; r = 0.67, mean 2.0 attempts per seed.
Sub-version 2's hot-set half did not read green: F8's 64-seed gate at 39 of 64 had 8 over 1.2x of the window model (p23 4.82x, p19 3.32x, p15 2.57x, p18 2.50x, p34 1.25x; p4, p8, p10 unattributed at 1.22x to 1.50x). AP-F8-3 (7 October 2026, 14:0x UTC, the hash lane): the cause of the whole residual is that `accept.rs` never executed the latency-shadow block. Its interpreter (`run_unit`) was written for class v2 and v3 and ran the 64 base instructions per iteration and nothing after instruction 63, while the hash (`verify.rs`, the kernels) runs the shadow 27 times at the end of every iteration; so every dynamic acceptance test (c), (c') judged a class v4 program the chain never hashes. Main's word (14:1x UTC): 0.3.21 ships object byte 5 (sub-version 1); sub-version 3 is 0.3.22's and starts with this fix. Sub-version 3, first commit: `run_unit` executes the shadow block after instruction 63 of every iteration, `reps` times with the iteration's sel, as the hash does; the test `acceptance_executes_the_shadow_block_as_the_verifier_does` pins the acceptance's execution to `verify.rs` on the devnet epoch-0 program and the six test eras (the output bit counts over the 64 units equal, and different with the shadow stripped), so the two paths cannot diverge silently again; `PROGRAM_SUBVERSION_V4` = 3 (a new acceptance verdict is a new stream); the devnet epoch-0 seed still accepts at attempt 1, so its program and fingerprint are sub-version 2's (e370fb2080b7dbb1) under the new id a785001687d8688a (the must-differ set: c120d7963abdcd96, 1a4230699a6b9c60, a788661687db4bb3); the packs zip sha256 4f2445c50c58d76a5544023492d8b858d0b07c5e372d31f9c90c4ce51f829154. The class behind p23, localised from its program and reproduced in the acceptance's own execution: site 7 (instruction 38) reads r6 after 25 `mulhi r6`, 31 `or r6 |= r4`, 35 `xor r6 ^= r4`, which is `r6 & ~r4`, an AND mask the lineage rule counted as fresh because the xor's operand is the or's; over 2^20 evaluations on the closed-form words site 7 reads 874,953 distinct word indices against about 1,046,500 for every other site (0.84 of uniform; 2.2 s on one core), over 2^24 8,979,203 against about 16,260,000 (0.55; 35 s). The second sub-version 3 commit (held, prepared in the worktree) is a per-site distinct-index ratio against the uniform expectation of the site's window, its threshold set from the clean seeds' spread and its sample size from the cost line above; the dynamic bounds as first specified (a most-repeated-value bound at 16,384 and a distinct floor at 2^19.5 over 2^20) do not reach p23 and are not committed. The crate suite at ddacfbd3 on box 2 (route "box 2 for class suite, priority normal", rc 0, 41 s, 14:20Z): 63 lib + 7 derive + 4 mixer + 19 packs + 2 recheck + 7 scratch = 102 passed, 0 failed, the agreement test included. F8's final 64-seed table on sub-version 2 (the attack-pass lane, 14:2x UTC): 9 over 1.2x (p23 4.82x, p19 3.32x, p15 2.57x, p18 2.50x, p56 2.01x, p10 1.50x, p8 1.38x, p34 1.25x, p4 1.22x), the 55 clean seeds at 0.9915x to 1.144x; the 256-item bucket entropy over the window separates the strong four only (0.637 to 0.974 against a clean minimum of 0.9865 over 848 site rows), so the threshold of the second commit's distinct-index ratio comes from a run of that ratio on the 55 clean seeds at 2^20. The static census at ddacfbd3 (box 2, 14:22Z, 4,096 chain-shaped seeds plus F8's p1 to p3): 4,099 programs, 0 lossy-sourced load sites of 65,584, 0 exhaustions, the accepted attempt geometric as before (1,328 at attempt 0, 917, 622, 409, 284, ... one each at 16 and 17; mean 1.998, max 17), so the shadow-executed verdicts move a handful of seeds' attempts and nothing else; the devnet epoch-0 seed at attempt 1, id a785001687d8688a.
Sub-version 3, second commit (7 October 2026, 14:44 UTC, the hash lane; the sub-version number stays 3 and the stream is unchanged: re-export diff 0 on the eight packs, the id a785001687d8688a, the seven fingerprints and the zip sha256 stand), two rules. The shared-operand rule, in the draw's source rule and in the acceptance's (a') pass: or-then-xor or or-then-sub on one operand is `d & ~s`, xor-then-or is `d | s`, so the second write leaves the register lossy although either op alone injects; any write to either register clears the relation. On p23 the chain's attempt 1 (id d65122675f16a1c7) now draws site 7 (instruction 38) from r5 and passes; the devnet epoch-0 seed still draws attempt 1, the same program. Rule (c''), the distinct-index ratio: over 4,096 units (2^20 evaluations per site, the closed-form words, the shadow executed) every load site's count of distinct word indices against the uniform expectation on its window (N - N^2 / 2W, the window 2^28 >> min(win, 2)) must reach 0.98 (`MIN_DISTINCT_RATIO_V4`), the last test of the chosen candidate; a candidate under it is rejected and the next attempt drawn under the 256 cap and the last resort. The floor from the 64-seed run at 2^20 (box 2): the 55 clean seeds' minimum over their site rows 0.9960 (p1 0.9990, median 1.0000); the strong five p23 0.8361, p18 0.9274, p19 0.9335, p15 0.9432, p56 0.9654; 0.98 sits 0.015 from each side. The chain's own candidates it refuses (the test `class_v4_distinct_ratio_rejects_the_low_entropy_band`, box 2, 2.1 to 2.2 s each): p15 attempt 3 id 52638ea2e8b0fd68 site 2 at 0.943, p18 attempt 2 id 9a37e9489d8ba698 site 6 at 0.927, p19 attempt 0 id 79d7441de0689223 site 15 at 0.933, p56 attempt 2 id 486a8ad2701ec3b5 site 2 at 0.965; p23's attempt 1 with site 7 put back to r6 is refused by (a') (`UnfreshLoadSource`) and, run anyway, by the ratio at 0.836 (874,928 distinct of 1,048,576). Main's rule for a staged 2^24 pass (taken only if 2^24 separates the weak four from the clean seeds by at least the 2^20 gap) was decided by the 2^24 lines (box 2, 35 s per seed on one core): the weak four p34 0.9181, p4 0.9614, p8 0.9630, p10 0.9612; the clean seeds p44 0.9612, p52 0.9613, p3 0.9971, p2 and p5 1.0004; p23's attempt 1 1.0004. Two clean seeds sit on the weak four's value, so a 2^24 floor that reaches the weak four rejects clean seeds, and the 0.961 that recurs on both sides is a band the ratio reads at 2^24 that F8's hot-set gate did not flag on p44 or p52. Committed: the ratio at 0.98 over 2^20 alone (`ACCEPT_UNITS_DISTINCT_V4` = 4096), no 2^24 stage. Open tail: p4, p8, p10 and p34 (1.22x to 1.50x on F8's gate) read 0.9927 to 0.9963 at 2^20, inside the clean spread; unattributed and chased. The (B) most-repeated-value bound stays in the file unwired (`MAX_SOURCE_REPEAT_V4`, `most_repeated`). Cost: the chosen candidate's acceptance gains one 2^20 pass, 2.1 to 2.2 s on one box-2 core, once per epoch draw per node.
The second sub-version 3 commit is 017e70376489251e18564c0abce7e466e606c8b3 (pushed 15:10 UTC's preceding hour, pre-push gate GREEN, CI run 37639406567 success). The crate suite at 017e7037 on box 2 (rc 0, 184 s): 64 lib + 7 derive + 4 mixer + 19 packs + 2 recheck + 7 scratch = 103 passed, 0 failed, 3 diagnostics ignored; the lib tests took 140.8 s against ddacfbd3's 41 s for the whole suite, because every class v4 draw in the tests now pays the 2^20 pass on its chosen candidate (2.2 s each); that is CI time, not node time. The static census at 017e7037 (box 2, 15:1x UTC, 4,096 chain-shaped seeds plus F8's p1 to p3, the draws in parallel over the box's cores since the ratio pass makes the serial run a four-hour job): 4,099 programs, 0 lossy-sourced load sites of 65,584 (57,322 inject, 8,262 bijective), 0 exhaustions; the accepted attempt 1,297 at attempt 0, 899, 609, 416, 298, 197, 125, 92, 54, 46, 21, 14, 13, 9, 6, 0, 2, 1 (mean 2.086, max 17) against ddacfbd3's 1,328, 917, 622, 409, 284, ... (mean 1.998, max 17): the ratio refuses about 4 percent of the candidates that pass every other test, one more attempt on about one seed in twelve; the devnet epoch-0 seed at attempt 1, id a785001687d8688a; F8's p2 at attempt 5, p3 at attempt 1. The attack-pass lane's class check of ddacfbd3's shadow-executed verdicts against 8bdcbdd8 (598,678 chain-shaped seeds): 11,990 (2.0 percent) accept at a different attempt, 0 exhausted, max attempt 32; on 017e7037 the pairing holds (the devnet epoch-0 program draws as a785001687d8688a) and its 64-seed gate at 2^24 and 10^6 exhaustion count were running at 15:1x UTC (finish about 16:05 UTC).
F8's 64-seed gate on 017e7037 (the attack-pass lane, box 2, last seed 16:00:20 UTC): 60 of 64 under 1.2x; the four over are the named tail and nothing else (p10 1.5036x, p8 1.3776x, p34 1.2505x, p4 1.2167x; hottest items 355 to 541 reads of 2^31, unattributed, inside the ratio's clean spread); p23, p19, p15, p18 and p56 under the line; the clean spread 0.9915x to 1.144x. Exhaustion on 017e7037: 0 in the attack-pass lane's 20,532 chain-shaped seeds (max attempt 29) plus this lane's 4,099 (max 17); the 10^6 count continues as a strengthening line. Cost line for the node: the chain's epoch draw now pays the 2^20 ratio pass on every candidate that reaches it (the accepted one, and the about 4 percent that fail there), 2.2 s per pass on one box-2 core, so about 2.3 s per epoch draw on that core and, by the attack-pass lane's reading, about 4 to 5 s per epoch per node on slower cores; the earlier rejections cost milliseconds. From the attack-pass lane sub-version 3 at 017e7037 reads green on both gates with the named tail; its close line went to the plan, main and the Counter ASIC lane. This row stays open on the tail (p4, p8, p10, p34) until it is attributed or ruled accepted.
Owed (recorded, not run, by the project lead's word): G2 (the CPU verifier on 1,024 hashes per card) on the amended stream; G3 (the Metal fuzz, edge, stats and determinism runs) on the amended stream; the hash-rate ladder re-measure on the M5 Max and the RTX 5090 (the amendment changes the base program's source draws, not the op mix or the load count, so the latency-bound rows of `docs/analysis/latency-shadow-2026-10-06.md` are expected to hold within their spread; unmeasured); AMD (the RX 9070 XT, PC 1); the 2019-class verifier core (O-1.14); F8's phase E (the 64-seed dynamic census) on the amended stream, which is the attack-pass lane's and the test of the per-op table. The row reads FIXED-AND-PASSED only after phase E passes against the amended class.
## Genesis forward-compatibility entries (7 October 2026, mission item 8, branch `genesis-forward`)
The three genesis fields of `docs/analysis/mission/mission.md` section 2.8, built on the node fork branch `genesis-forward` (from release-0.3.19-node dc141409) and the repo branch `genesis-forward`; the design and the gates in `docs/design/genesis-forward.md`. Every switch is never on the devnet (its digest c562d70e... does not move); the testnet genesis sets all three (the testnet lane re-pins and re-digests).
### GF1. A post-quantum signature scheme would need a hard fork, and every vote key is a public BLS12-381 point
"When a quantum computer comes, every BLS vote key is forged and the chain has no way to change the scheme without a fork of the kind you say you never need."
Status: Fixed (7 October 2026). `sig_scheme` byte in every vote item and key reveal (`finality::SIG_SCHEME_BLS12_381` = 0), `Params::sig_scheme` and `Params::sig_scheme_activation_daa` in the digest once set; any other byte refused by every node (`scheme_refusal`) until a program class the 95 percent signal moves to names it (`igneum::sig_scheme_of_class`, empty today). Gate: the digest test `override_params_carry_the_genesis_forward_fields_and_the_digest_moves_only_when_set` and `consensus_digest_covers_every_consensus_field_and_nothing_else` (30 edits); a scheme-1 vote refused over RPC, in a block, and as a successor's scheme (`a_vote_reveal_or_successor_of_another_signature_scheme_is_refused_until_a_class_names_it`); the fast-time probe on three nodes (`infra/fast-time/key-succession.mjs`, `probe-scheme`).
Answer: The byte costs nothing now and a fork later. The flip is a class change (spec 03 W1 as amended): the aggregated-vote format ships first (naive ML-DSA-44 votes at 8,192 voters cost 57 GB a day, `future.md` 7.3), the scheme second, both by the 95 percent signal with a floor height. Per tier at migration: a home miner on any card runs the updater and signs one succession item (GF2); nothing hardware-specific, the signature runs on the CPU.
### GF2. A vote key cannot move: a miner who changes keys re-earns 30 days of weight, and so does the post-quantum migration
"Your weight is bound to a key. Rotate it, lose a month. So nobody rotates, and the one day everyone must rotate, finality pauses for a month."
Status: Fixed (7 October 2026). W5 key succession implemented (spec 03 W5 as amended): a succession item signed by both keys, carried in blocks after the leaves, folds the old key's window blocks and forfeit term into the successor from the first checkpoint whose past holds the carrier (`successions_at`, `fold_successions`); once per key, a second succession refused, a successor that has itself handed over refused. Behind `finality_succession_activation_daa`. Gate: carried once and refused twice in the unit test on two nodes and in the fast-time harness.
Answer: The successor inherits the window, not a fresh one, so a key rotation costs no weight and the migration of GF1 is one item per key. The old key signs nothing after; a buyer of a key gets the clean transfer (spec 3.11.5) and the seller's copy is worthless.
### GF3. A 256 MB on-chip cache makes the lottery hash 2 to 3x cheaper for the card that has it, and the cache size is a constant
"The dataset is built from a 256 MiB cache. A datacentre part with a 256 MB last-level cache keeps the whole cache on die and skips the memory. Consumer cards cannot."
Status: Fixed as a genesis lever, measurement owed (7 October 2026). The latency ladder carries one cache rung beside N (`LatencyLadder::cache_rung`, 512 MiB, `LadderSignal::Cache` = both ladder bits, the same 90 percent in seven windows, one rung, never back, in the digest with the ladder); inadmissible at genesis until the verifier bound and the 8 GB tier are measured (design doc section 3); visible in `getBlockTemplate`'s `powEpoch` (`latencyLadderCacheStep`, `latencyLadderCacheMib`, `latencyLadderCacheBps`, `latencyLadderCacheAdmissible`) and in the daemon's ladder line.
Answer: Consumer LLC is 96 to 128 MB today and datacentre 256 MB (`chip-model-v3`, approximate), so the shortcut is a datacentre card's today and a consumer card's in a generation or two. The rung lets the miners double the cache by signal the day the shortcut shows on the hash-rate charts, without a fork; the measurement that admits it is the same verifier bound as every N rung. Per tier: a 512 MiB cache is held by every tier from 8 GB up; the day-cache build doubles (about 0.7 s on the reference core today, approximate); the verifier reads the cache, not the dataset, so the 10 ms bound decides.
### GF4. The class-group VDF falls to the same quantum computer
"Shor computes the class-group order; your epoch seed is then grindable."
Status: Conceded, flagged in spec 04 section 4.8 (7 October 2026); not sized. The fallback is a hash-chain delay behind the same version byte that moves the signature scheme; designed when the scheme flip is scheduled.
Answer: A grindable hourly seed is a liveness nuisance against the lottery, not a safety break: finality rests on the vote keys (GF1), the seed on the VDF. The two flip together by one class change.

View file

@ -357,3 +357,48 @@ service. The layer tracks the repo branch `master`; until this work merges, the
tree, so install writes a drop-in pinning `CAP_REPO_BRANCH=box-capacity` and removes it once `master` carries
`infra/build-server/capacity/run.sh` (self-healing after the merge). `--no-start` enables without starting;
`--smoke <job>` installs then runs one job's 10-minute smoke and prints the summary.
## 7. Spill-over between the boxes (7 October 2026, 15:0x UK)
the project lead's reading at 15:02 UK: build-1 at load 139 / 114 / 90 with both slots held and a queue (the horizon lane waited 1 h 40 min)
while build-2 read 4.5 / 27 / 46 with both slots free. The class router (section 6) pinned each class to its box with no
spill-over. Now (lib.sh `bs_route_spill`, master from this commit):
- The class is a PREFERENCE: a build, check or gate prefers box 1, a suite, bench or attack row box 2, a proving crate box 3.
- Before a run, the preferred box is read with one ssh (free slots of its slot count, 1-minute load). It takes the job when it
has a free slot and its load is at or under 64 (`BS_SPILL_LOAD`). Otherwise the other box (1 and 2 swap; 3 falls to 1) takes it
when THAT one qualifies; when neither does, the job queues on its own box. A box without a host file is never chosen; an
unreachable box reads as "down" and is skipped.
- The decision is the first route line of the run ("route: class suite prefers box 2; box 2 (free=0 slots=3 load1=70) is full or
over load 64: spilled to box 1 (free=1 slots=2 load1=20)"), the slot label carries "; spilled from box N", and the JSONL row
carries `"route": {"preferred", "box", "spilled", "reason"}` for the dashboard's job card.
- build-2 has THREE slots (its `slots` file reads 3 since 14:0x UK; provision.sh defaults a `-2` hostname to 3). Everything that
lands on box 2 runs at the bounded class, nice 10 on a 32-core band with -j 32, builds and gates included, so a suite beside
them keeps its number; since this commit a bounded run takes the band its slot owns (slot 0 the last 32 cores, slot 1 the 32
below, slot 2 the 32 below that), so three bounded runs never share a core. A gate still takes its slot ahead of queued suites.
- `--box N` still pins. Self-test: tools/ci/route-spill-check.sh (thirteen cases through `BS_ROUTE_STATE_<n>`, no ssh), in the gate.
## 8. The measure file is retired: per-core leases and the quiet class (7 October 2026, 15:07 UK)
Main's reading at 15:07 UK: on build-1 a sync-fuzz probe (the capacity lane's, SIGSTOPped since 09:45Z, no owner) held the global
measure flock for five and a half hours; beside it attack-f6's phase2b waited exclusive on the same file behind seven F2 solvers
holding it shared on cores 6-11,54-59, and every new shared taker (every build) queued behind the exclusive waiter: load 120, slots
free, nine waiting. On build-2 the era VDF bench held the file exclusive while pinned to one core and five jobs waited. One global
exclusive lock across unrelated measurements was the wrong design. Now:
- `infra/build-server/lease.sh`, installed on every box at `/srv/builds/_bin/lease` (provision.sh; by hand on build-1 and build-2
at 14:2x BST): `lease cores <set> --label "..." [--owner <agent>] -- <cmd>` takes a lease on THOSE CORES ONLY (one flock per
core, `_locks/core-<n>`, a `wait-<pid>` file with the label while it waits), runs the command under nice 10 and taskset, and
releases. Nothing else is excluded. `lease quiet --label "..." --owner <agent> -- <cmd>` is the whole-box class: refused (exit
73) while any slot or core lease is held, capped at 20 minutes, holder line with the owner in `_locks/quiet`. `lease status`,
`lease reap`.
- remote-run.sh: an unbounded run (nice 0, the full set) takes `quiet` shared and waits for it; a bounded run (suites, benches,
everything on box 2) never takes it. Every run keeps off leased cores (its set minus the `core-<n>` flocks, said once). A run's
keeper refreshes its holder file's mtime every 20 s and calls `lease reap`: a holder of a lease, the quiet file or a slot whose
process has been STOPPED for 5 minutes is killed and its file cleared, one line each in `_log/reaped.log`. The keeper closes the
lock descriptors it inherits (an orphaned `sleep 20` held a slot and a worktree lock 20 s past the release). BR_MEASURE=1 is the
quiet class with the same refusals; it needs a named owner (IGNEUM_AGENT).
- The lanes' own `flock -s /srv/builds/_locks/measure -c "nice -n 10 taskset -c <set> ..."` lines no longer hold anything a build
waits for; they become `/srv/builds/_bin/lease cores <set> --label "..." -- <cmd>`, and `flock -x .../measure` becomes
`lease quiet`. Self-tests: lease.sh --self-test and remote-run.sh --self-test-slots, run on build-1 by tools/ci/box-locks-check.sh
in the gate.

View file

@ -0,0 +1,86 @@
{
"gate": "P1 rehearsal: the class v4 activation on a fleet of real boxes (Counter ASIC 3.0)",
"verdict": "PASS",
"date": "2026-10-06",
"network": "igneum-devnet-400 (fleet-only, from the devnet genesis state)",
"object": {
"file": "docs/plans/counter-asic-3-gate/override-v4-rehearsal-1bps.json",
"fields": 17,
"pow_epoch_blocks": 600,
"pow_epoch_lead": 100,
"program_class_v4_signal_window_daa": 600,
"program_class_v4_activation_daa": 2400,
"threshold_bps": 9500,
"consensus_digest_on_devnet_400": "a15db4f0d6a5cc891582760ef5593e69014a5133261def84647cb7617528764f"
},
"binaries": {
"igneumd": "847ddfd1ba376009 (PC 2 job build-20261006-174823, the signalling fork 0562a7f2)",
"igneum-miner": "37178aeb09bc3a24",
"worker_first": "0.3.14 hive igneum-worker-cuda 1.0 (4 October): refused the generator-4 pack, the fleet stopped at the flip",
"worker_resume": "Linux generator-4 igneum-worker-cuda from the release tree 563485b, sha256 97e036e23f4ace66..., from 19:15:47Z"
},
"boxes": {
"mining": 15,
"seed": 1,
"stale_old_binary": 1,
"wave_pods_joined_as_v4_miners": 37,
"signalling_nodes_with_the_flip_line": 52
},
"timeline_utc": {
"first_block": "18:41:10",
"flip_daa_1202": "19:00:5x",
"stall_at_flip_old_worker_s": 90,
"fleet_stopped_old_worker": "19:04:14",
"resume_new_worker": "19:15:47",
"floor_daa_2400": "19:33:2x",
"sweep": "19:34:54"
},
"flip_line": "Program class v4 by miner signal: epoch 2 (share 10000 bps over 600 DAA ending at seed block f0606e203183798a728488ba8ce38ad5f552f53e3a7ac7b5e65d41c23fac5bf3, threshold 9500 bps, 599 of 599 blue blocks)",
"program_ids": {
"epoch_1_v3": "b237a661a3c7f7c1",
"epoch_2_v4": {
"pack": "24304f0788ea9408",
"cli_v4": "24304f0788ea9408",
"cli_v3": "d8927052c764f00b"
},
"epoch_3_v4": {
"pack": "cc266b4f5dbc3447",
"cli_v4": "cc266b4f5dbc3447",
"cli_v3": "de3a34f1f2130228"
},
"epoch_4_v4": {
"pack": "634018bab5e5f283",
"cli_v4": "634018bab5e5f283",
"cli_v3": "170e3aa4b2f69d60"
}
},
"checks": {
"flip_by_signal_identical_line_on_every_node": true,
"one_program_id_per_epoch_across_boxes_equal_cli_v4_differs_v3": true,
"pow_rejected_on_every_node": 0,
"miner_mismatched": 0,
"blocks_on_v4_on_every_box": true,
"stale_box_refused_by_digest": {
"refusing_peer_lines": 11,
"digest_mismatch_lines": 22,
"ever_a_peer": false,
"old_digest": "507f802b"
},
"stale_box_on_full_file": "refuses at parse (unknown field program_class_v4_activation_daa) and exits",
"floor_crossed_with_v4_in_force": true,
"sweep_19_34_54": {
"heights": "2502 to 2509",
"blue": "2432 to 2440",
"sinks_moving_with_height": 4,
"fork": false
},
"g2_serve_mode_epoch_2_pack": {
"found": 1024,
"distinct": 1024,
"digest": "7bf77506546730973a708ebfcf7d1f908f58ff9687b529e9ccca1d022f81e3e6",
"mac_recheck": "MATCH 1024 of 1024"
}
},
"cut_critical": "an old worker refuses a generator-4 pack by design, so publish 2 (the sixteen-field file) waits until every node AND every worker in the fleet is 0.3.15's; a 0.3.13 node given the file exits at parse; publish 1 carries no handshake split under the digest-compat rule",
"live_devnet_side_effect": "11 live miners at about half rate 18:42Z to 19:04Z (their GPUs shared with the rehearsal worker); no box left the live devnet"
}

View file

@ -0,0 +1,64 @@
{
"pass": true,
"checks": {
"new_and_old_on_13_fields_print_one_digest": true,
"the_16_field_digest_differs": true,
"n0_peers_with_n1_and_n2": true,
"old_node_peers_with_new": true,
"n3_has_no_peer": true,
"a_digest_refusal_line_exists": true
},
"rows": [
{
"node": "n0",
"binary": "new",
"fields": 13,
"digest": "a89be8a7e64b7d5a0d89d19c8837b3a8c2323fc277717e94c508eeb51a5e71e6",
"peers": 2,
"refusal": [
"Application directory: /tmp/igneum-digest-compat/n0",
"Data directory: /tmp/igneum-digest-compat/n0/igneum-devnet-995/datadir"
]
},
{
"node": "n1",
"binary": "new",
"fields": 13,
"digest": "a89be8a7e64b7d5a0d89d19c8837b3a8c2323fc277717e94c508eeb51a5e71e6",
"peers": 1,
"refusal": [
"Application directory: /tmp/igneum-digest-compat/n1",
"Data directory: /tmp/igneum-digest-compat/n1/igneum-devnet-995/datadir"
]
},
{
"node": "n2",
"binary": "old",
"fields": 13,
"digest": "a89be8a7e64b7d5a0d89d19c8837b3a8c2323fc277717e94c508eeb51a5e71e6",
"peers": 1,
"refusal": [
"Application directory: /tmp/igneum-digest-compat/n2",
"Data directory: /tmp/igneum-digest-compat/n2/igneum-devnet-995/datadir"
]
},
{
"node": "n3",
"binary": "new",
"fields": 16,
"digest": "db9a85f9ff08f72f11f7ba496b3060189c32bdf6cb53e94c810a4cc25585487a",
"peers": 0,
"refusal": [
"Application directory: /tmp/igneum-digest-compat/n3",
"Data directory: /tmp/igneum-digest-compat/n3/igneum-devnet-995/datadir"
]
}
],
"new_binary": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-0315fix/../igneum-node-ca3v4/target-ca3v4/release/igneumd",
"old_binary": "/Users/joshm/Projects/igneum/vendor/igneum-node/target-0314/release/igneumd",
"secs": 45,
"refusal_lines_rechecked": [
"Refusing peer 127.0.0.1:51123: consensus params digest mismatch, local a89be8a7e64b7d5a0d89d19c8837b3a8c2323fc277717e94c508eeb51a5e71e6 remote db9a85f9ff08f72f11f7ba496b3060189c32bdf6cb53e94c810a4cc25585487a (the peer's override file, environment or build differs)",
"Refusing peer 127.0.0.1:51393: consensus params digest mismatch, local a89be8a7e64b7d5a0d89d19c8837b3a8c2323fc277717e94c508eeb51a5e71e6 remote db9a85f9ff08f72f11f7ba496b3060189c32bdf6cb53e94c810a4cc25585487a (the peer's override file, environment or build differs)"
]
}

View file

@ -0,0 +1,94 @@
{
"pass": false,
"checks": {
"one_digest_on_old_and_new": true,
"new_miner_blocks_accepted": true,
"old_miner_blocks_accepted": true,
"zero_rejected_by_miners": true,
"no_wrong_version_or_reject_line": false,
"every_header_version_is_the_block_version": false,
"old_nodes_hold_the_new_blocks": false,
"counts_agree_at_the_end": false,
"clean_new_node_joined_and_synced": true,
"restarted_new_node_resynced": false
},
"new_binary": "/private/tmp/claude-501/-Users-joshm/cd75457f-4858-4f86-9634-7481ee056b7b/scratchpad/igneumd-713ef876",
"old_binary": "/Users/joshm/Projects/igneum/vendor/igneum-node/target-0314/release/igneumd",
"digests": [
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2"
],
"counts_after_mining": [
{
"count": 97,
"sink": "7c26e545e4982ac9",
"daa": 97
},
{
"count": 101,
"sink": "11439a8867718a2b",
"daa": 101
},
{
"count": 97,
"sink": "7c26e545e4982ac9",
"daa": 97
},
{
"count": 97,
"sink": "7c26e545e4982ac9",
"daa": 97
}
],
"final": [
{
"count": 138,
"sink": "360b8259384d8fc1",
"daa": 138
},
{
"count": 101,
"sink": "11439a8867718a2b",
"daa": 101
},
{
"count": 138,
"sink": "360b8259384d8fc1",
"daa": 138
},
{
"count": 138,
"sink": "360b8259384d8fc1",
"daa": 138
}
],
"accepted_old_new": [
138,
100
],
"rejected_by_miners_old_new": [
0,
0
],
"reject_lines": [
[
"HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting from peer 127.0.0.1:51290.",
"HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting from peer 127.0.0.1:51818.",
"HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting from peer 127.0.0.1:52104."
],
[
"P2P, got reject message: wrong block version: got 1026 but expected 2 from peer: 127.0.0.1:29831",
"P2P, got reject message: wrong block version: got 1026 but expected 2 from peer: 127.0.0.1:29831",
"P2P, got reject message: wrong block version: got 1026 but expected 2 from peer: 127.0.0.1:29831"
],
[],
[]
],
"header_versions": {
"0": 1,
"2": 138
},
"secs": 120
}

View file

@ -0,0 +1,102 @@
{
"pass": true,
"checks": {
"one_digest_on_old_and_new": true,
"new_miner_blocks_accepted": true,
"old_miner_blocks_accepted": true,
"zero_rejected_by_miners": true,
"no_wrong_version_or_reject_line": true,
"every_header_version_is_the_block_version": true,
"old_nodes_hold_the_new_blocks": true,
"counts_agree_at_the_end": true,
"clean_new_node_joined_and_synced": true,
"restarted_new_node_resynced": true,
"new_node_served_a_genesis_sync_and_survived": true,
"no_panic_in_any_node_log": true
},
"new_binary": "vendor/igneum-node-ca3v4/target-ca3v4/release/igneumd",
"old_binary": "/Users/joshm/Projects/igneum/vendor/igneum-node/target-0314/release/igneumd",
"digests": [
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2"
],
"counts_after_mining": [
{
"count": 165,
"sink": "44a4876cfc2ba782",
"daa": 165
},
{
"count": 165,
"sink": "44a4876cfc2ba782",
"daa": 165
},
{
"count": 165,
"sink": "44a4876cfc2ba782",
"daa": 165
},
{
"count": 165,
"sink": "44a4876cfc2ba782",
"daa": 165
},
{
"count": 165,
"sink": "44a4876cfc2ba782",
"daa": 165
}
],
"final": [
{
"count": 195,
"sink": "0be1fa5e67bedeb1",
"daa": 195
},
{
"count": 195,
"sink": "0be1fa5e67bedeb1",
"daa": 195
},
{
"count": 195,
"sink": "0be1fa5e67bedeb1",
"daa": 195
},
{
"count": 195,
"sink": "0be1fa5e67bedeb1",
"daa": 195
},
{
"count": 195,
"sink": "0be1fa5e67bedeb1",
"daa": 195
}
],
"served_join_count": 165,
"serving_node_alive": true,
"accepted_old_new": [
120,
75
],
"rejected_by_miners_old_new": [
0,
0
],
"reject_lines": [
[],
[],
[],
[],
[]
],
"header_versions": {
"0": 1,
"2": 195
},
"secs": 160
}

View file

@ -0,0 +1,112 @@
{
"pass": true,
"checks": {
"one_digest_on_old_and_new": true,
"new_miner_blocks_accepted": true,
"old_miner_blocks_accepted": true,
"zero_rejected_by_miners": true,
"no_wrong_version_or_reject_line": true,
"every_header_version_is_the_block_version": true,
"old_nodes_hold_the_new_blocks": true,
"counts_agree_at_the_end": true,
"clean_new_node_joined_and_synced": true,
"restarted_new_node_resynced": true,
"new_node_served_a_genesis_sync_and_survived": true,
"no_panic_in_any_node_log": true,
"new_node_refused_the_poisoned_blocks": true,
"poisoned_node_mined_something_to_refuse": true,
"old_hub_never_saw_a_1026_block": true
},
"new_binary": "vendor/igneum-node-ca3v4/target-ca3v4/release/igneumd",
"old_binary": "/Users/joshm/Projects/igneum/vendor/igneum-node/target-0314/release/igneumd",
"poisoned_binary": "/private/tmp/claude-501/-Users-joshm/cd75457f-4858-4f86-9634-7481ee056b7b/scratchpad/igneumd-713ef876",
"new_node_rejects_of_1026": 12,
"poisoned_blocks_accepted_by_its_own_node": 179,
"digests": [
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2",
"b0afb2ee93d0d6274cbf63d40ac7bed487202c5af84e035c8a634c521bffe6a2"
],
"counts_after_mining": [
{
"count": 147,
"sink": "9d12ad182ab77726",
"daa": 147
},
{
"count": 147,
"sink": "9d12ad182ab77726",
"daa": 147
},
{
"count": 147,
"sink": "9d12ad182ab77726",
"daa": 147
},
{
"count": 147,
"sink": "9d12ad182ab77726",
"daa": 147
},
{
"count": 147,
"sink": "9d12ad182ab77726",
"daa": 147
}
],
"final": [
{
"count": 180,
"sink": "040d5ae85d45bc24",
"daa": 180
},
{
"count": 180,
"sink": "040d5ae85d45bc24",
"daa": 180
},
{
"count": 180,
"sink": "040d5ae85d45bc24",
"daa": 180
},
{
"count": 180,
"sink": "040d5ae85d45bc24",
"daa": 180
},
{
"count": 180,
"sink": "040d5ae85d45bc24",
"daa": 180
}
],
"served_join_count": 147,
"serving_node_alive": true,
"accepted_old_new": [
108,
72
],
"rejected_by_miners_old_new": [
0,
0
],
"reject_lines": [
[],
[
"HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting from peer 127.0.0.1:56593.",
"HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting from peer 127.0.0.1:56829.",
"HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting from peer 127.0.0.1:57104."
],
[],
[],
[]
],
"header_versions": {
"0": 1,
"2": 180
},
"secs": 160
}

View file

@ -13,8 +13,14 @@
| # | Gate | Evidence required | State |
|---|---|---|---|
| P1 | the rehearsal plan and the v4 override object for the rented fleet | `docs/plans/counter-asic-3-rehearsal.md`; the objects with their digests | WRITTEN (not run: the fleet agent runs it): the publish object (the live 13 fields plus the floor `N6` and the window 86,400; digest `ac8e60ce205852bdda6b554f8cbfbd9dbb040187f487cbd8affe8103633dfd56` at the worked example N6 = 219,600) and the rehearsal object (every earlier switch at 0, window 3,600, floor 14,400; digest `bc2142b178ff367ae84ff0699ff523760d8878f883375da21864ed8d3ad39237`), both read from the signalling node's start lines on throwaway private networks; `override-v4-publish-example.json` and `override-v4-rehearsal.json` in this directory |
| P1 | the rehearsal plan and the v4 override object for the rented fleet | `docs/plans/counter-asic-3-rehearsal.md`; the objects with their digests | WRITTEN (not run: the fleet agent runs it): the publish object (the live 13 fields plus the floor `N6` and the window 86,400; digest on the devnet network id `65a42ab2e63d93ac02acf761b411b31acac6c9413bbd0c6a2f4cced10509efdf` at the worked example N6 = 219,600, fork 0562a7f2), the rehearsal object (every earlier switch at 0, window 3,600, floor 14,400; digest on suffix 400 `aef46983dfd121d5ecbefd056ff02d001b89315bd0547b4e1007b0b0ee7eee2c`) and the re-cut for the hour (600-DAA epochs, window 600, floor 2,400; digest on suffix 400 `a15db4f0d6a5cc891582760ef5593e69014a5133261def84647cb7617528764f`, the value the 16 fleet boxes printed); CORRECTED 18:4xZ: the digest folds the network id in, and the first values (`ac8e60ce`, `bc2142b1`, `d23394e7`) were read on throwaway suffixes; CORRECTED 18:5xZ: the rehearsal chain inherits the devnet's genesis bits (the object sets none; the fast-time harness lowers them), so its miners are the GPU workers, not 1-thread CPU miners (the fleet's 16 CPU miners at about 80 kH/s against about 2^27 hashes a block held the height at 0 for 20 minutes); the id assertion on a worker box reads the id from the exported pack's program.h (the `program pack checked ... <dir>` line names it), the Mac side unchanged; `override-v4-publish-example.json` and `override-v4-rehearsal.json` in this directory |
| P2 | miner-signalled class activation, implemented behind the override, the fast-time gate with three cases and the failed case | `docs/plans/counter-asic-3-node.md` section 6; `infra/fast-time/class-v4-signal.mjs` | GREEN on the Mac: two of three signalling never flips (7 epochs, 6,166 to 7,583 bps), all three flips at epoch 3 (the first full window, 10,000 bps, the same line on 3 of 3 nodes, the id assertion), nobody signalling flips at the floor epoch 5 and not before; the known-failed case fails on eight checks. Rows and summary files: node doc section 6.4; `class-v4-signal-{no-flip,flip,floor,failed-case}.json` |
| G6 (signalling) | the fork change on PC 2 with the igneum-pow feature | the job ids and their lines | GREEN on fork 0562a7f2 (main f39a8eb), three PC 2 jobs under one clearance ("PC 2 open for G6", 17:27Z), the mkdir lock taken 17:28:15Z and released 17:38:58Z, then 17:39:41Z to 17:55:31Z; prover on, app untouched. Job 1 `build-20261006-173017` (the six crates and the app): every build stage ok, the app tests 112 + 26 + 8, but kaspa-consensus 97 passed and 1 FAILED on 2.0's known flake `ban_is_decided_by_the_carrying_block_so_nodes_agree_on_every_voter_list` (`UnexpectedDifficulty(.., 487111630, 487128802)` in `mine_on_all`, the 0.3.10 cut's section 11 case, which also passed on the Mac and in this morning's job 155958), and cargo stopped the unit there. Treated as 2.0 did: job 2 `build-20261006-174027` (kaspa-consensus alone, 17:40 to 17:46Z): `RESULT test node [kaspa-consensus] exit 0 19 s`, `done exit 0 after 345 s ... every stage ok`. Job 3 `build-20261006-174823` (the five crates and the app, 17:48 to 17:55Z): `RESULT test node [kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows] exit 0 35 s` and `RESULT test app/igneum-app [igneum-app] exit 0 5 s`: kaspa-consensus-core 110 + 7 (`override_params_carry_the_program_class_v4_activation ... ok`, the signal window test inside it), igneum-exec 18, kaspa-pow 15 (`program_class_signal_rule ... ok` is consensus-core's; kaspa-pow's `program_class_v4_seeds_hash_the_shadow_program_over_the_v3_day_cache ... ok`, the feature on), igneum-miner 18, kaspa-p2p-flows 33, igneum-app 112 + 26 + 8; the job's own closing line reads `failed ... upload incomplete` because the relay's blob service refused one of the nine uploads (`igneum-app.exe.zst FAILED: blob PUT: no url in the reply: service_unavailable`, the shipper's 17:25Z fault) AFTER both test stages had closed with exit 0, so the STAGE and RESULT lines are the evidence, as the shipper's warning said. Where the runs went: these three on PC 2 under the clearance given before the build-server rule arrived (18:0xZ); no further Linux build or suite is needed by this brief; the next one from this lane goes to igneum-build-1 through tools/build-remote.sh |
| P3 | the 0.3.15 digest rule (main's ruling, 20:1x UK): an absent class v4 field contributes nothing to the digest, so publish 1 (the binary) splits no handshake and publish 2 (the sixteen-field file) is the one sweep | the compat case and the refusal case on real nodes; the pinned fixtures | GREEN on fork ca3-v4-order-fix 713ef876 (with the order-test race fix 791ff22c; both handed to the shipper for release-0.3.15-node): `infra/fast-time/digest-compat.mjs` (18:58Z, suffix 995, the live thirteen-field file copied, never written): the fixed node and a 0.3.14 node (`target-0314/release/igneumd`) on the thirteen-field file print ONE digest `a89be8a7e64b7d5a...` and peer (n0 has 2 peers, the old node 1: the compat case); the fixed node on the sixteen-field file (the exec pin, the floor 219,600, the window 86,400) prints `db9a85f9ff08f72f...` and has no peer, the handshake's `Refusing peer ...: consensus params digest mismatch` line in the log (the refusal case, the rule's known-failed case: present fields move the digest). On the devnet network id the sixteen-field object digests to `1dddfa5574d5536109a5ae68dc415b55e954bd11208608440845e19670505871` (the fixed Mac binary's start line = the pinned test value), the thirteen-field live file to `b18ed271f75dd464...` (0.3.14's value), fourteen fields `23e76936...`; no file `c562d70e...` (0.3.11 to 0.3.15 unchanged). Box line on 713ef876: kaspa-consensus 98, consensus-core 111 + 7, rc 0. The rehearsal object sets both fields, so its `a15db4f0` on devnet-400 stands. The shipper's independent readings on the 0.3.15 Mac binary (release-0.3.15-node = 4c6b129d + 0562a7f2 + 2e5d3d30 + 791ff22c + 713ef876, 19:0xZ): the same three values, and at tonight's live floor 226,800 the sixteen-field object reads `2c1162e2...`, the publish-2 digest if the floor stays there (recomputed at the publish); the 0.3.14 binary exits at parse on the sixteen-field file, which is why the file follows the binary; its suite on igneum-build-1 at 713ef876: kaspa-consensus 97 + the recorded flake alone 1 of 1, consensus-core 111 + 7, igneum-exec 20, kaspa-pow 15 with both engine tests, igneum-miner 18, p2p-flows 33. Summary `digest-compat-20261006.json` |
| P4 | the two 0.3.15 node faults found on the live devnet (the canary's version 1026, the pruned hub's sync panic), fixed in the same fork tree | the unit tests; the node-cut compat gate on real nodes (a MINING new node beside a 0.3.14 node on the live object, the old node accepting the new node's blocks; a clean new node joining through the old hub; a clean OLD node joining through the NEW node, which must survive serving a sync from the genesis; the new node restarted and re-synced; every header version the block version; no panic); the ledger rows | GREEN on fork ca3-v4-order-fix 7961c5f1 (= 791ff22c + 713ef876 + 17c60367 the signal gate + 7961c5f1 the retention error; the shipper's release-0.3.15-node). The gate `infra/fast-time/node-compat.mjs` (20:15:5x to 20:19:04Z, suffix 994, the live object plus CPU genesis bits, the 0.3.14 binary `target-0314/release/igneumd` as the old side): SUMMARY PASS: one digest `b0afb2ee93d0d627` on all five nodes; accepted old/new 120/75, rejected 0/0; header versions {0: the genesis, 2: 195}; counts 195/195/195/195/195 at the end (the clean new node through the old hub, the clean old node served from the genesis by the new node, which stayed alive, and the restarted new node all at 195); no `wrong block version`, reject or panic line in any log. The known-failed case on the canary binary 713ef876 beside 0.3.14 (20:00 to 20:02Z): SUMMARY FAIL, `HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting` on the old nodes, `P2P, got reject message: wrong block version` on the new, counts 138 against the new node's 101, the restarted new node never re-synced (the fleet's p2-3090-1). Unit tests: `block_template_uses_current_block_version` (2 on the thirteen-field state, 1026 with both v4 fields), `program_class_signal_rule`, `a_sync_request_below_retention_is_an_error_not_a_panic`; the box on 7961c5f1: kaspa-consensus 99, consensus-core 111 + 7, rc 0 (the shipper's six-crate line: exec 20, miner 18, pow 15, p2p-flows 33). Ledger rows N1 and N2 in `docs/fud-ledger.md`. RECEIVE SIDE (the clean canary, 21:3x UK: a 7961c5f1 node accepted and relayed a version-1026 block off a poisoned peer and was disconnected by every 0.3.14 peer): fork f1ea7a38 gates the header version RULE like the stamp (signalling inactive: the version must be exactly 2, 0.3.14's rule; active: the low byte); test inside `cheap_checks_run_before_the_pow_engine` (a 1026 header refused with `WrongBlockVersion(1026, 2)` before the engine on the old-format network, accepted with both fields installed); box on f1ea7a38: kaspa-consensus 99, consensus-core 111 + 7, rc 0. The gate with a POISONED peer (`--poisoned <713ef876 igneumd>` mining into the new node, 20:42:5x to 20:45:42Z): SUMMARY PASS: the new node refused the poisoned blocks 12 times (`HandleRelayInvsFlow flow error: wrong block version: got 1026 but expected 2, disconnecting`), the old hub's log carries no reject and no 1026 block (header versions {0: the genesis, 2: 180}), the new node stayed the hub's peer and mined 72 accepted blocks, counts 180 on every clean node including the clean joins and the restart. The stale blocks on the live devnet: a 0.3.14 node holding 1026 blocks is disconnected by its 0.3.14 peers by THEIR rule when it relays them, which no new-node change alters; new nodes are immune from f1ea7a38; the fleet wipes the poisoned datadirs. Owed: a truly pruned serving node in a harness (hours of fast-time chain at pruning depth 13,838). Summaries `node-compat-20261006-fixed.json`, `node-compat-20261006-canary-failed-case.json`, `node-compat-20261006-poisoned-peer.json` |
| P5 | the dependency pass for 0.3.15.1 (the night battery's audit, mirror branch fbb72e5: the fork's lock file) | `cargo audit` before and after on the box; the six-crate suite | DONE on fork `ca3-v4-deps` f8f0f1df (from 7961c5f1): `cargo audit` on igneum-build-1 before: 6 vulnerabilities (crossbeam-epoch RUSTSEC-2026-0204, h2 2026-0258, quinn-proto 2026-0185, ruint 2026-0220, rustls 2026-0285, tracing-subscriber 2025-0055; the battery's 22 counts advisory paths), 16 warnings (unmaintained, unsound, yanked); the three peer-reachable crates first (h2 0.4.6 to 0.4.20, quinn-proto 0.11.14 to 0.11.19, rustls 0.23.39 to 0.23.45 with rustls-webpki 0.103.15), then crossbeam-epoch 0.9.21 and ruint 1.20.1 (rand_pcg added), every one a patch or minor bump by the audit's own solution line; after: 1 vulnerability, 16 warnings; the one left is tracing-subscriber 0.2.25, pinned by ark-relations 0.5.1 through ark-groth16 and ark-snark (a 0.2 to 0.3 major bump outside the fork's pins, which name 0.3 in bridge/Cargo.toml), named here, not taken. The six-crate suite on the box on the new lock: kaspa-consensus 99, consensus-core 111 + 7, igneum-exec 20, kaspa-pow 15, igneum-miner 18, kaspa-p2p-flows 33, rc 0 (80 s). The 16 warnings are unmaintained or unsound transitive crates (async-std, atty, bincode 1, derivative, instant, mach, paste, proc-macro-error, rustls-pemfile, anyhow, event-listener, faster-hex, lru, chacha20, spin), none with a bump to take, each a dependency choice for 0.3.16 |
Summary files in this directory: `class-v4-20261006-1553Z-cpu.json` (G4 run 1), `class-v4-20261006-never-failed-case.json` (G4 run 2, the failed case), `class-v4-20261006-metal.json` (G4b), `class-v4-20261006-id-rerun.json` (G4 on the program-id fix, with the id assertion), `class-v4-20261006-id-failed-case.json` (the id assertion's failed case).

View file

@ -0,0 +1,33 @@
# The chip claim, public text (7 October 2026; REWRITTEN LAUNCH-FIRST 18:3x UK on the project lead's "I thought we were making it 2.1 from launch?": the testnet and mainnet objects set program_class_v4_activation_daa to 0, so class v4 is live from genesis and the launch number is 2.1x to 3.9x on day one; the 5x to 9x is the class v3 baseline the work started from, stated only as that; the devnet's own activation height is a devnet fact only. Served since 11:03 UK on master 9162c847 with main's two cuts: no mention of the disclosure prize until the publish word, and row 17 in evidence.md's eight-column shape)
Three texts and one ledger row, written by the Counter ASIC lane, which owns the chip model. Every number carries its label: measured (a card or a chain we ran, with the date), modelled (arithmetic on cited parts), claimed (a vendor's figure, never measured by us), designed (a rule in a class, not yet measured). Sources: `docs/analysis/chip-model-v3.md` sections 5 and 6, `docs/analysis/latency-shadow-2026-10-06.md`, `docs/plans/counter-asic-3-status.md`, `docs/analysis/attack-pass/f8-uniform.md` and `f4-weakday.md` (branch attack-pass), `docs/design/class-v5-stored-state.md`, the datacentre and market-cap rows of 7 October (lanes 3 and the fleet), the cryptanalysis plan in `docs/plans/funding.md`.
## 1. The home page's chip line (replaces the hero sentence served since 6 October 16:21Z)
Built for graphics cards. At launch the strongest chip in our public model reaches 2.1x to 3.9x per joule against an RTX 5090, under class v4 from the first block. Class v5 then makes the dataset the chain's own state, so a chip that stores it or recomputes it is wrong on every item. Without class v4 the same chip would reach 5x to 9x. The model and every measurement are public.
## 2. The litepaper's chip section (replaces the paragraph that begins "The chip model: 5x to 9x per joule")
The chip model. We price the strongest chip we can design against an RTX 5090 and publish the arithmetic. Class v4 is live from the first block on the testnet and the mainnet (the ladder's rung 0 at genesis), so the launch number is the class v4 row. The honest card: an RTX 5090 mines class v3 at 136 MH/s on 350 W in the bench and 290 W in the app (measured, 6 October 2026); an Apple M5 Max at 27 MH/s on 21 W (measured, 6 October 2026); an H100 SXM at 249 MH/s, 98 percent of its random-read ceiling like the 5090, 1.78x the 5090's hash at 1.15x the tuned 5090's hash per watt and a third of the hash per rented dollar (measured, 7 October 2026), so datacentre silicon does not change the chip question. The CPU verifier takes 2.33 ms per warp of 32 hashes on one M5 Max core under class v4 (measured, 6 October 2026), against a gate of 10 ms.
| The chip and the class | Edge over an RTX 5090 per joule | Label and date |
|---|---|---|
| At launch: a memory-controller chip that stores the whole dataset, under class v4 (about 100,000 integer ops per hash in the latency shadow, so the chip carries a GPU-class datapath beside its memory) | 2.1x with a core as costly per op as the GPU's (k = 1); 3.9x with the core Bitmain claimed for its Antminer X9 (k about 0.33), a product withdrawn before any unit shipped | modelled on measured card watts, 6 October 2026; the X9 figure claimed, never measured |
| The same chip at the ladder's second rung (about 200,000 ops per hash), reached by miner signal | about 2.8x | modelled, 7 October 2026 |
| Any chip under class v5, where the dataset is the chain's own state | a stateless or stale chip is wrong on every item, so the stored-dataset chip and the recompute chip are removed as categories; the verifier pays 0.2 ms more per warp | designed, 7 October 2026 |
| A chip caching the hottest 0.1 percent of items (about 1 MB of SRAM) | bounded at 1.067x at the ceiling, 1.005x on about half the hours and 1.048x on 5 percent | measured census of 1,024 programs, 7 October 2026; the source rule in the next class |
| A per-day FPGA that recomputes the dataset with cheap multipliers on a weak day | at most 12 percent more hash rate on 12 days a century, nothing on the other days and nothing for any chip | measured census of 2^24 days, 7 October 2026; the rule in the next class |
| When a stored-dataset chip pays for itself | at about USD 100 M of market cap in the first two years, not before | modelled, 7 October 2026 |
| The baseline the work started from: the same chip under class v3, without the shadow (the Ethash class) | 5x to 9x (5.1x on GDDR7, 9.2x on eight HBM3 stacks; the Ethash chips of this class reached 2.1x to 4.8x) | modelled, 6 October 2026; the precedent measured by others, 2020 to 2022; never the launch state |
What a miner sees from this. Class v4 costs a 5090 about 80 W more for 0.2 percent of rate, an M5 Max 16 W more for 1.5 percent, an RX 9070 XT and an RTX 4070 nothing (all measured, 6 October 2026). The ladder that sets how much work rides in the shadow starts at rung 0 at genesis and climbs by miner signal; its third rung is inadmissible today because a server core verifies it in 10.85 ms, over the gate (measured, 7 October 2026). On the devnet, which started on class v3, class v4 arrives by miner signal at a published height (a devnet fact, not a launch one). The next test of the model is not ours: the cryptanalysis plan buys three external lots against the mixer, the chained cache and the acceptance rule.
## 3. The miner page's line
Your card against the strongest chip we can price: an RTX 5090 at 136 MH/s on 350 W (measured 6 October 2026); at launch the chip reaches 2.1x to 3.9x per joule under class v4 (modelled on measured watts), and under class v5 it is wrong on every item because the dataset is the chain's own state (designed). Without class v4 it would be 5x to 9x. The model and the measurements are public.
## 4. The ledger row (docs/evidence.md row 17, in the table's eight columns as served)
| # | Claim | Where it is made | Status | Version or commit | Reproducible test | Result, date, machine | Independent verification |
|---|---|---|---|---|---|---|---|
| 17 | The chip resistance claim: at launch the strongest chip in the public model reaches 2.1x (k = 1) to 3.9x (k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 12 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state) | the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line | tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 core claimed and never measured | `docs/analysis/chip-model-v3.md` 5 and 6; `docs/analysis/latency-shadow-2026-10-06.md`; `docs/plans/counter-asic-3-status.md`; `docs/analysis/attack-pass/f8-uniform.md`, `f4-weakday.md`, `docs/analysis/ca3-v4-uniform.md`; `docs/design/class-v5-stored-state.md`; the H100 and market-cap rows of 7 October; `docs/plans/funding.md` (the three lots) | the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses `tools/attack/f8-uniform` and the F4 census; the verifier by `igneum-pow bench` | 136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.9x, 2.8x at launch; 1.067x at the ceiling; 12 percent on 12 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, PC 2's RTX 5090, PC 1's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 | none yet; the three cryptanalysis lots are the next test |

View file

@ -16,7 +16,7 @@ The class v4 activation on a chain of real boxes, in the shape the live devnet w
## 2. The override objects
Both objects below are JSON text to be written verbatim; a `never` height is `18446744073709551615`, which no JSON tool that goes through a double may rewrite (the fast-time harness's rule). The digests are what a node of the signalling commit prints at start (`Consensus params digest`); the fleet agent compares every box's line against them and the stale box's against its own.
Both objects below are JSON text to be written verbatim; a `never` height is `18446744073709551615`, which no JSON tool that goes through a double may rewrite (the fast-time harness's rule). The digests are what a node of the signalling commit (fork 0562a7f2) prints at start (`Consensus params digest`); the fleet agent compares every box's line against them and the stale box's against its own. CORRECTION 18:4xZ (the rehearsal's first reading): the digest folds the NETWORK ID in, so one file prints another digest on another `--devnet-suffix` (the same binary and file: `a15db4f0...` at suffix 400, `d23394e7...` at 996, `45eeea99...` at 401); the first values written here were read on throwaway suffixes and were wrong for the fleet's suffix 400. Every digest below now names its network id; the one the live devnet prints is the one read with no suffix.
### 2a. The live devnet publish object (NOT published by this plan; the shape the cut will use)
@ -26,7 +26,7 @@ The live file today (`/tmp/igneum-devnet/override-v3.json`, 13 fields, read 16:5
{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200,"program_class_v3_activation_daa":154800,"proving_v1_activation_daa":154800,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":198000,"exec_restart_number":27276,"exec_restart_hash":"bb45cf0dd2d7cc97ebfa5a2701527c09a8ede5d32de74efead9caa293b15688a","exec_restart_trust_daa":200000,"program_class_v4_activation_daa":N6,"program_class_v4_signal_window_daa":86400}
```
What the two fields do on the live devnet: every node of the signalling binary stamps object byte 4 into its templates from its first block, so the signal share climbs as the fleet updates; the class flips at the first epoch boundary whose window (the 86,400 DAA, one day, below that epoch's seed block) has 95 percent of its blue blocks signalling, which is about a day after the LAST box of 95 percent of the hash rate has updated; the floor `N6` flips it regardless at the latest. Digest of this object: `ac8e60ce205852bdda6b554f8cbfbd9dbb040187f487cbd8affe8103633dfd56` (read from the signalling node's start line, 18:05Z; the node also prints `Program class v4 from the override file: active from epoch 61 (DAA score 219600 rounded up to the epoch boundary at 219600, epochs of 3600 DAA)` and the window line) (with `N6` = 219,600 as the worked example; any other `N6` moves it).
What the two fields do on the live devnet: every node of the signalling binary stamps object byte 4 into its templates from its first block, so the signal share climbs as the fleet updates; the class flips at the first epoch boundary whose window (the 86,400 DAA, one day, below that epoch's seed block) has 95 percent of its blue blocks signalling, which is about a day after the LAST box of 95 percent of the hash rate has updated; the floor `N6` flips it regardless at the latest. Digest of this object on the DEVNET network id (no suffix), the signalling fork 0562a7f2: `65a42ab2e63d93ac02acf761b411b31acac6c9413bbd0c6a2f4cced10509efdf` (18:4xZ; the earlier `ac8e60ce...` was read at suffix 998 and does not apply); the no-file devnet digest on the same binary is `7f2e49be...`, the fork test's pinned value. The 0.3.15 binary may move both if 0.3.14 added a digest field: the shipper reads the live value from the 0.3.15 node. The node also prints `Program class v4 from the override file: active from epoch 61 (DAA score 219600 rounded up to the epoch boundary at 219600, epochs of 3600 DAA)` and the window line. CUT ORDER (found by the shipper, 18:3xZ): a 0.3.13 node refuses the 15-field file at PARSE (`unknown field program_class_v4_activation_daa`) and exits before any handshake, so the file reaches a node only with or after its 0.3.15 binary (the app applies the manifest's override after the update; the hand nodes and the seeds get the file in the same step as the binary, never before) (with `N6` = 219,600 as the worked example; any other `N6` moves it).
### 2b. The rehearsal object (the fleet chain)
@ -36,11 +36,11 @@ A fresh chain, every earlier switch at 0 (the testnet's shape: the chain is born
{"difficulty_v2_activation_daa":0,"proving_v0_activation_daa":0,"fees_v1_activation_daa":0,"finality_v3_activation_daa":0,"program_class_v3_activation_daa":0,"proving_v1_activation_daa":0,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":0,"exec_restart_number":18446744073709551615,"exec_restart_hash":"","exec_restart_trust_daa":18446744073709551615,"program_class_v4_activation_daa":14400,"program_class_v4_signal_window_daa":3600}
```
The arithmetic: epochs of 3,600 DAA, lead 600. Epoch `e`'s seed block is the last chain block below `3600 e - 600`; its window is full when that block's DAA is at least 3,600: epoch 1's seed block sits at DAA 2,999 (not full), epoch 2's at 6,599 (full). With every mining box signalling 4, the tally at epoch 2's seed block is 100 percent of the blue blocks in DAA 2,999 to 6,599, so the class flips at epoch 2, DAA 7,200, about 2 hours after genesis; the floor (epoch 4, DAA 14,400) is 2 hours later and is not reached by the run. Digest: `bc2142b178ff367ae84ff0699ff523760d8878f883375da21864ed8d3ad39237` (the signalling node's start line on this object, 18:05Z, with `Program class v4 from the override file: active from epoch 4 (DAA score 14400 ...)` and `Program class v4 signal window from the override file: 3600 DAA ...`); the devnet digest with no file at all is `7f2e49beabc253f327c5ac6bb457a674ea7f527af2971c95d3bdf65ef8bcf977` on this binary (the fork's pinned test), `c562d70e...` on 0.3.11 to 0.3.13.
The arithmetic: epochs of 3,600 DAA, lead 600. Epoch `e`'s seed block is the last chain block below `3600 e - 600`; its window is full when that block's DAA is at least 3,600: epoch 1's seed block sits at DAA 2,999 (not full), epoch 2's at 6,599 (full). With every mining box signalling 4, the tally at epoch 2's seed block is 100 percent of the blue blocks in DAA 2,999 to 6,599, so the class flips at epoch 2, DAA 7,200, about 2 hours after genesis; the floor (epoch 4, DAA 14,400) is 2 hours later and is not reached by the run. Digest on `igneum-devnet-400` (suffix 400): `aef46983dfd121d5ecbefd056ff02d001b89315bd0547b4e1007b0b0ee7eee2c` (the signalling node's start line, 18:4xZ; the earlier `bc2142b1...` was read at suffix 997); the node also prints `Program class v4 from the override file: active from epoch 4 (DAA score 14400 ...)` and `Program class v4 signal window from the override file: 3600 DAA ...`.
### 2c. The re-cut for the hour (18:1x UTC, the coordinator's "run it now")
If the fleet chain runs the devnet's 1 block/s, object 2b flips at 2 hours; this object flips at 20 minutes: epochs of 600 DAA (lead 100), window 600, floor 2,400. Epoch `e`'s seed block sits at DAA `600 e - 100`; the window is full from epoch 2 (seed at 1,100), so the flip is at epoch 2, DAA 1,200, minute 20 from the chain's start; the floor is epoch 4, DAA 2,400, minute 40; the run ends at DAA 1,800 (minute 30, one epoch after the flip; the floor is not reached). If the fleet chain is made to run 10 blocks/s instead, object 2b itself gives 6 / 12 / 24 minutes (window / flip / floor) and this one 1 / 2 / 4 minutes, too fast for a 15-minute sample cadence: use 2b. File `docs/plans/counter-asic-3-gate/override-v4-rehearsal-1bps.json`, digest `d23394e796fb542274207f1d281bc40126040e04f86882faa0b49ea3c3f1f91b` (the signalling node's start line, with `PoW schedule from the override file: epoch 600 DAA, lead 100 DAA, day 86400000 ms`, `Program class v4 from the override file: active from epoch 4 (DAA score 2400 ...)`, `Program class v4 signal window from the override file: 600 DAA ...`).
If the fleet chain runs the devnet's 1 block/s, object 2b flips at 2 hours; this object flips at 20 minutes: epochs of 600 DAA (lead 100), window 600, floor 2,400. Epoch `e`'s seed block sits at DAA `600 e - 100`; the window is full from epoch 2 (seed at 1,100), so the flip is at epoch 2, DAA 1,200, minute 20 from the chain's start; the floor is epoch 4, DAA 2,400, minute 40; the run ends at DAA 1,800 (minute 30, one epoch after the flip; the floor is not reached). If the fleet chain is made to run 10 blocks/s instead, object 2b itself gives 6 / 12 / 24 minutes (window / flip / floor) and this one 1 / 2 / 4 minutes, too fast for a 15-minute sample cadence: use 2b. File `docs/plans/counter-asic-3-gate/override-v4-rehearsal-1bps.json`, digest on `igneum-devnet-400` (suffix 400): `a15db4f0d6a5cc891582760ef5593e69014a5133261def84647cb7617528764f`, the value all 16 fleet boxes printed on the Linux build of the same fork commit 0562a7f2 (PC 2 job build-20261006-174823; the earlier `d23394e7...` was this file at suffix 996), with `PoW schedule from the override file: epoch 600 DAA, lead 100 DAA, day 86400000 ms`, `Program class v4 from the override file: active from epoch 4 (DAA score 2400 ...)`, `Program class v4 signal window from the override file: 600 DAA ...`).
```
{"difficulty_v2_activation_daa":0,"proving_v0_activation_daa":0,"fees_v1_activation_daa":0,"finality_v3_activation_daa":0,"program_class_v3_activation_daa":0,"proving_v1_activation_daa":0,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":0,"exec_restart_number":18446744073709551615,"exec_restart_hash":"","exec_restart_trust_daa":18446744073709551615,"pow_epoch_blocks":600,"pow_epoch_lead":100,"program_class_v4_activation_daa":2400,"program_class_v4_signal_window_daa":600}
@ -55,12 +55,12 @@ With this object the step-7 line reads `epoch 2 (share 10000 bps over 600 DAA ..
| 1 | Fetch the signalling commit's Linux binaries from the G6 build job (the shas in node-gates.md), verify every sha256, place `igneumd` and `igneum-miner` on every box; the 0.3.13 Linux `igneumd` on the stale box | every sha matches |
| 2 | Write the rehearsal object (2b) as `override.json` on every box, byte for byte (sha256 the file on each box and compare) | one sha on every box |
| 3 | Start the seed box: `igneumd --devnet --devnet-suffix=400 --nodnsseed --disable-upnp --listen=0.0.0.0:16411 --rpclisten=127.0.0.1:16410 --rpclisten-json=127.0.0.1:16412 --override-params-file=override.json --utxoindex --enable-unsynced-mining --yes --appdir=<fresh dir>` (ports of the box's choosing, never the live devnet's 26610/26611); read its first lines: `Consensus params digest` equals 2b's, `Program class v4 from the override file: active from epoch 4 (DAA score 14400 ...)`, `Program class v4 signal window from the override file: 3600 DAA ...` | the three lines |
| 4 | Start every mining box the same way with `--connect=<seed>:16411`, then its miner: `igneum-miner mine grpc://127.0.0.1:16410 1 100000000 <label> --engine igneum-pow --no-vote --payout-label <label>` (a CPU miner; a GPU box uses `--worker <path> --prepare-packs packs/prepare --exit-on-seed-change`, the app's shape) | every box's node prints the same digest and the two switch lines; every miner prints `epoch seed ... class v3 program id ...` for epoch 0 |
| 4 | Start every mining box the same way with `--connect=<seed>:16411`, then its miner on the box's GPU worker, the app's shape: `igneum-miner mine grpc://127.0.0.1:16410 1 100000000 <label> --engine igneum-pow --worker <igneum-worker-cuda> --worker-args "--pack packs/devnet" --prepare-packs packs/prepare --exit-on-seed-change --no-vote --payout-label <label>`. CORRECTED 18:5xZ from the run: the rehearsal object inherits the devnet's genesis bits (the fast-time harness lowers `genesis_bits` to 0x1f010000, 2^16 hashes a block; the object does not), so a 1-thread CPU miner (about 5 kH/s a box, 80 kH/s for 16, the fleet agent's reading) against the genesis difficulty (about 2^27 hashes a block, the fleet agent's figure) finds a block every 28 minutes for the whole fleet and the height stayed 0 for 20 minutes; the GPU workers find blocks at the chain's 1 a second | every box's node prints the same digest and the two switch lines; every miner prints `program pack checked for epoch seed <E> day <D>: attempt <A>, <dir>` for epoch 0 and the worker's `ready ... self-test PASS` |
| 5 | Start the stale box last, the same command on the 0.3.13 node, `--connect=<seed>:16411` | its log shows the handshake refusal (a digest mismatch line or `0 peers` after 60 s with connection attempts in the log) and its chain stays at its own genesis (block count 1 or its own lonely blocks if it mines; it must NOT mine: no miner on it) |
| 6 | Every 15 minutes, on every mining box: `getBlockDagInfo` (block count, sink, virtual DAA) and one `getBlockTemplate` (`powEpoch.programClass`, `nextProgramClass`, `programClassV4SignalBps`, `programClassV4SignalEpoch`); keep the lines | the shares read 10,000 bps on every box from the first template; sinks agree across boxes at each sample |
| 7 | At DAA 7,200 (about 2 h): every box's node prints `Program class v4 by miner signal: epoch 2 (share 10000 bps over 3600 DAA ending at seed block <hash>, threshold 9500 bps, N of N blue blocks)` with the SAME seed block hash and the same N on every box; the templates read class 4 from epoch 2; every miner prints `epoch seed <S2> ... class v4 program id <id>` | the same `<S2>`, the same `<id>` on every box |
| 8 | Run to DAA 10,800 (epoch 3, one epoch after the flip; two epochs after is 14,400, the floor, so the run stops at 10,800 plus 600) | the final sample |
| 9 | Collect every miner's `program and 256 MiB cache ready` lines (seed, class, id) and the template's `eraSeed` for epochs 2 and 3, and send them to the node lane; the Mac computes `igneum-pow show --epoch-hex <seed> --program-class v3|v4 --era-hex <era>` for each (the id assertion of the G4 harness, no binary needed on a box) | every box's v4 id equals the CLI's v4 id and differs from the CLI's v3 id of the same seed and era |
| 9 | The id assertion, the same Mac-side procedure with the box's id read from the pack, not a log line (a worker-mode miner prints no `program id` line; the Metal run of G4b showed the same): on every box, for epochs 2 and 3, take the `program pack checked for epoch seed <E> day <D>: attempt <A>, <dir>` line and read `<dir>/program.h`'s `IGNEUM_GENERATOR` (must be 4), `IGNEUM_PROGRAM_CLASS` ("v4"), `IGNEUM_PROGRAM_ID`, `IGNEUM_SEED_BYTES_HEX` and `IGNEUM_ERA_SEED_HEX` (the miner's `PREPARE sent ... class v4 era=<hex>` line carries the same era), and send the five values per box and epoch to the node lane; the Mac computes `igneum-pow show --epoch-hex <IGNEUM_SEED_BYTES_HEX> --program-class v4 --era-hex <IGNEUM_ERA_SEED_HEX>` and the same with `v3` | every box's `IGNEUM_PROGRAM_ID` equals the CLI's v4 id and differs from the CLI's v3 id of the same seed and era; one id per epoch across boxes |
| 10 | Stop every node and miner; destroy the instances by the fleet plan's rule | the report in section 4 is in |
Never: no live-devnet port, no live override file, no manifest, no `update-now`; the boxes' app installs (if any) are not touched (the rule of 5 October: a job never quits or restarts an app it did not start).
@ -71,13 +71,13 @@ Never: no live-devnet port, no live override file, no manifest, no `update-now`;
| Field | From | Pass rule |
|---|---|---|
| `digest` per box | the node's first lines | one value on every signalling box, equal to 2b's; the stale box prints 0.3.13's digest of the same file (a different value, since its binary lacks the two fields) |
| `digest` per box | the node's first lines | one value on every signalling box, equal to the object's suffix-400 value (2b `aef46983...`, 2c `a15db4f0...`); the stale box prints 0.3.13's digest of the same file (a different value, since its binary lacks the two fields) |
| `switch_lines` per box | the node's first lines | the floor line names epoch 4 and the window line names 3,600 DAA, on every signalling box |
| `signal_line` per box | step 7 | present on every signalling box, epoch 2, the same seed block hash, share 10,000 bps (at least 9,500) |
| `blocks_before`, `blocks_after` | `getBlocks` from any box, split at DAA 7,200 | both over 0 |
| `rejected` | every miner's `rejected=` STATUS count and every node's `PoW rejected` lines | 0 on every box |
| `sinks` at the end | `getBlockDagInfo` | one value on every signalling box |
| `program_ids` per epoch per box | the miners' lines | one id per epoch across boxes; epochs 2 and 3 class v4; the Mac's CLI check (step 9) holds |
| `program_ids` per epoch per box | the packs' `program.h` named by the miners' `program pack checked` lines (step 9) | one id per epoch across boxes; epochs 2 and 3 generator 4, class v4; the Mac's CLI check holds |
| `stale_box` | its log and `getBlockDagInfo` | refused (no peer), block count 1, no block of its ever appears on any signalling box's chain (its own genesis-state chain and the fleet's never merge) |
| `shares` per sample | step 6 | 10,000 bps on every box at every sample |

File diff suppressed because one or more lines are too long

View file

@ -171,7 +171,7 @@ Filled from a CPU census over programs (section 6).
## 8. What is unverified
- Everything in section 6 marked pending.
- The 1-hour VDF does not exist; the devnet stand-in of section 2 is a proposal.
- The 1-hour VDF: BUILT on 7 October 2026 (era VDF lane, after the attack pass's F7 row named it the gating dependency): `kaspa_consensus_core::era_vdf` (the class-group Wesolowski scheme on a fixed-width integer and the hash-chain fallback behind the genesis byte `vdf_scheme`), `kaspa_consensus::processes::era_vdf` (the cut rule, the day-of-blues input, the evaluator thread, the record store), behind `Params::era_vdf_activation_daa` (never on every network until the project lead's word per network); the stand-in of section 2 stands below the activation and is what the VDF reads its input from above it. Verified: the F7 re-roll harness against the real era cut fires with the VDF off and is silent with it on across 6 cuts (`tools/era-vdf/reroll.mjs`, the record `docs/analysis/era-vdf-2026-10-07.md` section 3), the parameters and the measured prove and verify times are in spec 04 section 4.6. Still unverified: the P2P relay of a record to a syncing peer (spec 4.5, owed before era 1 of any network with the switch set), the binding of the cut to the certified checkpoint (left at the O-4.3 reading, one function to change), an external review of the class-group port (O-4.1), and the 2019-class-core verify time, which is measured on a proxy until a 2019 host is rented (record section 5).
- The interleave's value against a chip with a programmable address decoder is nil (1.2); the claim is limited to hard-wired layouts.
- The window floor of 2^26 words is set by the 5090's L2 (96 MiB) and the 9070 XT's Infinity Cache (64 MB, vendor figures); a future card with a larger cache moves the floor, which is a genesis constant.
- No cryptanalysis of the stride (a multiply and a rotate before the mask); it is a bijection, so the address distribution is that of the register value, as today.

View file

@ -112,3 +112,10 @@ Standing rulings of the same hour: "We dont want to penalise holders" (dormant-c
| 3 | The latency ladder | Approved | The six rungs (27, 35, 53, 88, 173, 267 passes), rungs 0 to 2 admissible, rung 3 re-measured on a quiet core before genesis, 4 and 5 inadmissible until verifiers allow; every step by 90 percent in each of seven windows, never unconditional; `latency_ladder_activation_daa` = 0 on the testnet at rung 0. |
| 4 | Cryptanalysis | Approved, "make sure they find ZERO flaws, also cut costs if possible" | The engagement runs at the low point (about USD 80,000) unless a quote forces more; an internal attack pass precedes it so the firms find nothing new; every finding is fixed before the testnet go. The contracting entity and the prize are still the project lead's to confirm. |
| 5 | Re-cut the testnet genesis | Approved | One cut with 18 decimals, `TESTNET_1`, and the switches on from genesis: proof verification, the leave item, the signing bonus, finality v3, the ladder at rung 0. Nothing live is touched; the go checklist decides the date. |
## Era VDF (7 October 2026, 12:xx UK, era VDF lane): two decisions for the project lead
Question 1, the activation. The era VDF (spec 04 section 4.4) is in the node behind `era_vdf_activation_daa`, never on every network, with the genesis scheme byte `vdf_scheme` 0 (the class group) and `era_vdf_t` at the reference rate of igneum-build-1's core (spec 4.6). Facts: the F7 harness shows the stand-in grindable with one block of hash at no delay and the VDF closing it; the first era with a VDF is era 1, 180 days after a network's genesis; the P2P record relay (O-4.10) is owed before then. Recommendation: igneum-testnet-1 and mainnet carry `era_vdf_activation_daa: 0` in their genesis objects (the switch costs nothing before era 1 and the digest then pins it from the start); the live devnet keeps never (it will not reach era 1). Unblocks: the freeze of the era draw procedure and the C_era cut rule, which the attack pass's F7 row holds open on the VDF.
Question 2, the cut's binding (O-4.11). The node names the cut block as the chain block the lead rule names, certified or not (the O-4.3 reading of 3 October 2026), so a finality pause across the cut never leaves an era without a seed and the rule is a function of the header's past alone. The design document's wording binds the era draw to the last certified checkpoint. Facts: the grinding defence does not depend on the binding (the delay makes any candidate's draw unknowable); the certified binding couples the era seed to finality liveness and needs a rule for a certificate that lands after the cut (`docs/analysis/era-vdf-2026-10-07.md` section 4). Recommendation: keep the O-4.3 reading for the era as for the epoch; one function (`EraVdfManager::cut_block`) changes if the finality lane wants the certified binding. Unblocks: the sentence in spec 4.4 step 1 stops carrying "under the O-4.3 reading" once decided.

View file

@ -0,0 +1,21 @@
13:27:58.207 pd1 case switch-off-expect-eight: switch off, 10 listing nodes, joint 200 s, watch 180 s, expect eight
13:27:59.744 pd1 n0 up pid 690270 json 31292 p2p 31291
13:28:01.258 pd1 n1 up pid 690864 json 31302 p2p 31301 addpeer 31291,31291
13:28:02.766 pd1 n2 up pid 691561 json 31312 p2p 31311 addpeer 31291,31301
13:28:04.272 pd1 n3 up pid 692232 json 31322 p2p 31321 addpeer 31291,31311
13:28:05.777 pd1 n4 up pid 692823 json 31332 p2p 31331 addpeer 31291,31321
13:28:07.281 pd1 n5 up pid 693470 json 31342 p2p 31341 addpeer 31291,31331
13:28:08.785 pd1 n6 up pid 694063 json 31352 p2p 31351 addpeer 31291,31341
13:28:10.290 pd1 n7 up pid 694656 json 31362 p2p 31361 addpeer 31291,31351
13:28:11.797 pd1 n8 up pid 695316 json 31372 p2p 31371 addpeer 31291,31361
13:28:13.304 pd1 n9 up pid 695947 json 31382 p2p 31381 addpeer 31291,31371
13:31:35.437 pd1 phase 1 done at 200.1 s: node 0 at 243 blocks DAA 243; 0 directory listing line(s) on node 0
13:31:36.962 pd1 fresh up pid 705981 json 31392 p2p 31391 addpeer 31291
13:32:07.031 pd1 t=231.7 s fresh: 1 outbound, 0 inbound, 272 blocks, 0 draw line(s), 0 directory connection(s)
13:32:37.109 pd1 t=261.8 s fresh: 1 outbound, 0 inbound, 314 blocks, 0 draw line(s), 0 directory connection(s)
13:33:07.190 pd1 t=291.9 s fresh: 1 outbound, 0 inbound, 332 blocks, 0 draw line(s), 0 directory connection(s)
13:33:37.273 pd1 t=322.0 s fresh: 1 outbound, 0 inbound, 359 blocks, 0 draw line(s), 0 directory connection(s)
13:34:07.348 pd1 t=352.0 s fresh: 1 outbound, 0 inbound, 379 blocks, 0 draw line(s), 0 directory connection(s)
13:34:37.426 pd1 t=382.1 s fresh: 1 outbound, 0 inbound, 420 blocks, 0 draw line(s), 0 directory connection(s)
13:34:37.428 pd1 SUMMARY FAIL (switch-off-expect-eight): node 0 logged 0 listings; the fresh node never reached 8 outbound (last 1), 0 connection(s) from the directory, 0 draw line(s), 420 blocks; FAILED CHECK listing_lines_on_node_0, fresh_reached_eight_outbound, fresh_connected_from_directory
13:34:37.428 pd1 summary: /srv/builds/igneum-wt-peer-directory/docs/plans/mission-item-12-gate/peer-directory-switch-off-expect-eight.json

View file

@ -0,0 +1,21 @@
13:27:58.206 pd0 case switch-off-expect-one: switch off, 10 listing nodes, joint 200 s, watch 180 s, expect one
13:27:59.744 pd0 n0 up pid 690269 json 31092 p2p 31091
13:28:01.255 pd0 n1 up pid 690863 json 31102 p2p 31101 addpeer 31091,31091
13:28:02.763 pd0 n2 up pid 691557 json 31112 p2p 31111 addpeer 31091,31101
13:28:04.270 pd0 n3 up pid 692228 json 31122 p2p 31121 addpeer 31091,31111
13:28:05.775 pd0 n4 up pid 692819 json 31132 p2p 31131 addpeer 31091,31121
13:28:07.280 pd0 n5 up pid 693466 json 31142 p2p 31141 addpeer 31091,31131
13:28:08.786 pd0 n6 up pid 694058 json 31152 p2p 31151 addpeer 31091,31141
13:28:10.292 pd0 n7 up pid 694658 json 31162 p2p 31161 addpeer 31091,31151
13:28:11.800 pd0 n8 up pid 695318 json 31172 p2p 31171 addpeer 31091,31161
13:28:13.306 pd0 n9 up pid 695955 json 31182 p2p 31181 addpeer 31091,31171
13:31:35.436 pd0 phase 1 done at 200.1 s: node 0 at 250 blocks DAA 250; 0 directory listing line(s) on node 0
13:31:36.969 pd0 fresh up pid 705982 json 31192 p2p 31191 addpeer 31091
13:32:07.034 pd0 t=231.7 s fresh: 1 outbound, 0 inbound, 288 blocks, 0 draw line(s), 0 directory connection(s)
13:32:37.118 pd0 t=261.8 s fresh: 1 outbound, 0 inbound, 308 blocks, 0 draw line(s), 0 directory connection(s)
13:33:07.199 pd0 t=291.9 s fresh: 1 outbound, 0 inbound, 349 blocks, 0 draw line(s), 0 directory connection(s)
13:33:37.276 pd0 t=322.0 s fresh: 1 outbound, 0 inbound, 377 blocks, 0 draw line(s), 0 directory connection(s)
13:34:07.351 pd0 t=352.0 s fresh: 1 outbound, 0 inbound, 405 blocks, 0 draw line(s), 0 directory connection(s)
13:34:37.431 pd0 t=382.1 s fresh: 1 outbound, 0 inbound, 448 blocks, 0 draw line(s), 0 directory connection(s)
13:34:37.432 pd0 SUMMARY PASS (switch-off-expect-one): node 0 logged 0 listings; the fresh node never reached 8 outbound (last 1), 0 connection(s) from the directory, 0 draw line(s), 448 blocks
13:34:37.432 pd0 summary: /srv/builds/igneum-wt-peer-directory/docs/plans/mission-item-12-gate/peer-directory-switch-off-expect-one.json

View file

@ -0,0 +1,22 @@
13:27:58.204 pd2 case switch-on-expect-eight: switch on, 10 listing nodes, joint 200 s, watch 180 s, expect eight
13:27:59.743 pd2 n0 up pid 690268 json 31492 p2p 31491
13:28:01.254 pd2 n1 up pid 690860 json 31502 p2p 31501 addpeer 31491,31491
13:28:02.762 pd2 n2 up pid 691556 json 31512 p2p 31511 addpeer 31491,31501
13:28:04.268 pd2 n3 up pid 692227 json 31522 p2p 31521 addpeer 31491,31511
13:28:05.773 pd2 n4 up pid 692818 json 31532 p2p 31531 addpeer 31491,31521
13:28:07.279 pd2 n5 up pid 693462 json 31542 p2p 31541 addpeer 31491,31531
13:28:08.787 pd2 n6 up pid 694062 json 31552 p2p 31551 addpeer 31491,31541
13:28:10.291 pd2 n7 up pid 694659 json 31562 p2p 31561 addpeer 31491,31551
13:28:11.800 pd2 n8 up pid 695317 json 31572 p2p 31571 addpeer 31491,31561
13:28:13.304 pd2 n9 up pid 695954 json 31582 p2p 31581 addpeer 31491,31571
13:31:35.435 pd2 phase 1 done at 200.1 s: node 0 at 233 blocks DAA 233; 10 directory listing line(s) on node 0
13:31:36.954 pd2 fresh up pid 705980 json 31592 p2p 31591 addpeer 31491
13:32:07.025 pd2 the fresh node holds 10 outbound peers at 231.7 s (9 from the directory)
13:32:07.025 pd2 t=231.7 s fresh: 10 outbound, 0 inbound, 262 blocks, 1 draw line(s), 9 directory connection(s)
13:32:37.102 pd2 t=261.8 s fresh: 10 outbound, 0 inbound, 290 blocks, 1 draw line(s), 9 directory connection(s)
13:33:07.182 pd2 t=291.9 s fresh: 10 outbound, 0 inbound, 323 blocks, 1 draw line(s), 9 directory connection(s)
13:33:37.265 pd2 t=322.0 s fresh: 10 outbound, 0 inbound, 357 blocks, 1 draw line(s), 9 directory connection(s)
13:34:07.342 pd2 t=352.0 s fresh: 10 outbound, 0 inbound, 391 blocks, 1 draw line(s), 9 directory connection(s)
13:34:37.418 pd2 t=382.1 s fresh: 10 outbound, 0 inbound, 414 blocks, 1 draw line(s), 9 directory connection(s)
13:34:37.418 pd2 SUMMARY PASS (switch-on-expect-eight): node 0 logged 10 listings; the fresh node reached 8 outbound at 231.7 s, 9 connection(s) from the directory, 1 draw line(s), 414 blocks
13:34:37.419 pd2 summary: /srv/builds/igneum-wt-peer-directory/docs/plans/mission-item-12-gate/peer-directory-switch-on-expect-eight.json

View file

@ -0,0 +1,370 @@
{
"pass": false,
"expect": "eight",
"case": "switch-off-expect-eight",
"switch": "off",
"listing": 10,
"joint": 200,
"watch": 180,
"node0": {
"blocks": 243,
"daa": 243
},
"listing_lines_on_node_0": 0,
"eight_outbound_at_s": null,
"last": {
"t": 382.1,
"outbound": 1,
"inbound": 0,
"blocks": 420,
"daa": 420,
"drawn": 0,
"connected_from_directory": 0
},
"checks": {
"listing_lines_on_node_0": false,
"fresh_synced": true,
"fresh_reached_eight_outbound": false,
"fresh_connected_from_directory": false,
"fresh_held_one_outbound": true,
"no_directory_line_on_fresh": true
},
"failed_checks": [
"listing_lines_on_node_0",
"fresh_reached_eight_outbound",
"fresh_connected_from_directory"
],
"samples": [
{
"t": 206.7,
"outbound": 1,
"inbound": 0,
"blocks": 250,
"daa": 250,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 211.7,
"outbound": 1,
"inbound": 0,
"blocks": 255,
"daa": 255,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 216.7,
"outbound": 1,
"inbound": 0,
"blocks": 258,
"daa": 258,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 221.7,
"outbound": 1,
"inbound": 0,
"blocks": 264,
"daa": 264,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 226.7,
"outbound": 1,
"inbound": 0,
"blocks": 271,
"daa": 271,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 231.7,
"outbound": 1,
"inbound": 0,
"blocks": 272,
"daa": 272,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 236.7,
"outbound": 1,
"inbound": 0,
"blocks": 276,
"daa": 276,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 241.8,
"outbound": 1,
"inbound": 0,
"blocks": 284,
"daa": 284,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 246.8,
"outbound": 1,
"inbound": 0,
"blocks": 289,
"daa": 289,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 251.8,
"outbound": 1,
"inbound": 0,
"blocks": 292,
"daa": 292,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 256.8,
"outbound": 1,
"inbound": 0,
"blocks": 302,
"daa": 302,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 261.8,
"outbound": 1,
"inbound": 0,
"blocks": 314,
"daa": 314,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 266.8,
"outbound": 1,
"inbound": 0,
"blocks": 317,
"daa": 317,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 271.8,
"outbound": 1,
"inbound": 0,
"blocks": 321,
"daa": 321,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 276.9,
"outbound": 1,
"inbound": 0,
"blocks": 322,
"daa": 322,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 281.9,
"outbound": 1,
"inbound": 0,
"blocks": 326,
"daa": 326,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 286.9,
"outbound": 1,
"inbound": 0,
"blocks": 331,
"daa": 331,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 291.9,
"outbound": 1,
"inbound": 0,
"blocks": 332,
"daa": 332,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 296.9,
"outbound": 1,
"inbound": 0,
"blocks": 338,
"daa": 338,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 301.9,
"outbound": 1,
"inbound": 0,
"blocks": 341,
"daa": 341,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 306.9,
"outbound": 1,
"inbound": 0,
"blocks": 344,
"daa": 344,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 311.9,
"outbound": 1,
"inbound": 0,
"blocks": 349,
"daa": 349,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 317,
"outbound": 1,
"inbound": 0,
"blocks": 355,
"daa": 355,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 322,
"outbound": 1,
"inbound": 0,
"blocks": 359,
"daa": 359,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 327,
"outbound": 1,
"inbound": 0,
"blocks": 361,
"daa": 361,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 332,
"outbound": 1,
"inbound": 0,
"blocks": 369,
"daa": 369,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 337,
"outbound": 1,
"inbound": 0,
"blocks": 371,
"daa": 371,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 342,
"outbound": 1,
"inbound": 0,
"blocks": 374,
"daa": 374,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 347,
"outbound": 1,
"inbound": 0,
"blocks": 378,
"daa": 378,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 352,
"outbound": 1,
"inbound": 0,
"blocks": 379,
"daa": 379,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 357.1,
"outbound": 1,
"inbound": 0,
"blocks": 389,
"daa": 389,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 362.1,
"outbound": 1,
"inbound": 0,
"blocks": 400,
"daa": 400,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 367.1,
"outbound": 1,
"inbound": 0,
"blocks": 406,
"daa": 406,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 372.1,
"outbound": 1,
"inbound": 0,
"blocks": 410,
"daa": 410,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 377.1,
"outbound": 1,
"inbound": 0,
"blocks": 416,
"daa": 416,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 382.1,
"outbound": 1,
"inbound": 0,
"blocks": 420,
"daa": 420,
"drawn": 0,
"connected_from_directory": 0
}
],
"node": "/srv/builds/igneum-wt-peer-directory/vendor/igneum-node-pd/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-peer-directory/vendor/igneum-node-pd/target/release/igneum-miner",
"slot": 1,
"ports": {
"base": 31290,
"suffix": 976
}
}

View file

@ -0,0 +1,366 @@
{
"pass": true,
"expect": "one",
"case": "switch-off-expect-one",
"switch": "off",
"listing": 10,
"joint": 200,
"watch": 180,
"node0": {
"blocks": 250,
"daa": 250
},
"listing_lines_on_node_0": 0,
"eight_outbound_at_s": null,
"last": {
"t": 382.1,
"outbound": 1,
"inbound": 0,
"blocks": 448,
"daa": 448,
"drawn": 0,
"connected_from_directory": 0
},
"checks": {
"listing_lines_on_node_0": false,
"fresh_synced": true,
"fresh_reached_eight_outbound": false,
"fresh_connected_from_directory": false,
"fresh_held_one_outbound": true,
"no_directory_line_on_fresh": true
},
"failed_checks": [],
"samples": [
{
"t": 206.7,
"outbound": 1,
"inbound": 0,
"blocks": 261,
"daa": 261,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 211.7,
"outbound": 1,
"inbound": 0,
"blocks": 271,
"daa": 271,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 216.7,
"outbound": 1,
"inbound": 0,
"blocks": 276,
"daa": 276,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 221.7,
"outbound": 1,
"inbound": 0,
"blocks": 278,
"daa": 278,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 226.7,
"outbound": 1,
"inbound": 0,
"blocks": 282,
"daa": 282,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 231.7,
"outbound": 1,
"inbound": 0,
"blocks": 288,
"daa": 288,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 236.8,
"outbound": 1,
"inbound": 0,
"blocks": 294,
"daa": 294,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 241.8,
"outbound": 1,
"inbound": 0,
"blocks": 296,
"daa": 296,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 246.8,
"outbound": 1,
"inbound": 0,
"blocks": 303,
"daa": 303,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 251.8,
"outbound": 1,
"inbound": 0,
"blocks": 305,
"daa": 305,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 256.8,
"outbound": 1,
"inbound": 0,
"blocks": 307,
"daa": 307,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 261.8,
"outbound": 1,
"inbound": 0,
"blocks": 308,
"daa": 308,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 266.8,
"outbound": 1,
"inbound": 0,
"blocks": 311,
"daa": 311,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 271.8,
"outbound": 1,
"inbound": 0,
"blocks": 315,
"daa": 315,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 276.9,
"outbound": 1,
"inbound": 0,
"blocks": 322,
"daa": 322,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 281.9,
"outbound": 1,
"inbound": 0,
"blocks": 331,
"daa": 331,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 286.9,
"outbound": 1,
"inbound": 0,
"blocks": 341,
"daa": 341,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 291.9,
"outbound": 1,
"inbound": 0,
"blocks": 349,
"daa": 349,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 296.9,
"outbound": 1,
"inbound": 0,
"blocks": 357,
"daa": 357,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 301.9,
"outbound": 1,
"inbound": 0,
"blocks": 364,
"daa": 364,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 306.9,
"outbound": 1,
"inbound": 0,
"blocks": 369,
"daa": 369,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 311.9,
"outbound": 1,
"inbound": 0,
"blocks": 372,
"daa": 372,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 317,
"outbound": 1,
"inbound": 0,
"blocks": 374,
"daa": 374,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 322,
"outbound": 1,
"inbound": 0,
"blocks": 377,
"daa": 377,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 327,
"outbound": 1,
"inbound": 0,
"blocks": 384,
"daa": 384,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 332,
"outbound": 1,
"inbound": 0,
"blocks": 393,
"daa": 393,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 337,
"outbound": 1,
"inbound": 0,
"blocks": 396,
"daa": 396,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 342,
"outbound": 1,
"inbound": 0,
"blocks": 397,
"daa": 397,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 347,
"outbound": 1,
"inbound": 0,
"blocks": 402,
"daa": 402,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 352,
"outbound": 1,
"inbound": 0,
"blocks": 405,
"daa": 405,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 357.1,
"outbound": 1,
"inbound": 0,
"blocks": 413,
"daa": 413,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 362.1,
"outbound": 1,
"inbound": 0,
"blocks": 422,
"daa": 422,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 367.1,
"outbound": 1,
"inbound": 0,
"blocks": 428,
"daa": 428,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 372.1,
"outbound": 1,
"inbound": 0,
"blocks": 433,
"daa": 433,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 377.1,
"outbound": 1,
"inbound": 0,
"blocks": 440,
"daa": 440,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 382.1,
"outbound": 1,
"inbound": 0,
"blocks": 448,
"daa": 448,
"drawn": 0,
"connected_from_directory": 0
}
],
"node": "/srv/builds/igneum-wt-peer-directory/vendor/igneum-node-pd/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-peer-directory/vendor/igneum-node-pd/target/release/igneum-miner",
"slot": 0,
"ports": {
"base": 31090,
"suffix": 975
}
}

View file

@ -0,0 +1,366 @@
{
"pass": true,
"expect": "eight",
"case": "switch-on-expect-eight",
"switch": "on",
"listing": 10,
"joint": 200,
"watch": 180,
"node0": {
"blocks": 233,
"daa": 233
},
"listing_lines_on_node_0": 10,
"eight_outbound_at_s": 231.7,
"last": {
"t": 382.1,
"outbound": 10,
"inbound": 0,
"blocks": 414,
"daa": 414,
"drawn": 1,
"connected_from_directory": 9
},
"checks": {
"listing_lines_on_node_0": true,
"fresh_synced": true,
"fresh_reached_eight_outbound": true,
"fresh_connected_from_directory": true,
"fresh_held_one_outbound": false,
"no_directory_line_on_fresh": false
},
"failed_checks": [],
"samples": [
{
"t": 206.7,
"outbound": 1,
"inbound": 0,
"blocks": 242,
"daa": 242,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 211.7,
"outbound": 1,
"inbound": 0,
"blocks": 246,
"daa": 246,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 216.7,
"outbound": 1,
"inbound": 0,
"blocks": 253,
"daa": 253,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 221.7,
"outbound": 1,
"inbound": 0,
"blocks": 256,
"daa": 256,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 226.7,
"outbound": 1,
"inbound": 0,
"blocks": 260,
"daa": 260,
"drawn": 0,
"connected_from_directory": 0
},
{
"t": 231.7,
"outbound": 10,
"inbound": 0,
"blocks": 262,
"daa": 262,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 236.7,
"outbound": 10,
"inbound": 0,
"blocks": 269,
"daa": 269,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 241.7,
"outbound": 10,
"inbound": 0,
"blocks": 277,
"daa": 277,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 246.8,
"outbound": 10,
"inbound": 0,
"blocks": 280,
"daa": 280,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 251.8,
"outbound": 10,
"inbound": 0,
"blocks": 283,
"daa": 283,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 256.8,
"outbound": 10,
"inbound": 0,
"blocks": 286,
"daa": 286,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 261.8,
"outbound": 10,
"inbound": 0,
"blocks": 290,
"daa": 290,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 266.8,
"outbound": 10,
"inbound": 0,
"blocks": 298,
"daa": 298,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 271.8,
"outbound": 10,
"inbound": 0,
"blocks": 303,
"daa": 303,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 276.8,
"outbound": 10,
"inbound": 0,
"blocks": 311,
"daa": 311,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 281.8,
"outbound": 10,
"inbound": 0,
"blocks": 316,
"daa": 316,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 286.9,
"outbound": 10,
"inbound": 0,
"blocks": 317,
"daa": 317,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 291.9,
"outbound": 10,
"inbound": 0,
"blocks": 323,
"daa": 323,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 296.9,
"outbound": 10,
"inbound": 0,
"blocks": 334,
"daa": 334,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 301.9,
"outbound": 10,
"inbound": 0,
"blocks": 337,
"daa": 337,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 306.9,
"outbound": 10,
"inbound": 0,
"blocks": 342,
"daa": 342,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 311.9,
"outbound": 10,
"inbound": 0,
"blocks": 348,
"daa": 348,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 316.9,
"outbound": 10,
"inbound": 0,
"blocks": 350,
"daa": 350,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 322,
"outbound": 10,
"inbound": 0,
"blocks": 357,
"daa": 357,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 327,
"outbound": 10,
"inbound": 0,
"blocks": 359,
"daa": 359,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 332,
"outbound": 10,
"inbound": 0,
"blocks": 362,
"daa": 362,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 337,
"outbound": 10,
"inbound": 0,
"blocks": 364,
"daa": 364,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 342,
"outbound": 10,
"inbound": 0,
"blocks": 368,
"daa": 368,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 347,
"outbound": 10,
"inbound": 0,
"blocks": 380,
"daa": 380,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 352,
"outbound": 10,
"inbound": 0,
"blocks": 391,
"daa": 391,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 357.1,
"outbound": 10,
"inbound": 0,
"blocks": 393,
"daa": 393,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 362.1,
"outbound": 10,
"inbound": 0,
"blocks": 399,
"daa": 399,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 367.1,
"outbound": 10,
"inbound": 0,
"blocks": 401,
"daa": 401,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 372.1,
"outbound": 10,
"inbound": 0,
"blocks": 403,
"daa": 403,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 377.1,
"outbound": 10,
"inbound": 0,
"blocks": 409,
"daa": 409,
"drawn": 1,
"connected_from_directory": 9
},
{
"t": 382.1,
"outbound": 10,
"inbound": 0,
"blocks": 414,
"daa": 414,
"drawn": 1,
"connected_from_directory": 9
}
],
"node": "/srv/builds/igneum-wt-peer-directory/vendor/igneum-node-pd/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-peer-directory/vendor/igneum-node-pd/target/release/igneum-miner",
"slot": 2,
"ports": {
"base": 31490,
"suffix": 977
}
}

View file

@ -0,0 +1,112 @@
# Mission item 12: the weight-backed peer directory, prototype (status log)
Lane: horizon (mission items 4 and 12), 7 October 2026, after item 4's gate line. Branches: `peer-directory` (this repo, on
master) and `peer-directory-node` (the fork, on release-0.3.20-node dc141409; targets 0.3.21). Times UK (BST) unless marked Z.
The item (mission.md 2.12, invent.md 7.1): a coinbase section where a key above dust may publish its node's address; a fresh
node dials from that list weighted by 30-day weight, beside the DNS seeds. Gate: a fresh node with genesis peers only reaches 8
outbound from the directory; a 34 percent attacker eclipses under 0.1 percent of 1,000 fresh starts (simulation is fine); the NAT
fraction measured on the fleet (owed to the fleet lane, asked 7 October 13:0x UK; nothing rented). Dropped if the NAT fraction
makes the list useless: this lane reports, it does not decide.
## 1. What exists to build on (read before writing)
| Where | What | Used how |
|---|---|---|
| fork `consensus/core/src/finality.rs` `FinalityItem` (tags 1 vote, 2 certificate, 3 evidence, 4 leave), `encode_section` / `decode_section` (`items || len || "IGNF"` at the end of the coinbase payload), `Leave` (W7: `daa || pubkey || signature`, signed by the vote key, carried LAST so a decoder from before its tag stops at it) | the section codec and the item shape to copy | the address item is tag 5 with the same carriage rule: last, after leaves, and only once its switch is on |
| fork `consensus/src/processes/finality.rs` `ingest_leave` (block-carried, dated by the lowest carrier), `template_candidates` / `section_from` (what a template carries), `leave_active` (switch by DAA), `FinalityState.leaves` (persisted) | the ingest, the carriage and the switch pattern | the directory is a map key hash to (address, carrier DAA), persisted in the state the same way |
| fork `components/connectionmanager/src/lib.rs` `handle_outbound_connections` | dials from the address manager's prioritised random iterator, then the DNS seeders when connections are still missing | the directory draw goes between the two: addresses drawn by weight from the directory are fed to the address manager and dialled before the seeders are asked |
| fork `components/addressmanager/src/lib.rs` `add_address`, `iterate_prioritized_random_addresses` (weighted by failure count and prefix bucket) | the store a dialled address lives in | directory addresses enter it like seeder answers; the weight decides which enter, the store's own rule decides the order |
| fork `consensus/core/src/config/params.rs` `consensus_digest` (a switch at never is outside the digest; set, it is in) | the activation pattern every 0.3.16 switch follows | `peer_directory_activation_daa`, never on every network |
## 2. Design, as built (prototype; fork branch peer-directory-node on release-0.3.20-node dc141409, commits 0cad5106 and db28d331 (tests); repo branch peer-directory on master 819d536b, commits 9a64d18c and 3e0dfa17 (gate), pushed to origin)
| Item | Rule |
|---|---|
| Wire | `AddressAnnounce { daa, ip: [u8; 16], port, pubkey, signature }` (core `finality.rs`), 171 B, signed by the vote key the header names under `IGNEUM_ADDR_V1` over `"igneum-addr-v1/" || chain_id || 0 || daa || ip || port` |
| Carriage | in the MINER's extra data as `IGNP || hex(body)`, the key reveal's form (`IGNA` is already the EVM payout tag), never in the finality section: no switch for carriage, a node without the tag never meets it; `igneum-miner mine --announce <ip:port>` puts one in every template of every identity (`announced`, dated at the sink's DAA score when the run began) |
| Reading | `FinalityManager::on_block_body` at or above `peer_directory_activation_daa`: the announcement must be by the block's own key, verify, and name a routable-in-form address; one node-local entry per key, the newest carrier wins (not persisted: a restarted node refills it from the blocks it sees; a prototype's gap) |
| Report | `ConsensusApi::peer_directory()` rows with each key's weight at the latest determined checkpoint (0 under dust); entries older than a weight window dropped on the way |
| Draw | `ConsensusApi::peer_directory_draw(n, exclude)`: without replacement, proportional to weight, keys under dust never drawn |
| Dialling | `ConnectionManager::handle_outbound_connections`: after the address store's own iterator and before the DNS seeders, when connections are still missing, 2 x missing addresses from the draw enter the address store and are dialled at once (`DirectoryDraw` closure wired in `protocol/flows/src/service.rs` through the consensus's blocking session) |
| Switch | `peer_directory_activation_daa`, never on every network (the four network constants), in the digest once set, in `OverrideParams` and the 60x file |
| Not done | the RPC that lists the directory (the harness reads `getConnectedPeerInfo` and the node log instead); persistence across restarts; Ember and the phone's use of the list (invent.md 7.1's third hour) |
## 3. Gate plan
| Gate | How |
|---|---|
| a fresh node with genesis peers only reaches 8 outbound from the directory | fast-time network of 10 listing nodes (each announcing its loopback address) plus one fresh node started with `--outpeers=8`, `--nodnsseed` and one `--addpeer` to a genesis peer that serves blocks only; the fresh node's `getConnectedPeerInfo` shows 8 outbound within 5 min, all from the directory |
| a 34 percent attacker eclipses under 0.1 percent of 1,000 fresh starts | `sim/peer-directory/eclipse.py`: the draw as implemented (weighted, without replacement, 8 peers) over a table where the attacker's keys hold 34 percent of the weight and list 34 percent of the addresses; 1,000 starts; expected 1.8e-4 at 8 peers (invent.md model E); also 16 peers and the honest-majority-of-peers reading |
| the NAT fraction | RECEIVED from the fleet lane (ac055d60427caab99) at 13:1x UK, read at 12:06Z from each standing live-devnet node's own log, nothing rented: reachable from outside 2 of 14 (hub-1, RunPod, 26611 mapped: 2,844 inbound peer connections against 86 outbound; pool-1, RunPod, mapped 4463: 1,692 against 99); not reachable 12 of 14 (every Vast box: 0 inbound peer connections each, 96 to 542 outbound, no `--externalip`, no mapped port). So 86 percent of the fleet's nodes sit behind NAT and hold their 4 to 5 peers by dialling out; the two reachable nodes carry every inbound connection. Caveat (theirs): a rented fleet overstates the NAT share for datacentre operators and understates it for home miners; a fair read for "a node started with the defaults". Left out: the four Devnet 2 pods and the Hetzner seed. The marker was "Connected to incoming peer" against "Connected to outgoing peer" (the RPC server's local accepts are not peers). |
## 4. Runs
### The eclipse simulation (`sim/peer-directory/eclipse.py`, box 2, nice 10, 13:5x to 14:0x UK)
The draw as implemented (without replacement, proportional to weight at the latest checkpoint), 200 honest keys with Zipf
weights, the attacker at 34 percent of the weight split over `m` keys of equal weight, seed 7.
| starts | peers | attacker keys | eclipsed | rate | model E a^n | attacker majority of the peers |
|---|---|---|---|---|---|---|
| 1,000 | 8 | 8 | 0 | 0 | 1.79e-4 | 4.0 percent |
| 1,000 | 8 | 64 | 1 | 1.0e-3 | 1.79e-4 | 9.8 percent |
| 1,000 | 8 | 1,000 | 0 | 0 | 1.79e-4 | 10.7 percent |
| 1,000 | 16 | 8, 64, 1,000 | 0, 0, 0 | 0 | 3.19e-8 | 0, 9.2, 9.2 percent |
| 100,000 | 8 | 8 | 0 | 0 | 1.79e-4 | 4.8 percent |
| 100,000 | 8 | 64 | 14 | 1.4e-4 | 1.79e-4 | 10.7 percent |
| 100,000 | 8 | 1,000 | 22 | 2.2e-4 | 1.79e-4 | 11.6 percent |
The gate's line (under 0.1 percent of 1,000 fresh starts) is met at 100,000 starts: 1.4e-4 and 2.2e-4, where 1,000 starts
cannot resolve it (1 in 1,000 is exactly the line). Two readings beyond model E: with fewer attacker keys than peers an eclipse
is impossible under the draw without replacement (the dust threshold, 100 blocks a window on mainnet, prices each attacker
key at 100 blocks), and the attacker holds a MAJORITY of a fresh node's peers in about 11 percent of starts at 8 peers, which
is the number that matters for a relay-level attack rather than a full eclipse; 16 peers takes that to 9 percent and the
eclipse to zero in 1,000.
### Unit tests (box 2)
| Test | Result |
|---|---|
| core `address_announce_round_trips_and_verifies_under_its_key_only` | 1 of 1 |
| node `the_peer_directory_lists_a_blocks_own_key_at_its_newest_address_only_when_switched_on` (known-failed first: the switch at never lists nothing) | 1 of 1 |
| `cargo check -p kaspad -p igneum-miner --features kaspad/igneum-pow` on the branch | ok |
### The fast-time gate (box 2, `tools/fast-time-remote.sh --box 2`, binary of peer-directory-node 0cad5106, 13:27 to 13:34 UK)
`infra/fast-time/peer-directory.mjs`: ten listing nodes at one CPU thread each, every miner announcing its node's loopback
p2p address, every node advertising an unroutable external ip (10.255.0.0/16) so ordinary address gossip hands a fresh node
dead addresses only; after 200 s a fresh node starts with `--outpeers=8` and one `--addpeer` to node 0 and is watched 180 s.
| Case | Must | Got | Numbers |
|---|---|---|---|
| switch off, expect one (the known-failed case) | PASS | PASS | node 0 logged 0 listings; the fresh node held 1 outbound (node 0) for the whole watch, 0 draw lines, synced 448 blocks |
| switch off, expect eight (the harness's own failed shape) | FAIL | FAIL | 1 outbound, 0 from the directory |
| switch on, expect eight (THE GATE LINE) | PASS | PASS | node 0 logged 10 listings; the fresh node reached 8 outbound at 231.7 s (about 30 s after it started), 9 connections from the directory in one draw, synced 414 blocks |
Three harness faults on the way, each fixed: the log directory missing on the box (every case died at the redirect); the
peer count read `isOutbound` where the fork's `RpcPeerInfo` is `is_outbound`; and the wrapper's first run checking the
worktree root out on the box (fixed on horizon, 10140f9f, carried here).
## 5. Verdict line for main
The prototype does what invent.md 7.1 asked, on the numbers: a fresh node with one genesis peer and dead gossip reached 8
outbound from the directory in about 30 s; the eclipse by a 34 percent attacker is 1.4e-4 to 2.2e-4 at 8 peers over 100,000
starts (under the 0.1 percent line; 0 with 8 or fewer attacker keys, since the draw is without replacement), and the attacker
holds a majority of a fresh node's peers in about 11 percent of starts, which is the number to watch. The NAT reading (2 of 14
standing nodes reachable, 86 percent behind NAT with no inbound path) makes the list a SEED SUPPLEMENT, not a replacement:
today it would list the two reachable fleet nodes beside the seeds, and a home miner's node (the 12-of-14 class unless the app
maps a port) is a reader of the list, never a listing. Per tier: a home miner's node gains weight-backed peers beside the seeds
at no cost and lists nothing by default; a rig or a pool node with a mapped port opts in with `--announce` and 171 bytes a block;
the phone and Ember verify mode are not wired (not done). Listed as useful or dropped is main's and the project lead's call; this lane's
reading is "keep as a seed supplement behind its switch", which costs 1.7 MB a day per node at 10,000 listing keys (approximate,
from the item size) and nothing while the switch is never.
Open: an RPC that lists the directory; persistence across restarts; Ember and the phone; the testnet object and every network
file carry no `peer_directory` field (the switch stays never until a cut sets it).
## 6. Rebase for 0.3.21 (14:0x UK, the shipper's order)
peer-directory-node rebased onto release-0.3.20-node c4459193 in one round, no conflict: tip 2e32d5f6, on both box mirrors under its name. Suite on build-2 at 2e32d5f6: igneum-miner 19 of 19, connectionmanager 0 tests, consensus lib 116 of 117 (the ban flake only; cargo stopped before consensus-core's target). Digest on tools/fleet/override.json: 7bd98cc4..., the same as the pin's binary. Handed to the node lane a283f5f0d364ceef0.
Digest on the file hub-1 runs (/root/fleet/override.json, sixteen fields, the copy at /tmp/igneum-devnet/override-v3-live.json on build-1): eada4bda8aa8368c2b2c3d17744bc7a70a0ff0e996dad681884d3ac5de1207eb, the shipper's string, read 14:1x UK from the branch binary on build-2; 7bd98cc4 was master's tools/fleet/override.json, another file. The live digest is unchanged by this branch.

View file

@ -0,0 +1,142 @@
# Mission item 4: weight-gated deep fork choice, the gate (status log)
Lane: horizon (mission items 4 and 12), started 7 October 2026, 12:41 UK, on main's brief. Branches: `horizon` (this repo, on master
d1285cef) and `horizon-node` (the fork, on release-0.3.20-node dc141409, the 0.3.20 line; this work targets 0.3.21). Worktrees
`igneum-wt-horizon` and `igneum-wt-horizon/vendor/igneum-node-horizon`. Times are UK (BST) unless marked Z.
The item (mission.md 2.4): a tip whose fork point is older than D (10 min of past-median time) is a fork-choice candidate only
if the keys that built it hold at least a third of the weight table at the fork point. Gate, fast time: a 51 percent fresh-key
fork from 15 min back is refused by every honest node; a one-third-weight fork is accepted; partition heal unchanged. Known-failed
cases first.
## 1. What already existed (read before writing anything)
| Where | What | State |
|---|---|---|
| fork `consensus/src/processes/finality.rs` `deep_fork_refusal`, asked by `sink_search_algorithm` for every popped candidate | the rule, behind `fork_gate_activation_daa` (never on every network; in the digest once set) with `fork_gate_window_daa` 600 s; the fork point is the highest chain ancestor of the candidate on the sink's chain; depth = the larger of sink minus fork and candidate minus fork; builders = the vote keys of every block of the candidate's chain since the fork (chain blocks and mergesets); the table = `voters_at(fork)`; refused under `3 x weight < total`; node-local memo per refused block | on the 0.3.20 line since 61b22057 (6 October), fixes 3301cf32 (depth along both chains) and aa0182aa (no extension shortcut) |
| fork unit tests | `without_the_fork_gate_a_minor_keys_deep_fork_wins` (known-failed), `the_fork_gate_refuses_a_deep_fork_under_a_third_and_passes_the_rest` (25 percent refused, 75 percent passes, inside the window passes) | green on that line |
| `infra/fast-time/fork-gate.mjs` on branch ca3-v4-node (never on master) | a TWO-node harness: A at 75 percent, B at 25 percent, A IDLE through the split; cases gate-on hold (PASS 23:45Z 6 Oct), gate-off reorg (PASS), gate-off hold (FAIL as it must); run on the Mac; a `pkill -f` on the devnet suffix | the record in `docs/plans/counter-asic-3-node.md` 7.5 |
| the live devnet and Devnet 2 override files, the testnet object | no `fork_gate` field anywhere: the switch is never on every network | unchanged by this lane |
What the mission gate asks beyond that record: a FRESH key (0 of the table, not 25 percent), the honest side MINING through the
split (the record idled it), EVERY honest node (two, not one), the one-third case accepted, and the partition heal shown unchanged
with the gate on and off. Nothing in the rule needed to change for any of these; this lane adds the proof.
## 2. What this lane added
| Piece | Where | What |
|---|---|---|
| Fresh-key unit test | fork `a_fresh_keys_deep_fork_wins_only_with_the_gate_off` | known-failed first: at never a key with no block in the table wins by blue work; at 0 it is refused with weight 0 of the table |
| One-third line unit test | fork `the_gate_passes_at_one_third_of_the_table_and_refuses_under_it` | eight (honest mix, fork height) pairs; each outcome equals `3 x weight >= total` read from the table the gate reads; both sides seen |
| Three-node harness | `infra/fast-time/fork-gate.mjs` (new on master's line) | H1 and H2 honest and mining through every phase, B the third node; modes attack (B fresh, 3 of 5 threads), third (B at about half of the table), partition (H1 against H2); a reorg is read from the chain (`getVirtualChainFromBlock` of the pre-cut tip lists removed blocks), never from a key; blue work compared as BigInt; leftovers stopped by pid file |
| The six-case runner | `infra/fast-time/fork-gate-gate.mjs` | known-failed cases first, then the gate line, the one-third case, the two partition heals; GREEN only when every case gives the verdict it must and the two heals agree |
| Box wrapper | `tools/fast-time-remote.sh` (whole-body block; listed in `tools/ci/whole-body-check.sh`) | runs a harness on a box under a slot with a holder line and a JSONL row, the node binaries from a fork worktree's target on the box, results fetched back |
## 2a. The hole the known-failed cases found (13:1x to 13:3x UK)
The one-third unit test failed on box 2 at its second pair: B on every fourth block, fork at height 50, B holding 12 of 49 at
the fork, and the private tip WON. A probe test (temporary, since removed) read the gate's own answer at fork heights 40, 45,
48, 50, 55, 60 and 65: refused at 40, 48 and 60; let through at 45, 50 and 55 (65 is inside the window, a legitimate pass).
The let-through rows read "keys 2, signed 44 of 44": the honest key A was in the attacker's builders. The pattern is the fork
block's own key: heights that are multiples of 4 sit on B's blocks and were refused; the others sit on A's blocks and passed.
Cause: `collect_builders` took each chain block's mergeset (blues and reds) and GHOSTDAG's `mergeset_blues[0]` is the block's
selected parent, so the first private block above the fork carried the FORK block into the builders, and the fork block was
A's. A 25 percent key forking right after a 75 percent key's block walked through the gate with A's weight on its side. The
same reading let a private chain that merges honest blocks as side parents inherit every merged key. The 6 October harness
PASS (23:45Z) was luck: its pre-cut sink was B-built, so the leaked key was B's own.
Fix (fork, `collect_builders(block, sink, keys)`): a builder is the key of a block the sink does NOT have (not a DAG ancestor
of the sink, the sink itself included). The fork block is the sink's; honest blocks the attacker merged are the sink's; a
partition side's own blocks are not, so a heal is unchanged. New unit test, known-failed shape first:
`a_deep_fork_counts_only_the_blocks_the_sink_lacks_as_builders` (B from A's block at 45 refused; a private chain merging
honest blocks 46 to 80 wins at never and is refused at 0).
Also seen: `ban_is_decided_by_the_carrying_block_so_nodes_agree_on_every_voter_list` died with `UnexpectedDifficulty` in the
full crate run (box 2 under load 65) and passed alone (1 of 1, 13:1x UK); not this lane's change, noted for the suite count.
## 3. Runs
### Commits
| Repo | Branch | Commit | What |
|---|---|---|---|
| fork | horizon-node (on release-0.3.20-node dc141409) | eb32d2e0 | the builders fix (`collect_builders` skips every block the sink has) and the fork-gate tests on the two-instance lab |
| fork | horizon-node | a6a71ac2 | the lab inserts into the judge first and rebuilds a block refused on `UnexpectedDifficulty` (the suite's flake class) |
| repo | horizon (on master d1285cef) | 0dc2adff | the three-node harness, the six-case runner, the box wrapper, the whole-body check row |
### Suites (box 2, igneum-build-2, nice 10 on 32 cores)
| Run (UK) | Command | Result |
|---|---|---|
| 12:5x | `-- test --release -p kaspa-consensus` (the first test version, no fix) | 111 of 113: my one-third test FAIL (the hole), `ban_is_decided_by_the_carrying_block...` FAIL on `UnexpectedDifficulty` (load 65; passes alone 13:1x, 1 of 1) |
| 13:2x | `-- test --release -p kaspa-consensus --lib processes::finality::tests` (the fix, the lab) | 24 of 24 |
| 13:3x | `-- test --release -p kaspa-consensus` (eb32d2e0 content) | 112 of 114: two lab tests FAIL on `UnexpectedDifficulty` (the same flake class, now reachable by the lab's second instance) |
| 13:3x | `-- test --release -p kaspa-consensus` at a6a71ac2, 0dcaf4c5, ca7acb99 (the lab's rebuild, a pause between rebuilds, salted hashes) | 111 or 112 of 114 each: the two labs whose instances share one config still FAIL at block 61 under load; the probes showed `UnexpectedDifficulty` thirty times over 7.5 s, so neither clock nor hash memo |
| 13:38 | `-- test --release -p kaspa-consensus` at 6eb21fc9 (the lab on `DifficultyRule::KaspaSampled`, DAG-only bits) | 113 of 114 in the lib target, 3 ignored; the one FAIL is the pre-existing `ban_is_decided_by_the_carrying_block_so_nodes_agree_on_every_voter_list` (the same class: the dual rule's template bits and header stamp disagree at block 61, the first retarget, under load; passes alone). The lib target failing stops cargo before the integration targets. |
Fork commits after eb32d2e0, test-only: a6a71ac2, 0dcaf4c5, ca7acb99, f225ed9f, 6eb21fc9 (the lab inserts into the judge first and rebuilds on the flake; a pause between rebuilds; every lab its own hash words; the DAG-only difficulty rule; the import). The binary content of kaspad and igneum-miner is the same from eb32d2e0 on; the gate binary carries 6eb21fc9.
### Build (box 1, igneum-build-1)
| Run (UK) | Command | Result |
|---|---|---|
| (pending) | `tools/build-remote.sh --no-fetch -- build --release -p kaspad -p igneum-miner --features kaspad/igneum-pow` from the fork worktree at a6a71ac2 (igneum-pow at 8c728ca3 through an untracked paths override to `igneum-pow-amend`, the 0.3.20 pairing); two earlier submissions of the uncommitted tree were stopped by pid so the gate binary carries the commit | |
### Tree checks (the Mac, checks only)
`tools/ci/pre-push.sh --ci` on horizon 0dc2adff: GREEN, 43 checks in 31 s (13:3x UK).
### The gate (box 2, igneum-build-2, by main's word at 13:4x UK: box 1's slots stay with the 0.3.20 pin)
Binary: `igneumd` and `igneum-miner` of horizon-node 6eb21fc9 (igneum-pow 8c728ca3), built on box 2 at 13:5x UK
(`tools/build-remote.sh --box 2`, 113 s); run through `tools/fast-time-remote.sh --box 2` (one slot, the bounded class, 583 s
for the six cases side by side), case summaries and logs in `docs/plans/mission-item-4-gate/`. Window 60 DAA, joint 240 s,
split 180 s, watch 150 s; honest nodes H1 and H2 at one CPU thread each through every phase; the attacker at 3 threads in
the split (60 percent of the CPU hash: the refusal reads weight, not how much heavier the chain is).
| Case | Must | Got | Side 2 at the fork | Fork depth (DAA) | Blue work at the heal, side 2 against H1 | Honest nodes after the heal |
|---|---|---|---|---|---|---|
| attack, gate off, expect reorg (the rule's known-failed case) | PASS | PASS | 0 bps (fresh key) | 191 | 17,470,131 against 14,893,198 | H1 and H2 reorged at 510 s, 0 refusal lines |
| attack, gate off, expect hold (the harness's own failed shape) | FAIL | FAIL | 0 bps | 179 | 19,683,187 against 17,459,678 | H1 and H2 reorged at 480 s, 0 refusal lines |
| attack, gate on, expect hold (THE GATE LINE) | PASS | PASS | 0 bps | 197 | 20,297,242 against 14,807,915 | H1 and H2 HELD for the whole watch, 177 refusal lines each, both know side 2's tip; side 2 kept its chain |
| third, gate on, expect reorg (a one-third-weight fork accepted) | PASS | PASS | 6,000 bps | 166 | 28,969,222 against 24,628,833 | H1 and H2 reorged at 477 s, 0 refusal lines |
| partition, gate on, expect reorg | PASS | PASS | 3,719 bps | 194 | 18,089,683 against 11,490,863 | H1 reorged at 507 s, 0 refusal lines; H2 kept its chain |
| partition, gate off, expect reorg (the same outcome) | PASS | PASS | 5,583 bps | 191 | 19,576,159 against 12,270,631 | H1 reorged at 507 s, 0 refusal lines; H2 kept its chain |
GATE GREEN, 13:58 UK: 6 of 6 cases gave the verdict they must; the partition heal is the same with the gate on and off
(both reorg H1 onto the heavier side, 0 refusals). Reading "15 min back" at fast time: the devnet window is 600 s and the
mission's 15 min is one and a half windows; the harness keeps the window at 60 DAA and the split at 180 s, three windows
deep, so the refusal was asked from 61 s of the split on and held for its whole second half.
## 4. Consequences per tier
One sentence: with the fix, a renter's or a chain-splitter's deep reorg is bounded by weight for every tier alike, and an
honest key's own blocks are never refused.
| Tier | What the number means |
|---|---|
| Home miner (one card, any size, Windows, Linux or macOS, any vendor) | its node refuses the same tips a rig's or a pool's node refuses; a fresh-key chain from more than ten minutes back never becomes its sink while the key that built it holds under a third of the window; nothing to configure, nothing to pay; the memo costs one mergeset per refused block |
| Rig | the same; a rig's own blocks after a connectivity gap are built by its own keys and pass on share |
| Pool user | the pool's node holds the chain the pool mined; a renter cannot reorganise the pool's payouts past ten minutes without a third of the weight |
| Exchange and holder | a 12-hour double spend during a pause or in the first month (51-percent.md section 5 rank 2, USD 146 at 1 GH/s) is gone: the deep fork is refused at every honest node, lock or no lock |
| The rule's cost | none for certificates; a partition side under a third cannot reorg the other past the window (the intended outcome, unchanged by the fix) |
Before the fix the bound held only when the fork sat on a block of a small key; a renter forking right after a large
miner's block, or merging honest blocks into its chain, walked through on every tier.
## 5. Open
| Item | Owner |
|---|---|
| Merge: horizon-node is on release-0.3.20-node dc141409; main's word is to rebase onto the 0.3.20 pin once named (c4459193 the candidate) for 0.3.21; never a push to a release branch | this lane, when main names the pin |
| The difficulty red: `ban_is_decided_by_the_carrying_block...` and, before the lab moved to the DAG-only rule, two lab tests, on `UnexpectedDifficulty` at block 61 (the first retarget) under the full suite's load; the dual rule's template bits come from a virtual resolved at one instant and the header is stamped at a later one | the node lane for 0.3.21 (main's word: logged, not fixed here) |
| The fork's test block builder asks the gate (it runs the sink search from the parents it is given): any future test that builds a private chain on a gated instance builds it on the fork block instead; the lab's two-instance shape is the pattern | the node lane; a note in `consensus/src/consensus/test_consensus.rs` is owed |
| `tools/box` named in the brief does not exist; the box tooling is `tools/build-remote.sh` on `infra/build-server/lib.sh`, which `tools/fast-time-remote.sh` uses; the wrapper's first run checked the worktree root out on box 2 and took the fork sources with it (fixed the same hour: overlay only, 10140f9f) | main's reading |
| The devnet, Devnet 2 and the testnet object carry no `fork_gate` field: the switch stays never everywhere until a cut sets it (in the digest once set) | the shipper and the project lead |
## 6. Rebase for 0.3.21 (14:0x UK, the shipper's order)
horizon-node rebased onto release-0.3.20-node c4459193 (the 0.3.20 pin) in one round, no conflict: tip 437f0438, on both box mirrors under its name. Suite on build-2 at 437f0438: `-p kaspa-consensus` lib 117 of 119, 3 ignored (the ban difficulty flake and `a_chain_that_misses_an_adopted_lock_never_locks_here`, which passes alone 1 of 1 and passed in every earlier full run; load 113). Digest on tools/fleet/override.json (origin/master): 7bd98cc4..., the same as the pin's own binary on that file. Handed to the node lane a283f5f0d364ceef0 for the 0.3.21 merge after 55768f88, miner-reliability-20 and pool-finish-node.
Digest on the file hub-1 runs (/root/fleet/override.json, sixteen fields, the copy at /tmp/igneum-devnet/override-v3-live.json on build-1): eada4bda8aa8368c2b2c3d17744bc7a70a0ff0e996dad681884d3ac5de1207eb, the shipper's string, read 14:1x UK from the branch binary on build-2; 7bd98cc4 was master's tools/fleet/override.json, another file. The live digest is unchanged by this branch.

View file

@ -0,0 +1,933 @@
{
"pass": false,
"expect": "hold",
"case": "attack-gate-off-expect-hold",
"mode": "attack",
"gate": "off",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 0
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 0,
"others": 120,
"side2_bps": 0
},
"fork_daa": 247,
"fork_depth_daa_at_heal": 179,
"sinks": {
"joint": {
"h1": {
"hash": "0535d8ae67287238ee528bae5ab1f366273bd1c65c6a94342ec3879a40b81c88",
"blocks": 248,
"daa": 247,
"blue": 248,
"blueWork": "11209205",
"key": "8da9ddee…"
},
"h2": {
"hash": "0535d8ae67287238ee528bae5ab1f366273bd1c65c6a94342ec3879a40b81c88",
"blocks": 248,
"daa": 247,
"blue": 248,
"blueWork": "11209205",
"key": "8da9ddee…"
},
"side2": {
"hash": "0535d8ae67287238ee528bae5ab1f366273bd1c65c6a94342ec3879a40b81c88",
"blocks": 248,
"daa": 247,
"blue": 248,
"blueWork": "11209205",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "d5347d2cd3468901fda8e5a1caffe0be55d45eb12739577fadb0141d2291c2ba",
"blocks": 412,
"daa": 411,
"blue": 412,
"blueWork": "17459678",
"key": "8da9ddee…"
},
"h2": {
"hash": "d5347d2cd3468901fda8e5a1caffe0be55d45eb12739577fadb0141d2291c2ba",
"blocks": 412,
"daa": 411,
"blue": 412,
"blueWork": "17459678",
"key": "8da9ddee…"
},
"side2": {
"hash": "bfdb75e384417228a85f4098f8f2bb65d40c271ce674bea2d1cc8a3c6c335f53",
"blocks": 427,
"daa": 426,
"blue": 427,
"blueWork": "19683187",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 480.3,
"knows_side2_tip": true,
"refusal_lines": 0
},
"h2": {
"held": false,
"reorged": true,
"reorg_at_s": 480.3,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": true,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [
"every_honest_node_held",
"every_honest_node_logged_a_refusal"
],
"refusal_example": null,
"samples": [
{
"t": 425.2,
"h1": {
"blocks": 416,
"sink": "7b85c0879ec1a3f0",
"daa": 414,
"blue": 415,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 416,
"sink": "7b85c0879ec1a3f0",
"daa": 414,
"blue": 415,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 435,
"sink": "8bb7e69cea95ecaa",
"blue": 435,
"reorged": false
}
},
{
"t": 430.2,
"h1": {
"blocks": 423,
"sink": "44ee221996bf6519",
"daa": 422,
"blue": 423,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 423,
"sink": "44ee221996bf6519",
"daa": 422,
"blue": 423,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 441,
"sink": "46ee56ca78abcfa4",
"blue": 441,
"reorged": false
}
},
{
"t": 435.2,
"h1": {
"blocks": 430,
"sink": "fe81d0b598b34760",
"daa": 429,
"blue": 430,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 430,
"sink": "fe81d0b598b34760",
"daa": 429,
"blue": 430,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 445,
"sink": "bf71c714df908384",
"blue": 445,
"reorged": false
}
},
{
"t": 440.2,
"h1": {
"blocks": 440,
"sink": "acab21ca95900afc",
"daa": 439,
"blue": 440,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 440,
"sink": "acab21ca95900afc",
"daa": 439,
"blue": 440,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 448,
"sink": "b8fccb90e479e8a5",
"blue": 448,
"reorged": false
}
},
{
"t": 445.3,
"h1": {
"blocks": 443,
"sink": "d9051d6139ae8afa",
"daa": 442,
"blue": 443,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 443,
"sink": "d9051d6139ae8afa",
"daa": 442,
"blue": 443,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 456,
"sink": "2cc48c7a2084d5e4",
"blue": 456,
"reorged": false
}
},
{
"t": 450.3,
"h1": {
"blocks": 450,
"sink": "cea4dc9c673bd13a",
"daa": 449,
"blue": 450,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 450,
"sink": "cea4dc9c673bd13a",
"daa": 449,
"blue": 450,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 466,
"sink": "4f46263c45d1d32a",
"blue": 466,
"reorged": false
}
},
{
"t": 455.3,
"h1": {
"blocks": 452,
"sink": "1a8679962a368c21",
"daa": 451,
"blue": 452,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 452,
"sink": "1a8679962a368c21",
"daa": 451,
"blue": 452,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 472,
"sink": "12dcc825edc2fa17",
"blue": 472,
"reorged": false
}
},
{
"t": 460.3,
"h1": {
"blocks": 454,
"sink": "2a7c0240cfe56042",
"daa": 453,
"blue": 454,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 454,
"sink": "2a7c0240cfe56042",
"daa": 453,
"blue": 454,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 480,
"sink": "bbb6cf12cfd7419f",
"blue": 480,
"reorged": false
}
},
{
"t": 465.3,
"h1": {
"blocks": 454,
"sink": "2a7c0240cfe56042",
"daa": 453,
"blue": 454,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 454,
"sink": "2a7c0240cfe56042",
"daa": 453,
"blue": 454,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 488,
"sink": "f443140c40db1ff4",
"blue": 488,
"reorged": false
}
},
{
"t": 470.3,
"h1": {
"blocks": 456,
"sink": "2c2bf05b3992cb6b",
"daa": 455,
"blue": 456,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 456,
"sink": "2c2bf05b3992cb6b",
"daa": 455,
"blue": 456,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 493,
"sink": "0dd0e90fa5bb4330",
"blue": 493,
"reorged": false
}
},
{
"t": 475.3,
"h1": {
"blocks": 458,
"sink": "cde3915508aeb008",
"daa": 457,
"blue": 458,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 458,
"sink": "cde3915508aeb008",
"daa": 457,
"blue": 458,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 496,
"sink": "7adc2714fa7a10c9",
"blue": 496,
"reorged": false
}
},
{
"t": 480.3,
"h1": {
"blocks": 505,
"sink": "f08f99c6711510d7",
"daa": 504,
"blue": 505,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 505,
"sink": "f08f99c6711510d7",
"daa": 504,
"blue": 505,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 505,
"sink": "f08f99c6711510d7",
"blue": 505,
"reorged": false
}
},
{
"t": 485.3,
"h1": {
"blocks": 509,
"sink": "9dfb826d38e1f6a4",
"daa": 508,
"blue": 509,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 509,
"sink": "9dfb826d38e1f6a4",
"daa": 508,
"blue": 509,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 509,
"sink": "9dfb826d38e1f6a4",
"blue": 509,
"reorged": false
}
},
{
"t": 490.4,
"h1": {
"blocks": 511,
"sink": "a9bdeeb2148190f1",
"daa": 510,
"blue": 511,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 511,
"sink": "a9bdeeb2148190f1",
"daa": 510,
"blue": 511,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 511,
"sink": "a9bdeeb2148190f1",
"blue": 511,
"reorged": false
}
},
{
"t": 495.4,
"h1": {
"blocks": 522,
"sink": "fa5aefefc31e1c4c",
"daa": 521,
"blue": 522,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 522,
"sink": "fa5aefefc31e1c4c",
"daa": 521,
"blue": 522,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 522,
"sink": "fa5aefefc31e1c4c",
"blue": 522,
"reorged": false
}
},
{
"t": 500.4,
"h1": {
"blocks": 525,
"sink": "3f659b8355cf6fb8",
"daa": 524,
"blue": 525,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 525,
"sink": "3f659b8355cf6fb8",
"daa": 524,
"blue": 525,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 525,
"sink": "3f659b8355cf6fb8",
"blue": 525,
"reorged": false
}
},
{
"t": 505.4,
"h1": {
"blocks": 528,
"sink": "2b00a2431aecf64d",
"daa": 527,
"blue": 528,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 528,
"sink": "2b00a2431aecf64d",
"daa": 527,
"blue": 528,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 528,
"sink": "2b00a2431aecf64d",
"blue": 528,
"reorged": false
}
},
{
"t": 510.4,
"h1": {
"blocks": 534,
"sink": "f10f202f1b8adf79",
"daa": 533,
"blue": 534,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 534,
"sink": "f10f202f1b8adf79",
"daa": 533,
"blue": 534,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 534,
"sink": "f10f202f1b8adf79",
"blue": 534,
"reorged": false
}
},
{
"t": 515.4,
"h1": {
"blocks": 541,
"sink": "d70cd34944d6d565",
"daa": 540,
"blue": 541,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 541,
"sink": "d70cd34944d6d565",
"daa": 540,
"blue": 541,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 541,
"sink": "d70cd34944d6d565",
"blue": 541,
"reorged": false
}
},
{
"t": 520.4,
"h1": {
"blocks": 546,
"sink": "35d62b928d603bac",
"daa": 545,
"blue": 546,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 546,
"sink": "35d62b928d603bac",
"daa": 545,
"blue": 546,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 546,
"sink": "35d62b928d603bac",
"blue": 546,
"reorged": false
}
},
{
"t": 525.4,
"h1": {
"blocks": 552,
"sink": "11ee723145434436",
"daa": 551,
"blue": 552,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 552,
"sink": "11ee723145434436",
"daa": 551,
"blue": 552,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 552,
"sink": "11ee723145434436",
"blue": 552,
"reorged": false
}
},
{
"t": 530.4,
"h1": {
"blocks": 561,
"sink": "096fe4e02ba023a4",
"daa": 560,
"blue": 561,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 561,
"sink": "096fe4e02ba023a4",
"daa": 560,
"blue": 561,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 561,
"sink": "096fe4e02ba023a4",
"blue": 561,
"reorged": false
}
},
{
"t": 535.4,
"h1": {
"blocks": 566,
"sink": "58b6d24c4920da55",
"daa": 565,
"blue": 566,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 566,
"sink": "58b6d24c4920da55",
"daa": 565,
"blue": 566,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 566,
"sink": "58b6d24c4920da55",
"blue": 566,
"reorged": false
}
},
{
"t": 540.4,
"h1": {
"blocks": 567,
"sink": "3a757d2721963b97",
"daa": 566,
"blue": 567,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 567,
"sink": "3a757d2721963b97",
"daa": 566,
"blue": 567,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 567,
"sink": "3a757d2721963b97",
"blue": 567,
"reorged": false
}
},
{
"t": 545.4,
"h1": {
"blocks": 572,
"sink": "40cb1b5ff6ffa6e2",
"daa": 571,
"blue": 572,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 572,
"sink": "40cb1b5ff6ffa6e2",
"daa": 571,
"blue": 572,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 572,
"sink": "40cb1b5ff6ffa6e2",
"blue": 572,
"reorged": false
}
},
{
"t": 550.4,
"h1": {
"blocks": 577,
"sink": "e3f1416a5521d05a",
"daa": 576,
"blue": 577,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 577,
"sink": "e3f1416a5521d05a",
"daa": 576,
"blue": 577,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 577,
"sink": "e3f1416a5521d05a",
"blue": 577,
"reorged": false
}
},
{
"t": 555.5,
"h1": {
"blocks": 584,
"sink": "38b6a8056ad74b9d",
"daa": 583,
"blue": 584,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 584,
"sink": "38b6a8056ad74b9d",
"daa": 583,
"blue": 584,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 584,
"sink": "38b6a8056ad74b9d",
"blue": 584,
"reorged": false
}
},
{
"t": 560.5,
"h1": {
"blocks": 589,
"sink": "69c3e39a47332124",
"daa": 588,
"blue": 589,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 589,
"sink": "69c3e39a47332124",
"daa": 588,
"blue": 589,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 589,
"sink": "69c3e39a47332124",
"blue": 589,
"reorged": false
}
},
{
"t": 565.5,
"h1": {
"blocks": 592,
"sink": "6fe943b09aa71ffa",
"daa": 591,
"blue": 592,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 592,
"sink": "6fe943b09aa71ffa",
"daa": 591,
"blue": 592,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 592,
"sink": "6fe943b09aa71ffa",
"blue": 592,
"reorged": false
}
},
{
"t": 570.5,
"h1": {
"blocks": 595,
"sink": "b5217027de64f1a2",
"daa": 594,
"blue": 595,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 595,
"sink": "b5217027de64f1a2",
"daa": 594,
"blue": 595,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 595,
"sink": "b5217027de64f1a2",
"blue": 595,
"reorged": false
}
}
],
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 1,
"ports": {
"base": 30730,
"suffix": 981
}
}

View file

@ -0,0 +1,18 @@
12:48:26.701 fg1 case attack-gate-off-expect-hold: mode attack, gate off (window 60 DAA), joint 240 s, split 180 s, watch 150 s, honest 1 thread(s) each, attacker 3 (joint 0), expect hold
12:48:28.281 fg1 h1 up pid 480222 json 30732 p2p 30731
12:48:29.791 fg1 h2 up pid 481550 json 30742 p2p 30741 addpeer 30731
12:48:31.307 fg1 b up pid 482808 json 30752 p2p 30751 addpeer 30761,30762
12:52:34.325 fg1 phase 1 done at 240.0 s: H1 248 blocks sink 0535d8ae daa 247; H2 248 sink 0535d8ae; side 2 (b) 248 sink 0535d8ae; keys H1 8da9ddee H2 eda9cde4 side2 fresh (none yet)
12:52:34.384 fg1 weight at the fork (last 120 blue blocks of H1's chain): side 2 0, others 120, side 2 0 bps
12:52:34.387 fg1 links cut at 240.1 s; side 2 (b) now mines with 3 thread(s), the honest side with 1 each
12:55:34.491 fg1 phase 2 done at 420.2 s: H1 412 blocks sink d5347d2c daa 411 blue 412 work 17459678; side 2 427 sink bfdb75e3 daa 426 blue 427 work 19683187 (side 2 heavier: true); fork depth 179 DAA against window 60
12:55:34.491 fg1 links restored at 420.2 s; watching H1 and H2 for 150 s
12:56:04.581 fg1 t=450.3 s H1 450 blocks sink cea4dc9c held; H2 450 blocks sink cea4dc9c held; side 2 466 blocks sink 4f46263c
12:56:34.656 fg1 H1 left its pre-cut chain at 480.3 s: sink f08f99c6711510d7 by 0e2c6fe2
12:56:34.659 fg1 H2 left its pre-cut chain at 480.3 s: sink f08f99c6711510d7 by 0e2c6fe2
12:56:34.661 fg1 t=480.3 s H1 505 blocks sink f08f99c6 REORGED; H2 505 blocks sink f08f99c6 REORGED; side 2 505 blocks sink f08f99c6
12:57:04.702 fg1 t=510.4 s H1 534 blocks sink f10f202f REORGED; H2 534 blocks sink f10f202f REORGED; side 2 534 blocks sink f10f202f
12:57:34.751 fg1 t=540.4 s H1 567 blocks sink 3a757d27 REORGED; H2 567 blocks sink 3a757d27 REORGED; side 2 567 blocks sink 3a757d27
12:58:04.813 fg1 t=570.5 s H1 595 blocks sink b5217027 REORGED; H2 595 blocks sink b5217027 REORGED; side 2 595 blocks sink b5217027
12:58:04.825 fg1 SUMMARY FAIL (attack-gate-off-expect-hold): side 2 held 0 bps of the blue blocks at the fork; fork depth 179 DAA against window 60; side 2 blue work 19683187 against H1 17459678 at the heal; H1 reorged at 480.3 s (0 refusal lines, knows side 2's tip: true); H2 reorged at 480.3 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true; FAILED CHECK every_honest_node_held, every_honest_node_logged_a_refusal
12:58:04.825 fg1 summary: /srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-attack-gate-off-expect-hold.json

View file

@ -0,0 +1,930 @@
{
"pass": true,
"expect": "reorg",
"case": "attack-gate-off-expect-reorg",
"mode": "attack",
"gate": "off",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 0
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 0,
"others": 120,
"side2_bps": 0
},
"fork_daa": 240,
"fork_depth_daa_at_heal": 191,
"sinks": {
"joint": {
"h1": {
"hash": "e66998c63f8f5d2f3f9108a5c61730f3d5ee06a0553f47e3bce314a0e4d7228e",
"blocks": 242,
"daa": 240,
"blue": 241,
"blueWork": "8689541",
"key": "8da9ddee…"
},
"h2": {
"hash": "e66998c63f8f5d2f3f9108a5c61730f3d5ee06a0553f47e3bce314a0e4d7228e",
"blocks": 242,
"daa": 240,
"blue": 241,
"blueWork": "8689541",
"key": "8da9ddee…"
},
"side2": {
"hash": "e66998c63f8f5d2f3f9108a5c61730f3d5ee06a0553f47e3bce314a0e4d7228e",
"blocks": 242,
"daa": 240,
"blue": 241,
"blueWork": "8689541",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "6e932c389f2ad01a39873317f9549f0a336e66a7c840669dd5bcb44a979a19ad",
"blocks": 421,
"daa": 420,
"blue": 421,
"blueWork": "14893198",
"key": "8da9ddee…"
},
"h2": {
"hash": "6e932c389f2ad01a39873317f9549f0a336e66a7c840669dd5bcb44a979a19ad",
"blocks": 421,
"daa": 420,
"blue": 421,
"blueWork": "14893198",
"key": "8da9ddee…"
},
"side2": {
"hash": "d93a1238a397f68337f4bc28d6197a61a32fbca176f3e7f4248b6b330e239cc8",
"blocks": 432,
"daa": 431,
"blue": 432,
"blueWork": "17470131",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 510.4,
"knows_side2_tip": true,
"refusal_lines": 0
},
"h2": {
"held": false,
"reorged": true,
"reorg_at_s": 510.4,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": true,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"samples": [
{
"t": 425.2,
"h1": {
"blocks": 426,
"sink": "719c3c11c14e54ed",
"daa": 425,
"blue": 426,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 426,
"sink": "719c3c11c14e54ed",
"daa": 425,
"blue": 426,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 438,
"sink": "c704e731f479e045",
"blue": 438,
"reorged": false
}
},
{
"t": 430.2,
"h1": {
"blocks": 432,
"sink": "fe308e06a3529e00",
"daa": 431,
"blue": 432,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 432,
"sink": "fe308e06a3529e00",
"daa": 431,
"blue": 432,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 442,
"sink": "7ca72d6ee8a28b46",
"blue": 442,
"reorged": false
}
},
{
"t": 435.2,
"h1": {
"blocks": 436,
"sink": "ba7e8b5f41caf493",
"daa": 435,
"blue": 436,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 436,
"sink": "ba7e8b5f41caf493",
"daa": 435,
"blue": 436,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 446,
"sink": "5459e40a1e9137e0",
"blue": 446,
"reorged": false
}
},
{
"t": 440.2,
"h1": {
"blocks": 438,
"sink": "19432a8a37b593ad",
"daa": 437,
"blue": 438,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 438,
"sink": "19432a8a37b593ad",
"daa": 437,
"blue": 438,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 455,
"sink": "5fdf67fc248f6500",
"blue": 455,
"reorged": false
}
},
{
"t": 445.3,
"h1": {
"blocks": 446,
"sink": "aada7bf1e3d16b25",
"daa": 445,
"blue": 446,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 446,
"sink": "aada7bf1e3d16b25",
"daa": 445,
"blue": 446,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 466,
"sink": "85ccafa8a9d39de0",
"blue": 466,
"reorged": false
}
},
{
"t": 450.3,
"h1": {
"blocks": 447,
"sink": "1761d6112c00d161",
"daa": 446,
"blue": 447,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 447,
"sink": "1761d6112c00d161",
"daa": 446,
"blue": 447,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 475,
"sink": "594fe7ff929c559f",
"blue": 475,
"reorged": false
}
},
{
"t": 455.3,
"h1": {
"blocks": 455,
"sink": "3d694806975e096e",
"daa": 454,
"blue": 455,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 455,
"sink": "3d694806975e096e",
"daa": 454,
"blue": 455,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 480,
"sink": "bf92fbb2a472d205",
"blue": 480,
"reorged": false
}
},
{
"t": 460.3,
"h1": {
"blocks": 461,
"sink": "4c2da8fcb362f14d",
"daa": 460,
"blue": 461,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 461,
"sink": "4c2da8fcb362f14d",
"daa": 460,
"blue": 461,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 482,
"sink": "90cc4fd0fac9bef2",
"blue": 482,
"reorged": false
}
},
{
"t": 465.3,
"h1": {
"blocks": 467,
"sink": "4cd5d4fda0703837",
"daa": 466,
"blue": 467,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 467,
"sink": "4cd5d4fda0703837",
"daa": 466,
"blue": 467,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 484,
"sink": "8d822c253a2939d6",
"blue": 484,
"reorged": false
}
},
{
"t": 470.3,
"h1": {
"blocks": 473,
"sink": "beeb70fa234a9a4b",
"daa": 472,
"blue": 473,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 473,
"sink": "beeb70fa234a9a4b",
"daa": 472,
"blue": 473,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 487,
"sink": "ed057653dec1d416",
"blue": 487,
"reorged": false
}
},
{
"t": 475.3,
"h1": {
"blocks": 480,
"sink": "47c9ab35c01f2f7d",
"daa": 479,
"blue": 480,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 480,
"sink": "47c9ab35c01f2f7d",
"daa": 479,
"blue": 480,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 495,
"sink": "072c31cf3c13877b",
"blue": 495,
"reorged": false
}
},
{
"t": 480.3,
"h1": {
"blocks": 486,
"sink": "918c05c7a79ce8ed",
"daa": 485,
"blue": 486,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 486,
"sink": "918c05c7a79ce8ed",
"daa": 485,
"blue": 486,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 504,
"sink": "6ab8526912a92786",
"blue": 504,
"reorged": false
}
},
{
"t": 485.4,
"h1": {
"blocks": 491,
"sink": "5b94825a96af8031",
"daa": 490,
"blue": 491,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 491,
"sink": "5b94825a96af8031",
"daa": 490,
"blue": 491,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 511,
"sink": "e5d2d2cf5ca14138",
"blue": 511,
"reorged": false
}
},
{
"t": 490.4,
"h1": {
"blocks": 494,
"sink": "7d0fd0b857ad553d",
"daa": 493,
"blue": 494,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 494,
"sink": "7d0fd0b857ad553d",
"daa": 493,
"blue": 494,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 518,
"sink": "6acadef70d9d2b7e",
"blue": 518,
"reorged": false
}
},
{
"t": 495.4,
"h1": {
"blocks": 500,
"sink": "4e35f238846e9802",
"daa": 499,
"blue": 500,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 500,
"sink": "4e35f238846e9802",
"daa": 499,
"blue": 500,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 524,
"sink": "a0e5e0544388677b",
"blue": 524,
"reorged": false
}
},
{
"t": 500.4,
"h1": {
"blocks": 502,
"sink": "1f3cf87f97e6661c",
"daa": 501,
"blue": 502,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 502,
"sink": "1f3cf87f97e6661c",
"daa": 501,
"blue": 502,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 530,
"sink": "f0ba9826edcf48e7",
"blue": 530,
"reorged": false
}
},
{
"t": 505.4,
"h1": {
"blocks": 509,
"sink": "532a873c7ad783c5",
"daa": 508,
"blue": 509,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 509,
"sink": "532a873c7ad783c5",
"daa": 508,
"blue": 509,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 537,
"sink": "b138a9d8ba0b3454",
"blue": 537,
"reorged": false
}
},
{
"t": 510.4,
"h1": {
"blocks": 545,
"sink": "b878077d471c2a73",
"daa": 544,
"blue": 545,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 545,
"sink": "b878077d471c2a73",
"daa": 544,
"blue": 545,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 545,
"sink": "b878077d471c2a73",
"blue": 545,
"reorged": false
}
},
{
"t": 515.4,
"h1": {
"blocks": 556,
"sink": "95bd60b5bf755ef1",
"daa": 555,
"blue": 556,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 556,
"sink": "95bd60b5bf755ef1",
"daa": 555,
"blue": 556,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 556,
"sink": "95bd60b5bf755ef1",
"blue": 556,
"reorged": false
}
},
{
"t": 520.4,
"h1": {
"blocks": 569,
"sink": "258f2c477b49756d",
"daa": 568,
"blue": 569,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 569,
"sink": "258f2c477b49756d",
"daa": 568,
"blue": 569,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 569,
"sink": "258f2c477b49756d",
"blue": 569,
"reorged": false
}
},
{
"t": 525.4,
"h1": {
"blocks": 571,
"sink": "e8cc9969d5d1dc10",
"daa": 570,
"blue": 571,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 571,
"sink": "e8cc9969d5d1dc10",
"daa": 570,
"blue": 571,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 571,
"sink": "e8cc9969d5d1dc10",
"blue": 571,
"reorged": false
}
},
{
"t": 530.5,
"h1": {
"blocks": 573,
"sink": "da3da53755578a2d",
"daa": 572,
"blue": 573,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 573,
"sink": "da3da53755578a2d",
"daa": 572,
"blue": 573,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 573,
"sink": "da3da53755578a2d",
"blue": 573,
"reorged": false
}
},
{
"t": 535.5,
"h1": {
"blocks": 576,
"sink": "65ba87e325c66754",
"daa": 575,
"blue": 576,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 576,
"sink": "65ba87e325c66754",
"daa": 575,
"blue": 576,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 576,
"sink": "65ba87e325c66754",
"blue": 576,
"reorged": false
}
},
{
"t": 540.5,
"h1": {
"blocks": 580,
"sink": "11e564345e74f2cb",
"daa": 579,
"blue": 580,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 580,
"sink": "11e564345e74f2cb",
"daa": 579,
"blue": 580,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 580,
"sink": "11e564345e74f2cb",
"blue": 580,
"reorged": false
}
},
{
"t": 545.5,
"h1": {
"blocks": 585,
"sink": "b1d2689648b6d765",
"daa": 584,
"blue": 585,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 585,
"sink": "b1d2689648b6d765",
"daa": 584,
"blue": 585,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 585,
"sink": "b1d2689648b6d765",
"blue": 585,
"reorged": false
}
},
{
"t": 550.5,
"h1": {
"blocks": 594,
"sink": "0856bc8c348725e9",
"daa": 593,
"blue": 594,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 594,
"sink": "0856bc8c348725e9",
"daa": 593,
"blue": 594,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 594,
"sink": "0856bc8c348725e9",
"blue": 594,
"reorged": false
}
},
{
"t": 555.5,
"h1": {
"blocks": 597,
"sink": "b0b25422dae4fab7",
"daa": 596,
"blue": 597,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 597,
"sink": "b0b25422dae4fab7",
"daa": 596,
"blue": 597,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 597,
"sink": "b0b25422dae4fab7",
"blue": 597,
"reorged": false
}
},
{
"t": 560.5,
"h1": {
"blocks": 606,
"sink": "733de568ec588157",
"daa": 605,
"blue": 606,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 606,
"sink": "733de568ec588157",
"daa": 605,
"blue": 606,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 606,
"sink": "733de568ec588157",
"blue": 606,
"reorged": false
}
},
{
"t": 565.5,
"h1": {
"blocks": 608,
"sink": "ad3c86d071a93e75",
"daa": 607,
"blue": 608,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 608,
"sink": "ad3c86d071a93e75",
"daa": 607,
"blue": 608,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 608,
"sink": "ad3c86d071a93e75",
"blue": 608,
"reorged": false
}
},
{
"t": 570.5,
"h1": {
"blocks": 613,
"sink": "a38c3c8af9f6290d",
"daa": 612,
"blue": 613,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 613,
"sink": "a38c3c8af9f6290d",
"daa": 612,
"blue": 613,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 613,
"sink": "a38c3c8af9f6290d",
"blue": 613,
"reorged": false
}
}
],
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 0,
"ports": {
"base": 30690,
"suffix": 980
}
}

View file

@ -0,0 +1,18 @@
12:48:26.701 fg0 case attack-gate-off-expect-reorg: mode attack, gate off (window 60 DAA), joint 240 s, split 180 s, watch 150 s, honest 1 thread(s) each, attacker 3 (joint 0), expect reorg
12:48:28.258 fg0 h1 up pid 480223 json 30692 p2p 30691
12:48:29.769 fg0 h2 up pid 481517 json 30702 p2p 30701 addpeer 30691
12:48:31.285 fg0 b up pid 482726 json 30712 p2p 30711 addpeer 30721,30722
12:52:34.300 fg0 phase 1 done at 240.0 s: H1 242 blocks sink e66998c6 daa 240; H2 242 sink e66998c6; side 2 (b) 242 sink e66998c6; keys H1 8da9ddee H2 eda9cde4 side2 fresh (none yet)
12:52:34.368 fg0 weight at the fork (last 120 blue blocks of H1's chain): side 2 0, others 120, side 2 0 bps
12:52:34.370 fg0 links cut at 240.1 s; side 2 (b) now mines with 3 thread(s), the honest side with 1 each
12:55:34.480 fg0 phase 2 done at 420.2 s: H1 421 blocks sink 6e932c38 daa 420 blue 421 work 14893198; side 2 432 sink d93a1238 daa 431 blue 432 work 17470131 (side 2 heavier: true); fork depth 191 DAA against window 60
12:55:34.481 fg0 links restored at 420.2 s; watching H1 and H2 for 150 s
12:56:04.569 fg0 t=450.3 s H1 447 blocks sink 1761d611 held; H2 447 blocks sink 1761d611 held; side 2 475 blocks sink 594fe7ff
12:56:34.644 fg0 t=480.4 s H1 486 blocks sink 918c05c7 held; H2 486 blocks sink 918c05c7 held; side 2 504 blocks sink 6ab85269
12:57:04.714 fg0 H1 left its pre-cut chain at 510.4 s: sink b878077d471c2a73 by eda9cde4
12:57:04.717 fg0 H2 left its pre-cut chain at 510.4 s: sink b878077d471c2a73 by eda9cde4
12:57:04.719 fg0 t=510.4 s H1 545 blocks sink b878077d REORGED; H2 545 blocks sink b878077d REORGED; side 2 545 blocks sink b878077d
12:57:34.769 fg0 t=540.5 s H1 580 blocks sink 11e56434 REORGED; H2 580 blocks sink 11e56434 REORGED; side 2 580 blocks sink 11e56434
12:58:04.814 fg0 t=570.5 s H1 613 blocks sink a38c3c8a REORGED; H2 613 blocks sink a38c3c8a REORGED; side 2 613 blocks sink a38c3c8a
12:58:04.822 fg0 SUMMARY PASS (attack-gate-off-expect-reorg): side 2 held 0 bps of the blue blocks at the fork; fork depth 191 DAA against window 60; side 2 blue work 17470131 against H1 14893198 at the heal; H1 reorged at 510.4 s (0 refusal lines, knows side 2's tip: true); H2 reorged at 510.4 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true
12:58:04.822 fg0 summary: /srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-attack-gate-off-expect-reorg.json

View file

@ -0,0 +1,930 @@
{
"pass": true,
"expect": "hold",
"case": "attack-gate-on-expect-hold",
"mode": "attack",
"gate": "on",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 0
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 0,
"others": 121,
"side2_bps": 0
},
"fork_daa": 233,
"fork_depth_daa_at_heal": 197,
"sinks": {
"joint": {
"h1": {
"hash": "7ed68743f22e711490406bd380cf66fff94579fcc981344c317a46c18cbc61bb",
"blocks": 234,
"daa": 233,
"blue": 234,
"blueWork": "8862250",
"key": "eda9cde4…"
},
"h2": {
"hash": "7ed68743f22e711490406bd380cf66fff94579fcc981344c317a46c18cbc61bb",
"blocks": 234,
"daa": 233,
"blue": 234,
"blueWork": "8862250",
"key": "eda9cde4…"
},
"side2": {
"hash": "7ed68743f22e711490406bd380cf66fff94579fcc981344c317a46c18cbc61bb",
"blocks": 234,
"daa": 233,
"blue": 234,
"blueWork": "8862250",
"key": "eda9cde4…"
}
},
"split": {
"h1": {
"hash": "036cfe568f3d403351b338ea41b7069ad4eae0ce95308436a25b6c7aa4c1372a",
"blocks": 396,
"daa": 395,
"blue": 396,
"blueWork": "14807915",
"key": "8da9ddee…"
},
"h2": {
"hash": "036cfe568f3d403351b338ea41b7069ad4eae0ce95308436a25b6c7aa4c1372a",
"blocks": 396,
"daa": 395,
"blue": 396,
"blueWork": "14807915",
"key": "8da9ddee…"
},
"side2": {
"hash": "abb93c2318f6c2ebdbd4e14a50b3d73ba7f483b8b06b18666e1015d038d4011a",
"blocks": 431,
"daa": 430,
"blue": 431,
"blueWork": "20297242",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": true,
"reorged": false,
"reorg_at_s": null,
"knows_side2_tip": true,
"refusal_lines": 177
},
"h2": {
"held": true,
"reorged": false,
"reorg_at_s": null,
"knows_side2_tip": true,
"refusal_lines": 177
}
},
"checks": {
"side2_under_a_third_at_fork": true,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": true,
"every_honest_node_reorged": false,
"every_honest_node_logged_a_refusal": true,
"no_honest_node_logged_a_refusal": false,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": "Fork choice: block 41225a00fb38b6394540a5707443bd70e678484cbd20aa3fa8e807bb24df8d4a forks 252 DAA back from the sink's chain and its builders hold 0.00% of the weight table at the fork (under one third); not a sink candidate (ledger 51-percent rank 2)",
"samples": [
{
"t": 425.2,
"h1": {
"blocks": 407,
"sink": "b9b5fef0daea5b74",
"daa": 406,
"blue": 407,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 407,
"sink": "b9b5fef0daea5b74",
"daa": 406,
"blue": 407,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 436,
"sink": "b87c6327c3fc8298",
"blue": 436,
"reorged": false
}
},
{
"t": 430.2,
"h1": {
"blocks": 412,
"sink": "cd9991f248c9bf46",
"daa": 411,
"blue": 412,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 412,
"sink": "cd9991f248c9bf46",
"daa": 411,
"blue": 412,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 439,
"sink": "4c1b609fad53a391",
"blue": 439,
"reorged": false
}
},
{
"t": 435.2,
"h1": {
"blocks": 417,
"sink": "b2bb0dc00c197917",
"daa": 416,
"blue": 417,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 417,
"sink": "b2bb0dc00c197917",
"daa": 416,
"blue": 417,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 444,
"sink": "e382cbd3ac4dcf26",
"blue": 444,
"reorged": false
}
},
{
"t": 440.2,
"h1": {
"blocks": 422,
"sink": "b711c4c23605870d",
"daa": 421,
"blue": 422,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 422,
"sink": "b711c4c23605870d",
"daa": 421,
"blue": 422,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 446,
"sink": "c35dab954cd4acf4",
"blue": 446,
"reorged": false
}
},
{
"t": 445.2,
"h1": {
"blocks": 428,
"sink": "f99b9d3dbc93d6c8",
"daa": 427,
"blue": 428,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 428,
"sink": "f99b9d3dbc93d6c8",
"daa": 427,
"blue": 428,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 449,
"sink": "d92f53e2d140db35",
"blue": 449,
"reorged": false
}
},
{
"t": 450.3,
"h1": {
"blocks": 431,
"sink": "1e9a17dfc27a2670",
"daa": 430,
"blue": 431,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 431,
"sink": "1e9a17dfc27a2670",
"daa": 430,
"blue": 431,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 456,
"sink": "162c5de6d4f74ea9",
"blue": 456,
"reorged": false
}
},
{
"t": 455.3,
"h1": {
"blocks": 438,
"sink": "65be332f457b64e0",
"daa": 437,
"blue": 438,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 438,
"sink": "65be332f457b64e0",
"daa": 437,
"blue": 438,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 461,
"sink": "6b7998086b33a85d",
"blue": 461,
"reorged": false
}
},
{
"t": 460.3,
"h1": {
"blocks": 446,
"sink": "5ee05b91ff342422",
"daa": 445,
"blue": 446,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 446,
"sink": "5ee05b91ff342422",
"daa": 445,
"blue": 446,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 468,
"sink": "2ef3594b2514a766",
"blue": 468,
"reorged": false
}
},
{
"t": 465.3,
"h1": {
"blocks": 451,
"sink": "faad0b35cf941017",
"daa": 450,
"blue": 451,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 451,
"sink": "faad0b35cf941017",
"daa": 450,
"blue": 451,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 471,
"sink": "e57eb146c13fa041",
"blue": 471,
"reorged": false
}
},
{
"t": 470.3,
"h1": {
"blocks": 454,
"sink": "b5f88fdf5097139b",
"daa": 453,
"blue": 454,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 454,
"sink": "b5f88fdf5097139b",
"daa": 453,
"blue": 454,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 479,
"sink": "bac0a7b389fb2418",
"blue": 479,
"reorged": false
}
},
{
"t": 475.3,
"h1": {
"blocks": 463,
"sink": "e93acb17237cc9b3",
"daa": 462,
"blue": 463,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 463,
"sink": "e93acb17237cc9b3",
"daa": 462,
"blue": 463,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 481,
"sink": "7a533b847de36a0c",
"blue": 481,
"reorged": false
}
},
{
"t": 480.3,
"h1": {
"blocks": 467,
"sink": "ea2b7270dba4deba",
"daa": 466,
"blue": 467,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 467,
"sink": "ea2b7270dba4deba",
"daa": 466,
"blue": 467,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 485,
"sink": "5f36be5a27dc4f1f",
"blue": 485,
"reorged": false
}
},
{
"t": 485.3,
"h1": {
"blocks": 470,
"sink": "caab7c55e1185c44",
"daa": 469,
"blue": 470,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 470,
"sink": "caab7c55e1185c44",
"daa": 469,
"blue": 470,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 490,
"sink": "9b058d11e4a8a864",
"blue": 490,
"reorged": false
}
},
{
"t": 490.4,
"h1": {
"blocks": 472,
"sink": "19057874449c22ea",
"daa": 471,
"blue": 472,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 472,
"sink": "19057874449c22ea",
"daa": 471,
"blue": 472,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 493,
"sink": "422c79a9ec4780dd",
"blue": 493,
"reorged": false
}
},
{
"t": 495.4,
"h1": {
"blocks": 475,
"sink": "b6f0161d81faa3e9",
"daa": 474,
"blue": 475,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 475,
"sink": "b6f0161d81faa3e9",
"daa": 474,
"blue": 475,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 497,
"sink": "33c53267da1c7bd5",
"blue": 497,
"reorged": false
}
},
{
"t": 500.4,
"h1": {
"blocks": 482,
"sink": "6e821ff35e24b700",
"daa": 481,
"blue": 482,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 482,
"sink": "6e821ff35e24b700",
"daa": 481,
"blue": 482,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 501,
"sink": "6536fa666794da92",
"blue": 501,
"reorged": false
}
},
{
"t": 505.4,
"h1": {
"blocks": 486,
"sink": "bee5af11ed677228",
"daa": 485,
"blue": 486,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 486,
"sink": "bee5af11ed677228",
"daa": 485,
"blue": 486,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 510,
"sink": "be1419603638e1fb",
"blue": 510,
"reorged": false
}
},
{
"t": 510.4,
"h1": {
"blocks": 491,
"sink": "ed71ec00678a9e10",
"daa": 490,
"blue": 491,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 491,
"sink": "ed71ec00678a9e10",
"daa": 490,
"blue": 491,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 515,
"sink": "7b32f850c803ef02",
"blue": 515,
"reorged": false
}
},
{
"t": 515.4,
"h1": {
"blocks": 499,
"sink": "ed904ccbc28c9980",
"daa": 498,
"blue": 499,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 499,
"sink": "ed904ccbc28c9980",
"daa": 498,
"blue": 499,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 519,
"sink": "a26ebf100dcba0c2",
"blue": 519,
"reorged": false
}
},
{
"t": 520.4,
"h1": {
"blocks": 503,
"sink": "462b0c2c35c67c54",
"daa": 502,
"blue": 503,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 503,
"sink": "462b0c2c35c67c54",
"daa": 502,
"blue": 503,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 526,
"sink": "aaaebc1389fe11fd",
"blue": 526,
"reorged": false
}
},
{
"t": 525.4,
"h1": {
"blocks": 504,
"sink": "87f25e6a386a8165",
"daa": 503,
"blue": 504,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 504,
"sink": "87f25e6a386a8165",
"daa": 503,
"blue": 504,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 529,
"sink": "c096bda65c99e6f8",
"blue": 529,
"reorged": false
}
},
{
"t": 530.4,
"h1": {
"blocks": 510,
"sink": "6c5d1398a7445010",
"daa": 509,
"blue": 510,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 510,
"sink": "6c5d1398a7445010",
"daa": 509,
"blue": 510,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 533,
"sink": "3341a65b2b284f17",
"blue": 533,
"reorged": false
}
},
{
"t": 535.4,
"h1": {
"blocks": 514,
"sink": "0d661c0b8cc34ab8",
"daa": 513,
"blue": 514,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 514,
"sink": "0d661c0b8cc34ab8",
"daa": 513,
"blue": 514,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 542,
"sink": "7c3a8038df112ee7",
"blue": 542,
"reorged": false
}
},
{
"t": 540.4,
"h1": {
"blocks": 525,
"sink": "f2b83fc149654b8d",
"daa": 524,
"blue": 525,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 525,
"sink": "f2b83fc149654b8d",
"daa": 524,
"blue": 525,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 545,
"sink": "36e6ba6e49e7c6f2",
"blue": 545,
"reorged": false
}
},
{
"t": 545.5,
"h1": {
"blocks": 533,
"sink": "8f1d9ad77edf47d0",
"daa": 532,
"blue": 533,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 533,
"sink": "8f1d9ad77edf47d0",
"daa": 532,
"blue": 533,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 554,
"sink": "857f2b7d99ddcba6",
"blue": 554,
"reorged": false
}
},
{
"t": 550.5,
"h1": {
"blocks": 537,
"sink": "4cd35471c65af8ff",
"daa": 536,
"blue": 537,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 537,
"sink": "4cd35471c65af8ff",
"daa": 536,
"blue": 537,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 560,
"sink": "5ed4bc501713930a",
"blue": 560,
"reorged": false
}
},
{
"t": 555.5,
"h1": {
"blocks": 544,
"sink": "1174f796fda59e3e",
"daa": 543,
"blue": 544,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 544,
"sink": "1174f796fda59e3e",
"daa": 543,
"blue": 544,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 565,
"sink": "f7d136350536fa6b",
"blue": 565,
"reorged": false
}
},
{
"t": 560.5,
"h1": {
"blocks": 548,
"sink": "f6ed678d1b0a96f8",
"daa": 547,
"blue": 548,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 548,
"sink": "f6ed678d1b0a96f8",
"daa": 547,
"blue": 548,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 568,
"sink": "37ac0328bf8f53e4",
"blue": 568,
"reorged": false
}
},
{
"t": 565.5,
"h1": {
"blocks": 556,
"sink": "86f4c28c6ba82275",
"daa": 555,
"blue": 556,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 556,
"sink": "86f4c28c6ba82275",
"daa": 555,
"blue": 556,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 572,
"sink": "9d9ec576b2742464",
"blue": 572,
"reorged": false
}
},
{
"t": 570.5,
"h1": {
"blocks": 560,
"sink": "3210de1f85cbfc76",
"daa": 559,
"blue": 560,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"h2": {
"blocks": 560,
"sink": "3210de1f85cbfc76",
"daa": 559,
"blue": 560,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": true
},
"side2": {
"blocks": 574,
"sink": "3496b576346db4bb",
"blue": 574,
"reorged": false
}
}
],
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 2,
"ports": {
"base": 30770,
"suffix": 982
}
}

View file

@ -0,0 +1,16 @@
12:48:26.709 fg2 case attack-gate-on-expect-hold: mode attack, gate on (window 60 DAA), joint 240 s, split 180 s, watch 150 s, honest 1 thread(s) each, attacker 3 (joint 0), expect hold
12:48:28.273 fg2 h1 up pid 480230 json 30772 p2p 30771
12:48:29.784 fg2 h2 up pid 481529 json 30782 p2p 30781 addpeer 30771
12:48:31.299 fg2 b up pid 482751 json 30792 p2p 30791 addpeer 30801,30802
12:52:34.314 fg2 phase 1 done at 240.0 s: H1 234 blocks sink 7ed68743 daa 233; H2 234 sink 7ed68743; side 2 (b) 234 sink 7ed68743; keys H1 8da9ddee H2 eda9cde4 side2 fresh (none yet)
12:52:34.371 fg2 weight at the fork (last 121 blue blocks of H1's chain): side 2 0, others 121, side 2 0 bps
12:52:34.375 fg2 links cut at 240.1 s; side 2 (b) now mines with 3 thread(s), the honest side with 1 each
12:55:34.483 fg2 phase 2 done at 420.2 s: H1 396 blocks sink 036cfe56 daa 395 blue 396 work 14807915; side 2 431 sink abb93c23 daa 430 blue 431 work 20297242 (side 2 heavier: true); fork depth 197 DAA against window 60
12:55:34.484 fg2 links restored at 420.2 s; watching H1 and H2 for 150 s
12:56:04.568 fg2 t=450.3 s H1 431 blocks sink 1e9a17df held; H2 431 blocks sink 1e9a17df held; side 2 456 blocks sink 162c5de6
12:56:34.640 fg2 t=480.3 s H1 467 blocks sink ea2b7270 held; H2 467 blocks sink ea2b7270 held; side 2 485 blocks sink 5f36be5a
12:57:04.717 fg2 t=510.4 s H1 491 blocks sink ed71ec00 held; H2 491 blocks sink ed71ec00 held; side 2 515 blocks sink 7b32f850
12:57:34.757 fg2 t=540.5 s H1 525 blocks sink f2b83fc1 held; H2 525 blocks sink f2b83fc1 held; side 2 545 blocks sink 36e6ba6e
12:58:04.809 fg2 t=570.5 s H1 560 blocks sink 3210de1f held; H2 560 blocks sink 3210de1f held; side 2 574 blocks sink 3496b576
12:58:04.822 fg2 SUMMARY PASS (attack-gate-on-expect-hold): side 2 held 0 bps of the blue blocks at the fork; fork depth 197 DAA against window 60; side 2 blue work 20297242 against H1 14807915 at the heal; H1 held (177 refusal lines, knows side 2's tip: true); H2 held (177 refusal lines, knows side 2's tip: true); side 2 kept its chain: true
12:58:04.822 fg2 summary: /srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-attack-gate-on-expect-hold.json

View file

@ -0,0 +1,653 @@
{
"pass": true,
"expect": "reorg",
"case": "partition-gate-off-expect-reorg",
"mode": "partition",
"gate": "off",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 1
},
"keys": {
"h1": "8da9ddee…",
"h2": null,
"side2": "eda9cde4…"
},
"share_at_fork": {
"side2": 67,
"others": 53,
"side2_bps": 5583
},
"fork_daa": 244,
"fork_depth_daa_at_heal": 191,
"sinks": {
"joint": {
"h1": {
"hash": "58126c351aa6324a992a47fa66cca22369450c9835d312772c87d3e1aeb409a0",
"blocks": 245,
"daa": 244,
"blue": 245,
"blueWork": "9558440",
"key": "8da9ddee…"
},
"h2": {
"hash": "58126c351aa6324a992a47fa66cca22369450c9835d312772c87d3e1aeb409a0",
"blocks": 245,
"daa": 244,
"blue": 245,
"blueWork": "9558440",
"key": "8da9ddee…"
},
"side2": {
"hash": "58126c351aa6324a992a47fa66cca22369450c9835d312772c87d3e1aeb409a0",
"blocks": 245,
"daa": 244,
"blue": 245,
"blueWork": "9558440",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "a27a341c694aacf22b1a78f44d433e4104f58b23533afb73b7ee6fb9aa37afe4",
"blocks": 405,
"daa": 404,
"blue": 405,
"blueWork": "12270631",
"key": "8da9ddee…"
},
"h2": {
"hash": "f9181a06c0db716fe4ed200f6f0046c9cb8fae2eb57dd7da4697a268c434a232",
"blocks": 436,
"daa": 435,
"blue": 436,
"blueWork": "19576159",
"key": "eda9cde4…"
},
"side2": {
"hash": "f9181a06c0db716fe4ed200f6f0046c9cb8fae2eb57dd7da4697a268c434a232",
"blocks": 436,
"daa": 435,
"blue": 436,
"blueWork": "19576159",
"key": "eda9cde4…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 507.4,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": false,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"samples": [
{
"t": 427.2,
"h1": {
"blocks": 411,
"sink": "d783538ac11687a9",
"daa": 410,
"blue": 411,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 439,
"sink": "aa3da0091db61498",
"blue": 439,
"reorged": false
}
},
{
"t": 432.2,
"h1": {
"blocks": 416,
"sink": "5084ccaac5b21e34",
"daa": 415,
"blue": 416,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 448,
"sink": "c98585f0a1f24913",
"blue": 448,
"reorged": false
}
},
{
"t": 437.2,
"h1": {
"blocks": 421,
"sink": "950f8b1c7d4cac21",
"daa": 420,
"blue": 421,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 452,
"sink": "c4ad3925b38e9285",
"blue": 452,
"reorged": false
}
},
{
"t": 442.2,
"h1": {
"blocks": 424,
"sink": "767cbc91083a48b0",
"daa": 423,
"blue": 424,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 455,
"sink": "c91b500a6d256eb3",
"blue": 455,
"reorged": false
}
},
{
"t": 447.3,
"h1": {
"blocks": 429,
"sink": "d36268f977a04b6a",
"daa": 428,
"blue": 429,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 465,
"sink": "c5a91c0c5352b0d6",
"blue": 465,
"reorged": false
}
},
{
"t": 452.3,
"h1": {
"blocks": 438,
"sink": "a7ecf244074b481d",
"daa": 437,
"blue": 438,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 468,
"sink": "cd8cfb427f0c78e4",
"blue": 468,
"reorged": false
}
},
{
"t": 457.3,
"h1": {
"blocks": 441,
"sink": "29f8733c4ec6fa4b",
"daa": 440,
"blue": 441,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 473,
"sink": "277bfdb7682804f9",
"blue": 473,
"reorged": false
}
},
{
"t": 462.3,
"h1": {
"blocks": 451,
"sink": "ae072b1106d34635",
"daa": 450,
"blue": 451,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 476,
"sink": "d0f88a7c220ff170",
"blue": 476,
"reorged": false
}
},
{
"t": 467.3,
"h1": {
"blocks": 458,
"sink": "9fcfa1edabf60dd7",
"daa": 457,
"blue": 458,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 486,
"sink": "9f66b943e3b972b7",
"blue": 486,
"reorged": false
}
},
{
"t": 472.3,
"h1": {
"blocks": 462,
"sink": "8dcef0d603690a6e",
"daa": 461,
"blue": 462,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 493,
"sink": "a70a07e96280ab1e",
"blue": 493,
"reorged": false
}
},
{
"t": 477.3,
"h1": {
"blocks": 464,
"sink": "c6dceb1dfe662639",
"daa": 463,
"blue": 464,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 495,
"sink": "1b6074e848285a01",
"blue": 495,
"reorged": false
}
},
{
"t": 482.4,
"h1": {
"blocks": 468,
"sink": "97b7a7a0a8248207",
"daa": 467,
"blue": 468,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 500,
"sink": "a4045188d0e6fb97",
"blue": 500,
"reorged": false
}
},
{
"t": 487.4,
"h1": {
"blocks": 472,
"sink": "2326f1a0ffea21d8",
"daa": 471,
"blue": 472,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 504,
"sink": "813c0c0f3fe0d62b",
"blue": 504,
"reorged": false
}
},
{
"t": 492.4,
"h1": {
"blocks": 476,
"sink": "2e8644b1907a9120",
"daa": 475,
"blue": 476,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 508,
"sink": "9345acb132973ee6",
"blue": 508,
"reorged": false
}
},
{
"t": 497.4,
"h1": {
"blocks": 481,
"sink": "04a443536671ab8f",
"daa": 480,
"blue": 481,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 512,
"sink": "0db73c40f531667d",
"blue": 512,
"reorged": false
}
},
{
"t": 502.4,
"h1": {
"blocks": 487,
"sink": "9afd673d306dc43c",
"daa": 486,
"blue": 487,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 519,
"sink": "e749191f14932323",
"blue": 519,
"reorged": false
}
},
{
"t": 507.4,
"h1": {
"blocks": 527,
"sink": "afed1d222ea7304d",
"daa": 526,
"blue": 527,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 527,
"sink": "afed1d222ea7304d",
"blue": 527,
"reorged": false
}
},
{
"t": 512.4,
"h1": {
"blocks": 538,
"sink": "54deb8450401f863",
"daa": 537,
"blue": 538,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 538,
"sink": "54deb8450401f863",
"blue": 538,
"reorged": false
}
},
{
"t": 517.4,
"h1": {
"blocks": 543,
"sink": "089737446a988ecf",
"daa": 542,
"blue": 543,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 543,
"sink": "089737446a988ecf",
"blue": 543,
"reorged": false
}
},
{
"t": 522.4,
"h1": {
"blocks": 547,
"sink": "fddba72379711067",
"daa": 546,
"blue": 547,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 547,
"sink": "fddba72379711067",
"blue": 547,
"reorged": false
}
},
{
"t": 527.4,
"h1": {
"blocks": 553,
"sink": "d090e72490be6b7a",
"daa": 552,
"blue": 553,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 553,
"sink": "d090e72490be6b7a",
"blue": 553,
"reorged": false
}
},
{
"t": 532.4,
"h1": {
"blocks": 561,
"sink": "1caf40533f6a2857",
"daa": 560,
"blue": 561,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 561,
"sink": "1caf40533f6a2857",
"blue": 561,
"reorged": false
}
},
{
"t": 537.4,
"h1": {
"blocks": 573,
"sink": "e5b85c33b2b6aa09",
"daa": 572,
"blue": 573,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 573,
"sink": "e5b85c33b2b6aa09",
"blue": 573,
"reorged": false
}
},
{
"t": 542.4,
"h1": {
"blocks": 578,
"sink": "317e329b8505cf1e",
"daa": 577,
"blue": 578,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 578,
"sink": "317e329b8505cf1e",
"blue": 578,
"reorged": false
}
},
{
"t": 547.5,
"h1": {
"blocks": 581,
"sink": "e19cd0330c3cdf25",
"daa": 580,
"blue": 581,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 581,
"sink": "e19cd0330c3cdf25",
"blue": 581,
"reorged": false
}
},
{
"t": 552.5,
"h1": {
"blocks": 586,
"sink": "38f228d7ffc3d0c1",
"daa": 585,
"blue": 586,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 586,
"sink": "38f228d7ffc3d0c1",
"blue": 586,
"reorged": false
}
},
{
"t": 557.5,
"h1": {
"blocks": 592,
"sink": "ae515bf3d13e4865",
"daa": 591,
"blue": 592,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 592,
"sink": "ae515bf3d13e4865",
"blue": 592,
"reorged": false
}
},
{
"t": 562.5,
"h1": {
"blocks": 595,
"sink": "eeade53e9149c05a",
"daa": 594,
"blue": 595,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 595,
"sink": "eeade53e9149c05a",
"blue": 595,
"reorged": false
}
},
{
"t": 567.5,
"h1": {
"blocks": 597,
"sink": "a939e8caebf875d6",
"daa": 596,
"blue": 597,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 597,
"sink": "a939e8caebf875d6",
"blue": 597,
"reorged": false
}
},
{
"t": 572.5,
"h1": {
"blocks": 605,
"sink": "1e585f80dc1050fa",
"daa": 604,
"blue": 605,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 605,
"sink": "1e585f80dc1050fa",
"blue": 605,
"reorged": false
}
}
],
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 5,
"ports": {
"base": 30890,
"suffix": 985
}
}

View file

@ -0,0 +1,16 @@
12:48:26.732 fg5 case partition-gate-off-expect-reorg: mode partition, gate off (window 60 DAA), joint 240 s, split 180 s, watch 150 s, honest 1 thread(s) each, attacker 3 (joint 1), expect reorg
12:48:28.310 fg5 h1 up pid 480280 json 30892 p2p 30891
12:48:29.828 fg5 h2 up pid 481840 json 30902 p2p 30901 addpeer 30921
12:52:32.849 fg5 phase 1 done at 240.0 s: H1 245 blocks sink 58126c35 daa 244; H2 245 sink 58126c35; side 2 (h2) 245 sink 58126c35; keys H1 8da9ddee H2 undefined side2 eda9cde4
12:52:32.909 fg5 weight at the fork (last 120 blue blocks of H1's chain): side 2 67, others 53, side 2 5583 bps
12:52:34.922 fg5 links cut at 242.1 s; side 2 (h2) now mines with 3 thread(s), the honest side with 1 each
12:55:35.025 fg5 phase 2 done at 422.2 s: H1 405 blocks sink a27a341c daa 404 blue 405 work 12270631; side 2 436 sink f9181a06 daa 435 blue 436 work 19576159 (side 2 heavier: true); fork depth 191 DAA against window 60
12:55:35.026 fg5 links restored at 422.2 s; watching H1 for 150 s
12:56:05.117 fg5 t=452.3 s H1 438 blocks sink a7ecf244 held; side 2 468 blocks sink cd8cfb42
12:56:35.193 fg5 t=482.4 s H1 468 blocks sink 97b7a7a0 held; side 2 500 blocks sink a4045188
12:57:00.247 fg5 H1 left its pre-cut chain at 507.4 s: sink afed1d222ea7304d by eda9cde4
12:57:05.255 fg5 t=512.4 s H1 538 blocks sink 54deb845 REORGED; side 2 538 blocks sink 54deb845
12:57:35.285 fg5 t=542.5 s H1 578 blocks sink 317e329b REORGED; side 2 578 blocks sink 317e329b
12:58:05.313 fg5 t=572.5 s H1 605 blocks sink 1e585f80 REORGED; side 2 605 blocks sink 1e585f80
12:58:05.317 fg5 SUMMARY PASS (partition-gate-off-expect-reorg): side 2 held 5583 bps of the blue blocks at the fork; fork depth 191 DAA against window 60; side 2 blue work 19576159 against H1 12270631 at the heal; H1 reorged at 507.4 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true
12:58:05.318 fg5 summary: /srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-partition-gate-off-expect-reorg.json

View file

@ -0,0 +1,653 @@
{
"pass": true,
"expect": "reorg",
"case": "partition-gate-on-expect-reorg",
"mode": "partition",
"gate": "on",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 1
},
"keys": {
"h1": "8da9ddee…",
"h2": null,
"side2": "eda9cde4…"
},
"share_at_fork": {
"side2": 45,
"others": 76,
"side2_bps": 3719
},
"fork_daa": 245,
"fork_depth_daa_at_heal": 194,
"sinks": {
"joint": {
"h1": {
"hash": "5c4cb584dc015892ddff89337412ed0883d9f97b4e791e7af2f7f64764f4f1e8",
"blocks": 246,
"daa": 245,
"blue": 246,
"blueWork": "8723556",
"key": "8da9ddee…"
},
"h2": {
"hash": "5c4cb584dc015892ddff89337412ed0883d9f97b4e791e7af2f7f64764f4f1e8",
"blocks": 246,
"daa": 245,
"blue": 246,
"blueWork": "8723556",
"key": "8da9ddee…"
},
"side2": {
"hash": "5c4cb584dc015892ddff89337412ed0883d9f97b4e791e7af2f7f64764f4f1e8",
"blocks": 246,
"daa": 245,
"blue": 246,
"blueWork": "8723556",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "753507c15705f4ea8ed19bc2697a11c489a052a45f28c64f9ac3d8400fa66647",
"blocks": 412,
"daa": 411,
"blue": 412,
"blueWork": "11490863",
"key": "8da9ddee…"
},
"h2": {
"hash": "32b7c1757fa288eed0a8ab709e49fd86f72be531db19c58e63d0aeca7a34c4f6",
"blocks": 440,
"daa": 439,
"blue": 440,
"blueWork": "18089683",
"key": "eda9cde4…"
},
"side2": {
"hash": "32b7c1757fa288eed0a8ab709e49fd86f72be531db19c58e63d0aeca7a34c4f6",
"blocks": 440,
"daa": 439,
"blue": 440,
"blueWork": "18089683",
"key": "eda9cde4…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 507.4,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": false,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"samples": [
{
"t": 427.2,
"h1": {
"blocks": 420,
"sink": "3c08068c8a93e490",
"daa": 419,
"blue": 420,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 443,
"sink": "5058df4580954747",
"blue": 443,
"reorged": false
}
},
{
"t": 432.2,
"h1": {
"blocks": 424,
"sink": "de896377901e94a8",
"daa": 423,
"blue": 424,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 455,
"sink": "a07d00aee9d3376d",
"blue": 455,
"reorged": false
}
},
{
"t": 437.2,
"h1": {
"blocks": 435,
"sink": "eaff2cb616a46c0e",
"daa": 434,
"blue": 435,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 461,
"sink": "4783198d038613db",
"blue": 461,
"reorged": false
}
},
{
"t": 442.2,
"h1": {
"blocks": 439,
"sink": "103fa3b3ea629ad7",
"daa": 438,
"blue": 439,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 465,
"sink": "91ba868ec4f84eef",
"blue": 465,
"reorged": false
}
},
{
"t": 447.3,
"h1": {
"blocks": 448,
"sink": "8437327aa78561ba",
"daa": 447,
"blue": 448,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 474,
"sink": "bb9ac90349a213e3",
"blue": 474,
"reorged": false
}
},
{
"t": 452.3,
"h1": {
"blocks": 450,
"sink": "03bd0f5a924e03ee",
"daa": 449,
"blue": 450,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 478,
"sink": "eca80f2c1a2106d5",
"blue": 478,
"reorged": false
}
},
{
"t": 457.3,
"h1": {
"blocks": 461,
"sink": "3fc505f8af71ea5f",
"daa": 460,
"blue": 461,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 481,
"sink": "017216a3976533dd",
"blue": 481,
"reorged": false
}
},
{
"t": 462.3,
"h1": {
"blocks": 463,
"sink": "a1b1d645d944446d",
"daa": 462,
"blue": 463,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 488,
"sink": "3ade2bb97b8e7548",
"blue": 488,
"reorged": false
}
},
{
"t": 467.3,
"h1": {
"blocks": 466,
"sink": "5b046e8c04baa74e",
"daa": 465,
"blue": 466,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 494,
"sink": "179ad98b8bcc4ea5",
"blue": 494,
"reorged": false
}
},
{
"t": 472.3,
"h1": {
"blocks": 470,
"sink": "aaf64f90ccac9c8a",
"daa": 469,
"blue": 470,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 498,
"sink": "1970740c1d9b0f6c",
"blue": 498,
"reorged": false
}
},
{
"t": 477.3,
"h1": {
"blocks": 473,
"sink": "b557fc4c16174c35",
"daa": 472,
"blue": 473,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 506,
"sink": "078df046b3a9953f",
"blue": 506,
"reorged": false
}
},
{
"t": 482.3,
"h1": {
"blocks": 480,
"sink": "7d7d5f6734e2c727",
"daa": 479,
"blue": 480,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 510,
"sink": "9e0f40b6766308e8",
"blue": 510,
"reorged": false
}
},
{
"t": 487.4,
"h1": {
"blocks": 485,
"sink": "6d3ff10f4556a1ca",
"daa": 484,
"blue": 485,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 512,
"sink": "aecf9f8ea6485764",
"blue": 512,
"reorged": false
}
},
{
"t": 492.4,
"h1": {
"blocks": 489,
"sink": "1196cd92a2989963",
"daa": 488,
"blue": 489,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 518,
"sink": "0a3a90068c2d74c0",
"blue": 518,
"reorged": false
}
},
{
"t": 497.4,
"h1": {
"blocks": 494,
"sink": "27ca83564fbe47ce",
"daa": 493,
"blue": 494,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 524,
"sink": "42b2b2dc9f1e4be2",
"blue": 524,
"reorged": false
}
},
{
"t": 502.4,
"h1": {
"blocks": 498,
"sink": "3f2c6e34b36b890e",
"daa": 497,
"blue": 498,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 531,
"sink": "d19320eebc86c66d",
"blue": 531,
"reorged": false
}
},
{
"t": 507.4,
"h1": {
"blocks": 535,
"sink": "126029df2f9caa4a",
"daa": 534,
"blue": 535,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 535,
"sink": "126029df2f9caa4a",
"blue": 535,
"reorged": false
}
},
{
"t": 512.4,
"h1": {
"blocks": 543,
"sink": "533898421902f564",
"daa": 542,
"blue": 543,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 543,
"sink": "533898421902f564",
"blue": 543,
"reorged": false
}
},
{
"t": 517.4,
"h1": {
"blocks": 546,
"sink": "85a43553233cf34b",
"daa": 545,
"blue": 546,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 546,
"sink": "85a43553233cf34b",
"blue": 546,
"reorged": false
}
},
{
"t": 522.4,
"h1": {
"blocks": 554,
"sink": "bed61271f9a8252d",
"daa": 553,
"blue": 554,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 554,
"sink": "bed61271f9a8252d",
"blue": 554,
"reorged": false
}
},
{
"t": 527.4,
"h1": {
"blocks": 560,
"sink": "9e91960a30057229",
"daa": 559,
"blue": 560,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 560,
"sink": "9e91960a30057229",
"blue": 560,
"reorged": false
}
},
{
"t": 532.4,
"h1": {
"blocks": 564,
"sink": "cd6d1240dc30d2e4",
"daa": 563,
"blue": 564,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 564,
"sink": "cd6d1240dc30d2e4",
"blue": 564,
"reorged": false
}
},
{
"t": 537.4,
"h1": {
"blocks": 570,
"sink": "0208b9cbd8adfa78",
"daa": 569,
"blue": 570,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 570,
"sink": "0208b9cbd8adfa78",
"blue": 570,
"reorged": false
}
},
{
"t": 542.4,
"h1": {
"blocks": 573,
"sink": "627692d65f09e879",
"daa": 572,
"blue": 573,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 573,
"sink": "627692d65f09e879",
"blue": 573,
"reorged": false
}
},
{
"t": 547.5,
"h1": {
"blocks": 576,
"sink": "84b34763f034804b",
"daa": 575,
"blue": 576,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 576,
"sink": "84b34763f034804b",
"blue": 576,
"reorged": false
}
},
{
"t": 552.5,
"h1": {
"blocks": 580,
"sink": "4ce7dce8da22296a",
"daa": 579,
"blue": 580,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 580,
"sink": "4ce7dce8da22296a",
"blue": 580,
"reorged": false
}
},
{
"t": 557.5,
"h1": {
"blocks": 585,
"sink": "f8c7fb1089532dde",
"daa": 584,
"blue": 585,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 585,
"sink": "f8c7fb1089532dde",
"blue": 585,
"reorged": false
}
},
{
"t": 562.5,
"h1": {
"blocks": 590,
"sink": "07d408946584e422",
"daa": 589,
"blue": 590,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 590,
"sink": "07d408946584e422",
"blue": 590,
"reorged": false
}
},
{
"t": 567.5,
"h1": {
"blocks": 598,
"sink": "3ffc006b66a221b9",
"daa": 597,
"blue": 598,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 598,
"sink": "3ffc006b66a221b9",
"blue": 598,
"reorged": false
}
},
{
"t": 572.5,
"h1": {
"blocks": 602,
"sink": "b3827232c44b06d2",
"daa": 601,
"blue": 602,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 602,
"sink": "b3827232c44b06d2",
"blue": 602,
"reorged": false
}
}
],
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 4,
"ports": {
"base": 30850,
"suffix": 984
}
}

View file

@ -0,0 +1,16 @@
12:48:26.723 fg4 case partition-gate-on-expect-reorg: mode partition, gate on (window 60 DAA), joint 240 s, split 180 s, watch 150 s, honest 1 thread(s) each, attacker 3 (joint 1), expect reorg
12:48:28.322 fg4 h1 up pid 480248 json 30852 p2p 30851
12:48:29.844 fg4 h2 up pid 481994 json 30862 p2p 30861 addpeer 30881
12:52:32.858 fg4 phase 1 done at 240.0 s: H1 246 blocks sink 5c4cb584 daa 245; H2 246 sink 5c4cb584; side 2 (h2) 246 sink 5c4cb584; keys H1 8da9ddee H2 undefined side2 eda9cde4
12:52:32.918 fg4 weight at the fork (last 121 blue blocks of H1's chain): side 2 45, others 76, side 2 3719 bps
12:52:34.929 fg4 links cut at 242.1 s; side 2 (h2) now mines with 3 thread(s), the honest side with 1 each
12:55:35.033 fg4 phase 2 done at 422.2 s: H1 412 blocks sink 753507c1 daa 411 blue 412 work 11490863; side 2 440 sink 32b7c175 daa 439 blue 440 work 18089683 (side 2 heavier: true); fork depth 194 DAA against window 60
12:55:35.035 fg4 links restored at 422.2 s; watching H1 for 150 s
12:56:05.124 fg4 t=452.3 s H1 450 blocks sink 03bd0f5a held; side 2 478 blocks sink eca80f2c
12:56:35.198 fg4 t=482.4 s H1 480 blocks sink 7d7d5f67 held; side 2 510 blocks sink 9e0f40b6
12:57:00.246 fg4 H1 left its pre-cut chain at 507.4 s: sink 126029df2f9caa4a by eda9cde4
12:57:05.255 fg4 t=512.4 s H1 543 blocks sink 53389842 REORGED; side 2 543 blocks sink 53389842
12:57:35.298 fg4 t=542.5 s H1 573 blocks sink 627692d6 REORGED; side 2 573 blocks sink 627692d6
12:58:05.330 fg4 t=572.5 s H1 602 blocks sink b3827232 REORGED; side 2 602 blocks sink b3827232
12:58:05.334 fg4 SUMMARY PASS (partition-gate-on-expect-reorg): side 2 held 3719 bps of the blue blocks at the fork; fork depth 194 DAA against window 60; side 2 blue work 18089683 against H1 11490863 at the heal; H1 reorged at 507.4 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true
12:58:05.334 fg4 summary: /srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-partition-gate-on-expect-reorg.json

View file

@ -0,0 +1,930 @@
{
"pass": true,
"expect": "reorg",
"case": "third-gate-on-expect-reorg",
"mode": "third",
"gate": "on",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 2
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 72,
"others": 48,
"side2_bps": 6000
},
"fork_daa": 268,
"fork_depth_daa_at_heal": 166,
"sinks": {
"joint": {
"h1": {
"hash": "037edd2a2976af7aec3f6b00d0532706f2c4bc48a2ace2d4e92f027492aa42af",
"blocks": 269,
"daa": 268,
"blue": 269,
"blueWork": "17483286",
"key": "0e2c6fe2…"
},
"h2": {
"hash": "037edd2a2976af7aec3f6b00d0532706f2c4bc48a2ace2d4e92f027492aa42af",
"blocks": 269,
"daa": 268,
"blue": 269,
"blueWork": "17483286",
"key": "0e2c6fe2…"
},
"side2": {
"hash": "037edd2a2976af7aec3f6b00d0532706f2c4bc48a2ace2d4e92f027492aa42af",
"blocks": 269,
"daa": 268,
"blue": 269,
"blueWork": "17483286",
"key": "0e2c6fe2…"
}
},
"split": {
"h1": {
"hash": "06f666ba3c449f2fab804e75612a00c748a8d56568806c30a7006aae175bb27d",
"blocks": 426,
"daa": 425,
"blue": 426,
"blueWork": "24628833",
"key": "8da9ddee…"
},
"h2": {
"hash": "06f666ba3c449f2fab804e75612a00c748a8d56568806c30a7006aae175bb27d",
"blocks": 426,
"daa": 425,
"blue": 426,
"blueWork": "24628833",
"key": "8da9ddee…"
},
"side2": {
"hash": "71396cf73521295ad0fbeb320de980989ea0788dbc859129d8972a9e091e0a3c",
"blocks": 435,
"daa": 434,
"blue": 435,
"blueWork": "28969222",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 477.3,
"knows_side2_tip": true,
"refusal_lines": 0
},
"h2": {
"held": false,
"reorged": true,
"reorg_at_s": 477.3,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": false,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"samples": [
{
"t": 427.2,
"h1": {
"blocks": 431,
"sink": "9a61020b8d49143a",
"daa": 430,
"blue": 431,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 431,
"sink": "9a61020b8d49143a",
"daa": 430,
"blue": 431,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 441,
"sink": "05e6f9d4ef677d31",
"blue": 441,
"reorged": false
}
},
{
"t": 432.2,
"h1": {
"blocks": 439,
"sink": "a4130e66a3d4a3df",
"daa": 438,
"blue": 439,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 439,
"sink": "a4130e66a3d4a3df",
"daa": 438,
"blue": 439,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 449,
"sink": "f7d08aee6cf90b41",
"blue": 449,
"reorged": false
}
},
{
"t": 437.2,
"h1": {
"blocks": 442,
"sink": "e1cc039c584559d7",
"daa": 441,
"blue": 442,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 442,
"sink": "e1cc039c584559d7",
"daa": 441,
"blue": 442,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 455,
"sink": "fa56a0ac4e5fb99c",
"blue": 455,
"reorged": false
}
},
{
"t": 442.2,
"h1": {
"blocks": 444,
"sink": "6904c2a75c7344fe",
"daa": 443,
"blue": 444,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 444,
"sink": "6904c2a75c7344fe",
"daa": 443,
"blue": 444,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 459,
"sink": "04b09f8a190c4051",
"blue": 459,
"reorged": false
}
},
{
"t": 447.2,
"h1": {
"blocks": 449,
"sink": "d55f8f3f03247788",
"daa": 448,
"blue": 449,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 449,
"sink": "d55f8f3f03247788",
"daa": 448,
"blue": 449,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 462,
"sink": "a6fa74e0833a3e0c",
"blue": 462,
"reorged": false
}
},
{
"t": 452.2,
"h1": {
"blocks": 454,
"sink": "feb2905b292ab9c7",
"daa": 453,
"blue": 454,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 454,
"sink": "feb2905b292ab9c7",
"daa": 453,
"blue": 454,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 464,
"sink": "093014496d558f65",
"blue": 464,
"reorged": false
}
},
{
"t": 457.3,
"h1": {
"blocks": 462,
"sink": "b10ed98591f50ec6",
"daa": 461,
"blue": 462,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 462,
"sink": "b10ed98591f50ec6",
"daa": 461,
"blue": 462,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 469,
"sink": "7a00df10d8b78e18",
"blue": 469,
"reorged": false
}
},
{
"t": 462.3,
"h1": {
"blocks": 467,
"sink": "ebf8c4bd560f2ccf",
"daa": 466,
"blue": 467,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 467,
"sink": "ebf8c4bd560f2ccf",
"daa": 466,
"blue": 467,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 474,
"sink": "ade976380c06a8d8",
"blue": 474,
"reorged": false
}
},
{
"t": 467.3,
"h1": {
"blocks": 471,
"sink": "311bf74ecfb1cb33",
"daa": 470,
"blue": 471,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 471,
"sink": "311bf74ecfb1cb33",
"daa": 470,
"blue": 471,
"key": "8da9ddee",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 482,
"sink": "a1dc4abe41431122",
"blue": 482,
"reorged": false
}
},
{
"t": 472.3,
"h1": {
"blocks": 476,
"sink": "0fc8726976801d2b",
"daa": 475,
"blue": 476,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"h2": {
"blocks": 476,
"sink": "0fc8726976801d2b",
"daa": 475,
"blue": 476,
"key": "eda9cde4",
"reorged": false,
"knows_side2_tip": false
},
"side2": {
"blocks": 487,
"sink": "c2c737c0d473f009",
"blue": 487,
"reorged": false
}
},
{
"t": 477.3,
"h1": {
"blocks": 492,
"sink": "df2ecca49796b69c",
"daa": 491,
"blue": 492,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 492,
"sink": "df2ecca49796b69c",
"daa": 491,
"blue": 492,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 492,
"sink": "df2ecca49796b69c",
"blue": 492,
"reorged": false
}
},
{
"t": 482.3,
"h1": {
"blocks": 498,
"sink": "33ab64dbbd41433e",
"daa": 497,
"blue": 498,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 498,
"sink": "33ab64dbbd41433e",
"daa": 497,
"blue": 498,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 498,
"sink": "33ab64dbbd41433e",
"blue": 498,
"reorged": false
}
},
{
"t": 487.3,
"h1": {
"blocks": 506,
"sink": "4fa4f31a124990c1",
"daa": 505,
"blue": 506,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 506,
"sink": "4fa4f31a124990c1",
"daa": 505,
"blue": 506,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 506,
"sink": "4fa4f31a124990c1",
"blue": 506,
"reorged": false
}
},
{
"t": 492.3,
"h1": {
"blocks": 510,
"sink": "4121ec5f7e04b3a2",
"daa": 509,
"blue": 510,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 510,
"sink": "4121ec5f7e04b3a2",
"daa": 509,
"blue": 510,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 510,
"sink": "4121ec5f7e04b3a2",
"blue": 510,
"reorged": false
}
},
{
"t": 497.3,
"h1": {
"blocks": 518,
"sink": "217d584ac3d48f32",
"daa": 517,
"blue": 518,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 518,
"sink": "217d584ac3d48f32",
"daa": 517,
"blue": 518,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 518,
"sink": "217d584ac3d48f32",
"blue": 518,
"reorged": false
}
},
{
"t": 502.3,
"h1": {
"blocks": 527,
"sink": "d14ed2e5f11c7df7",
"daa": 526,
"blue": 527,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 527,
"sink": "d14ed2e5f11c7df7",
"daa": 526,
"blue": 527,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 527,
"sink": "d14ed2e5f11c7df7",
"blue": 527,
"reorged": false
}
},
{
"t": 507.3,
"h1": {
"blocks": 533,
"sink": "23cf4405d058110e",
"daa": 532,
"blue": 533,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 533,
"sink": "23cf4405d058110e",
"daa": 532,
"blue": 533,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 533,
"sink": "23cf4405d058110e",
"blue": 533,
"reorged": false
}
},
{
"t": 512.4,
"h1": {
"blocks": 537,
"sink": "ec6ff75c8617a6c0",
"daa": 535,
"blue": 536,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 537,
"sink": "ec6ff75c8617a6c0",
"daa": 535,
"blue": 536,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 537,
"sink": "ec6ff75c8617a6c0",
"blue": 536,
"reorged": false
}
},
{
"t": 517.4,
"h1": {
"blocks": 542,
"sink": "53152f1d8342a7ba",
"daa": 541,
"blue": 542,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 542,
"sink": "53152f1d8342a7ba",
"daa": 541,
"blue": 542,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 542,
"sink": "53152f1d8342a7ba",
"blue": 542,
"reorged": false
}
},
{
"t": 522.4,
"h1": {
"blocks": 548,
"sink": "1425827f8f967320",
"daa": 547,
"blue": 548,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 548,
"sink": "1425827f8f967320",
"daa": 547,
"blue": 548,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 548,
"sink": "1425827f8f967320",
"blue": 548,
"reorged": false
}
},
{
"t": 527.4,
"h1": {
"blocks": 554,
"sink": "12bc21c9e6549a42",
"daa": 553,
"blue": 554,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 554,
"sink": "12bc21c9e6549a42",
"daa": 553,
"blue": 554,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 554,
"sink": "12bc21c9e6549a42",
"blue": 554,
"reorged": false
}
},
{
"t": 532.4,
"h1": {
"blocks": 559,
"sink": "b17be188847a071b",
"daa": 558,
"blue": 559,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 559,
"sink": "b17be188847a071b",
"daa": 558,
"blue": 559,
"key": "8da9ddee",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 559,
"sink": "b17be188847a071b",
"blue": 559,
"reorged": false
}
},
{
"t": 537.4,
"h1": {
"blocks": 564,
"sink": "bcf3066ae7329fb1",
"daa": 563,
"blue": 564,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 564,
"sink": "bcf3066ae7329fb1",
"daa": 563,
"blue": 564,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 564,
"sink": "bcf3066ae7329fb1",
"blue": 564,
"reorged": false
}
},
{
"t": 542.4,
"h1": {
"blocks": 571,
"sink": "3c8486f382c004a4",
"daa": 570,
"blue": 571,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 571,
"sink": "3c8486f382c004a4",
"daa": 570,
"blue": 571,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 571,
"sink": "3c8486f382c004a4",
"blue": 571,
"reorged": false
}
},
{
"t": 547.4,
"h1": {
"blocks": 573,
"sink": "55eb5bc461792e81",
"daa": 572,
"blue": 573,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 573,
"sink": "55eb5bc461792e81",
"daa": 572,
"blue": 573,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 573,
"sink": "55eb5bc461792e81",
"blue": 573,
"reorged": false
}
},
{
"t": 552.4,
"h1": {
"blocks": 577,
"sink": "c5812db6a85bdadb",
"daa": 576,
"blue": 577,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 577,
"sink": "c5812db6a85bdadb",
"daa": 576,
"blue": 577,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 577,
"sink": "c5812db6a85bdadb",
"blue": 577,
"reorged": false
}
},
{
"t": 557.4,
"h1": {
"blocks": 579,
"sink": "a43cb662dec21b79",
"daa": 578,
"blue": 579,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 579,
"sink": "a43cb662dec21b79",
"daa": 578,
"blue": 579,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 579,
"sink": "a43cb662dec21b79",
"blue": 579,
"reorged": false
}
},
{
"t": 562.4,
"h1": {
"blocks": 586,
"sink": "a5490f8482cfcac1",
"daa": 585,
"blue": 586,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 586,
"sink": "a5490f8482cfcac1",
"daa": 585,
"blue": 586,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 586,
"sink": "a5490f8482cfcac1",
"blue": 586,
"reorged": false
}
},
{
"t": 567.5,
"h1": {
"blocks": 590,
"sink": "85ff280e3c193276",
"daa": 589,
"blue": 590,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 590,
"sink": "85ff280e3c193276",
"daa": 589,
"blue": 590,
"key": "eda9cde4",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 590,
"sink": "85ff280e3c193276",
"blue": 590,
"reorged": false
}
},
{
"t": 572.5,
"h1": {
"blocks": 591,
"sink": "eb3dbbc60e30ebba",
"daa": 590,
"blue": 591,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"h2": {
"blocks": 591,
"sink": "eb3dbbc60e30ebba",
"daa": 590,
"blue": 591,
"key": "0e2c6fe2",
"reorged": true,
"knows_side2_tip": true
},
"side2": {
"blocks": 591,
"sink": "eb3dbbc60e30ebba",
"blue": 591,
"reorged": false
}
}
],
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 3,
"ports": {
"base": 30810,
"suffix": 983
}
}

View file

@ -0,0 +1,18 @@
12:48:26.802 fg3 case third-gate-on-expect-reorg: mode third, gate on (window 60 DAA), joint 240 s, split 180 s, watch 150 s, honest 1 thread(s) each, attacker 3 (joint 2), expect reorg
12:48:28.386 fg3 h1 up pid 480989 json 30812 p2p 30811
12:48:29.896 fg3 h2 up pid 482351 json 30822 p2p 30821 addpeer 30811
12:48:31.410 fg3 b up pid 483327 json 30832 p2p 30831 addpeer 30841,30842
12:52:34.426 fg3 phase 1 done at 240.0 s: H1 269 blocks sink 037edd2a daa 268; H2 269 sink 037edd2a; side 2 (b) 269 sink 037edd2a; keys H1 8da9ddee H2 eda9cde4 side2 0e2c6fe2
12:52:34.451 fg3 weight at the fork (last 120 blue blocks of H1's chain): side 2 72, others 48, side 2 6000 bps
12:52:36.465 fg3 links cut at 242.1 s; side 2 (b) now mines with 3 thread(s), the honest side with 1 each
12:55:36.572 fg3 phase 2 done at 422.2 s: H1 426 blocks sink 06f666ba daa 425 blue 426 work 24628833; side 2 435 sink 71396cf7 daa 434 blue 435 work 28969222 (side 2 heavier: true); fork depth 166 DAA against window 60
12:55:36.573 fg3 links restored at 422.2 s; watching H1 and H2 for 150 s
12:56:06.662 fg3 t=452.2 s H1 454 blocks sink feb2905b held; H2 454 blocks sink feb2905b held; side 2 464 blocks sink 09301449
12:56:31.716 fg3 H1 left its pre-cut chain at 477.3 s: sink df2ecca49796b69c by 0e2c6fe2
12:56:31.720 fg3 H2 left its pre-cut chain at 477.3 s: sink df2ecca49796b69c by 0e2c6fe2
12:56:36.729 fg3 t=482.3 s H1 498 blocks sink 33ab64db REORGED; H2 498 blocks sink 33ab64db REORGED; side 2 498 blocks sink 33ab64db
12:57:06.776 fg3 t=512.4 s H1 537 blocks sink ec6ff75c REORGED; H2 537 blocks sink ec6ff75c REORGED; side 2 537 blocks sink ec6ff75c
12:57:36.826 fg3 t=542.4 s H1 571 blocks sink 3c8486f3 REORGED; H2 571 blocks sink 3c8486f3 REORGED; side 2 571 blocks sink 3c8486f3
12:58:06.880 fg3 t=572.5 s H1 591 blocks sink eb3dbbc6 REORGED; H2 591 blocks sink eb3dbbc6 REORGED; side 2 591 blocks sink eb3dbbc6
12:58:06.890 fg3 SUMMARY PASS (third-gate-on-expect-reorg): side 2 held 6000 bps of the blue blocks at the fork; fork depth 166 DAA against window 60; side 2 blue work 28969222 against H1 24628833 at the heal; H1 reorged at 477.3 s (0 refusal lines, knows side 2's tip: true); H2 reorged at 477.3 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true
12:58:06.890 fg3 summary: /srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-third-gate-on-expect-reorg.json

View file

@ -0,0 +1,796 @@
{
"green": true,
"at": "2026-10-07T12:58:08.407Z",
"heal_unchanged": true,
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"cases": [
{
"mode": "attack",
"gate": "off",
"expect": "reorg",
"must": "PASS",
"line": "the rule's known-failed case: without the gate a fresh key's heavier deep fork wins on every honest node",
"slot": 0,
"name": "attack-gate-off-expect-reorg",
"verdict": "PASS",
"as_it_must": true,
"secs": 581,
"exit": 0,
"summary_line": "SUMMARY PASS (attack-gate-off-expect-reorg): side 2 held 0 bps of the blue blocks at the fork; fork depth 191 DAA against window 60; side 2 blue work 17470131 against H1 14893198 at the heal; H1 reorged at 510.4 s (0 refusal lines, knows side 2's tip: true); H2 reorged at 510.4 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true",
"summary": {
"pass": true,
"expect": "reorg",
"case": "attack-gate-off-expect-reorg",
"mode": "attack",
"gate": "off",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 0
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 0,
"others": 120,
"side2_bps": 0
},
"fork_daa": 240,
"fork_depth_daa_at_heal": 191,
"sinks": {
"joint": {
"h1": {
"hash": "e66998c63f8f5d2f3f9108a5c61730f3d5ee06a0553f47e3bce314a0e4d7228e",
"blocks": 242,
"daa": 240,
"blue": 241,
"blueWork": "8689541",
"key": "8da9ddee…"
},
"h2": {
"hash": "e66998c63f8f5d2f3f9108a5c61730f3d5ee06a0553f47e3bce314a0e4d7228e",
"blocks": 242,
"daa": 240,
"blue": 241,
"blueWork": "8689541",
"key": "8da9ddee…"
},
"side2": {
"hash": "e66998c63f8f5d2f3f9108a5c61730f3d5ee06a0553f47e3bce314a0e4d7228e",
"blocks": 242,
"daa": 240,
"blue": 241,
"blueWork": "8689541",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "6e932c389f2ad01a39873317f9549f0a336e66a7c840669dd5bcb44a979a19ad",
"blocks": 421,
"daa": 420,
"blue": 421,
"blueWork": "14893198",
"key": "8da9ddee…"
},
"h2": {
"hash": "6e932c389f2ad01a39873317f9549f0a336e66a7c840669dd5bcb44a979a19ad",
"blocks": 421,
"daa": 420,
"blue": 421,
"blueWork": "14893198",
"key": "8da9ddee…"
},
"side2": {
"hash": "d93a1238a397f68337f4bc28d6197a61a32fbca176f3e7f4248b6b330e239cc8",
"blocks": 432,
"daa": 431,
"blue": 432,
"blueWork": "17470131",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 510.4,
"knows_side2_tip": true,
"refusal_lines": 0
},
"h2": {
"held": false,
"reorged": true,
"reorg_at_s": 510.4,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": true,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 0,
"ports": {
"base": 30690,
"suffix": 980
}
},
"log": "/srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-attack-gate-off-expect-reorg.log"
},
{
"mode": "attack",
"gate": "off",
"expect": "hold",
"must": "FAIL",
"line": "the harness's own failed shape: with the gate off it must not report a hold",
"slot": 1,
"name": "attack-gate-off-expect-hold",
"verdict": "FAIL",
"as_it_must": true,
"secs": 581,
"exit": 1,
"summary_line": "SUMMARY FAIL (attack-gate-off-expect-hold): side 2 held 0 bps of the blue blocks at the fork; fork depth 179 DAA against window 60; side 2 blue work 19683187 against H1 17459678 at the heal; H1 reorged at 480.3 s (0 refusal lines, knows side 2's tip: true); H2 reorged at 480.3 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true; FAILED CHECK every_honest_node_held, every_honest_node_logged_a_refusal",
"summary": {
"pass": false,
"expect": "hold",
"case": "attack-gate-off-expect-hold",
"mode": "attack",
"gate": "off",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 0
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 0,
"others": 120,
"side2_bps": 0
},
"fork_daa": 247,
"fork_depth_daa_at_heal": 179,
"sinks": {
"joint": {
"h1": {
"hash": "0535d8ae67287238ee528bae5ab1f366273bd1c65c6a94342ec3879a40b81c88",
"blocks": 248,
"daa": 247,
"blue": 248,
"blueWork": "11209205",
"key": "8da9ddee…"
},
"h2": {
"hash": "0535d8ae67287238ee528bae5ab1f366273bd1c65c6a94342ec3879a40b81c88",
"blocks": 248,
"daa": 247,
"blue": 248,
"blueWork": "11209205",
"key": "8da9ddee…"
},
"side2": {
"hash": "0535d8ae67287238ee528bae5ab1f366273bd1c65c6a94342ec3879a40b81c88",
"blocks": 248,
"daa": 247,
"blue": 248,
"blueWork": "11209205",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "d5347d2cd3468901fda8e5a1caffe0be55d45eb12739577fadb0141d2291c2ba",
"blocks": 412,
"daa": 411,
"blue": 412,
"blueWork": "17459678",
"key": "8da9ddee…"
},
"h2": {
"hash": "d5347d2cd3468901fda8e5a1caffe0be55d45eb12739577fadb0141d2291c2ba",
"blocks": 412,
"daa": 411,
"blue": 412,
"blueWork": "17459678",
"key": "8da9ddee…"
},
"side2": {
"hash": "bfdb75e384417228a85f4098f8f2bb65d40c271ce674bea2d1cc8a3c6c335f53",
"blocks": 427,
"daa": 426,
"blue": 427,
"blueWork": "19683187",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 480.3,
"knows_side2_tip": true,
"refusal_lines": 0
},
"h2": {
"held": false,
"reorged": true,
"reorg_at_s": 480.3,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": true,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [
"every_honest_node_held",
"every_honest_node_logged_a_refusal"
],
"refusal_example": null,
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 1,
"ports": {
"base": 30730,
"suffix": 981
}
},
"log": "/srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-attack-gate-off-expect-hold.log"
},
{
"mode": "attack",
"gate": "on",
"expect": "hold",
"must": "PASS",
"line": "the gate line: a fresh-key fork from three windows back, heavier by blue work, refused by every honest node",
"slot": 2,
"name": "attack-gate-on-expect-hold",
"verdict": "PASS",
"as_it_must": true,
"secs": 581,
"exit": 0,
"summary_line": "SUMMARY PASS (attack-gate-on-expect-hold): side 2 held 0 bps of the blue blocks at the fork; fork depth 197 DAA against window 60; side 2 blue work 20297242 against H1 14807915 at the heal; H1 held (177 refusal lines, knows side 2's tip: true); H2 held (177 refusal lines, knows side 2's tip: true); side 2 kept its chain: true",
"summary": {
"pass": true,
"expect": "hold",
"case": "attack-gate-on-expect-hold",
"mode": "attack",
"gate": "on",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 0
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 0,
"others": 121,
"side2_bps": 0
},
"fork_daa": 233,
"fork_depth_daa_at_heal": 197,
"sinks": {
"joint": {
"h1": {
"hash": "7ed68743f22e711490406bd380cf66fff94579fcc981344c317a46c18cbc61bb",
"blocks": 234,
"daa": 233,
"blue": 234,
"blueWork": "8862250",
"key": "eda9cde4…"
},
"h2": {
"hash": "7ed68743f22e711490406bd380cf66fff94579fcc981344c317a46c18cbc61bb",
"blocks": 234,
"daa": 233,
"blue": 234,
"blueWork": "8862250",
"key": "eda9cde4…"
},
"side2": {
"hash": "7ed68743f22e711490406bd380cf66fff94579fcc981344c317a46c18cbc61bb",
"blocks": 234,
"daa": 233,
"blue": 234,
"blueWork": "8862250",
"key": "eda9cde4…"
}
},
"split": {
"h1": {
"hash": "036cfe568f3d403351b338ea41b7069ad4eae0ce95308436a25b6c7aa4c1372a",
"blocks": 396,
"daa": 395,
"blue": 396,
"blueWork": "14807915",
"key": "8da9ddee…"
},
"h2": {
"hash": "036cfe568f3d403351b338ea41b7069ad4eae0ce95308436a25b6c7aa4c1372a",
"blocks": 396,
"daa": 395,
"blue": 396,
"blueWork": "14807915",
"key": "8da9ddee…"
},
"side2": {
"hash": "abb93c2318f6c2ebdbd4e14a50b3d73ba7f483b8b06b18666e1015d038d4011a",
"blocks": 431,
"daa": 430,
"blue": 431,
"blueWork": "20297242",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": true,
"reorged": false,
"reorg_at_s": null,
"knows_side2_tip": true,
"refusal_lines": 177
},
"h2": {
"held": true,
"reorged": false,
"reorg_at_s": null,
"knows_side2_tip": true,
"refusal_lines": 177
}
},
"checks": {
"side2_under_a_third_at_fork": true,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": true,
"every_honest_node_reorged": false,
"every_honest_node_logged_a_refusal": true,
"no_honest_node_logged_a_refusal": false,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": "Fork choice: block 41225a00fb38b6394540a5707443bd70e678484cbd20aa3fa8e807bb24df8d4a forks 252 DAA back from the sink's chain and its builders hold 0.00% of the weight table at the fork (under one third); not a sink candidate (ledger 51-percent rank 2)",
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 2,
"ports": {
"base": 30770,
"suffix": 982
}
},
"log": "/srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-attack-gate-on-expect-hold.log"
},
{
"mode": "third",
"gate": "on",
"expect": "reorg",
"must": "PASS",
"line": "a fork whose builder holds at least a third of the table at the fork is accepted",
"slot": 3,
"name": "third-gate-on-expect-reorg",
"verdict": "PASS",
"as_it_must": true,
"secs": 583,
"exit": 0,
"summary_line": "SUMMARY PASS (third-gate-on-expect-reorg): side 2 held 6000 bps of the blue blocks at the fork; fork depth 166 DAA against window 60; side 2 blue work 28969222 against H1 24628833 at the heal; H1 reorged at 477.3 s (0 refusal lines, knows side 2's tip: true); H2 reorged at 477.3 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true",
"summary": {
"pass": true,
"expect": "reorg",
"case": "third-gate-on-expect-reorg",
"mode": "third",
"gate": "on",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 2
},
"keys": {
"h1": "8da9ddee…",
"h2": "eda9cde4…",
"side2": "0e2c6fe2…"
},
"share_at_fork": {
"side2": 72,
"others": 48,
"side2_bps": 6000
},
"fork_daa": 268,
"fork_depth_daa_at_heal": 166,
"sinks": {
"joint": {
"h1": {
"hash": "037edd2a2976af7aec3f6b00d0532706f2c4bc48a2ace2d4e92f027492aa42af",
"blocks": 269,
"daa": 268,
"blue": 269,
"blueWork": "17483286",
"key": "0e2c6fe2…"
},
"h2": {
"hash": "037edd2a2976af7aec3f6b00d0532706f2c4bc48a2ace2d4e92f027492aa42af",
"blocks": 269,
"daa": 268,
"blue": 269,
"blueWork": "17483286",
"key": "0e2c6fe2…"
},
"side2": {
"hash": "037edd2a2976af7aec3f6b00d0532706f2c4bc48a2ace2d4e92f027492aa42af",
"blocks": 269,
"daa": 268,
"blue": 269,
"blueWork": "17483286",
"key": "0e2c6fe2…"
}
},
"split": {
"h1": {
"hash": "06f666ba3c449f2fab804e75612a00c748a8d56568806c30a7006aae175bb27d",
"blocks": 426,
"daa": 425,
"blue": 426,
"blueWork": "24628833",
"key": "8da9ddee…"
},
"h2": {
"hash": "06f666ba3c449f2fab804e75612a00c748a8d56568806c30a7006aae175bb27d",
"blocks": 426,
"daa": 425,
"blue": 426,
"blueWork": "24628833",
"key": "8da9ddee…"
},
"side2": {
"hash": "71396cf73521295ad0fbeb320de980989ea0788dbc859129d8972a9e091e0a3c",
"blocks": 435,
"daa": 434,
"blue": 435,
"blueWork": "28969222",
"key": "0e2c6fe2…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 477.3,
"knows_side2_tip": true,
"refusal_lines": 0
},
"h2": {
"held": false,
"reorged": true,
"reorg_at_s": 477.3,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": false,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 3,
"ports": {
"base": 30810,
"suffix": 983
}
},
"log": "/srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-third-gate-on-expect-reorg.log"
},
{
"mode": "partition",
"gate": "on",
"expect": "reorg",
"must": "PASS",
"line": "partition heal with the gate on: the heavier side wins",
"slot": 4,
"name": "partition-gate-on-expect-reorg",
"verdict": "PASS",
"as_it_must": true,
"secs": 581,
"exit": 0,
"summary_line": "SUMMARY PASS (partition-gate-on-expect-reorg): side 2 held 3719 bps of the blue blocks at the fork; fork depth 194 DAA against window 60; side 2 blue work 18089683 against H1 11490863 at the heal; H1 reorged at 507.4 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true",
"summary": {
"pass": true,
"expect": "reorg",
"case": "partition-gate-on-expect-reorg",
"mode": "partition",
"gate": "on",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 1
},
"keys": {
"h1": "8da9ddee…",
"h2": null,
"side2": "eda9cde4…"
},
"share_at_fork": {
"side2": 45,
"others": 76,
"side2_bps": 3719
},
"fork_daa": 245,
"fork_depth_daa_at_heal": 194,
"sinks": {
"joint": {
"h1": {
"hash": "5c4cb584dc015892ddff89337412ed0883d9f97b4e791e7af2f7f64764f4f1e8",
"blocks": 246,
"daa": 245,
"blue": 246,
"blueWork": "8723556",
"key": "8da9ddee…"
},
"h2": {
"hash": "5c4cb584dc015892ddff89337412ed0883d9f97b4e791e7af2f7f64764f4f1e8",
"blocks": 246,
"daa": 245,
"blue": 246,
"blueWork": "8723556",
"key": "8da9ddee…"
},
"side2": {
"hash": "5c4cb584dc015892ddff89337412ed0883d9f97b4e791e7af2f7f64764f4f1e8",
"blocks": 246,
"daa": 245,
"blue": 246,
"blueWork": "8723556",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "753507c15705f4ea8ed19bc2697a11c489a052a45f28c64f9ac3d8400fa66647",
"blocks": 412,
"daa": 411,
"blue": 412,
"blueWork": "11490863",
"key": "8da9ddee…"
},
"h2": {
"hash": "32b7c1757fa288eed0a8ab709e49fd86f72be531db19c58e63d0aeca7a34c4f6",
"blocks": 440,
"daa": 439,
"blue": 440,
"blueWork": "18089683",
"key": "eda9cde4…"
},
"side2": {
"hash": "32b7c1757fa288eed0a8ab709e49fd86f72be531db19c58e63d0aeca7a34c4f6",
"blocks": 440,
"daa": 439,
"blue": 440,
"blueWork": "18089683",
"key": "eda9cde4…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 507.4,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": false,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 4,
"ports": {
"base": 30850,
"suffix": 984
}
},
"log": "/srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-partition-gate-on-expect-reorg.log"
},
{
"mode": "partition",
"gate": "off",
"expect": "reorg",
"must": "PASS",
"line": "partition heal with the gate off: the same outcome, so the heal is unchanged",
"slot": 5,
"name": "partition-gate-off-expect-reorg",
"verdict": "PASS",
"as_it_must": true,
"secs": 581,
"exit": 0,
"summary_line": "SUMMARY PASS (partition-gate-off-expect-reorg): side 2 held 5583 bps of the blue blocks at the fork; fork depth 191 DAA against window 60; side 2 blue work 19576159 against H1 12270631 at the heal; H1 reorged at 507.4 s (0 refusal lines, knows side 2's tip: true); side 2 kept its chain: true",
"summary": {
"pass": true,
"expect": "reorg",
"case": "partition-gate-off-expect-reorg",
"mode": "partition",
"gate": "off",
"window": 60,
"joint": 240,
"split": 180,
"watch": 150,
"threads": {
"honest_each": 1,
"attacker_split": 3,
"attacker_joint": 1
},
"keys": {
"h1": "8da9ddee…",
"h2": null,
"side2": "eda9cde4…"
},
"share_at_fork": {
"side2": 67,
"others": 53,
"side2_bps": 5583
},
"fork_daa": 244,
"fork_depth_daa_at_heal": 191,
"sinks": {
"joint": {
"h1": {
"hash": "58126c351aa6324a992a47fa66cca22369450c9835d312772c87d3e1aeb409a0",
"blocks": 245,
"daa": 244,
"blue": 245,
"blueWork": "9558440",
"key": "8da9ddee…"
},
"h2": {
"hash": "58126c351aa6324a992a47fa66cca22369450c9835d312772c87d3e1aeb409a0",
"blocks": 245,
"daa": 244,
"blue": 245,
"blueWork": "9558440",
"key": "8da9ddee…"
},
"side2": {
"hash": "58126c351aa6324a992a47fa66cca22369450c9835d312772c87d3e1aeb409a0",
"blocks": 245,
"daa": 244,
"blue": 245,
"blueWork": "9558440",
"key": "8da9ddee…"
}
},
"split": {
"h1": {
"hash": "a27a341c694aacf22b1a78f44d433e4104f58b23533afb73b7ee6fb9aa37afe4",
"blocks": 405,
"daa": 404,
"blue": 405,
"blueWork": "12270631",
"key": "8da9ddee…"
},
"h2": {
"hash": "f9181a06c0db716fe4ed200f6f0046c9cb8fae2eb57dd7da4697a268c434a232",
"blocks": 436,
"daa": 435,
"blue": 436,
"blueWork": "19576159",
"key": "eda9cde4…"
},
"side2": {
"hash": "f9181a06c0db716fe4ed200f6f0046c9cb8fae2eb57dd7da4697a268c434a232",
"blocks": 436,
"daa": 435,
"blue": 436,
"blueWork": "19576159",
"key": "eda9cde4…"
}
}
},
"per_node": {
"h1": {
"held": false,
"reorged": true,
"reorg_at_s": 507.4,
"knows_side2_tip": true,
"refusal_lines": 0
}
},
"checks": {
"side2_under_a_third_at_fork": false,
"fork_deeper_than_window": true,
"side2_heavier_at_heal": true,
"every_honest_node_learned_side2_chain": true,
"every_honest_node_held": false,
"every_honest_node_reorged": true,
"every_honest_node_logged_a_refusal": false,
"no_honest_node_logged_a_refusal": true,
"side2_kept_its_chain": true
},
"failed_checks": [],
"refusal_example": null,
"node": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneumd",
"miner": "/srv/builds/igneum-wt-horizon/vendor/igneum-node-horizon/target/release/igneum-miner",
"slot": 5,
"ports": {
"base": 30890,
"suffix": 985
}
},
"log": "/srv/builds/igneum-wt-horizon/docs/plans/mission-item-4-gate/fork-gate-partition-gate-off-expect-reorg.log"
}
]
}

View file

@ -0,0 +1,16 @@
# Relay deploy, 7 October 2026 (the X23 tree, MF-11)
| What | Value |
|---|---|
| Deployed | 2026-10-07 15:38 BST, from branch update-return bd9b4f4e, relay/ |
| Production deployment | https://igneum-relay-iabqarnby-igneum.vercel.app (inspect KHyscUSVKueXueqxvZBRKkaUHwJR), aliased https://relay.igneum.network |
| Previous production | https://igneum-relay-bgy767z40-igneum.vercel.app |
| Env added | RELAY_RUN_PUB (the public half of ~/.config/igneum/relay-run-key, made by `node tools/relay.mjs keygen` the same hour); RELAY_INTAKE_COMPAT unset |
| Read-backs | /wake answers; fn=machines carries the bound field; the console page answers 200 and c/machines carries poll fields; a v1-shaped register by hostname (key tier) answers named:false bound:false; a signed start-app from the Mac answers 200 (item 394); an unsigned run from master's old tool is refused |
## Rollback
`cd relay && npx --yes vercel@latest --global-config ~/.config/igneum/vercel --scope igneum rollback https://igneum-relay-bgy767z40-igneum.vercel.app`
(or `vercel promote https://igneum-relay-bgy767z40-igneum.vercel.app`). Seconds; nothing to undo in the database: the X23 code adds relay_machines.secret_hash and the
table relay_wake_seen, both ignored by the old code. What a rollback loses: signed run verification (the old relay never
had it), the start-app kind, the ping; PC 2's new agent registers by hostname on either.

View file

@ -0,0 +1,555 @@
# Igneum Miner 0.3.17: the 0.3.17 tree as cut on 7 October 2026
The hourly cut after 0.3.16, under the coordinator's rule: every new switch at never on the devnet so the digest does not move; cut what is green and list what waited. Worktree `igneum-wt-ship0317` (release-0.3.17 from release-0.3.15 a9eb58f1), node `release-0.3.17-node` in `vendor/igneum-node-0317` (from f1ea7a38).
> Renumbered 7 October 2026, 03:3xZ (main): the tree this plan describes is **0.3.18**. Its canary on 12153428 failed on two further faults (the idle-peer guard closes every peer mid headers-proof IBD; a window-below-epoch log flood at 1,900 lines a minute), and tonight's **0.3.17** became the node-only hotfix on the 0.3.16 app tree (f1ea7a38 plus 90aaf38e = b3c228fa; see docs/plans/release-0.3.17.md on release-0.3.17). The version scheme is three-part everywhere, so "0.3.16.1" was not available. Rows below keep "0.3.17" where they were written; read it as this tree. Branches: release-0.3.18 (app), release-0.3.18-node (fork, 12153428). Decimals is 0.3.19.
> Renumbered again 7 October 2026, 08:2xZ (main): this feature tree is **0.3.19**; **0.3.18** is the app-only cut on the 0.3.17 tree (the engine's clock sample gated on igneum_getExecStatus, ledger N7; docs/plans/release-0.3.18.md on release-0.3.18-app); decimals is 0.3.20. Branches now: release-0.3.19 (app), release-0.3.19-node (fork, the mirror). Rows below keep the "0.3.18" they were written with; read it as this tree.
> Renumbered a third time, 7 October 2026 10:4x UK (main): this feature tree is **0.3.20** (the dc141409 line plus the class v4 amendment, option A on AP-F8-1, and the proof archive; its canary gate as ordered). **0.3.19** is an app-only cut on the 0.3.18 app tree plus miner-ui-5 on the node pin 5899f603 (docs/plans/release-0.3.19.md on release-0.3.19). Branches: release-0.3.20 (app), release-0.3.20-node (fork, the mirror). Rows below keep the numbers they were written with.
## 1. The node tree
| Branch | Tip | In 0.3.17 | Why |
|---|---|---|---|
| ca3-v4-0316 (node lane) | 9a44fcb8 (df787727 the last functional commit: igneum_getNodeInfo; the miner stall guard e6e1fbe2 with exit 45, the idle-peer drop 96619b7d) | yes | the unwrap class, the seven-window rule, the finality cause fields, igneum_getRecentBlocks, difficulty rule v3, the fork gate, vote-or-burn, the signing bonus, the miner's stall exit (every switch absent from the live object = never) |
| ladder-node (ladder lane) | 1591ee1d | yes | the latency ladder behind latency_ladder_activation_daa (never); the receive gate reads header_signals_active (the lane's merge note, applied as c6e12005's parent commit) |
| exec-sync-0313 (proving lane) | 40fd8d8c | yes (merged last, 02d15a87; params.rs both sides kept) | consensus proof verification behind proving_consensus_verify_daa (never), the override's program ids as the statement's ids, sp1 as cfg(not(windows)) dependencies with the host-backed verify on Windows (the first merge of a344fea3 broke the Windows cross-build in sp1-jit; 40fd8d8c fixed it) |
| finality-pause-node-0317 (finality lane) | fdc71bb5, rebased | yes (cherry-picked as d8d1de1a onto the exec-free base) | W7 the departure announcement behind finality_leave_activation_daa (never on the devnet; 0 and leave_delay 3600 in the testnet genesis per the project lead); protocol 17; the devnet digest unchanged by test |
| tail-emission-node-0317 (economy lane) | 5647533f, rebased | yes (cherry-picked as 996b592f, its params hunks without exec-sync's proving fields) | its coinbase table replaces the subsidy table the silence rules build on; the rebase resolves it; no digest move while CURRENT |
| kaspad igneum-pow default feature | 75467244 | yes | the node lane's N5 finding: a bare `cargo build -p kaspad` validated PoW with the kHeavyHash stub; every box reads igneum_getNodeInfo's powEngine "igneum-pow" in the table, "stub" is a FAIL |
| kaspa-testing-integration | cc78a21f | fixed here | compiles again (the block store's evm argument, Header's vote_key_hash, the Params const as a let, the four finality ops in the RPC sanity match); the box suite is to compile every test crate from here |
Final node tree 02d15a87 (75467244 + exec-sync-0313 40fd8d8c). Digests on the exec-free tree c6e12005 (Mac binary, direct run): the live sixteen-field object eada4bda (unchanged), the thirteen-field file b18ed271, no file c562d70e. A harness note: scratchpad digest.sh printed the no-file digest for a file that reads eada4bda while other nodes held its ports; it now says UNTRUSTED when the override lines are missing.
## 2. The app tree
| Branch | Tip | In 0.3.17 |
|---|---|---|
| ladder (repo side) | 7003f9f | yes (igneum-pow's chain_program_shadow, which the fork's kaspa-pow needs) |
| exec-app-0314 | 032f03c | yes (clean; docs as the union) |
| relay | 28c028b | yes (ci.yml on the release side) |
| ember-tune-0317 (rebased on a9eb58f1) | bb37c993 | yes: the release lines kept (job_active, install_asked, the stale-exporter line), live.rs one merged module, publish-manifest.sh Ember's guard |
| miner-ui-4 (rebased on a9eb58f1) | afc331bd | yes: ui/ whole with Ember's finality words, extnode.rs on igneum_getNodeInfo, N4 stall exits, the port-collision check, POST /api/shot, an external node that goes away hands the ports to the app after 60 s; src/live.rs stays Ember's |
| proving-v2 | d8055a7 | NO (its exec content is in c00e608; its extra is the proof-system v2 slot, its own cut, per its owner) |
App tests on the merged tree: 175 + 28 + 8; UI 41. A whole-file take of a lane's branch on an older base dropped the release tree's own fixes (seen and reverted): the lanes rebase onto the release tree and resolve there; the shipper merges, never substitutes files.
## 2a. Linux binaries by glibc (main's rule, 7 October 2026, 01:3x UK)
HiveOS images are Ubuntu 20.04 (glibc 2.31); the canary pods on 22.04 (2.35) refused the box's binaries. From now: the HiveOS package carries the 2.31 zig build (the build-server lane produces and container-tests it; the 0.3.17 HiveOS row stays at 0.3.16 in the index until that package lands, then joins with its own read-back); seeds and generic Linux binaries are the 2.35 build; the fleet's own boxes (Ubuntu 24.04) take the box's native build. The Mac and Windows rows publish on their gates.
## 2b. Checklist line for every cut (the node lane, 7 October 2026)
`cargo check -p kaspa-testing-integration --tests` runs in the box suite of every cut and in the fork's CI (the crate had not compiled since the finality fields landed; no integration test ran on any cut until this one). The 0.3.17 box chain carries it as its last step (rebuild-on-fix.sh step_box / the node chain's "integration check"). 0.3.18 node notes: relay pipelining bb397f99 and the sync-request fuzz gate 6cf33d36 on ca3-v4-0318; aa0182aa and 812c3ac2 from ca3-v4-0316.
## 2c. A kill matches a pid, never a name (main's row, 7 October 2026, 01:xx UK)
My `pkill -f build-remote.sh` at 00:17 UK (to stop my own chained box builds before a rerun) killed the decimals lane's box run on the same Mac: the pattern matched every build-remote.sh, not mine. Rule: every kill in the ship tooling matches a pid file or the exact command line (the run's log path, the worktree path), never a tool's name; the ship scripts run through tools/ci/pre-push.sh's kill-by-name check before the next cut. Tonight's scratch runbooks hold no pkill by tool name any more (the chains are started with their log path in the command and stopped by that path).
## 2d. The canary's FAIL (01:37Z): the IBD guard refuses signal-version relay blocks
c17-1 (02d15a87, the live sixteen-field object) could not join: "IBD with headers proof from <peer> was unsuccessful (peer relayed block ... header version mismatch: got 1026, expected 2 at DAA score 237583)", 483 lines in 15 minutes against all four peers, peers 0, blocks 0. Cause: protocol/flows/src/ibd/flow.rs:390 (upstream's Toccata guard, f94053a0) compares the syncer's relay-block version with the plain block version before the pruning proof; under the open window the sink carries legal 1026 signal blocks. The line is on f1ea7a38 too, so the LIVE devnet has refused every fresh join (the headers-proof IBD path) since publish 2 opened the window at 22:43Z; tonight's moves passed because every node had a datadir (the relay path) or began its IBD before the hub moved. Fix (node lane): the comparison gated like the pre-ghostdag rule (block_version_of under header_signals_active), known-failed test first; main decides between 0.3.17 carrying it and a 0.3.16.1 node hotfix. Checklist line from here: the canary's target is a FRESH node joining the LIVE object's chain, which carries signal headers once a window is open; the pre-cut harness (node-compat.mjs) gets a chain with signal-version headers under an open window.
## 2e. The rebuild on the fix (7 October 2026, 02:3x to 02:4x Z)
The node lane's IBD-guard fix (ca3-v4-0317-fix 90aaf38e) merged into release-0.3.17-node as 12153428. Every binary rebuilt on it:
| piece | commit string | sha256 (first 8) | bytes |
|---|---|---|---|
| Linux igneumd, box native (glibc 2.39, the fleet's canary sha) | 12153428 | 5a4a0d68 | 57,347,232 |
| Windows igneumd.exe (cross, box) | 12153428 | 1e0b49ef | 52,367,360 |
| Windows igneum-miner.exe | 12153428 | 7ca9ca01 | |
| Mac igneumd (Apple Silicon) | 12153428 | 7416d4a5 | 47,837,248 |
| Igneum-Miner-0.3.17.dmg (packaged object = the live sixteen-field object, prover pair aboard) | | 916248b8 | 44,244,489 |
| HiveOS igneumd (zig, glibc 2.31) | 12153428 | 378340e4 | 56,022,096 |
| igneum-hive-0.3.17.tar.gz (2.31 node pair + the box's CUDA/OpenCL workers) | | 04fb7d45 | 27,193,392 |
Suite on 12153428: green (two load flakes pass alone), `cargo check -p kaspa-testing-integration --tests` rc 0. Digests read directly from the rebuilt node: sixteen-field eada4bda (igneum_getNodeInfo powEngine igneum-pow), thirteen-field b18ed271, no file c562d70e: unchanged from 0.3.16.
Windows inputs pushed 02:37Z (igneumd.exe 1e0b49ef, workers d7a413c7, 6f4bb57f, 0d68d06e, the Linux prover pair), pin b5a16f5b on release-0.3.17, windows.yml run 37562948420 dispatched 02:38Z.
HiveOS 2.31 smoke in an ubuntu:20.04 container on igneum-build-1 (ldd 2.31): igneumd, igneum-miner, igneum-worker-cuda and igneum-worker-opencl all load and answer. Found on the way: GNU tar materialises the Mac's extended headers as `._` files beside every file in the package (the live 0.3.16 tar has twelve of them; harmless, HiveOS ran it). Fixed in make-hive-package.sh (COPYFILE_DISABLE, no Mac metadata, c5187a70); the 0.3.17 tar extracts clean (10 files, 0 `._`).
The scratch staging copy (`r0317/dlsite-stage`) had the superseded DMG d25ab372 and installer d1065ac0 removed; the manifest is re-written there once the Windows run's installer is fetched. The live downloads folder holds no 0.3.17 file until the deploy step, which waits on the fleet's second canary line.
## 2f. Ready at the line (7 October 2026, 02:5x Z)
- Windows: run 37562948420 green (engine, window host, payload, installer, smoke run), Igneum-Miner-Setup-0.3.17.exe 93e29580, 62,814,273 bytes, fetched into the scratch copy. The scratch manifests (token folder and public) read: version 0.3.17, mac 916248b8, windows 93e29580, consensus.override = the live sixteen-field object (floor 831,600, window 86,400), HiveOS alias held at 0.3.16 until its own row.
- The deploy runbook is `scratchpad/r0317/deploy.sh` (step_preflight, step_manifest = the live folder written and deployed in one step with the same inputs, step_update_now for d937c69d, ae432dc7 and 1ccfe586, step_hive, step_readback). It runs only on the fleet's second canary line.
- Hands and seed: the build-server lane builds the seed's 2.35 pair and the hands' native pair on 12153428 and restarts them LAST on my line (observer, node 1, then the seed), each read back by commit string, digest and powEngine.
- The Discord card is dry-run at `tools/community/out/release_0.3.17.json` (four reader-facing change lines, no em dash); it posts only once the HiveOS row is live, since the card carries every platform.
- The 0.3.16.1 fallback (main's rule: if the retry fails off the IBD-guard fix) is prepared and not pushed: scratch worktree `r0317/fallback-03161-node`, branch release-0.3.16.1-node at b3c228fa = f1ea7a38 plus a hand-port of 90aaf38e (the helper gates on class_signal_active() alone, since f1ea7a38 has no ladder; the ladder assert dropped from the test). `cargo check` on consensus-core, consensus and p2p-flows clean, the unit test green. The node lane confirms it is the same adaptation it made for its 0318 tree (f2b25fdf).
- The node lane's two-daemon window test (10226194 on the fix branch, separable, touches only testing/integration) is NOT in 0.3.17's pin; run on the box against 12153428 plus the commit (worktree vendor/igneum-node-twodaemon, so the path igneum-pow resolves): `a_fresh_node_joins_a_chain_of_signalling_headers_and_no_window_refuses_them ... ok`, 11.46 s, 03:01Z. It rides the next tree.
- Pairing rule (the build-server lane, 02:5xZ): a fork build takes igneum-pow by path from the igneum worktree it sits in; a fork worktree under a master checkout fails in kaspa-pow (`chain_program_shadow` missing). 0.3.17's artefacts paired with release-0.3.17 at a73e400c to 6f8d7a7e, whose igneum-pow is unchanged since 6b30e855. The tool logs the pairing from the next master.
## 3. Owed
- exec-sync-0313's consensus verification behind a feature off for Windows (the proving lane), then its two commits.
- finality-pause-node rebased onto the 0.3.17 node (the finality lane).
- decimals (B10 gate), vote-weigh and tail-emission docs: the next cut.
- The fork gate's fast-time line is GREEN on aa0182aa (gate on: a 27.5 percent key's private chain 183 DAA deep refused, 268 refusal lines, the honest node kept its chain; gate off, the known-failed case reorgs), one commit past the 9a44fcb8 this tree carries; the switch is at never on every network, so 9a44fcb8 stands for 0.3.17 (the chains were building) and aa0182aa rides 0.3.18. vote-or-burn and the signing bonus: the replay gate FAILED on 9a44fcb8 (this tree) and PASSES on 812c3ac2 (the silence read as a function of the block's own past), which rides 0.3.18 with aa0182aa; both switches are at never here, so a devnet node behaves the same; the signing bonus is settable at the testnet genesis on the project lead's word, the devnet not a candidate until the longer-span replay passes.
- The canary in the full form before publish (a new node mining beside an old one with a poisoned peer, ten minutes, a mid-window re-sync), the staged-in-scratch rule, every new switch at never.
## 4. For the 0.3.18 tree (not in 0.3.17)
- miner-ui-4 past afc331bd: 74c12665 (the merge check: a node whose blocks never merge reads "behind" and the miner is held; src/merge.rs, observer poll, one UI line; 172 box + 42 UI tests green). The UI lane's word, 03:2xZ.
- ca3-v4-0317-fix 10226194: the two-daemon window test (green on 12153428 plus the commit, 03:01Z).
- The node lane's ca3-v4-0318 tree (98c78d9a, f2b25fdf).
- The build-server lane's pairing log line in build-remote.sh (which igneum-pow a fork build paired with).
## 5. The 0.3.18 cut after the hotfix (main's order, 7 October 2026, 03:5xZ)
Once "0.3.17 live" (the hotfix) is read back: rebuild release-0.3.18-node on 12153428 plus the node lane's 4f4bb9c9 (ca3-v4-0317-fix: the idle-peer drop counts headers, proof, trusted data and UTXO chunks as deliveries and never fires during a sync; the class-signal missing-history warning once per epoch per process; kaspa-p2p-lib 20, kaspa-p2p-flows 35, class_signal 1 green on the box; the same patch onto ca3-v4-0318 by 05:15 UK), every platform, digests unchanged, suites, the integration check, the window test again on the merged tip; merge the UI lane's list (afc331bd in, then 74c12665, 89501ce8, b7d33e8e, the doc-only d29a8663), ember-tune-0317 c9b31edc; re-stage 0.3.18 in scratch; the fleet's full canary on its pods with the headers-proof fresh join decisive; publish 0.3.18 on its line, whenever that falls, morning included. Ember's PC 1 window ("PC 1 on 0.3.18") and the UI lane's Mac check follow its update-nows.
Tips (node lane, 03:5xZ): release tree 4f4bb9c9 on ca3-v4-0317-fix (cherry-pick alone onto 12153428); feature tree a3fe9f67 on ca3-v4-0318 (on e1197983, a lockfile-only commit). A follow-up on both is due (the ping flow's `syncing` flag IBD-only; "sink at genesis" broke the peer-drop gate's `--case still` and is redundant for the canary): take it WITH 4f4bb9c9. The hotfix canary (0.3.17, node d712b498) started 03:58Z on c17-1; its clock in UTC: headers through about 04:17, synced 04:40, reads 04:50, cases 05:25.
**Final node inputs (main, 04:0xZ): the 0.3.18 node tree is ca3-v4-0318 at 6e4ace3f** (a3fe9f67 the fix, b21659e4 the IBD-only follow-up, 6e4ace3f igneum_getNodeInfo's `blockrate` object for the merge check; exec-only, digest untouched; all suites green). The release-branch form (4f4bb9c9 + ddda7d52 on 12153428) is the equivalent and not used. After "0.3.17 live": release-0.3.18-node = 6e4ace3f, every platform rebuilt, then the canary.
**Dated fault for this tree (the node lane, 05:05Z, found by the Mac's headers-proof join gate, not by a canary):** when the devnet's pruning point first leaves genesis (chain DAA 185,799, about eight hours from 05:00Z at 1 bps), every fresh join by headers proof fails for the next 2,644 DAA (about 45 minutes) with "IBD with headers proof ... unsuccessful (DAA window data has only N entries)": upstream's sampled-window walk (661 samples at rate 4) runs out at a gap in the trusted blocks instead of reaching genesis. Nodes already on the chain are untouched. Fix: joiner-side on the trusted path only, digest-neutral (the walk accepts a window that runs out at origin when the block is younger than the span), coming on ca3-v4-0318 (hash to follow); 0.3.18 carries it. Any fresh-join canary timed inside that 45-minute window FAILS from this, not from the idle-peer fix: time 0.3.18's canary outside it (before about 12:50Z or after about 13:40Z, to be firmed from the live DAA).
**Main's rule for the 0.3.18 cut (05:1xZ):** it carries the node lane's joiner-side fix for the pruning-point fault (on the 0.3.18 tree the N6 guard would otherwise ban the syncer for ten minutes per retry), and **0.3.18 is live on every node before 13:30 UK (12:30Z)**, ahead of the devnet's first pruning (DAA 185,799, about 14:00 UK). If the full canary cannot close by then, publish 1 goes on the clean form plus the node lane's harness for that case, and the cases follow as confirmation, as with the hotfix. Every new chain, the testnet genesis included, meets the same fault once at its first pruning: the testnet go checklist (docs/plans/testnet-go.md) gets the line.
**Correction (the node lane, 05:1xZ): the devnet does NOT meet the young-window fault, and the 185,799 clock was wrong** (the canary's 155,700 headers were taken for the chain's DAA; live DAA was 250,809 at 05:00Z, the hub's 39th pruning move was at 03:46Z and the 03:50Z fresh join synced clean). Pruning samples sit at multiples of the finality depth (pruning.rs is_pruning_sample); the devnet's finality depth is 43,200 blocks, so its first non-genesis pruning point was at blue score 43,200, sixteen window spans past genesis, and the walk from any devnet pruning point fills its window. The fault needs a pruning point within 2,644 DAA of genesis, which only a chain with finality depth under 2,644 can have: the fast-time profile (120) where the gate found it. The testnet at the compiled numbers never meets it either. So: no fresh-join window to avoid, the 45-minute rule is dropped, main's "before 13:30 UK" rule for 0.3.18 no longer rests on this fault (main to confirm), and the testnet go line is softened to a note. The fix still rides 0.3.18 (joiner-side, digest-neutral, relaxes the rule only when the walk ended within one sample of genesis, impossible on the devnet; unit test green on the box; hash to follow).
**Main, 05:2xZ: the 13:30 UK deadline is withdrawn.** 0.3.18 ships on its normal gates with the full canary, no shortcut; the young-window fix stays in the tree as a harness-profile correctness fix. ca3-v4-0318 tip 06:04 UK: e3798a17 (69647bd6 per-path finality timers plus the join bench, test-only plus accounting; e3798a17 the young-window fix, unit test green). The fresh-join cost fix is next on the branch with its own hash and before/after numbers; its "before" figure comes from the 0.3.18 canary's IBD-end line (the new "finality time: bodies ..., virtual ..., weight tables ..., signatures ..., persists ..." line), which I relay verbatim (route (b); a read-only joiner from the box, route (a), is main's word).
ca3-v4-0318 tip 06:06 UK: **aa49613f** (on e3798a17; carries the IBD-end "finality time" line; p2p-flows and kaspad check clean on the box). The cut takes aa49613f or the later tip the node lane names; the canary's fresh-join IBD-end line goes to the node lane verbatim.
## 6. The 0.3.18 inputs as of 05:2xZ (main's list)
- Node: the ca3-v4-0318 tip the node lane names at the cut; now **8220c944** (06:10 UK; on aa49613f): a block's votes BLS-checked across cores before the finality lock, exact by the two-path test. Box, 500-checkpoint chain of 22,221 blocks: join 218 s to 104 s, finality 130 s to 30 s, signatures 97 s to 9 s. Suites at 8220c944 on igneum-build-1: kaspa-consensus lib 110 passed, 0 failed, 3 ignored (the two new finality tests inside); kaspa-p2p-flows 37 passed; kaspad check clean at aa49613f with nothing in kaspad changed since. The real split of the canary's 8 s per checkpoint comes from the IBD-end timers line of the 0.3.18 canary's fresh join, relayed verbatim to the node lane.
- App: miner-ui-4 afc331bd (in), 74c12665, 89501ce8, b7d33e8e, **bac43ac4** (main's row from PC 1's 04:51Z relaunch: the card watchdog judges a miner only while the node is synced; a silence fault from the sync is released when the node syncs and the card starts again with an Activity line; a worker silent from its start is faulted at 60 s; one Activity line per card at its first start; known-failed test from the PC 1 row in release-0.3.17.md; 173 box + 42 UI tests green) plus the doc commits (d29a8663, 0e2e8a31); ember-tune bb37c993 (c9b31edc is job-script only); exec-app 032f03c; relay 28c028b.
- Gates: the same as every cut; the full canary with the fresh join decisive; publish on its line.
## 7. The node tree is a merge (05:5xZ)
ca3-v4-0318 (8220c944) does not contain release-0.3.18-node 12153428: their merge-base is 9a44fcb8 (ca3-v4-0316), so the branch lacks the ladder 1591ee1d, exec-sync 40fd8d8c, finality d8d1de1a, tail-emission 996b592f, the kaspad igneum-pow default 75467244, cc78a21f and 90aaf38e (it carries f2b25fdf, its own adaptation of the canary fix). The 0.3.18 node is the merge of 8220c944 (or later) into 12153428. A no-commit test merge conflicts in six files (consensus/core/src/igneum.rs, pre_ghostdag_validation.rs, ibd/flow.rs, v10/blockrelay/flow.rs, the two integration test files): the node lane resolves it, pushes release-0.3.18-node to the mirror, runs the suites and the integration check, and names the tip. The branch stays at 12153428 until then.
PC 1's 0.3.17 relaunch (section 7 of release-0.3.17.md) adds a node row for this tree: the template RPC blocks under the finality catch-up after a restart on a synced node (every getBlockTemplate timed out at 5 s for 90 s); the app row (bac43ac4) needs its rule changed to "template timeouts while the node reports synced are not a card fault".
## 8. The app tree assembled (05:4xZ)
release-0.3.18 at da5c40ba: miner-ui-4 afc331bd (in), 74c12665, 89501ce8, b7d33e8e, d29a8663, 0e2e8a31, bac43ac4, 5d9c55a2 (the UI lane's rule from PC 1's read-back: "template fetch timed out" is the miner's heartbeat, the card reads "waiting for the node to answer block templates", judged again from its last line; 173 box + 42 UI tests on its branch), ember-tune c9b31edc (job script; bb37c993 in), exec-app 032f03c (in), relay 28c028b (in). One merge fix by the shipper: 74c12665's merge view called the three-argument `live::fetch`; this tree's is the four-argument form (e5336dc7 gave it the engine's Shared), so engine.rs:3200 passes `&shared` as server.rs does (da5c40ba). App tests on the Mac: 179 + 28 + 8 passed. The node pin, binaries, inputs and staging wait on the node lane's merged release-0.3.18-node tip.
UI lane on the merge fix (05:5xZ): correct; the four-argument form wins. Note: Ember's fetch clamps the window to 300 s, so the merge view sees 300 s rather than 600; the check needs a block of ours inside 120 s, so it holds. The merge view, TemplateTimeout and the synced gate are present on origin/release-0.3.18.
**The node merge, the node lane, 06:0xZ (not yet pushed):** 8220c944 into 12153428 resolved: five conflicts taken as the release side (the ladder's header_signals_active reading, the test fixes), the sixth the relay flow (the pipelined handle_block carries the 0.3.16 IgneumProofMissing retry arm ahead of the N6 arm); one copy of header_version_acceptable kept. Box at the merged tree: kaspad check clean, integration check clean, consensus-core 123, p2p-flows 37, p2p-lib 20. A real finding: kaspa-consensus failed 1 of 112 then 4 of 112 under a box load of 178, every panic WrongBlockVersion(1026 or 32770, 2) in a plain-block test: a test-order race both parents carry (the signal and ladder tables are process-wide statics; two tests install and restore them; a block-building test inside that window reads the installed version; the two load flakes of the 12153428 suite were this). Fix: an install lock the two installers hold; the suite runs twice on the quiet box; if green the merge is pushed as release-0.3.18-node with the lock commit on top (tip about 06:20Z); if a reader still races, the two installing tests run serially as a second pass, said before the push.
**Suite rule change with the merged node (the node lane, 06:1xZ):** the install lock did not close the row (2 of 112 still WrongBlockVersion(32770, 2) on the quiet box), so the two installing tests (block_template_uses_current_block_version, cheap_checks_run_before_the_pow_engine) move out of the lib unit-test binary into their own target consensus/tests/igneum_installed_signals. From the 0.3.18 node on, the suite runs `cargo test -p kaspa-consensus` without `--lib` (all targets) so both run; a `--lib` run silently skips them. Push with the merged tree once the lib suite is green twice and the new target once, about 06:35Z.
## 9. The node tree: release-0.3.18-node = 1cf43254 (the node lane, 06:09Z)
The merge of ca3-v4-0318 (8220c944) into 12153428 with the six resolutions, plus the race fix in the same commit (the two installing tests moved to consensus/tests/igneum_installed_signals.rs and igneum_order_tests.rs; INSTALL_TEST_LOCK for installers sharing a binary). Box at 1cf43254: kaspad check clean, integration check clean, kaspa-consensus lib 110 twice on the quiet box, the two new targets 1 each, consensus-core 123, p2p-flows 37, p2p-lib 20. A second merge may follow within the half hour (PC 1's two node rules: template answers under catch-up, synced reads behind), else the cut is at 1cf43254. The shipper's box and Mac chains started on 1cf43254 at 06:1xZ (scratch only; the pin, inputs and staging wait for the node lane's cut word).
**Main, 06:1xZ:** the chain runs on 1cf43254 now. The node lane's two PC 1 rules (getBlockTemplate answers inside its timeout during the finality catch-up; isSynced false while the catch-up is behind the sink) land as a second merge with a node rebuild only if they reach the release branch before the inputs push; otherwise they are 0.3.19, said here. Full canary with the fresh join decisive, the IBD-end timers line to the node lane, publish on its line.
## 10. Builds on 1cf43254 (06:10Z on)
| piece | commit string | sha256 (first 8) | bytes | note |
|---|---|---|---|---|
| Linux igneumd, box native (the fleet's canary sha) | 1cf43254 | 96858a88 | 57,408,800 | 06:11Z; the fleet has it with GO for the full form |
| Linux igneum-miner, box native | | fac45489 | 10,213,112 | |
| Windows igneumd.exe (cross) | 1cf43254 | c29ee9e5 | 52,340,224 | 06:12Z |
| Windows igneum-miner.exe | | 7ca9ca01 | 11,219,456 | unchanged from the 12153428 build |
| Mac igneumd | 1cf43254 | 25b464b4 | 47,888,704 | |
| Igneum-Miner-0.3.18.dmg | | a3822c4d | 44,248,348 | packaged object = the live sixteen; prover pair aboard; version 0.3.18 |
Digests on the Mac binary: sixteen eada4bda (igneum_getNodeInfo: powEngine igneum-pow, blockrate {bps 1, finalityDepth 43200, ghostdagK 18, mergeDepth 3600, pruningDepth 108000}), thirteen b18ed271, no file c562d70e (re-read on free ports, 06:15Z). All three unchanged from 0.3.16/0.3.17.
(suite, integration check, seed, hive, inputs, Windows run, staging, canary: rows as they land)
## 11. The cut: release-0.3.18-node = ae17ad00 (the node lane's word, 06:14Z)
1cf43254 plus the second merge of ca3-v4-0318 (e3a00bf0: 4f7c56a0 PC 1's two node rules, getBlockTemplate answers inside its timeout during the finality catch-up and isSynced reads false while the catch-up is behind; e3a00bf0 the installing-tests move on that branch too). Box at ae17ad00: `cargo test -p kaspa-consensus` 111 in the lib plus the two moved targets 1 each, p2p-flows 37, `cargo check -p kaspad -p kaspa-rpc-service -p kaspa-testing-integration --tests` clean. One hand fix in the merge: the W7 leaves block in template_section rides the template snapshot like the other items (newest 32, emitted last under the leave_active gate). The 1cf43254 chain was stopped at its seed step (its suite: see r0318/ae17/box-1cf43254.log) and both chains restarted on ae17ad00 at 06:2xZ. The canary form gains two reads for PC 1's rules: on a restart with a kept datadir, every getBlockTemplate inside 5 s through the catch-up (no "template fetch timed out" lines), and isSynced false while the catch-up holds the finality state, true after.
**Canary on ae17ad00 started 06:16:47Z** (c18-1, RTX 3070 pod, wiped datadir, node b06c1a97, 57,447,712 bytes, miner fac45489; the 1cf43254 early run stopped by pid; it had read vline 1cf43254, digest eada4bda, igneum_getNodeInfo with blockrate, IBD from 4 peers). Clock: headers through about 06:52Z, synced 07:12Z, mining reads to 07:22Z, the restart reads (isSynced false then true every 10 s; max template_ms, timeout count, template count) to about 07:40Z, then the relay and poison cases with c18-1 as the target.
## 12. Builds on ae17ad00 (06:15Z on)
| piece | commit string | sha256 (first 8) | bytes | note |
|---|---|---|---|---|
| Linux igneumd, box native (the canary sha) | ae17ad00 | b06c1a97 | 57,447,712 | 06:16Z; the canary runs on it from 06:16:47Z |
| Linux igneum-miner | | fac45489 | 10,213,112 | unchanged from 1cf43254 |
| Windows igneumd.exe (cross) | ae17ad00 | e4979672 | 52,391,424 | pow link 9 |
| Mac igneumd | ae17ad00 | a56469d7 | 47,905,856 | |
| Igneum-Miner-0.3.18.dmg | | f617b63b | 44,241,681 | packaged object = the live sixteen; prover pair aboard |
| Windows inputs | ae17ad00 | | | pushed 06:19Z (workers d7a413c7 etc, the Linux prover pair), pin 1c8fb76a on release-0.3.18, windows.yml run 37580969266 dispatched 06:20Z |
Digests on the Mac binary: sixteen eada4bda (igneum_getNodeInfo powEngine igneum-pow, blockrate as before), thirteen b18ed271, no file c562d70e (free ports). All three unchanged. Box suite on ae17ad00 rc 0 (kaspa-consensus without --lib, so the two moved targets ran). Identity grep on the payload UI clean. The 1cf43254 builds (96858a88, c29ee9e5, 25b464b4, DMG a3822c4d) are superseded and not staged.
| generic Linux igneumd (class seed, glibc 2.35) | ae17ad00 | 99809615 | 56,141,136 | pow link 6; the seed's own build comes from the build-server lane |
|---|---|---|---|---|
| HiveOS igneumd (class hive, glibc 2.31) | ae17ad00 | a2e734d1 | 56,141,776 | pow link 6 |
| igneum-hive-0.3.18.tar.gz | | 21ea06eb | 27,234,062 | 2.31 node pair + the box's workers 193ec36f/7a35ff2a; no `._` entries; ubuntu:20.04 container smoke on the box: all four load (06:23Z) |
| hands igneumd (box native, the build-server lane, pairs with igneum 1c8fb76a) | ae17ad00 | 17209647 | 57,448,096 | held for the line; OUT_DIR apart from b06c1a97 |
| seed igneumd (class seed, the build-server lane) | ae17ad00 | fa9c2f12 | 56,139,920 | GLIBC_2.34; held for the line |
| hands / seed igneum-miner | | 9adcb707 / 2ebaee58 | 10,213,112 / 10,234,128 | |
Integration check on ae17ad00 rc 0 (06:18Z). The hands and seed go last on the shipper's line after the miners (observer, node 1, seed), each read back by commit string, digest and igneum_getNodeInfo powEngine with its blockrate object.
## 13. Staged at the line (06:29Z)
Windows run 37580969266 green (06:27Z): Igneum-Miner-Setup-0.3.18.exe 2fcaf093, 62,822,510 bytes. Scratch copy r0318/dlsite-stage (fresh from the live folder): manifests at 0.3.18, mac f617b63b, windows 2fcaf093, consensus.override = the live sixteen-field object, HiveOS alias held at 0.3.17 until its own row; the live folder holds no 0.3.18 file. Runbook r0318/deploy.sh (preflight passes; step_manifest, step_update_now, step_hive with the 0.3.18 tar, step_readback with igneum_getNodeInfo). Discord card dry-run (four reader-facing lines, no em dash). Everything waits on the canary's line.
## 14. The canary (c18-1, ae17ad00 / b06c1a97)
- 06:34:54Z: headers 47 percent (59,425) in one IBD session since 06:17:32Z, 17 minutes in and 5 past the old guard mark; SendPingsFlow 0, idle-drop lines 0, "completed with error" 0; the class-signal warning is ONE line (49,844 on the hotfix). Headers through about 06:55Z on the 3070, synced about 07:15Z.
## 15. HOLD on ae17ad00: the exec RPC panic (the node lane, 06:4xZ)
The Mac's headers-proof join gate killed its joining node mid-IBD on the canary-fix binaries: a panic at igneum/exec/src/rpc.rs ("index out of bounds: the len is 0 but the index is 0") on a tokio worker, and the node's panic hook exits the process. The site: eth_getBlockByNumber reading state.records[n] after resolve_block mapped "latest" to tip_number() = 0 on an exec state with no records (the follower still "waiting for consensus to sync"); a local client polled 127.0.0.1:26790 during the IBD and the node died at 66 percent of the headers stage. Every records index in that file is unchecked on both trees (0.3.17's 5899f603: rpc.rs lines 474, 550, 591 to 629; ae17ad00: 575, 651, 692 to 730), so any fresh or restarting node with the exec RPC bound and a client asking eth_getBlockByNumber("latest") before the follower has a record dies. The shipped app itself calls only eth_blockNumber and igneum_* methods (none index records by block number), so the live 0.3.17 risk is a wallet or third-party client on a fresh node mid-IBD; the fix (every index bounds-checked; "latest" on an empty state answers null; a test on an empty state) goes onto ca3-v4-0318 and into release-0.3.18-node as a third merge. The pin stays at ae17ad00 on origin and nothing is staged further until the third tip; the ae17ad00 canary runs on (nothing attached to its eth_ port) as an early read of the other rows.
**The 0.3.17.1 question settled (07:0xZ, main's rule: a hotfix only if the shipped app sends the panic class):** no. The dead harness node's site is eth_getBlockByNumber; the shipped app sends eth_blockNumber and eleven igneum_* methods only (grep of the whole 0.3.17 app tree); its two records-indexing polls (igneum_getAssignedShards, igneum_getProofRecords) run only after the node reads synced, when a devnet follower already holds records from the packaged restart block; the two it sends while unsynced index nothing. The wallet app sends eth_getBlockByNumber to its own node. Ledger N7 carries the method map. The public testnet filter now blocks ten eth_ and six igneum_ records-indexing methods (seed1, verified; tree copy committed).
## 16. The cut moves to release-0.3.18-node = e69e8a39 (the node lane, 06:53Z; the pin released)
ae17ad00 plus the third merge of ca3-v4-0318 (500ddd66): c6a62e00 (every chain-block read in the exec JSON-RPC bounds-checked; an exec state with no record answers an error instead of indexing; test on the empty state) and 500ddd66 (GetBlockTemplate stage timers: a debug line per request, an info line once per 10 s past 500 ms; the epoch-seed walk and the live sink tally memoised). One hand resolution: the payout parameter on igneum_getProofRecords beside the bounds-checked read. Box at e69e8a39: igneum-exec 26, kaspa-consensus 111 plus the two moved targets, p2p-flows 37, kaspad / rpc-service / testing-integration checks clean. Both chains restarted on it 06:55Z (the ae17ad00 logs kept in r0318/ae17/). The canary form gains two reads: an eth_ client polling the joining node's exec port through the IBD (the node survives), and the first slow "GetBlockTemplate N ms" line on the pool's loaded node after the publish.
## 17. Builds on e69e8a39, the pin (06:55Z on)
| piece | commit string | sha256 (first 8) | bytes | note |
|---|---|---|---|---|
| Linux igneumd, box native (the canary sha) | e69e8a39 | 252c8dad | 57,461,600 | the form started from the wipe 07:02:54Z |
| Linux igneum-miner | | fac45489 | 10,213,112 | unchanged |
| Windows igneumd.exe (cross) | e69e8a39 | c1a42970 | 52,379,136 | pow link 9; inputs pushed 07:03Z, pin 0911b00f, windows.yml run 37585128393 dispatched 07:04Z |
| Mac igneumd | e69e8a39 | 066807ae | 47,921,280 | |
| Igneum-Miner-0.3.18.dmg | | cb30080e | 44,247,374 | packaged object = the live sixteen; prover pair aboard; version 0.3.18 |
| generic Linux igneumd (class seed, 2.35) | e69e8a39 | 7d67cb51 | 56,152,400 | |
| HiveOS igneumd (class hive, 2.31) | e69e8a39 | 6293b443 | 56,153,104 | |
| igneum-hive-0.3.18.tar.gz | | 72699158 | 27,236,213 | 20.04 container smoke clean, no `._` entries (07:08Z) |
| hands igneumd / seed igneumd (the build-server lane, pairs with igneum e9ccb3a1) | e69e8a39 | 37610a77 / 63cd49be | 57,461,984 / 56,152,720 | held for the line |
Digests on the Mac binary (explicit arguments; a zsh loop had not split them on the first pass): sixteen eada4bda, thirteen b18ed271, no file c562d70e. igneum_getNodeInfo: powEngine igneum-pow, blockrate {bps 1, finalityDepth 43200, ghostdagK 18, mergeDepth 3600, pruningDepth 108000}. The N7 class on the Mac binary: eth_getBlockByNumber ["latest", false] on an empty exec state answers {"result": null} and the node lives (the node lane: null is Ethereum's shape for a block that is not there; every other read on an empty or short state answers an error object). The branch tip moved to 2a014bf1 (test-only: all 46 exec RPC methods through the real dispatcher on empty and one-record states, no panic); the pin stays at e69e8a39, whose shipped bytes it equals.
The chain's own log was lost mid-run (the build-server lane removed scratchpad/r0317 and r0318 whole while dropping its superseded pairs; rule now: no lane removes a scratch directory it did not create; this lane's scratch is r0318-ship); the suite and the integration check are re-run on the box for the record (r0318-ship/suite-e69e8a39.log).
**Suite on e69e8a39 (the box, 07:08Z, re-run for the record):** igneum-exec 26, kaspa-pow 19, kaspa-consensus 111 (lib) + the two moved targets 1 each, consensus-core 123 + 7, kaspa-p2p-flows 37, igneum-miner 16; 0 failed; rc 0. `cargo check -p kaspa-testing-integration --tests` rc 0. Together with the node lane's own runs at e69e8a39 and 2a014bf1 this is the gate's suite line.
## 18. Staged at the line (07:1xZ)
Windows run 37585128393 green (07:10Z): Igneum-Miner-Setup-0.3.18.exe 669d5676, 62832534 bytes. Scratch copy r0318-ship/dlsite-stage (fresh from the live folder): manifests at 0.3.18, mac cb30080e, windows 669d5676, consensus.override = the live sixteen-field object, HiveOS alias held at 0.3.17 until its own row; the live folder holds no 0.3.18 file. Runbook r0318-ship/deploy.sh (preflight passes). Discord card dry-run (five reader-facing lines, no em dash). Everything waits on the canary's cases line.
**For 0.3.19 (not the pin), the node lane 07:1xZ:** ca3-v4-0318 = fd7de1b4 (the live class-signal tally refreshed off the request path; kaspad check clean, consensus 109). e69e8a39 memoises the tally for 10 s per sink, so the pool's canary should show the "pow epoch" stage near zero on nine requests in ten and the full walk (1 to 1.6 s) on the tenth; if the stage line shows it high on most requests the reading is wrong. fd7de1b4 is the first 0.3.19 node commit (main, 07:1xZ: no four-part numbers, the parser refuses them); 0.3.18 ships as pinned at e69e8a39 on its canary's line.
## 19. The canary on the pin (c18-1, e69e8a39 / 252c8dad, from the wipe 07:02:54Z)
- 07:21Z: 17 minutes into one IBD session, headers 37 percent (48,160 at 07:16:45Z), past the old guard mark; SendPingsFlow 0, idle-drop 0, IBD errors 0, the class-signal warning 1 line, node alive; the eth_getBlockByNumber poller at 208 polls, 0 error answers, all null. Headers through about 07:45Z, synced about 08:05Z.
## 20. PC 1 and PC 2 after the project lead reopened the apps (07:28Z reads, read-only)
- PC 2 (1ccfe586): back online; its app came back on 0.3.16 and applied 0.3.17 at reopen (update: version 0.3.17, current, updated_from 0.3.16); node 2.1.0 synced at DAA 262,124 with 5 peers; the RTX 5090 mining at 100 MH/s (pid 19620, 11 accepted in its first minutes), no fault lines. The update-now takes it to 0.3.18 directly.
- PC 1 (ae432dc7): app 0.3.17 (reopened, uptime 531 s at the read), node binary 5899f603 (3 marks, pow link 9), digest eada4bda; but the node is in a restart loop: state "restarting", starts 16, igneumd not running at the read; every card "waiting for the node to sync" (mining "waiting", not faulted this time). Cause being read from the engine's node lines and the node logs (read-only job). Rule for the publish: PC 1 and PC 2 take their update-nows first and their workers are read back at their rates as the per-box table's first line.
**Latency ladder rung 3 re-measured (the node lane, 07:46Z, in the shipper's box window under the measure hold):** reps 88, cold alone 9.04 ms, cold with the SMT sibling loaded 10.85 ms, averages of 50 at 5.95 and 10.11 ms; over the 10 ms gate by 0.85, so rung 3 stays inadmissible and the testnet genesis freezes with 88 false. Rung 2 as the control on the same run: 9.25 ms cold loaded, admissible.
**PC 1's restart loop IS the N7 class on the shipped kit (07:5xZ, the engine log app-20733-072141.log):** every node start ends 2 to 5 s later with "panicked at igneum/exec/src/rpc.rs:591:41: index out of bounds: the len is 0 but the index is 0" (eth_getBlockByNumber's records[n] in 0.3.17's tree), the app restarts it every 40 s (starts 16 at 07:28Z), the miners are stopped each time. CORRECTED 08:0xZ: the client is the shipped engine itself: `latest_block_time` (update.rs:46) POSTs eth_getBlockByNumber ["latest", false] through curl to the exec port every 9 s as the app's clock sample once the node reports blocks > 0 and peers > 0 (engine.rs:3752); export-pack's lines were its own failure after the node died (igneum-miner's export-pack speaks gRPC only). So every 0.3.17 node with the app attached dies within 9 s of blocks arriving whenever its exec follower has no record: PC 1's loop, and any fresh install's IBD (the hotfix canary had no app attached). Proving-off would change nothing and was not run. c6a62e00 (0.3.18) ends it; the 0.3.18 canary's eth_ poller is the app's own call and the node lives on it. PC 1 takes the 0.3.18 update-now first on the publish line.
**Main's publish rule (08:0xZ):** 0.3.18 publishes on the canary's synced line plus the poller summary (the decisive reads for the N7 fault, the app's own 9-second call surviving the whole IBD), not the cases line. Order: PC 1's update-now first (it is crash-looping), then PC 2, the Mac, the fleet one box at a time under the lock rule, the hands and the seed by their owner. The restart reads and the cases follow on the same pod as confirmation; a FAIL there is a rebuild, not a rollback of this fix. Then "0.3.18 live" with the per-box table and the card.
## 21. HOLD on e69e8a39 (the fleet, 08:2xZ): the block stage cannot complete against any peer
The canary passed its headers proof at 08:00Z (one session, zero guard lines, the app's eth_ poll alive at 918 polls) and froze at 2,728 blocks: every IBD attempt (110 by 08:2xZ, every peer) ends "block f52e64f4... carries 1 proof records whose proofs this peer did not deliver in 20 s". The node lane's reading: exec-sync's daemon installs the proof oracle whatever proving_consensus_verify_daa says (daemon.rs, after the keys branch), so the IBD flow (flow.rs, the 0.3.16 "carried proofs come from the syncer" rule) demands the proof bytes of every record-carrying block before queuing it while the serve flow answers only from a peer's bounded recent pool; block 2,728 is months old, so a fresh node of this tree can join NO network, and a synced one would verify every relayed record natively and refuse blocks its 0.3.17 peers accept (a split in waiting). Fix, joiner-side: the oracle carries its switch (active_from); the body rule, the IBD fetch and the relay retry apply to blocks at or above it only, so at never the node is 0.3.17 on this path. A direct commit on release-0.3.19-node; the pin moves to it; the canary re-runs from the wipe.
## 22. For the 0.3.19 node: ledger N8 (the project lead's word, 08:5x UK)
The execution layer credits merged blocks at the chain block's DAA (the UTXO coinbase pays each its own): d840537b on ca3-v4-0318, field `subsidy_per_block_activation_daa` (u64::MAX = never as compiled on every network; in the digest once set). The devnet takes it at an upgrade height carried by 0.3.19 (a DAA score past the rollout's last box under the lock rule, set in DEVNET_PARAMS at the cut; every node must carry it before that height or its EVM state diverges); the testnet from genesis. The node lane names the value when the 0.3.19 cut line is named (its canary's pass).
**App tree (08:3xZ):** miner-ui-4 taken through 3661dc9e (a2670ace the two site captures; 3661dc9e carries 0e6c1248's exec-record clock gate without its version bumps), on top of the earlier chain; app tests 180 + 28 + 8 green. Ember: c870c383 (job-script only, equals bb37c993 on the app) at the rebuild.
**The exec lane agrees (08:4xZ):** exec-sync-0313 HEAD carries the same shape (ProofOracle::active_from() = proving_consensus_verify_daa, set by the daemon before the oracle is handed out; the IBD pre-fetch gated on it; the body rule and the relay retry already gated); igneum-exec 23 green. The node lane's direct commit on release-0.3.19-node is the one the pin takes; the shapes reconcile at the next merge; the switch must live on the oracle (one source). Exec lane's 0.3.19 tips: node exec-sync-0313 HEAD, app exec-app-0314 731268a1. Its "0.3.19.1" (the finality-horizon skip) is a four-part number and so 0.3.20 material.
## 23. The pin: release-0.3.19-node = dc141409 (the node lane, 08:36Z)
e69e8a39 plus the oracle switch: `ProofOracle::active_from` carries `proving_consensus_verify_daa`; the body rule, the IBD proof fetch and the relay retry apply to blocks at or above it only (one source, on the oracle: igneum/exec/src/proving.rs:1499, read at flow.rs:1024 and body_validation_in_context.rs:47), so at never a node demands no proof of any block; the sink takes the switch from Params through proof_sink (daemon.rs:1018). Box at dc141409: igneum-exec 27, consensus-core 123, kaspa-consensus 111 plus the two moved targets, p2p-flows 37, kaspad and testing-integration checks clean. The exec lane's own commit ac6e32cd on exec-sync-0313 has the same shape and stays as the record (reconciled at the next merge). Both chains restarted on dc141409 at 08:4xZ (scratch r0319-ship); the pairs rebuild by the build-server lane. Canary reads for this tip: the block stage passes 2,728 and every record-carrying block with no "Proofs: asking" line, the IBD-end line with its finality time, the synced line, the app's eth_ poll alive throughout, the restart reads, the cases, and on pool-1 after the publish the first slow "GetBlockTemplate N ms" line. The 0.3.19 cut line is that canary's pass; the node lane then names the devnet DAA for the per-block subsidy height (N8).
**Hands and seed pairs on dc141409 (the build-server lane, 08:4xZ, pairs with igneum 5010536a):** hands igneumd 8be8a5a5 (57,460,512 B), igneum-miner 92740aea; seed-class igneumd 4910352b (56,154,320 B, GLIBC_2.34), igneum-miner 9d7c4b8a; held for the line after the miners.
## 24. Builds on dc141409 (08:43Z on)
| piece | commit string | sha256 (first 8) | bytes | note |
|---|---|---|---|---|
| Linux igneumd, box native (the canary sha) | dc141409 | 3f9aca09 | 57,461,792 | GO sent to the fleet 08:5xZ; the form from the wipe on c18-1 |
| Linux igneum-miner | | fac45489 | 10,213,112 | unchanged |
(cross, suite, integration check, seed, hive, Mac, DMG, inputs, Windows run, staging: rows as they land; the first chain start on this tip at 08:37Z had picked up the hotfix tree's script copy and was stopped at 08:39Z; its one native build, d712b498, is the 0.3.17 binary again and is not used)
## 25. The canary on dc141409 (c18-1, from the wipe 08:51:50Z)
Node 3f9aca09, POLL_EXEC=1 and the exec-status fields; the e69e8a39 node stopped by its kill file (frozen at 2,728 blocks since 08:00Z). The form prints the count of "Proofs: asking" lines and of "carries N proof record" IBD errors beside the IBD-end line. Clock: headers through about 09:27Z, the block stage past 2,728 about 09:35Z, synced about 09:50Z, mining reads to 10:00Z, the restart reads to about 10:15Z, then the relay and poison cases; pool-1's "GetBlockTemplate N ms" line after the publish.
## 26. Owed to the 0.4.0 cut (the launch-pack lane, 08:5xZ; not this tree)
- The signing step (docs/plans/launch-pack.md section 2.4 on master 9b98b837, gate LG-3 in docs/plans/testnet-go.md): implemented only once the certificates exist (Windows EV Authenticode and an Apple Developer organisation account, both under Igneum Labs LTD, enrolled the day the entity exists; the owner approved both 7 October 2026). Until then 0.4.0 ships unsigned and the download page says so. The step keeps ship-app.mjs's list and adds: windows.yml signtool through the CA's cloud KSP (two secrets; unsigned and marked so without them), Inno SignTool=; ship-app.mjs fetch osslsigncode verify (CN Igneum Labs LTD, SHA-256, timestamp; unsigned fails); build-dmg.sh codesign Developer ID with runtime and timestamp, notarytool --wait Accepted, stapler on the app and the DMG, no quarantine strip; publish-manifest.sh signed_by and notarized per platform, --public refused without; ship-app.mjs verify osslsigncode and spctl --assess on the live files; the download page and the Discord card say "Signed by Igneum Labs LTD" only when the manifest does; tools/ci/signed-release-check.sh in pre-push. Keys never on igneum-build-1.
- docs/analysis/income-tiers.md regenerated from tools/launch/income-tiers.json at the 0.4.0 cut, the rows re-measured on class v4 (node tools/launch/income-tiers.mjs; --check in pre-push).
Names to build against (the launch-pack lane): GitHub secrets IGNEUM_CODESIGN_ACCOUNT and IGNEUM_CODESIGN_KEY (TOTP seed or API key by the CA; absent on forks and PRs, so unsigned and marked); notary keychain profile igneum-notary (xcrun notarytool store-credentials, once on the Mac); the codesign identity "Developer ID Application: Igneum Labs LTD (TEAMID)" read from one place (an env or a packaged-config field), the team id dropping in without a code change. Values reach the shipper by relay, never the repository (launch-pack.md section 5, items 3 and 4).
**Branch tip past the pin (the node lane, 09:04Z):** release-0.3.19-node = aea0ca5c on the mirror, on dc141409: the proof archive (ledger N9's second half; every carried record's proof kept under the exec db dir's proofs/ for the pruning window, served to a joiner's IgneumRequestProofRecords past the pool's 600-block window, dropped below the pruning point; no digest field, no behaviour on the devnet at never). Box: igneum-exec 28, p2p-flows 37, kaspad and testing-integration checks clean. **The pin stays at dc141409** (the canary running on it since 08:51Z; a re-pin only on a FAIL); aea0ca5c is 0.3.20's first node commit otherwise.
**Suite coverage note (the node lane, 09:1xZ):** `processes::pruning_proof::igneum_m20_tests::witnesses_are_checked_in_epoch_order_under_their_own_seeds` fails deterministically (MissingEpochSeed(1, _, 109) not matched) on an untouched e69e8a39 worktree with `--features igneum-pow` at any box load; it sits behind `#[cfg(feature = "igneum-pow")]`, so no lane's suite line (`cargo test -p kaspa-consensus` without the feature) compiles it, which is why 111 reads green. Owner: the M20 pruning-proof witness work (the epoch seed table of a proof-synced node). Not a publish blocker by itself (the gated path is what the live binaries run; the red is a test or seed-table reading), but the suite line must either carry the feature or the red must be fixed before "all green" covers it: a row for the 0.3.20 suite rule.
## 27. miner-ui-5 is the 0.3.19 app (the project lead's word at 13:1x UK: "deploy the miner look")
Taken onto release-0.3.19 as five cherry-picks on the miner-ui-4 chain: 2b4775b4 (the ladder's engine half: ladder.json, api/ladder chain facts, the block card), 449fb207 (the ladder on every page, the count-up, the block card, Earnings in IGN), b49db57d (the plan, the captures, the first-share runbook, the Discord rules), 3973ff8b (live-dag.js 2.0.1, the site lane's real-data options), e9fe1106 (u04 and u14 reshot); tip a199160d. Gate: app 197 + 28 + 8, UI 47 (notices 8, tune-line 5, update-card 4, view 30), all green. Next: push, windows.yml, the DMG (after the dc141409 Mac chain), staging; the read-back line adds "app miner-ui-5" and the pack's two shas. If the canary slips past 15:00 UK, main decides whether the app ships alone as 0.3.19 on the 5899f603 pin and the feature node becomes 0.3.20.
## 28. The prover pair (main's order, 09:0xZ): handed over, verified, the CI check in
The fleet's 14 standing provers had proved nothing since the hotfix: their harness (tools/fleet/box-prover.py) exported igneum_exportSegments from the restart block 27276 to the tip on every claim and replayed 133,000 blocks through 72142, where every port (the 6 October floor exporter, master's 6c3cc8b9 core, the kit's 263bf4ce) computes state root 0x8e1bef4b against the nodes' 0x9851d7e2 (hub-1, p1-5090 and PC 2's 0.3.17 node agree: 27276 0xed27bb2d, 72141 0x103190f4, 72142 0x9851d7e2, 72143 0x212e7703). The shipped prover never replays that far: the app exports [first-1, last] with the account dump (prover.rs, the 0.3.14 rule). On p1-5090 the kit's pair (host 71bc2438, export 263bf4ce; the same bytes in the 0.3.17 and 0.3.19 kits; the proving crate and the exec types identical across both trees) fed the app's form reads "account dump: 83 accounts after chain block 160829, state root equals the node's; replayed 8 segments, every state root equals the node's", so the pair is right; the fleet rolls it one box at a time with box-prover.py exporting the app's way, the two pairing lines read after one segment. The from-27276 divergence at 72142 is with the node and exec lanes (not on any proof path). "e809e396" is not a prover on the box (the hands run igneumd only). CI: tools/ci/prover-pair-check.sh (the prover's evm-types tree equals the pinned node's; --self-test; in pre-push, 32 checks) reads ok on the pin. One oddity for the node lane: igneum_exportSegments ["0x2747d","0x27485"] answered with segments 160829 to 160837, 64 blocks below the ask.
## 29. The 0.3.20 tree as of 10:2xZ (after the split and the Mac's reboot)
- Numbers: 0.3.19 = the app-only miner-ui-5 cut on pin 5899f603 (release-0.3.19); this tree = 0.3.20 (release-0.3.20 app, release-0.3.20-node = dc141409 on the mirror, the canary on it running as 0.3.20's gate); decimals 0.3.21 unless main says otherwise.
- Node side to come on release-0.3.20-node (the node lane): aea0ca5c the proof archive; the amended class v4 (CLASS_SIGNAL_V4 = 5; byte-4 signals never count; the kaspa-pow vector test; the daemon's window line naming object and sub-version; igneum-pow at the hash lane's a0aaca92 with the seven packs under the sub-version-1 stamp); the exec RPC listener watchdog (a dead listener rebound once with a log line, a second death within a minute exits; gated on the every-method test and a kill-the-listener unit test); N8's subsidy height (DEVNET_PARAMS at the cut). The flip arithmetic (the Counter ASIC lane, plan 6.6 on ca3-v4-node fa5bc9e6): the two v4 fields publish only after the one-sweep rollout and every worker on the 0.3.20 tree; the floor moves to the publish DAA + 604,800 rounded up to the epoch boundary (882,000 for a 12:00 UK publish on 7 October; recomputed from the live DAA at the publish), the window 86,400 unchanged; the earliest flip about 6 days 10 hours after the publish, never before every node has had the sweep plus a week. The rollout clock for that: 32 minutes for the 14 standing boxes under the lock rule, the hands and the seed about 3 minutes after, the Mac and PCs within minutes.
- App side: ember-heat (worktree igneum-wt-ember-heat, 7c779035, cd1034a2, 0d5fc6d2 on e9fe1106: heat mode in the engine and Ember, the region and price step at first run, the cost-against-rent row, two gate checks; app 198, UI 56, gate 32 green), main's word; its PC 1 4-hour hold is a runbook, not a gate. The ui-ota channel (the same lane): a "ui" object inside the signed manifest body, one signature; the bundle under dl/ as the installers; ui.version three-part; publish-manifest.sh gains --ui <tar> (the one writer); the kill switch is the object's absence plus a fallback on any mismatch; a CI check that the manifest's ui.sha256 equals the public tar. The 0.3.19 gate module (execrpc) and version carry over at the merge.
- Ledger: N10 (eadae138): the 72142 port-versus-node root is the thin-record export after a snapshot cut at FULL_RECORDS 1,200, not a rule; the exec lane owes the one-time re-execution that fattens thin records and an exporter that refuses a thin segment; "accounts" in an export is the tip state.
- The Mac rule (main, after the 10:5x UK crash): the Mac builds only the macOS binaries and the DMG, one at a time under the build lock; every other build, suite and the Windows cross-build on the box or a PC; the app gate runs on the box.
**Node lane, 10:3xZ:** (1) the exec RPC listener watchdog is written for the node line (`rpc::serve_watched`: the listener task polled every 10 s, rebound once on a death with "exec RPC listener died: {reason}; rebound on {listen}", a second death within a minute exits 3 "so the app sees it"; gated by the every-method test and a tokio kill-the-task test). (2) **The igneum-pow of the 0.3.20 cut is the hash lane's 8c728ca3** (ca3-v4-amend), not a0aaca92: a0aaca92 keyed the load-source rule on the whole class with the shadow's pass count inside, so the base program moved with the ladder rung (caught by the fork's ladder test on the box 10:06Z) and did not compile against dc141409 (no chain_program_shadow); 8c728ca3 keys it on the class with the pass count set aside and carries the ladder igneum-pow underneath; the pinned ids do not move. (3) On the node line for the observer: igneum_claimSegment, igneum_getProofClaims and `claims` on igneum_getProofRecords (the claim posted before a prove), plus the app lane's four finality methods. Tips as each lands.
## 30. The dc141409 canary: synced 10:29:19Z, every decisive read clean (the fleet)
c18-1 (RTX 3070 pod, wiped datadir, from 08:51:50Z; the Mac's reboot cut the form's shell at 10:5x UK and it re-attached): synced at 141,357 blocks (headers 141,617, 3 peers). IBD-end line: "10:29:13.927+00:00 [INFO ] IBD with peer 213.173.107.74:16516 completed successfully; finality time: bodies 16592238 ms over 141699 blocks, virtual 1061858 ms over 36183 changes, weight tables 968243 ms over 359709 tables (3832285840 blocks walked), signatures 711416 ms over 439307, persists 40537 ms over 3324"; the relay catch-ups after it a second each. Counts: "Proofs: asking" 0, "carries N proof record" 0 (the oracle switch holds), SendPingsFlow 0, idle-drop 0, the class-signal warning 1 line, 6 IBD sessions, 2 "completed with error" before the re-attach (lines owed with the restart reads). The app's poller: 4,217 eth_getBlockByNumber calls, 0 errors, 0 non-null, the node alive. At the synced line the exec layer read "exec not synced: this node's consensus starts at pruning point 36a7ba0d" (executedTipHash null). Next: ten minutes of mining, the hub read about 10:40Z, the restart reads to about 10:55Z, the cases on the fresh pods. Prover roll 7 of 13 paid; hub-1's prover moved to the pair and the [first-1, last] export.
**App side taken for 0.3.20:** ui-ota 0247b065, c337f768, 10c881dc (on miner-ui-5 b322e9fa: the "ui" object inside the signed body plus the entry's own signature, kept; "size"; src/uiota.rs; Settings > Interface; publish-manifest.sh --ui/--no-ui; tools/ui-ota/publish.mjs with --verify as the post-deploy step; ui/VERSION 1.0.0 embedded, 1.0.1 the first bundle, min_engine 0.3.20 so a 0.3.19 engine ignores it) and ember-heat 7c779035, cd1034a2, 0d5fc6d2; both green on the box. The miner-reliability branch (workers start only when READY = synced and an executed tip; the node watchdog never counts the catch-up; the restart ladder with no permanent fault; fault lines to the intake) is main's call: a 0.3.19 follow-up on the same pin, or this tree's app.
**Fleet kit rule (main, 10:3xZ):** one prover identity per box; the kit never ships an identity file; box-prover generates its own at first start from the box's label and keeps it in the registry row; a shipped or duplicated identity is refused at start with a line. Migration on the shared boxes (9e4ba6b0 on three, faa34a1a on one) one at a time, prover only; prover identity keys are not vote keys (no weight, no signal), so the lock rule does not apply.
**Main (10:4xZ): miner-reliability rides 0.3.20's app** with ember-heat and ui-ota (ee09ae8b, docs only, goes with its three); no app-only follow-up and no renumber. One exception: if the reliability lane's watchdog-clock and export-lock changes land small and green before the node line is ready, main decides an app-only 0.3.20 then (the feature node would become 0.3.21). PC 1 today: the Arc held off and the packs-ahead restart on 0.3.19.
## 31. Rows from PC 1 on 0.3.19 (11:3x to 11:4x UK)
- The template path on the 0.3.17 node: on PC 1 (24 identities, 3 cards x 8) every getBlockTemplate times out at 5 s on every identity ("template fetch timed out (5 s) for identity N", 59 + 43 + 28 lines in two minutes), no STATUS line; the 0.3.19 watchdog treats it as the miner's heartbeat ("waiting for the node to answer block templates") so the cards wait rather than fault. The node logs no per-request time on this tree (the timers are 500ddd66, 0.3.20). Mitigation today: identities down to 2 per card by the project lead's tap (no signed job can set identities: a script may not POST /api/cards, and the runner's --cards-off only toggles enabled; a runner option for identities is a small jobrun.rs change, main's word). The fix is the 0.3.20 node on PC 1 first (the memoised tally 500ddd66; fd7de1b4 the off-path refresh is 0.3.21 material unless main moves it).
- Two app faults for the reliability lane's rules, both 0.3.20's app (main): (1) engine.rs:3032, the runner's --stop-miners hold is not released while a following job runs (the resume fires only when job_hold is set and no job holds the miners), so a read-only watch job kept both cards off for three minutes; (2) orphan igneum-miner.exe processes the app no longer tracks (two alive under --stop-miners with their rows at pid 0, one after) hammer the node's template RPC beside the tracked miners, part of why PC 1's template calls ran past 5 s. Sizing of the orphan-kill for an app-only 0.3.20: below.
- The fleet's prover-identity finding was withdrawn (no box shares a key; the "shared" hashes were a carrier block's record list); no migration. The kit rule stands on another ground: `igneum-miner key-hash <label>` is a pure function of the label, so the 0.3.20 kit seeds the identity from the box, keeps the hash on the registry row, refuses a duplicate.
- The node lane's IBD-end read (ca3-v4-node d49c71d0): the weight-table cache (64 tables, cleared past that) made every 2,000-body IBD batch walk the window again (359,709 tables for 5,335 checkpoints, 968 s); fix on release-0.3.20-node: WEIGHT_TABLE_CACHE = 1,024, oldest-first eviction, never a clear, a unit test; bodies 16,592 s is thread time behind the finality state lock, not CPU; the next fresh join on the pod should read 60 to 70 minutes.
**Main (11:5x UK):** a one-off exception to the 6 October rule, the project lead's word ("you will have to fix it"): exactly one POST to /api/cards on PC 1 from the Intel lane's scratch script (not in the tree), the 5090 and the 9070 XT to 2 identities and the Arc off; the engine restarts the workers on the change. The permanent path is the reliability lane's signed `cards` job kind (per-card enabled, identities, power_pct, through the app's own card path, read back), which replaces any runner option: 0.3.20.
## 29. The app side assembled on release-0.3.20 (11:5x UK)
Picked by hash onto ca1742cd, in order: miner-ui-5 810bf5a1 and b322e9fa; ui-ota 0247b065, c337f768, 10c881dc, ee09ae8b; ember-heat 7c779035, cd1034a2, 0d5fc6d2. A merge of ui-ota was tried first and aborted (14 files, its base is miner-ui-5's lineage, which this tree carries as picks). Five conflicts, each resolved by union: publish-manifest.sh keeps Ember's `activation_passed` form and takes ui-ota's `--ui`/`--no-ui` arms, UI block and the `"${UI:-}"` argument (one python call, `sys.argv[1:13]`); the Settings literal in config.rs and the SettingsState literal in engine.rs carry both heat and `ui_builtin`; app.js's export carries both lanes' names; pre-push.sh runs the ui-ota self-test and the heat gate reader. The Mac's `igneum-ota-sign` was rebuilt under the build lock (the stale binary lacked `sign-ui`, the pre-push self-test read RED). UI tests on the box: heat-region 8, notices 8, tune-line 5, ui-ota 4, update-card 4, view 30, all pass. The app gate (`cargo test --release`) runs on the box; the push follows its green.
**Held in the 0.3.20 verdict (the fleet, 11:4x UK):** isSynced reads false at the tip on dc141409 (7 of 12 reads false with blocks equal to headers and no IBD session; a 17-read sample 13 false; the 0.3.17 control reads true 12 of 12). Mining, acceptance and the exec follower unaffected. Relayed to the node lane: a fix on the node line or a ruling that the kit reads the tip. The publish waits on that word.
**Main (12:0x UK), the Arc B580 for 0.3.20's worker kit:** Intel's OpenCL compiler folds the kernel's `rotr_var` helper `rotate(x, (0u - n) & 31u)` into a left rotate by n, so every variable right-rotate is wrong on Intel. The fix is worker-side only: on an Intel platform the worker rewrites that one helper line to the shift form before clBuildProgram, behind the vendor check; no consensus or pack change; the vectors self-test already refuses the wrong hash. When the Intel lane reports bench d green on the Arc (vectors pass, a rate row), its host.c patch goes into the shipped igneum-worker-opencl for 0.3.20 (Windows and Linux) with a unit test that the Intel path builds the shift form and the AMD path is unchanged; the Intel vendor badge and card row from intel-arc ride the same cut. Asked of the Intel lane: the patch as one commit, the test and its command, the badge and card-row hashes. The 0.3.20 worker build holds on that; nothing else in the cut waits on it.
**The fleet (12:0x UK):** the 0.3.20 cases (relay and poison against c18-1 on dc141409) start at about 11:20Z after a re-rent (relay on a 3090, poison on an A5000); the ten-member pool window keys on CASES END; prover roll 10 of 13 paired.
**The Arc fix is in (12:2x UK):** the Intel lane's bench d green on the Arc B580 (run-ia-arc-bench-20261007-d, 11:20:41Z to 11:22:26Z, the Arc alone through --cards-off; self-test PASS on class v4, the v3 control and the live pack, 96 of 96 vector lanes each; fingerprints f410c731b6bc2d31 v4 and 90f794dd556f7a3b v3 equal to the Mac's and the 5090's; 11.011 MH/s on v4, 11.019 v3, 11.002 live pack, 10.882 with --exchange local, memprobe ceiling 11.02; the lane-0 register trace identical to the CPU interpreter in all 55,809 snapshots). Picked: 26e135a3 → 9088293a (proto-opencl/intel_rotr.h, host.c +17, test_intel_rotr.c, the pre-push run line; the C test passes on the box); 18453bb1's six app files as their own diff → adf79cec (detect.rs, sweep.rs, app.css, app.js, view.test.mjs, ui-mock server.mjs; the view test's prove sentence in this tree's wording). UI tests on the box after: heat-region 8, notices 8, tune-line 5, ui-ota 4, update-card 4, view 31, all pass. Still to come from the Intel lane: the cards_leave_off runner param (jobs.rs, jobrun.rs, publish-jobs.sh --cards-leave-off), hash when its box tests are green.
**App gate green on the box (12:28 UK):** `cargo test --release` on 5b3bd023 (211 + 28 + 8, rc=0, 11:26:51Z) and again on 357b88b3 after the Intel changes (213 + 28 + 8, rc=0, 11:28:34Z; the two new tests are detect.rs's B580 rows). The pre-push gate's 34 checks pass on the tree, the ui-ota self-test among them. release-0.3.20 pushed on this row.
**Main (12:3x UK): hold for the isSynced fix.** The 0.3.20 app's execrpc gate and the worker-start rule both key on the node reporting synced; a flapping flag would hold workers back on the machines the cut is meant to make plug-and-play (PC 1's morning in a new form). The node lane carries the fix as a blocker on the node line with a test; the pin waits for it. If it slips past 16:00 UK, main hears the node lane's estimate and decides again. Everything else as set.
**cards_leave_off in (12:33 UK):** the Intel lane's 9e794503 → afb0cfe9 (jobrun.rs: a run job's --cards-off cards restored as OFF with identities and cap kept when the job's bool param cards_leave_off is set, the report line "cards LEFT OFF ... persisted by the app"; jobs.rs kinds doc; publish-jobs.sh --cards-leave-off emitting "cards_leave_off": true beside cards_off). App gate on the box, third pass: 214 + 28 + 8, rc=0, 11:32Z. Nothing else from the Intel lane for the cut. The reliability lane's hashes are app 38a30397 and fork f067f7c1, held until its pod injector finishes (about 45 minutes) and it sends the lines.
**The fleet (12:4x UK), prover roll: a 12 GB prover on the open devnet is paired and unpaid.** p2-4070-1 with the 0.3.17 pair: 7 segments "pair ok", 56 shards accepted, 0 refused, and 0 of 8 claims paid in 25 minutes (7 segment_refused "segment already paid; end to end 203 s", one "does not chain to ... which is proven"; 4 held, 3 held_expired). Its claims are fresh at claim time (margin 432 to 518 DAA, 5 to 7 candidates, rank_by fnv), so a 24 GB box claims the same segment and pays it inside the 4070's 203 s. p1-4070 paid once this morning (2.6 IGN after 176 s). Per tier: a home prover on a 12 GB card earns nothing while a 3090 or 4090 is awake on the same segments; the fix is the claim rule (a settled-depth claim with a per-key reservation, or the candidate hash spread over keys), which the node lane carries on the 0.3.20 line; until then a 4070 prover is a verifier that never pays, not a box fault. The roll continues with p1-3080 and hub-1; p2-4070-1 recorded "pair ok, unpaid (race)". Proving feed 11:35Z: provers_10m 4, 62 shards, lag 248 s. Asked of the node lane: confirm the claim-rule change is on the tip I pin.
## 30. The node line: release-0.3.20-node = 8097d600 (the node lane, 12:5x UK), the cut rule
8097d600 = dc141409, the archive aea0ca5c, then one commit: isSynced is the consensus rule alone on GetInfo and the template (headers and blocks at the tip), `behind` answers from the hook's stamp and never from a lock try, the finality catch-up reported apart as `finalityBehind` on igneum_getNodeInfo (the fix main holds for); the amended class v4 as object 5 (byte-4 blocks never count; devnet epoch-0 vectors pinned; the window line names the object and sub-version 1); the weight-table cache bounded at 1,024, oldest-first; the template snapshot refreshed only while templates are wanted (join bench 220 to 92 ms a checkpoint); the submit reply ahead of the virtual state; template wait 100 ms; igneum_getFinalityWeights and igneum_getFinalityCheckpoints {last} with signers. igneum-pow pair = the hash lane's 8c728ca3 (not a0aaca92). Suites green on build-2 12:28 to 12:37 UK (consensus-core 123, kaspa-pow 17, exec RPC, four finality tests, flows, rpc-service). Binaries building on build-1 for the two gates (mixed-version Devnet 2 beside the 5899f603 pair, the digest test).
A second commit follows behind the gates: igneum_getFinalityKey, igneum_getProofRecordsByKey, the observer's claims (igneum_claimSegment, igneum_getProofClaims, claims on getProofRecords), the settled claim floor for the provers (the fleet's 4070 race), and the listener watchdog (N7's macOS shape); its suites on build-2 now.
**The cut rule (shipper, 12:5x UK):** the pin is the second commit if its suites and gates are green by 15:30 UK (the watchdog is in 0.3.20's scope, the claim floor answers a live finding); past 15:30 UK the pin is 8097d600, the rest goes to 0.3.21, and main hears at the 16:00 checkpoint with the node lane's estimate. Asked of the node lane: its estimate, the commit string and gate lines on green, and what the kit's wait steps read on this line (isSynced alone, or finalityBehind too). Note: two notes meant for the node lane went to ae892a8b0f78fe31c by mistake earlier; the node lane is a283f5f0d364ceef0.
**Main (13:0x UK): the cut rule accepted, two conditions on the second commit.** The claim floor ships with its own test line (a 12 GB prover paid at least once in the ten-member window, or a harness equivalent); the watchdog with the N7 shape reproduced then clean; both named in the tip. Publish order unchanged: canary from the wipe on c18-1, PC 1 first, then PC 2 and the Mac, one box at a time with lock lines. The pin is reported the moment it is named. Sent to the node lane and the fleet (the fleet may be asked for the 12 GB line on a pod inside its pool window, the p2-4070-1 read).
**The node lane's estimate (13:1x UK), UK clock:** second-commit suites green about 13:35; the commit on release-0.3.20-node right after (one line on top of 8097d600); its binaries on build-1 about 13:50; the two gates about 14:15; the fleet's 12 GB line about 15:00 if started by 14:20. Any slip past 15:30 pins 8097d600. The conditions as the node lane will meet them: the watchdog's line is its unit test (the listener task killed, nothing answers, the watchdog rebinds once, the exec RPC answers again, a second kill reaches the exit hook); a live reproduction from outside the process is not possible (a task death, not a signal), so the test is the line, named in the tip. The claim floor's line comes from the fleet on a 12 GB pod: igneum_getProvingStatus settledNumber/settledDaa/settledBy and `settled` on every igneum_getAssignedShards row, box-prover claiming only settled rows. The kit's wait steps on this line: synced = GetInfo's isSynced alone (headers and blocks at the tip, as 0.3.17 read it); finalityBehind for display and the app's "catching up finality" line only; box-prover does not wait on finalityBehind (settled rows answer it).
**The floor at the publish (the node lane's note, the coordinator has it):** the live override file (6 October 22:49Z) carries program_class_v4_activation_daa 831,600 and window 86,400, so every 0.3.17 node signals byte 4 today and flips to the OLD v4 stream at epoch 231, about 13 October 09:00 UK, whatever is signalled. The 0.3.20 file must carry the moved floor (publish DAA + 604,800 rounded up to the epoch boundary, about 882,000 for a publish today), and the sweep must replace every 0.3.17 node before 13 October 09:00 UK, or the straggler forks alone then.
**Main's rulings (13:2x UK).** (1) The watchdog's unit test (listener task killed in process, rebind once, exec RPC answers again, second kill reaches the exit hook) meets the condition; named in the tip, no live line. The claim floor's live 12 GB line stands. (2) The binaries publish stays on the 15:30 UK pin rule. (3) The floor-moved live file (program_class_v4_activation_daa = publish DAA + 604,800 rounded up to the epoch boundary, window 86,400) is a live manifest change and goes out only on the project lead's explicit word: staged beside the release with its digest and a one-line diff against eada4bda, named in the 16:00 UK report, NOT published. The binaries publish carries the live file as it is (eada4bda). (4) The sweep of every 0.3.17 node (fleet, hands, seed, PC 1, PC 2, the Mac) runs with the binaries publish and must finish before 13 October 09:00 UK; that date goes in every rollout lock line. (5) The 16:00 report confirms what a 0.3.20 node on the OLD file does at epoch 231 (the Counter lane: it flips to the amended stream and the devnet stays whole if every node is 0.3.20), so the project lead chooses between the file move and the sweep alone.
**Epoch 231 on the old file, confirmed by the node lane with the reference (13:4x UK):** `program_class_for_epoch_signalled` (consensus/core/src/igneum.rs line 452 on 8097d600) answers the floor first through `program_class_for_epoch` / `program_class_for_epoch_at` (line 320, the floor rounded up to the epoch boundary): V4 at every epoch at or past 231 for 831,600; the signal branch is consulted only where the floor says V3. The class is one enum value on both binaries; the stream is each binary's own igneum-pow (`pow_class_of`, `Epoch::chain_program_shadow`): 8c728ca3 with sub-version 1 on 0.3.20, the 6 October generator on 0.3.17, and `program_id` (the "sub/" suffix on 8c728ca3) refuses the other's blocks. So at epoch 231 on the old file every 0.3.20 node flips to the amended stream; if every node is 0.3.20 by then the devnet stays whole with no file change; a 0.3.17 node flips to the old stream and forks alone. A 0.3.20 node does nothing differently between the old file and the moved file: the signalling byte is 5 either way (`template_signal_byte` line 499 stamps it whenever both fields are set), the window line prints whichever floor it reads, the rounding is the same on a different number, the tally and the seven-window rule untouched. For the project lead: the sweep alone works if every node is 0.3.20 before 13 October 09:00 UK and leaves no second file; the file move buys a week's margin for stragglers at the price of a digest change on every node (the sweep anyway). Digest: 8097d600 with the live file reads eada4bda, as 5899f603 does.
**Staging recipe (scratch r0319-app, 13:4x UK):** `digest-box.sh <igneumd on the box> <file|''> <port>` starts the binary on the box alone (no peers, own appdir) and prints "Consensus params digest: <hex>", killed by pid (the lost r0312/digest.sh replaced; the Mac builds and runs nothing). `stage-floor-file.sh <live daa> <igneumd on the box> <port>` writes ov16-floor-<floor>.json from the live sixteen-field object (floor = ceil((daa + 604,800) / 3,600) * 3,600, window untouched), prints the one-line diff and both digests. Dry run on the 0.3.17 binary at DAA 275,300: live eada4bda, moved floor 882,000 reads 844ebde1 (the moved digest is read again on the pinned binary at the publish). Not published.
**8097d600 alone is not a pin (the node lane's honesty line, 13:5x UK):** the whole finality test module on build-2 shows one red unit test on 8097d600, `the_template_answers_and_synced_reads_behind_while_the_catch_up_holds_the_state` (500ddd66's PC 1 rule test, line 37 asserting `behind` TRUE while the state is held, the expectation the isSynced ruling inverted); the code is right, the test is stale; on 8097d600 the lane had run the four new finality tests, consensus-core, kaspa-pow and the exec RPC suite, not the whole module. Shipper's word: the fallback pin is a one-line test-only commit directly on 8097d600 (behind false with the state held, the snapshot still answers), pushed as release-0.3.20-node, the second commit rebased on it without the test change; the whole module run on the fallback; the gate lines on the 8097d600 build carry to the fallback (byte-identical code, stated in the tip) but the pinned binary is rebuilt from the fallback commit for the commit-string read-back. No known-red pins.
**The fallback pin is on the mirror (14:0x UK): release-0.3.20-node = 6b94c823**, the test-only commit directly on 8097d600 (no code line touched; binaries byte-identical to 8097d600's; the two gate lines on the 8097d600 build carry to it, stated in the tip). The whole finality module runs on 6b94c823 from a clean worktree on build-2. The second commit (key methods, claims, settled floor, watchdog) lands as 6b94c823's child when its suites are green; both binaries rebuild on build-1 then (the fallback's for the read-back, the second commit's for the fleet's 12 GB line). The vendor worktree has the mirror branch fetched; the checkout happens at the pin.
## 31. The candidate pin 6a3432a3, the builds started (14:5x UK)
release-0.3.20-node = 6a3432a3 (the child of the fallback 6b94c823, itself the test-only child of 8097d600): the key methods, the observer's claims, the settled claim floor, the listener watchdog. Suites on build-2 13:46 to 13:48 UK: the whole finality module 25 passed, the exec suite 29 passed with the watchdog's test `rpc::watchdog_tests::the_watchdog_rebinds_a_dead_listener_once_and_exits_on_the_second_death`, kaspad, flows, rpc-service green; consensus-core 123 and kaspa-pow 17 on the same code. The fallback's own line: the whole finality module on 6b94c823 from a clean worktree, 25 passed. Gate rule (shipper): lines on one binary do not carry to a binary whose code changed; 6a3432a3 runs the digest gate and the ten-minute mixed-version gate on its own binary (lines about 14:20 UK with the commit string read back), the fallback on its own (about 14:40 UK); the first mixed-version run on the 8097d600 build is void (the path was replaced mid-run). The digest harness reads the thirteen-field b18ed271 and the sixteen-field moved digest by design; eada4bda on the pinned binary with the live file is the deploy gate's read.
igneum-pow: release-0.3.20 carries the hash lane's 8c728ca3 tree at 00249643 (the pair for both candidates; byte-equal). Asked of the build-server lane: the seed and hands pairs for both candidates, held until the pin and the canary. Asked of the Counter lane: that nothing past 8c728ca3 touches igneum-pow, and the hash lane's pairing line.
Speculative builds on the candidate from the ship worktree, box only, sequential (scratch r0320/build-candidate.sh, started 12:52Z): igneum-pow tests, fleet-native node (target-0320), hive class (target-0320-hive), Windows cross of the node and the app, the app's Linux binary; shas and commit strings at the end. If the pin falls back, the same script runs on 6b94c823. The Mac builds only its own binaries and the DMG, under the lock, after the pin.
**The amended class v4 packs (the hash lane, 15:0x UK), what the fleet reads on the canary.** igneum-pow 8c728ca3, byte-equal to 00249643's tree; program id 1a4230699a6b9c60 (generator 4, class v4, sub-version 1) on every v4 pack; the pre-amendment id c120d7963abdcd96 is the must-differ vector (tests/recheck.rs and the node's kaspa-pow test); the v3 control mx8-devnet-epoch0 id 73bcbfe8ccf988f1 fingerprint 90f794dd556f7a3b unchanged. Fingerprints 2^24 at base 0, equal on Metal, Apple OpenCL and the RTX 5090 (NVRTC 12.8 sm_120): v4-devnet-epoch0 867dbc45cfb36b4d (vectors 756301bf1739a7ee); v4-era-0 2146ecacc8c75a8e; era-1 fe52602393f6d3d4; era-2 3b206471a13912b4; era-3 c3f03c4a5d7333aa; era-4 f1dfd7209f15bb97; era-5 8c194da64fadf31d. Packs at proto-cuda/packs-ca3-v4 on ca3-v4-amend; the eight-pack kit zip sha256 889ec99976d2728b4b5035bfa476032e5b6a13b928968fc45236d5f25084aa39. G1 (d8859522): PC 2 run-ca3-v4-amend-g1-pc2-20261007 09:41Z, the 0.3.17 worker, self-test PASS on all eight packs, every fingerprint equal. The pairing line (igneum-pow 8c728ca3 against the fork's kaspa-pow, `test --release -p kaspa-pow --features igneum-pow`) is in flight on the box; the node lane's own run of the source-rule line on the 0.3.20 fork passed (1a4230699a6b9c60 equal, c120d7963abdcd96 differs, v3 unchanged, object byte 5).
**Interop fact from the void 8097d600 run (the node lane, 15:1x UK):** on the live sixteen-field file plus genesis_bits (one digest on all five nodes, f92a0675), the 5899f603 hub accepted every block the 8097d600 node mined, 235 accepted and 0 rejected, the old node's headers at version 1026 (block version 2, object byte 4) and the new node's at 1282 (byte 5), counts equal on all five at 472 before the restart step. So a 0.3.17 node takes byte-5 headers from a 0.3.20 node and relays them: the amended class's one interop question, answered. The run's four FAILED checks are the binary swap (n1 restarted two seconds before 6a3432a3's build replaced the path) and one harness expectation written for a file without v4 fields (`every_header_version_is_the_block_version`); the clean runs use the thirteen-field object. 6a3432a3's own gates started 13:53 UK, lines about 14:08 UK; the fallback's follow.
**Clock correction (12:55 BST, read from `date`):** the UK stamps in the rows from "Main's rulings" to the interop fact above ran ahead of the clock by one to two hours (the lanes' quoted "13:4x", "13:53", "14:08", "15:0x UK" included). The true times, from the commit times of the rows: main's conditions 12:40 BST; main's rulings 12:41; the epoch-231 confirmation and the staging recipe 12:43; the stale test and the fallback shape 12:47; the fallback 6b94c823 on the mirror 12:48; igneum-pow 8c728ca3 taken 12:51; section 31 and the speculative builds 12:52 (the script started 11:52:06Z = 12:52 BST); the pack table 12:53; the interop fact 12:54. The 15:30 BST pin rule and the 16:00 BST report stand on the true clock; the node lane's estimates (gates about 14:20, the 12 GB line about 15:00) are re-read against `date` when its lines land. From here every stamp in this plan is `TZ=Europe/London date`.
**The build-server lane (12:53 BST):** the four 0.3.20 pairs build serially on build-1 in its own worktree igneum-wt-bs0320 (igneum 00249643 detached, igneum-pow byte-identical to 8c728ca3; vendor/igneum-node-0320a = 6a3432a3, 0320b = 6b94c823): a hands (native 2.39), a seed (--ship seed, 2.35), b hands, b seed; artefacts in its scratch bs0320/<a|b>-<hands|seed>/; pairing line "pairs with igneum 00249643 (detached): igneum-pow 0.2.0", rustc 1.99.0 both sides. Lines (sha256, commit string, glibc need) as each lands; nothing to the hands or the seed before the pin and the canary. Found: neither 6a3432a3 nor 6b94c823 carries rust-toolchain.toml (the fork's copy is on fork master 37f1206b, not an ancestor); inside an app worktree rustup walks up to the app tree's pin, so every build on the line is pinned; a standalone checkout of release-0.3.20-node is not. Owed for 0.3.21 (not now: a file commit would move the pin and re-run the gates for no code change): rust-toolchain.toml on the node line.
## 32. Two defects on the line, the pin moves to b7cc37e7, no fallback commit (13:0x BST, `date`)
**N12, the watchdog and a held port (6a3432a3's own digest gate):** the gate's four nodes share one exec JSON-RPC port; the watchdog counted "cannot bind: address in use" as a listener death (rebind at 10 s, "died twice within a minute" at 20 s, exit 3), three of four nodes gone before the harness read their peers, where 0.3.17 and 8097d600 warn and live without the exec RPC. Digest facts came out right before the exits: thirteen fields a89be8a7 on both binaries, the sixteen-field object db9a85f9 refused with the mismatch line. Fix 09124180: a bind failure is a retry every poll with one line a minute and no death counted; a death after a successful bind keeps rebind-once-then-exit. Tests: `the_watchdog_rebinds_a_dead_listener_once_and_exits_on_the_second_death` and `a_held_port_is_retried_and_never_counted_as_a_death`; exec suite 30 passed.
**N13, the kept-datadir death (the fleet, starting 6a3432a3 on pool-1's kept 0.3.17 copy):** `called Result::unwrap() on an Err value: DeserializationError(Io(Kind(UnexpectedEof)))` at consensus/src/model/stores/virtual_state.rs:250. Cause: 10db4b61 (0.3.16 feature line, vote-or-burn and the signing bonus) added `silent: bool` to `BlockRewardData` under `#[serde(default)]`; bincode is not self-describing and ignores serde defaults, so the virtual-state row a 0.3.17 node wrote (three-field rewards in `mergeset_rewards`) reads short on every build from 10db4b61 on: dc141409, 8097d600, 6b94c823, 6a3432a3, 09124180 all die at start on any kept 0.3.17 datadir; no canary saw it because every canary wiped. Fix b7cc37e7: the store reads the live row in the current layout first; on a deserialization error it decodes the row as a mirror of the v1 layout, converts with `silent` false and rewrites it under the same key in the current layout; version suffix unchanged. Test green on build-2 (a v1 row in a temp DB: the current layout reads it short, the store reads and rewrites it, a second open reads first-try) plus the kaspad check.
**The pin rule now:** candidate b7cc37e7 (8097d600 → 6b94c823 → 6a3432a3 → 09124180 → b7cc37e7), igneum-pow 8c728ca3. No fallback commit on the line (6b94c823 dies on a kept datadir); if b7cc37e7's gates are not green by 15:30 BST, 5899f603 stays live and 0.3.20 ships later on green. New gate before the canary, whatever the pin: the kept-datadir start, the pinned binary on a copy of a standing 0.3.17 box's datadir on a scratch pod, the rewrite line as the pass (the fleet). The node lane's clock: b7cc37e7's build about 14:45 BST, the kept-datadir read about 14:50, digest and ten-minute gates about 15:05, the 12 GB claim line about 14:50 to 15:00 on the 6a3432a3 pod (claim code unchanged). The build-server lane's a/b pairs are void and rebuild on b7cc37e7; the shipper's speculative builds on 6a3432a3 stopped by pid (script 30520 and its child) and restart on b7cc37e7; the vendor worktree now at b7cc37e7. Also: the amended v4 packs were stale in the app tree (igneum-pow's recheck tests read the old program id c120d7963abdcd96 from program.json); proto-cuda/packs-ca3-v4 taken from 8c728ca3 as its own commit; tests rerunning on the box.
**Main (13:1x BST):** the fallback reading stands (b7cc37e7 by 15:30 BST or 5899f603 stays live and 0.3.20 ships later today on green). Three additions: (1) standing rule, every canary runs a wiped and a kept datadir, the kept-datadir start a named gate in every release, in docs/plans/release-rules.md (rule 4) and the miner-reliability register as its own fault class (asked of the reliability lane); (2) the Windows shape: the pinned Windows node once against a copy of PC 2's datadir (PC 2 only) before PC 1 gets the build (asked of the build-server lane, with the pairs moved to b7cc37e7); (3) the 16:00 report stands, but on green earlier the publish goes out on green with the clock time. The fleet has the rule and runs the kept start on the pod copy, then the wipe canary on c18-1, then the kept read on c18-1 before its restart step.
**6b94c823's gates on its own binary (the node lane; the lane's "13:56 to 14:08 UK" = about 12:56 to 13:08 BST):** sha b1b7d47b, string 6b94c823. Digest gate: thirteen fields a89be8a7 on both binaries (compat), the sixteen-field object db9a85f9 refused with the mismatch line; the one FAILED check `n3_has_no_peer` is the refused connection's reconnect in flight at the read (harness fix 36d3efdc: the minimum of five reads). Mixed-version gate, ten minutes on the thirteen-field file: digest b0afb2ee on all five, the 5899f603 hub accepted every block the 6b94c823 node mined (146 new, 246 old, 0 rejected), header versions plain 2, counts equal at 536, 695 and 785 through the clean join via the old hub, the join served by the new node, and the new node's restart; the one FAILED check `no_panic_in_any_node_log` is six "Address already in use" panics in the two old nodes' server threads at start (the gates overlapped on ports 29830/29831; now a 20 s gap). So 8097d600's code is clean on both gates by the checks that bear on the binary; the fallback is still not a pin (N13). From here only b7cc37e7 gets gates, on its own binary when its build lands (about 13:48 BST by the clock), lines about 14:05 BST with sha and string.
**Main (13:2x BST):** the driver check (the Intel lane's b9487dcf on driver-check: the table, the install thread with one elevated prompt, the manifest's drivers object, the row strip; box 219 + 32 + 8, cross green, pre-push 35, UI 32) rides 0.3.21 with the drivers manifest once the NVIDIA and AMD hashes are real; 0.3.20's app tree is closed. Clock: the node lane's "UK" stamps read an hour fast (UTC+2); every estimate is re-read against `TZ=Europe/London date` before it is quoted; the node lane and the fleet told to quote that clock. The 15:30 BST checkpoint is the real clock.
**The fleet's prover roll, p1-3080 (13:1x BST):** the pair right (28 "pair ok"), 0 paid in its history (2,167 claims), every proof since the pair dies at the compressed step at the memory wall (device_used 9,859 of 9,885 MiB), 29 of 29, miner off the proving step. Per tier: a 10 GB card does not prove on this pair; the failing prover took the 3080's GPU from its miner for 16 hours for nothing. On the shipper's word (fleet config, no key moves): PROVER=0 on p1-3080 with its lock line and the miner's rate before and after; the same reading for standing boxes under 12 GB; one rented 3080 for an hour to measure the lower-memory SP1 threshold. For main: the kit and the app refusing to start proving under 12 GB with the reason shown, until the measured threshold lands. hub-1 is the roll's last box.
## 33. The pin moves to c4459193; the 12 GB rule in the app; the roll complete (13:3x BST, `date`)
**b7cc37e7 struck:** its digest gate passed (12:14 to 12:16Z: thirteen fields a89be8a7 on both binaries, the sixteen-field object db9a85f9 refused, the live file's own digest eada4bda as 5899f603 reads it) and the fleet's kept-datadir read passed on p12-vast (12:17Z: "Virtual state: the row was written by a build before the silence field (a kept datadir); read as the v1 layout and rewritten in the current one (1 mergeset rewards)", the finality blob converted, 1,747 locks, no panic; a second start first-try; 6a3432a3 the known-failed shape), but its ten-minute mixed-version gate FAILED at the restart step (12:24:19Z): the node died on its own datadir LOCK (conn_builder.rs:167) because the 6a3432a3 watchdog sleeps its whole 10 s poll before checking shutdown, so every node from 6a3432a3 on takes up to 10 s longer to stop than 5899f603 (the fleet's "a 12-second timeout does not stop the node" was this) and a restart inside that window meets the lock. Fix c4459193 (b7cc37e7's child, rpc.rs alone): the poll in 250 ms steps returning the moment shutdown is set; test `a_shutdown_returns_within_a_second_whatever_the_poll`; exec suite 31 passed with the three watchdog tests. **The candidate pin is c4459193**, igneum-pow 8c728ca3; its build on build-1 from 12:30Z, digest and ten-minute gates on its own binary, lines about 12:50Z (13:50 BST). Carry ruling (shipper): the b7cc37e7 kept read is evidence, not the gate line; the gate line is the kept read on c18-1 on c4459193's binary before the canary's restart step. The b7cc37e7 pairs (hands bc6b3b3b / 240eb0a4, seed ffaa441d / 64207cc0) are evidence only; the build-server lane rebuilds on c4459193 and runs the PC 2 Windows kept-datadir job on c4459193's exe.
**Main (13:2x BST):** the attack-pass lane's F8 re-gate on object 5 heads to FAIL at 1.2x on nine of the first thirty seeds (worst p31 at 29.3x), far better than the old stream; 0.3.20 ships object 5 as it stands (the live floor flips every node to the OLD stream on 13 October otherwise); sub-version 2 on the Counter lane is 0.3.21's; the 16:00 report tells the project lead the floor move is recommended, not optional. Build slots: suites to build-2, builds and gates on build-1 (tools worktree fast-forwarded to master 1cf850c9; build-remote routes test and bench to box 2 by class).
**The 12 GB rule in the app (4c89372a):** MIN_VRAM_MB_PROVE_ANY 11,800 and PROVE_UNDER_12GB_LINE "proving needs a 12 GB card; mining continues" in provedefault.rs with `the_prover_refuses_every_nvidia_card_under_12gb_and_says_why`; the prover loop refuses before the sync wait when every present NVIDIA card is under it (status off, the sentence); the tile sentence in app.js (PROVE_MIN_GB 12) with its view test. App gate on the box: 215 + 28 + 8, rc=0 (13:13 BST); UI 31 of 31; the Windows cross of the app running. In the fleet's kit: box-prover refuses the same way (gpu-fleet f7d40af5, on 18 boxes at 12:14Z; p1-3080's line "RESULT refused 2026-10-07T12:14:30Z proving needs a 12 GB card; mining continues (memory.total 10240 MiB)"). PROVER=0 on p1-3080 (12:13:57Z, node and miner untouched, no key moved; lock line 98.6 percent at checkpoint 8931): the miner 38.5 to 39.8 MH/s before, 44.7 to 45.9 after, plus 17 percent.
**The prover roll complete (12:19:40Z):** 11 of 13 standing provers paid on the 71bc2438/263bf4ce pair (hub-1 last, 4.6410 IGN after 143 s); p2-4070-1 pair ok and unpaid (the claim race), p1-3080 prover off. Feed 12:25Z: provers_10m 5, 67 shards, lag 351 s.
**Prover kit facts from the 3080 hour (the fleet, corrected 12:3xZ):** every standing box's sp1-gpu-server is a binary built for its own card (p1-3080 sm_86, p2-3090-3 sm_86, p1-4070 sm_89, p1-5090 sm_120); a server for the wrong architecture fails every proof in 12 s with "CudaRustError: named symbol not found" and the miner never notices. Kit consequence (0.3.21 design, routed to main): one server per architecture picked by compute capability at install, or a fat binary. The 610-driver question is open again (the first host had both faults at once); one more rented 610 host with an sm_86 server separates them. The threshold series restarted on thr-3080b with the sm_86 server.
**For 0.3.21 (the Intel lane):** driver-check tip 1982dbc7 on release-0.3.20's 4c89372a (the stray #[test] at detect.rs:990 removed; the table measured: NVIDIA 617.42 f115c927…, AMD 26.9.2 593c1d73… with the https://www.amd.com/ referer, Intel 32.0.101.9034; box 219 + 32 + 8, gate 35, UI 32).
**The 3080 hour's number (the fleet, 12:33Z):** on thr-3080b (RTX 3080 10 GB, driver 570.211.01, the sm_86 server, p1-3080's own failed segment 163366..163373), at the DEFAULT SP1_GPU_ELEMENT_THRESHOLD the chain proof completes, rc 0, 66 s wall for the eight-block segment, device_used 8,642 MiB of 9,883 with nothing else on the card. p1-3080 fails because its miner holds about 1,547 MiB and 8,642 plus 1,547 passes 9,885 (its "free_mib=25" on every one of 29 failures). Per tier: a 10 GB card is a mine-only card OR a prove-only card, never both; the sentence "proving needs a 12 GB card; mining continues" is right for the default mine-and-prove install and stays; a 10 GB owner who wants to prove instead can, at the cost of the miner (box-prover's MINER=pause on the fleet; an app choice to route for 0.3.21). Lower thresholds reading now for whether the compressed step fits beside the miner's 1.5 GB; the number goes in the bench log with this sentence. The 610-driver host approved (one rent, the sm_86 server). Build-server lane: c4459193 as vendor/igneum-node-0320d, the sequence from 13:33 BST (hands, seed, the Windows cross of c4459193, then 6a3432a3's as the known-failed shape); the PC 2 kept-datadir job on those two exes.
**The wipe canary's clock (13:4x BST):** c18-1 is held by the 0.3.20 cases until about 14:00 BST, so a wipe canary on it would read synced about 15:40 BST, past the checkpoint. The shipper's call: the fleet rents a second one-shot pod of c18-1's class now and starts the wipe canary on c4459193's binary the moment its build lands (synced about 15:15 BST by the 98-minute class), with the kept read on the pool-1 0.3.17 copy and the restart step on that pod; c18-1 keeps the cases and the pool window. The wipe is the decisive read by rule (release-rules.md 3), so the pin never cuts without it and b7cc37e7's lineage does not stand in; a slip past 15:30 BST holds the pin to the wipe line and main hears the clock.
**Main (13:4x BST):** the clock change accepted as set: the wipe canary on the second rented pod is the decisive read, the pin follows its synced line, and a slip past 15:30 BST is reported as a clock, not cut on the other lines.
**The wipe canary pod (the fleet, 12:37Z):** c19-1, RunPod pod wpuke4tfu0vr49, RTX 3070 community, USD 0.13/h, c18-1's class; c4459193's igneumd sha256 45be9b02d1b002f5 (string c4459193 read back, build-1 12:34Z) and igneum-miner c7cfc40b; the dc141409 form (wipe, IBD from the pruning-point proof, synced, ten minutes mining with the exec poller, the hub read, the restart read on the kept datadir) with the kept read on pool-1's 0.3.17 copy armed behind the synced line. Synced about 14:20Z (15:20 BST) by the 98-minute class.
**The 10 GB tier, closed (the fleet, 12:3xZ; docs/bench-log.md "The 10 GB prover tier"):** below the default threshold the server dies before the compressed step ("Failed to read the response: early eof" at 12 to 13 s, about 5,000 MiB used) at 524288, 262144 and 131072 alike; the default is the only working value and completes alone at 8,642 MiB. Per tier: a 10 GB card is mine-only or prove-only, never both; the rule "proving needs a 12 GB card; mining continues" stands for the default install; a 10 GB owner who chooses to prove gives up the miner (MINER=pause on the fleet; the app's switch is a 0.3.21 routing for main). A 12 GB card fits both with about 2 GB spare, a 16 GB card with 5.8 GB. The 610 read runs on thr-610 (driver 610.57.04, the sm_86 server), one proof.
**CASES END, PASS on dc141409 (the fleet, 12:37:18Z; run from 11:18:10Z):** relay c17-relay (the 0.3.17 relay build, pool-1's kept 1026 datadir, 22 peers, 1,038 blocks relayed) and poison c17-poison (the poisoned copy, mining from 12:24:44Z) against c18-1 (dc141409): over 13 polls, poison wbv 0 and got 1 (one block at first contact, nothing after, the hub refused it), relay R_wbv 0, c18-1 T_wbv 0 throughout, T_from_relay 0, T_1026 headers seen and none accepted, blocks moving with the tip, powEngine igneum-pow; the hub holds 900 of c18-1's blocks in its last 900 and names it in 1 reject (the right outcome). The isSynced flap visible on this line (4 of 6 polls false at the tip), fixed from 8097d600. The three pods destroyed. The ten-member pool window rented at 12:37:36Z. The carry to c4459193 goes to main on the node lane's diff argument (every file from dc141409 to c4459193 against block and header acceptance, the version check, relay and peer handling); if any touches them the cases rerun on c4459193 beside c19-1 whatever the clock.
**The wipe canary started (c19-1, 12:38:55Z):** the node on a wiped datadir on c4459193 (sha256 45be9b02d1b002f5486d0f0108571c3b6042094113ad9da6f3d3d9ffc0072bba asserted before the put; the node's own line igneumd/2.1.0-c4459193, digest eada4bda8aa8368c, finalityBehind false, pruningDepth 108000, 4 peers at 12:39:26Z, IBD from the pruning-point proof), the exec poller every 5 s, the miner c7cfc40b staged. Clock by the 98-minute class: synced about 14:17Z (15:17 BST), the ten-minute mining read and the hub read to 14:30Z, the restart read to 14:40Z, the kept read on pool-1's copy right after. Polls every four minutes.
**The cases rerun on c4459193 (13:4x BST):** the node lane's diff dc141409 → c4459193 (13 files, +1,155 −48) touches one of the cases' four categories: consensus/core/src/igneum.rs sets CLASS_SIGNAL_V4 4 → 5 (the node stamps byte 5, the tally counts byte 5 and above); header_version_acceptable, signalled_version, class_signal_of and the IBD guard untouched; flow_context.rs keeps the relay broadcast's place and payload (submit_rpc_block returns after the block task, on_new_block spawned); nothing on inbound relay or peer paths; the rest is finality, stores, exec RPC and tests. By rule the cases rerun on c4459193; the expected shape is dc141409's with the header byte read 5 (the void 8097d600 run showed the old hub taking 235 byte-5 headers, 0 rejected). To land inside the checkpoint the fleet runs it now beside c19-1: the target a fresh pod with c4459193 on pool-1's kept 0.3.17 copy (synced in minutes by the N13 path, itself a kept-datadir read on the pinned binary), the relay and poison pods against it, CASES END about 15:05 BST if the pods are up by 12:48Z.
**The 610 read (the fleet, 12:40:14Z):** thr-610 (RTX 3080 10 GB, driver 610.57.04, the sm_86 server, the same segment) completes the chain proof, rc 0, 87 s, device_used 8,729 MiB. The 610-series driver proves; this morning's "named symbol not found" was the architecture alone (an Ada server on Ampere cards). The kit's driver rule keeps its floor (570 or newer, CUDA 12.8) with no upper bound; the rule that matters is one GPU server per compute capability, picked by nvidia-smi compute_cap at install (a mismatch fails every proof in 12 s and the miner never notices): 0.3.21's kit design for main. The 10 GB tier unchanged (proves alone at 8.6 to 8.7 GB, never beside its miner; 12 GB fits both with 2 GB spare).
**Main (13:4x BST):** all three accepted: the cases rerun on the pin's binary; the 610 question closed (driver floor 570, no upper bound; the per-architecture GPU server is the prover rule for 0.3.21); 1982dbc7 is the driver-check tip for 0.3.21. The cut set and the clock stand; nothing further from main until the wipe line or a slip.
**The cases rerun started (the fleet, 12:42Z):** target c20-1 with c4459193 (in/igneumd-45be9b02d1b002f5, the string read back) on pool-1's kept 1026 datadir (its start recorded as a kept-datadir read on the pinned binary: the rewrite line, then the catch-up from 129,398 blocks, 15 to 20 minutes), a fresh c17-relay and c17-poison, the dc141409 form on the target's synced line with the byte-5 header shape expected; CASES END about 14:15Z (15:15 BST). The pool window's daemon on pool-1 (03457d96) since 12:40:39Z with its ten members.
**c4459193 hands pair held (the build-server lane, 13:41 BST):** native glibc 2.39, 381 s, rc 0, pairs with igneum 00249643: igneum-pow 0.2.0, rustc 1.99.0 by the tree's own pin. igneumd 57,628,576 B sha256 a80ed39caf885d314f97ce88863afcb307cbb4acc45a10828b2443a41e5d27d2 (string c4459193 twice, 6 igneum-pow/src/ paths, the N13 line present, needs GLIBC_2.39); igneum-miner 10,214,648 B sha256 70a5180f30fab73fde0b3f33cfd37d2d68899eab70290b86c924e02980b09afd (8 paths). Held at scratch bs0320/d-hands/release/. The seed pair from 13:41 BST, then the two Windows cross builds.
**c4459193's node gates, both PASS on its own binary (the node lane; built 12:33Z, sha256 45be9b02d1b002f5, string read back):** digest gate 12:33:27 to 12:35:05Z: thirteen fields a89be8a7 on both binaries, sixteen fields db9a85f9 refused with the line and no peer, the live file's digest eada4bda unmoved. Mixed-version gate 12:35:26 to 12:45:39Z: digest b0afb2ee on all five, 215 new and 314 old blocks accepted, 0 rejected, plain header version 2, counts equal at 312, 441 and 529 through both clean joins and the restart step (the new node restarted 12:43:08Z on its own datadir and resynced, where b7cc37e7 met the lock), no panic in any log. The node side's lines are complete; outstanding for the pin: the wipe canary on c19-1, the cases rerun on c20-1, the 12 GB settled-claim line.
**The Mac's binaries start now (13:4x BST):** on the node gates' pass the Mac builds its own igneumd and igneum-miner from c4459193 under the build lock, one at a time (the Mac rule), so the DMG follows the wipe line by minutes; a canary fail voids them.
**The cases rerun's target up (the fleet, 12:45:46Z):** c20-1 on c4459193 (sha 45be9b02d1b002f5 read back on the pod) started on pool-1's 0.3.17 copy with the N13 rewrite line and "state blob of layout 2 read and converted" once, panicked 0, catching up from 129,398 blocks: the pinned binary's kept-datadir read on a second pod. Relay j84mqzlai3yax9 and poison vnm1dlx8hkocq2 against it on its synced line; CASES END about 14:20Z (15:20 BST). c19-1's wipe IBD at 15 percent of the headers at 12:43Z, on the class. The Mac's node build started under the lock (scratch r0320/mac-build.sh: igneumd and igneum-miner, then the DMG).
**c4459193 seed pair held (the build-server lane, 13:48 BST):** zigbuild 2.35, 374 s, rc 0; igneumd 56,304,592 B sha256 4a2d8a8db0911a9236264ba7ef9ca8891e8a9df53f0892a695e176323cfaf415 (string c4459193, the N13 line, needs GLIBC_2.34 against the seed ceiling 2.35); igneum-miner 10,235,664 B sha256 18099e976b844653d2ccc7a2d37cef785d7537cbc3df042b073c7fcd8dd31fd0. Held at scratch bs0320/d-seed/x86_64-unknown-linux-gnu/release/. The c4459193 pair set is complete: hands a80ed39c / 70a5180f, seed 4a2d8a8d / 18099e97. The Windows cross of c4459193 from 13:48 BST, then 6a3432a3's, then the PC 2 gate job.
**Slip (the fleet, 12:5xZ; 13:5x BST):** the cases rerun's target starts from pool-1's kept copy 22,000 blocks behind the tip and this line walks the headers first (the morning's poison pod: 50 minutes on the same copy), so c20-1 syncs about 14:50 BST and CASES END lands about 16:10 BST, forty minutes past the checkpoint; no faster path. The rest holds: c19-1's wipe on the class (synced about 15:17 BST), p12-vast synced about 14:45 with the 12 GB line about 15:00 to 15:10. Put to main: (a) hold the pin to CASES END and publish about 16:15 BST, or (b) cut about 15:35 on the wipe line, the kept reads, the restart and the 12 GB line with the cases as a confirmation before the sweep's first box (the diff argument: the class byte is the only touch; the old hub took 235 byte-5 headers in the void run). The shipper's read: (a). The pods run on unchanged.
## 34. The 0.3.20 artefacts on the pin c4459193 (14:0x BST), staged for a one-step publish
**Main (13:5x BST):** (a) for miner-reliability: 0.3.21 takes both halves; 0.3.20's trees stay closed; its app half rebases onto the published 0.3.20 tree right after the publish. The report states what a 0.3.20 user still meets from MF-1 to MF-10 and what the pin removes (N13, N12 and the shutdown poll, the template latency memo).
**The shipper's box builds (scratch r0320, the vendor worktree at c4459193, app 4c89372a, igneum-pow 8c728ca3's tree):** fleet-native node (2.39) igneumd a8d08da5 57,628,128 B / igneum-miner 474273ce 10,214,648 B; hive class (2.31) igneumd b1c4e841 56,305,232 B / igneum-miner 4050c255 10,236,448 B; every igneumd carries c4459193 twice, no stub string, the N13 line; Windows cross igneumd.exe 49502cc7 52,569,088 B / igneum-miner.exe b4871b5d 11,219,968 B; the app's Windows exes igneum-app.exe b73120f1 3,956,224 B, igneum-ota-sign.exe 5355a5b6, igneum-prove-verify.exe 15998eab; the app's Linux binary igneum-app dd22a4ae 3,389,776 B. igneum-pow tests on the box with the amended packs: 61 + 7 + 4 + 19 + 2 + 7, 0 failed. The Mac, under the lock: igneumd b306baba 48,072,304 B and igneum-miner deb4d263 (c4459193 twice, the N13 line), the DMG Igneum-Miner-0.3.20.dmg 44,467,804 B sha256 73796c5febc20506 (build 202610071249, hdiutil VALID, the prover pair in).
**The workers with the Arc fix (built on the box from this tree's proto-opencl at 9088293a's content):** Linux 2.31 for HiveOS: igneum-worker-cuda c08e4694 6,761,464 B, igneum-worker-opencl 56cbe32f 312,304 B; Linux 2.35: cuda ed3dfe58, opencl 8ef81d8d; Windows igneum-worker-opencl.exe 618a2b10 530,432 B (mingw on the box, static, imports KERNEL32 and msvcrt only, the rewrite text present), in place of the 01:33 build (479,232 B, kept as igneum-worker-opencl-0133-old.exe in scratch); igneum-worker-cuda.exe unchanged since 0.3.19 (its sources untouched). Asked of the Intel lane: how bench d's exe (3c62470f) was built and whether it is the same source; if its exe differs the installer is rebuilt on it.
**HiveOS package:** igneum-hive-0.3.20.tar.gz 27 MB sha256 d9dd12dfe900bf9f (the 2.31 node pair, the 2.31 workers, version.txt naming c4459193 and 8c728ca3; no AppleDouble entries). Smoke in ubuntu:20.04 on the box (glibc 2.31): igneumd answers "igneumd 2.1.0" with c4459193 in the binary, igneum-miner its usage, both workers load and answer.
**Windows inputs and installer:** payload-inputs.zip published 13:59 BST (igneumd.exe 49502cc7, igneum-miner.exe b4871b5d, the three mingw runtime DLLs, igneum-worker-cuda.exe with nvrtc64_120_0 and nvrtc-builtins64_128, igneum-worker-opencl.exe 618a2b10, the 0.3.17 Linux prover pair 71bc2438 / 263bf4ce, which stands because the pre-push prover-pair-check read c4459193's evm-types equal to the exec pin's; IGNEUM_NODE_SRC = the vendor worktree at c4459193); windows.yml run 37625030022 dispatched 12:59:29Z on a255b095, the installer fetched with fetch-ci-artifacts.sh (no --deploy) when it lands.
**Staging plan at the publish (one step):** a scratch copy of the downloads folder, `IGNEUM_DLSITE=<copy> publish-manifest.sh --no-deploy --version 0.3.20 --mac <dmg> --win <installer> --override <the live sixteen-field file, digest eada4bda> --notes ...`, the hive tar through publish-public.sh, the floor-moved file beside the release with its digest and the one-line diff (not published); then the deploy step on CASES END, PC 1 first, the lock lines carrying "sweep complete before 13 October 09:00 UK", hands and seed last by the build-server lane.
## 35. the project lead's orders (15:1x UK by main's stamp; 14:0x BST on `date`): the floor file publishes, the sweep in waves, 0.3.21 tonight
(1) The floor-moved file publishes WITH 0.3.20: floor = live DAA at the publish + 604,800 rounded up to the epoch (3,600), window unchanged, the digest read back on c4459193's binary, the one-line diff against eada4bda in the publish record. Dry run at 14:07 BST, DAA 286,227: floor 892,800, digest 43725627 on c4459193 (the live file reads eada4bda on the same binary). Consequence: a node on the old file refuses a node on the new one as a peer, so an unswept box is isolated, not forked, until its turn. (2) Fast: the cut word on CASES END, everything staged so the publish is one step; the sweep in parallel waves as the lock lines allow (PC 1 first by the app's poller, then PC 2, the Mac, the seed, the hands and the fleet), each box read back by commit string; with the moved file the seed, the hands and the fleet move in the first wave. (3) Build-2 for anything that still builds. (4) The Windows OpenCL worker: the Intel lane's exe af53f194 (built on build-1 by build-windows.sh's line, static, KERNEL32 and msvcrt only; the Arc self-test PASS 96 of 96 on v4-devnet-epoch0 867dbc45cfb36b4d, the mx8 control and the live pack; 11.008 MH/s on v4) ships in place of the shipper's own box build 618a2b10 (same source, not Arc-tested); the inputs re-pushed with it and windows.yml re-dispatched: run 37625870876 (37625030022 and 37625590021 cancelled). (5) Standing rules from 0.3.21 (release-rules.md 4a, 4b, 5 at a36298c4): every gate starts on every candidate as it builds; warm pods per gate class; the sweep in waves. (6) 0.3.21 starts the moment the sweep ends: sub-version 2 (07a809a7, byte 7, pending the F8 census), miner-reliability both halves, driver-check 1982dbc7, the fork gate from horizon-node, the under-12 GB prove-instead routing, the per-architecture prover server; the tree staged tonight, the first gate about 19:30 BST (app, build-2), the node candidate's parallel gates from its first binary.
**The sweep's wave plan (the fleet, 14:1x BST), accepted:** from a 16:15 BST publish, each box's move = override.json replaced, c4459193 put as in/igneumd-45be9b02d1b002f5 with the sha asserted, the node killed by its kill file and started on its kept datadir (the rewrite line once, about 80 s to the tip), read back by the commit string in its log line and the digest from its handshake line, synced, the miner back; the prover pair under bin-0320 and the prover restarted by file after synced. Wave 1, 16:15 to 16:21, read back by 16:23: hub-1 first, pool-1 (its daemon stopped first), the eight heaviest voters (p1-5090, p1-4090, p2-4090-3, p2-4090-1b, p2-3090-1 to 4), with the seed and the hands in the same minutes from the build-server lane: more than two thirds of the live table's weight on the new digest at once; the first lock on the new side (the folded certificate on hub-1, about 16:27) is the line between waves. Wave 2, 16:27 to 16:32: p1-a5000, p1-4070, p2-4070-1, p1-3080; the lock line by 16:35. Wave 3, 16:35 to 16:40: the Devnet 2 six (dn2-seed first). The prover roll behind each wave's synced reads, 16:25 to 16:50. PC 1, PC 2 and the Mac on their pollers. Sweep end about 16:42 BST; provers paid on the pair by about 16:50. Risk named: a refusing datadir would cost a 98-minute resync (today's two kept reads say none will). The go = the override file with its sha256 and digest at the cut word. Warm pods for 0.3.21 (rule 4b): w-target and w-relay syncing on the kept copy, w-poison when a 3070 frees, p12-vast kept; USD 11.52 a day.
**0.3.21's clocks (14:1x BST):** the F8 census on byte 7 (07a809a7) lands about 15:00 BST; the pass line is all 64 seeds under 1.2x of the window model plus the hash lane's suite and G1; if it fails or slips past 20:00 BST, 0.3.21's node ships byte 5 again with the re-pin dropped. The node lane's dry merges into c4459193 are clean (miner-reliability-20 f067f7c1 one file, horizon-node 6eb21fc9 six commits on finality.rs); at the sweep-end word the branch commits, suites on build-2 about 16:50 to 17:05 BST, the first candidate binary on build-1 about 17:10 BST with every gate started from the build. App side: release-0.3.21 on origin at 0b75cf52 (release-0.3.20's closed tree, driver-check 1982dbc7's three commits, the 0.3.21 version strings, master 819d536b merged with its 51-check gate; the hands script's pgrep in the bracket form); the reliability lane rebases its app half by cherry-pick onto it as miner-reliability-21 (tip fb3a4a40, gate on build-2 running). Still to write on the app side: the under-12 GB prove-instead routing and the per-architecture prover server.
**The 12 GB settled-claim line, first half (the fleet, 13:23Z):** p12-vast (RTX 3060 12 GB) on c4459193 (45be9b02d1b002f5, the string read back) on pool-1's kept copy from 12:36:35Z (first-try start, the copy already rewritten by b7cc37e7's read), synced 13:22:47Z; box-prover (cf335220, the settled filter) started 13:23:01Z with the sm_86 server; first claim "segment 164198..164205 (8 shards, fresh) margin=414 tip=287564 settledNumber=0x28185 settledDaa=0x46317 settledBy=finality candidates=5 rank_by=fnv", pair ok. The floor read: settled number 164,229 at settled DAA 287,511 by finality, 31 blocks above the claimed segment, the claim behind the floor as the rule wants. The paid record by the pod's key is the second half, about 3 to 10 minutes.
**Main (14:3x BST):** the project lead withdrew the Harmony overlay; apps-harmony is parked. 0.3.21's UI branches in order: gpu-logos (a414b6bdc81d348d8, takes the Prove switch fix), ui-overlap-fixes (a7aa33bf8ae230f46; its wallet half is the wallet's cut), scene-parity (a75edb2ef8015f21c; live-dag.js and proof-core.js, the blank-canvas fix first); each instructed: rebase onto release-0.3.21, push as <name>-21, UI tests and the app gate on build-2, tip and lines to the shipper; 19:30 BST stands. A site rebuild lane ships through the site gate on master, outside 0.3.21. The Windows installer run 37625870876 failed its inputs check on the stale node-source pin (e69e8a39 at a255b095 against the payload's c4459193); the pin pushed (4ae4a54e) and run 37627661560 dispatched 14:20 BST.
## 36. CUT BLOCKER (the fleet's 12 GB line, 13:25Z; 14:3x BST): the 0.3.20 node reads the proving ids as unknown
On p12-vast, c4459193 (45be9b02, string read back) over pool-1's kept copy with the hub's override-16.json: the node's start lines read "[igneum-exec] proving v1: ... shard program id unknown, aggregator id unknown", then "verifying keys embedded: shard program id 0x2b1a81cb... aggregator id 0x474678f3..."; hub-1 on 5899f603 prints the ids in the proving v1 line. The node builds its segment statement with zeros for the ids and the pair's proof is refused: "RESULT seg 164198 FAILED: statement differs from the node's at hex offset 472 (lengths 616 vs 616); ours ...2b1a81cb413236cf... node ...0000000000" (the claim behind the settled floor was right: settledNumber 0x28185 by finality, margin 414; the 3060 proved 8 of 8 shards in 135.9 s at 8,487 MiB). Per tier: swept as it stands, every standing prover stops paying from the sweep minute and the feed goes to zero while the miners run on; a 0.3.20 home prover earns nothing. The pin HOLDS. Asked of the node lane: the cause (where 5899f603 takes the ids from, which of the five commits since dc141409 loses them), a fix with a test (a node on the live override file reports the ids; a zero-id statement the known-failed shape) as c4459193's child, its time; the fixed binary takes the full gate set from its build (rule 4a; the warm pods are up, about 80 minutes). The sweep plan, the floor file, the installer and the app tree are unaffected. Earliest publish by the shipper's read: about 17:30 BST if the cause is small. Main told.
**Main (14:3x BST):** the hold agreed (a prover cannot be split from its node); the pin waits for the ids fix as c4459193's child and takes the full set from its build; new rule 4c (release-rules.md 706b46d3): on every candidate a node on the live override file reports both proving ids on its start line and a prover's first statement is accepted, a zero-id statement the known-failed shape, run beside the kept-datadir read on the warm pod; the fleet has it. Publish on green with the clock; the 16:00 report carries the slip and the cause.
**The Windows installer (14:26 BST):** run 37627661560 on 4ae4a54e success; Igneum-Miner-Setup-0.3.20.exe 63,025,372 B sha256 45b2f3fb54f40f83 and igneum-windows-app.zip 91,038,211 B; inside the zip igneumd.exe 49502cc7 and igneum-worker-opencl.exe af53f194 (the Arc-proven worker), as pushed. fetch-ci-artifacts.sh copies into the live downloads folder by design; both files moved out to scratch r0320/win at once (rule 2: the live folder changes only in the deploy step). The installer carries c4459193's node: if the ids fix lands as a child commit, the node's Windows exe, the payload inputs and this installer are rebuilt on it (about 35 minutes: the cross on the box, push-inputs, one windows.yml run), as are the native, hive, seed, hands and Mac binaries and the DMG.
**The blocker is the start environment (the node lane's read of the code and hub-1's process, the fleet's restart on the pod, 14:3x to 14:4x BST):** the statement's ids come from the override object's two fields, then IGNEUM_PROOF_PROGRAM_IDS, then the verifier host's `--mode id` under IGNEUM_PROOF_VERIFIER; the live file carries no id fields; every standing box's supervisor sets the host (hub-1 included), and the app sets it whenever it finds igneum-prove-host. The pod's node was started bare for the kept read; 5899f603 bare reads the same. Restarted on p12-vast with the env at 13:30:28Z, the same c4459193 prints both ids and the 3060's first statement is accepted at 13:33:56Z (8 of 8 shards); the paid half then lost the claim race ("segment already paid; end to end 113.1 s", p2-4070-1's shape) and runs on. The swept fleet keeps the env through box-standing.sh. The node lane's child commit (`resolve_program_ids`: the override first, then the env or host, then the embedded keys; test known-failed first) makes a bare node self-sufficient; it is the fix for a hand-started node without the host, which has no prover to submit to it. Put to main: pin c4459193 as it stands (its gates landing: c19-1 synced about 14:40 BST, c20-1 synced 13:33:08Z and the cases form running, CASES END about 15:50 BST), publish about 15:55 BST, the ids commit as 0.3.21's first node commit with rule 4c's gate on every 0.3.21 candidate (ids-gate.py, the fleet's README item 19); or the child now with the full set again, publish about 16:35. The shipper's read: pin c4459193. The pool window ended 13:30:23Z with 0 shares (the daemon's template RPC timed out from 12:54Z; the members' prepare stall at the epoch boundary): the pool lane's, not 0.3.20's.
**Main (14:3x BST): THE PIN IS c4459193 as it stands.** The ids commit (55768f88, `resolve_program_ids`, the exec suite 32 with `a_bare_node_resolves_its_program_ids_from_the_embedded_keys`) is 0.3.21's first node commit, with the ids gate on every candidate from now. Publish on CASES END about 15:55 BST. The sweep: the project lead is taking PC 1 offline for cable work (a Mac mini comes online), so PC 1 is not first and not in the waves; its app updates on its poller when it returns, its lock line "PC 1 offline at the sweep, updates on return". First wave: this Mac, the fleet, the seed, the hands, and PC 2 if it polls. The Mac mini is a new machine and takes the 0.3.20 DMG as a fresh install when the project lead has it up. The shipper's parallel builds on 55768f88 (box and Mac, started 14:36) stopped by pid; the vendor worktree back on c4459193; the pinned Mac binaries and the DMG 73796c5f kept.
## 37. The pin's canary lines on c4459193 (c19-1; the fleet, 14:4x BST)
sha256 45be9b02d1b002f5 asserted at the put, igneumd/2.1.0-c4459193 in the node's log, digest eada4bda8aa8368c. **The wipe (decisive):** IBD from the pruning-point proof 12:38:55Z, synced 13:35:50Z (156,046 blocks, 3 peers), 57 minutes; the IBD-end line "finality time: bodies 3864115 ms over 156238 blocks, virtual 373190 ms over 16695 changes, weight tables 48145 ms over 5595 tables (53495046 blocks walked), signatures 607815 ms over 473073"; proof-asking 0, carried records 0, pings 0, idle drops 0; the exec poller 682 calls, 0 errors, the first non-null answer at the synced line (exec synced true). **Mining** from 13:36:00Z: at +166 s 34.2 MH/s, mined 16, accepted 12, rejected 0, got_reject 0, wrong_version 0, isSynced true at the tip on every read (the dc141409 flap is gone: 8097d600's fix). The ten-minute and hub reads about 13:46Z, the restart read about 13:50Z. **The kept read (rule 4):** pool-1's 0.3.17 copy on the same pod, first start 13:38:14Z with the N13 rewrite line and the finality blob converted (1,747 locks), no panic; second start 13:38:33Z first-try, no panic: PASS, c20-1's shape. **The cut word:** c20-1's CASES END about 14:50Z (15:50 BST; the poison pod's IBD from the relay started 13:38Z).
**The seeds (main's ruling, 14:4x BST):** the three testnet seeds run `--testnet --netsuffix=1` with no override at height 0 on 1c19441d; c4459193's seed-class build prints testnet digest 9537868d against 1c19441d's b7d8c915, so a swap would move the pre-go testnet's digest. Ruling (b): the seeds stay on 1c19441d and the RPC blocklist stays; the testnet genesis re-cut replaces the seeds' line at the go on the project lead's word. The devnet floor file has nothing to do with the seeds (the shipper's earlier order corrected). Wave 1 is the Mac, the hands and the fleet, PC 2 on its poller.
**PC 2 is back and the Windows kept-datadir gate PASSED (13:38 to 13:39Z):** PC 2's 10:46Z silence was the whole PC losing power (Kernel-Power 41, 6008, no bugcheck; two such events today), not the 0.3.19 update-now, which had returned cleanly at 10:34:38Z; it polls again since 13:38:32Z. run-20261007-125433 on it: c4459193's igneumd.exe (df19315d) on a robocopy of the app's datadir (1,147 MB), "verdict c4459193 (pass): the N13 line seen; consensus built and the servers started", the copy removed, 0 leftover nodes; 6a3432a3 on its own copy died at virtual_state.rs:250 with InvalidBoolEncoding(20), the Windows shape of the known-failed read. A datadir cut off mid-write twice by power loss started clean on the pin. The reliability register's MF-11 is renamed to the machine going silent with nothing reporting it.
**The testnet digest move, for the record (the build-server lane, 14:4x BST; not a 0.3.20 item):** between 1c19441d and c4459193 the testnet's genesis, base params, finality table, fee table and pow schedule are unchanged; five additions enter the digest on c4459193 and are the whole b7d8c915 → 9537868d move: finality_leave_activation_daa 0 with finality.leave_delay 3,600; program_class_v3_activation_daa 0 (unconditional); program_class_v4_activation_daa 0 (the signal window 0 stays out); pow_genesis_dataset_log2 28 (unconditional); emission EmissionSchedule::TESTNET_1 (launch_rate 100 UNIT, ramp 90 days from 10 percent, monthly steps with step_decay_q32 4,172,697,914, tail 100 bps a year). Everything else new sits at never and stays out; dns_seeders is not a digest field. No override pins the old value (1c19441d has none of the five fields), so every testnet node swaps together or not at all: the testnet-go checklist's digest line reads 9537868d on this line, to be re-read on the genesis re-cut's binary at the go.
**PC 2 out of the waves too (main via the Counter lane, 14:5x BST):** the project lead is taking PC 2 down for cable work (PC 1 is back, but it is his desk and no job goes to it); both PCs update on their pollers on return, their lock lines "offline at the sweep, updates on return". The sub-version-2 Windows G1 completed on PC 2 before it went down (13:46:36 to 13:46:50Z, exit 0, eight of eight fingerprints equal to the Mac's). Wave 1 is the Mac, the hands and the fleet.
**c19-1's last pin lines (the fleet; FORM END rc 0 at 13:50:53Z):** mining at +666 s 34.3 MH/s, mined 66, accepted 66, rejected 0, got_reject 0, wrong_version 0, isSynced true at the tip on every read, the exec follower moving; the hub holds 41 blocks by c19-1's key 2c7cc291d38579e0 in its last 700, rejects naming the pod 0; the restart on the kept datadir 13:47:15Z: the stop and the start inside seven seconds (09124180's poll proving itself against b7cc37e7's LOCK death), synced again 13:48:39Z (157,048 blocks, 4 peers), 84 seconds after the kill, isSynced true on the first read and never false after; 109 templates in the read, max template_ms 3,432, "template fetch timed out" 0; the kept read passed at 13:38Z. Every line the rule reads is in from c19-1; CASES END from c20-1 is the one left (about 14:50Z). c19-1 stays up until the shipper's word. p1-5090 ran the sub-version-2 G1 meanwhile (eight of eight equal to the Mac's) and is back under its supervisor.
**The prover roll corrected (the fleet, 15:0x BST):** box-prover's "RESULT paid" line read the segment record's `paid` field without comparing its keyHash to the box's own, so a segment paid to ANY prover printed as the box's pay; this morning's PAIRED verdicts rested on it. The truth from hub-1's segment records (157486..164691, 899 segments, 378 paid) by paid.keyHash: p1-4090 94, p2-4090-3 54, hub-1 41, p1-4070 20, p2-4070-1 11, p12-vast 3, two keys the registry does not hold (bb9f9057a373f647 99, the devnet's biggest payer; e809e39672ed32db 56), and ZERO in two hours for p1-5090, p2-4090-1b, p2-3090-1 to 4 and p1-a5000. So: the pair is on 13 of 13 and pairs on every box; 5 of 13 are paid by the chain's record, 7 are not (their logs under read: race or stuck); the 12 GB race reading was half right (p2-4070-1 at 12 GB is paid 11 times while the 3090s and the 5090 are not, so card size is not what holds those six). The 12 GB line, real: p12-vast was paid three segments by its own key on the bare 55768f88 node, each claimed behind the settled floor (164438 claimed 13:41:00Z paid at carrier 164494 5.8388 IGN; 164502 at 13:45:06Z paid 4.8263; 164510 at 13:48:58Z paid 4.5727); on c4459193 with the verifier env (13:31 to 13:37Z) the statement was accepted and no race won in that window: the pin's line is "statement accepted, 0 paid in six minutes", not blocking. box-prover fixed (gpu-fleet 4c872785: a paid line only on its own key, "paid_other" otherwise), on every standing box; running provers take it at their next restart by kill file, which the sweep's prover roll does. Who holds bb9f9057 and e809e396 (PC 2's app, the Mac, the hands' CPU prover) is for main.
**Docs on master (15:01 BST):** the 0.3.20 plan, release-rules.md and the 0.3.21 plan landed as ship-docs-0320 3ad0c60d through tools/ci/merge-to-master.sh (the full gate green on the branch, 52 checks; the merge 41e0eb45). From here plan rows land the same way.
**The two paid prover keys outside the fleet's registry (15:3x BST):** e809e39672ed32db (56 segments in two hours) is PC 2's card 1, identity 1 (label win-1ccfe586-1-1; read by the build-server lane's read-only job run-20261007-142054 on PC 2 at 15:38 BST through the installed igneum-miner's key-hash; PC 2 is back since the cable work). bb9f9057a373f647 (99 segments, the devnet's biggest payer) is none of PC 2's seven labels, nor the Mac's (mac-d937c69d-1 = 8fafda27…), and build-1 runs no prover; PC 1 (labels win-ae432dc7-1/-2 and their identities) is the remaining candidate, its key read by the project lead from the Prove page's Details on main's ask (no job on PC 1). For 0.3.21 the app reports its vote key hashes to the intake on every poll, so this is never a hand read again.
**Devnet 2 (the fleet, 15:4x BST):** dn2-override.json is an eleven-field object without the v4 floor or window, so Devnet 2 has no flip date and the 13 October line does not apply to its lock lines; igneumd-83702a35 and c4459193 both print digest 4a0b8726… on it, so wave 3 is a plain binary swap on the four pods (read-back 4a0b8726) and bps-seed moves at the build-server lane's convenience with no digest step. The cases on c20-1: the poison pod mining from 14:41:25Z, the thirteen polls to about 14:54Z, the restart and the hub read after: CASES END about 14:57Z (15:57 BST); the per-box sweep form rehearsed on w-target meanwhile. The publish about 16:00 BST on it.
## 38. 0.3.20 LIVE (15:54:17 BST, 7 October 2026)
**Main's word (15:4x BST):** (b): publish on the dc141409 cases plus the diff argument and the c19-1 relay evidence; the separate-host relay re-run (CASES END about 17:15 BST) is a halt condition: a FAIL stops the sweep after the wave in progress and rolls back by the per-box form; no public Discord card or site version bump until it reads PASS. The cases rerun on c20-1 was void on its relay half: RunPod put the target, the relay and the poison pods on one host and a pod cannot reach another on its own host by the public address (the no-hairpin class); the poison half stood (13 rejected, 0 accepted). The hairpin class is a rent-time host check in the fleet's script.
**The cut:** live DAA 294,073 at 15:51 BST; the floor-moved file ov16-floor-900000.json (sha256 294f1f80f1c8aecf…): program_class_v4_activation_daa 831600 → 900000, window 86400 unchanged, the fifteen other fields as live; digest on c4459193 4bbbe8162ea9fff277aa5b16b4ffad9e2262ba5e6a5acac7b697ff77211e7328 (the live file reads eada4bda on the same binary). Staged in a scratch copy of the downloads folder (publish-manifest.sh --no-deploy --public with the DMG 73796c5f and the installer 45b2f3fb; publish-public.sh --hive with igneum-hive-0.3.20.tar.gz d9dd12df); the staged manifest differed from the live one in the override object alone; the staged folder's names differed from the live one in the 0.3.20 files added and the 0.3.19 and 0.3.17 public files pruned. The deploy step: the staged folder onto the live one, `vercel deploy --prod` (Production igneum-qu5chjxiq, aliased dl.igneum.network), 15:54:17 BST. Read back: the live manifest version 0.3.20, mac 73796c5f, windows 45b2f3fb, floor 900000; the three public aliases 200 (windows exe, mac dmg, hive tar).
**The sweep's go (15:55 BST):** the fleet's wave 1 (hub-1 first, pool-1, the eight heaviest; the fleet-native pair a8d08da5 / 474273ce; read-back by commit string c4459193, digest 4bbbe816, synced; the lock line "sweep complete before 13 October 09:00 UK (the margin; the floor is now DAA 900,000)"), the hands in the same minutes (the build-server lane, mode 2 with the object and the digest), wave 2 on the first lock on the new side, wave 3 the Devnet 2 four as a plain swap (4a0b8726), bps-seed at the build-server lane's convenience with no file change; the seeds and the RPC filter untouched (main's (b)); PC 1 and PC 2 on their pollers on return; the Mac on its poller; the Mac mini a fresh install when the project lead has it up. The Discord card and the site's version line held for the relay re-run's PASS.
**Correction to the sweep's node pair (16:02 BST):** the fleet-native igneumd a8d08da5 named in the go is gone: the shipper's scratch copy was overwritten at 13:55Z by the stopped 55768f88 chain's native step (05016dee, no c4459193 string) and the box's target-0320 path with it. The sweep's node pair for every fleet box is the build-server lane's c4459193 hands pair from the same tree (igneum 00249643, igneum-pow 8c728ca3, build-1, native 2.39, the string read back): igneumd a80ed39caf885d314f97ce88863afcb307cbb4acc45a10828b2443a41e5d27d2 (57,628,576 B), igneum-miner 70a5180f30fab73fde0b3f33cfd37d2d68899eab70290b86c924e02980b09afd (10,214,648 B). The shipped artefacts are unaffected: the hive tar (d9dd12df, smoked with c4459193 ×2), the Windows exes in the installer (49502cc7, c4459193 ×2) and the Mac node in the DMG (b306baba, c4459193 ×2) were verified from their own files. Scratch rule note: a chain writing into a shared --out directory must stop before another candidate starts; the restart on a new candidate reused the same out paths.
**Wave 1, the hands (the build-server lane, 15:56:09 to 15:57:45 BST, rc 0):** igneumd a80ed39c installed as /srv/hands/bin/igneumd-2.1.0-c4459193, the sixteen-field object written (floor 900000, window 86400). observer-node restarted 15:57:08 from eada4bda: first executing line "exec state loaded from a snapshot: tip 165393", digest 4bbbe816 MATCHES, the commit string present, powEngine igneum-pow, blockrate bps 1, finalityDepth 43200, ghostdagK 18, mergeDepth 3600, pruningDepth 108000. node1 restarted 15:57:30: tip 165348, digest MATCHES, the string present, powEngine igneum-pow. Lock line carried. Seeds and the RPC filter untouched; bps-seed follows.
**Interface 1.0.1 over the air (main's order, the shipper as publisher; 16:03:37 BST):** the 0.3.20 tree's app/igneum-app/ui packed as igneum-ui-1.0.1.tar.gz (408,212 B, sha256 0749f37c67c028c8cf1052ed099fea0f1cc159ad6cefdc75d74d2f2de1d9fc45), min_engine 0.3.20, signed with the release key (igneum-ota-sign sign-ui; key fingerprint 8f186e37…), staged first in a scratch copy of the downloads folder (the only field differing from the live token manifest: `ui`; the release notes kept by passing them back), then the deploy step (the staged folder onto the live one, vercel deploy, aliased 16:03 BST). `publish.mjs --verify`: the live manifest's ui entry against the bundle in the folder, sha256 matches, signature verifies. Not published to the public manifest: the publisher's --public arm fails on the ui entry ("the public igneum-app-latest.json would still carry the token": the ui URL is under the token path and publish-public.sh has no ui rewrite), so dl/public/ui/ is missing by design today; the apps read the token manifest, which is where the channel lives; the --public ui arm is a 0.3.21 packaging fix. The Mac's app taking 1.0.1 on its poller: read back below when it polls (hourly, or Settings > Check now).
**Wave 1 (the fleet, 14:59:08Z):** on igneumd a80ed39c (the floor file 294f1f80 read 4bbbe816 on it on the scratch pod at 14:58Z), the lock before at checkpoint 9259, 80.4 percent; hub-1, pool-1, p1-5090, p1-4090, p2-4090-3, p2-4090-1b, p2-3090-1 to -4 moving in parallel; the miner 70a5180f for the prover roll behind the waves. The relay re-run's three pods carried the old file (eada4bda) and would have read a false FAIL beside the swept fleet; the floor file put on c19-1, c21-relay and c21-poison at 15:00:37Z, the run restarted 15:02Z after a script fault of the fleet's (fixed): CASES END about 16:20Z (17:20 BST), a read on the live digest. bps-seed is build-1 itself (a bare process under /home/build/dn2seed), the build-server lane's, the digest unmoved there.
**Wave 1 read-back (the fleet, 15:04:51Z):** hub-1 on a80ed39c, igneumd/2.1.0-c4459193, digest 4bbbe816, override 294f1f80, the N13 rewrite line once, synced at 162,489 with 6 peers, the miner back; the same read on p1-5090, p1-4090, p2-4090-3, p2-3090-4 (1 to 2 peers, climbing); p2-4090-1b and p2-3090-2 on the new binary and digest with 0 peers yet (the old-digest peers refuse them, the new ones are the boxes landing beside them; miners held by the supervisor's zero-peer rule until they peer); pool-1's daemon stopped and its node starting on the supervisor's next pass; p2-3090-1 and p2-3090-3 not answering that read's ssh in time (the next pass). The panic counts on the first boxes (10 to 20) are the RocksDB LOCK class from the supervisor's retries while the old process still held the datadir, not consensus; every node is up regardless. The certificate line between waves about 15:10Z.
**bps-seed (the build-server lane, 16:07:49 to 16:08:00 BST):** the Devnet 2 seed is a bare process on build-1 (/home/build/fleet-share/igneumd-83702a35…, --devnet-suffix=2, appdir /home/build/dn2seed, the eleven-field devnet2-override.json b75d1f58); both binaries read 4a0b8726 on that file first; SIGTERM, the native c4459193 (a80ed39c) started with the identical argument line, down 11 s; read back igneumd/2.1.0-c4459193, the string twice, the digest 4a0b8726 unchanged, the N13 line on the kept datadir, GRPC and P2P up, 23 "PoW accepted" in 20 s, no panic.
**The Mac (this machine, in wave 1 on its poller):** its hourly check at 16:01 BST had read the 0.3.19 manifest (published 10:23Z) though the CDN served 0.3.20 since 15:54 BST, a stale read of seven minutes (the app's own cache or a stale edge; a 0.3.21 note for the ui-ota health ping, which reads the same path); Settings > Check now at 16:04:58 BST read 0.3.20 (published 15:02:36Z), downloaded and ready by 16:05:20; the apply is the app's own (auto_update on), awaited; interface 1.0.1 after it.
**0.3.21:** update-return-21b a64c193f (the eGPU card kind for a USB4 or Thunderbolt router in the device's parent chain, the Power Helper's fault line and its stale-prefix fix; app tests 255 + 32 + 8, UI 76) is ready on the lane's side; its push to origin is refused by GitHub ("remote: Internal Server Error", three tries 15:08 to 15:10Z), the lane retrying.
**The Mac's apply is held by the app's own guard (16:12 BST):** update ready, wait = "the network lost 33% of its identities in the last 10 minutes; holding the update": the wave sweep restarts ten fleet nodes at once (each miner off for the 80 s restart, the zero-peer boxes held longer), the app reads that as an identity drop and holds its apply until the count recovers, then applies at its rollout slot (":MM past the hour", the per-machine minute). The guard is right by design (an app must not update into a shedding network); the consequence is that the apps' pollers apply after the fleet's waves settle, not alongside them. For the rules: a wave sweep and the apps' identity-drop guard interact this way; the publish record names the apps' apply times as they land. The Mac's apply time is read back below.
**Wave 1 complete, wave 2 started (the fleet, 15:12 to 15:22Z):** all ten read back on the pin (a80ed39c, igneumd/2.1.0-c4459193, digest 4bbbe816, the file 294f1f80, the N13 rewrite line once, synced or catching up): hub-1 (10 peers), pool-1 (its node restarted 15:08:27Z), p1-5090, p1-4090, p2-4090-3, p2-3090-4, and p2-4090-1b, p2-3090-1, p2-3090-2, p2-3090-3. The fault behind the last four: the standing nodes run `--addpeer=<the Hetzner seed> --nodnsseed`; the seed's own move refused their handshake and they dialled nothing for 17 minutes at 0 peers (the six others had the hub in their address books); fixed by HUB_PEER (the hub as a second addpeer) in their supervisors and a kill-file restart, all four at 3 peers. The lock: hub-1's last LOCKED at checkpoint 9263 (15:05:03Z, 49.9 percent at the moment of locking; the folded certificate 98.1 percent, 13 of 16 voters, weight 4,954); no LOCKED since, the digest split's expected shape while the four wave-2 voters still vote on the old side; the first new-side lock being read. Wave 2 (p1-a5000, p1-4070, p2-4070-1, p1-3080) started 15:22Z on the 98.1 reading (their move brings weight onto the new side). Rule for the fleet's supervisors (from this): every standing node carries the hub as a second addpeer, so a seed's move never leaves a box peerless.
**Master re-pinned (16:21 BST):** packaging/windows/node-source.pin on master moved to c4459193 (master-pin-0320 a0b44b48 through merge-to-master.sh, the full gate 55 checks green; the merge 6996ad3f), so master's windows-ci reads green at its payload-inputs step again (red on every master run since 6 October 21:15Z on the stale pin); release rule 14: master is re-pinned at every publish, a named line in the cut list.
**The Mac on 0.3.20 and interface 1.0.1 (16:23 BST):** the identity-drop guard lifted as wave 1 settled; the app applied 0.3.20 on its own (updated_from 0.3.19), its node log node-20261007-152327.log starting 16:23:27 BST on igneumd/2.1.0-c4459193, digest 4bbbe816 on its own kept datadir (the N13 rewrite line once), synced at 164,411 blocks with 5 peers at 16:25; interface 1.0.1 taken over the air at 16:23:58 BST (source ota, published_version 1.0.1, active_version 1.0.1, embedded 1.0.0, confirmed true, no error). The Mac is in wave 1 as ordered.
**Wave 2 and the first lock on the new side (the fleet, 15:23 to 15:29Z):** p1-a5000, p1-4070, p2-4070-1, p1-3080 read back on the pin (a80ed39c, c4459193, 4bbbe816, 294f1f80, the rewrite line once, synced); three sat peerless on the seed-only addpeer (wave 1's class) and carry the hub peer since 15:26Z. The lock: the old side's last at 9263 (15:05:03Z); the new side locked from checkpoint 9313 at 15:28:08Z (84.9 percent of active, 70.1 of total), 9315, 9316 (87.5 of active, 74.9 of total); a 23-minute gap while the table's weight split across the two digests, votes accepted at the hub throughout, no certificate at two thirds until all fourteen stood on the new digest with peers; the folded certificate at 9317 78.3 percent (14 to 15 voters). Equivocation warnings on the hub at 9280 and 9292 by four keys that are not fleet vote keys (e15fe94b, a405c3e6, 7b8ef6fd, ea53bd41: the Mac, PC 2 or the hands re-voting across the split); the fleet's keys did not equivocate. The supervisor rule is in: box-standing.sh adds the hub as a second addpeer by default (gpu-fleet 34ef30f8, README item 20, CLAUDE.md), on all fourteen, live at each next kill-file restart (seven already). Wave 3 (the Devnet 2 four, a binary swap, digest 4a0b8726) started 15:29Z; the prover roll runs behind waves 1 and 2 (the 70a5180f miner, the pair under bin-0320, box-prover with the key check and the drift flag). For the rules: a digest-moving sweep costs the chain its locks for the length of the split (23 minutes here); the hub plus the heaviest voters in one wave shortens it, and every node carrying the hub as a peer shortens it further.
**Known red on the published pin, test-only (the node lane after the era VDF lane's read, 16:3x BST):** two consensus INTEGRATION test targets, consensus/tests/igneum_installed_signals.rs (`block_template_uses_current_block_version`) and consensus/tests/igneum_order_tests.rs, assert the signalled header version 1026 (object 4) while 8097d600 moved the class signal to object 5 and the header carries 1282; red on c4459193 and every commit since 8097d600; outside the suite set the node lane ran (the two installing-test targets were split into their own binaries this morning for the test-order race and left out of the set). The code is right; the tests are stale, 6b94c823's class. The pin stays c4459193 as published and swept (its binaries keep their commit string); the test-only fix (branch signal-tests-fix on the mirror, 1026 → 1282) is the FIRST step of 0.3.21's node order on 55768f88, and the rule from now: the two integration targets are in every suite set (`cargo test -p kaspa-consensus` without --lib). era-vdf-node 394a5902 is 0.3.22's (the shipper's call; every network at never, era 1 at least 180 days past a genesis).
## 39. SWEEP COMPLETE (15:39Z, 16:39 BST)
The devnet: hub-1, pool-1 and the twelve voters on igneumd a80ed39c (string c4459193), the floor file 294f1f80, digest 4bbbe816, the N13 rewrite line once each, synced, the miners back (the 70a5180f miner as the prover roll reaches each box); the first new-side lock 9313 at 15:28:08Z after the 23-minute split, locks every 20 to 40 seconds since at 70 to 81 percent of the total, the folded certificate 78 to 82 percent of the table. The hands (observer-node, node1) and the Mac on the pin with 4bbbe816; the Mac on interface 1.0.1. Devnet 2: dn2-seed, dn2-1, dn2-2, dn2-3 and bps-seed on c4459193, digest 4a0b8726 unchanged; a ten-minute four-way split from the fleet's Devnet 2 move (dn2-seed's supervisor env rebuilt without EVM_PORT and P2P_PORT, box-dn2.sh with no defaults, the seed's node refusing "--evm-rpclisten=127.0.0.1:"), healed at 15:36:25Z, all four at block 76,857 and a new lock at checkpoint 2313 at 15:39:38Z. The testnet seeds on 1c19441d by main's (b). PC 1 and PC 2 update on their pollers on return; the Mac mini a fresh install. Every lock line: "sweep complete before 13 October 09:00 UK (the margin; the floor is now DAA 900,000)". The prover roll runs behind the waves (p1-a5000 refused by design on its drift flag; hub-1 claiming on the pin). Open on the sweep: the relay re-run on c19-1 on the live digest, CASES END about 16:25Z (17:25 BST), the halt condition and the Discord card's gate; build-1's port 26621 (the Devnet 2 seed's p2p) reads closed from outside (a firewall rule predating the sweep; the build-server lane reads it); the two PCs' return.
The fleet's six faults in the sweep, each a rule in its tooling now: the seed-only addpeer (seven peerless boxes, 23 minutes without a lock; the hub as a default second peer, 34ef30f8); the pool kill file's "all" taking pool-1's supervisor; the loop's expected digest and wanted sha left on the old pin; the cases script re-putting the old object (OVERRIDE input); the sweep's pow read-back term; the Devnet 2 env filter. The shipper's: the speculative build chains sharing one --out directory (a candidate's artefacts overwritten by the next chain's; the sweep's pair taken from the build-server lane's copy at /srv/artefacts/0320-c4459193/ instead), and the first order to the seeds with the devnet file (corrected before any box moved).
**0.3.21 staging word given at 16:39 BST** (the node lane: c631c64b first on 55768f88, then the eight in order; the whole suite set; the candidate's binary with every gate from the build on the warm set). The reliability injector pod rented at the same minute.

View file

@ -0,0 +1,66 @@
# Release 0.3.21 (the cut after 0.3.20; staged 7 October 2026, 14:2x BST)
Tree: release-0.3.21 on origin, from release-0.3.20's closed tree (the 0.3.20 cut: pin c4459193, igneum-pow 8c728ca3 at byte 5, the floor-moved file). Rules: docs/plans/release-rules.md (4a every gate on every candidate from its build; 4b warm pods per gate class; 5 the sweep in waves).
## 1. What rides it (the project lead's list, 7 October 2026)
| Item | Owner | State |
|---|---|---|
| driver-check 1982dbc7 (the driver table measured: NVIDIA 617.42, AMD 26.9.2 with the amd.com referer, Intel 32.0.101.9034; the install thread with one elevated prompt; `--drivers` on the manifest) | the Intel lane | in: 39187097, afe4e2eb, 416d1f5d |
| miner-reliability, app half (miner-reliability-21 aa2bb07e, 18 commits: the readiness gate and the ladder, the load clock, the hold owner, the orphan sweep, the FAULT lines to the intake, the signed `cards` job kind, execrpc with `probe`; box 226 + 32 + 8, UI 49) | the reliability lane | merged into release-0.3.21 |
| miner-reliability, miner half (miner-reliability-20 f067f7c1: STATUS while waiting for a template, identities capped from the node's template time, the fetch back-off) | the node lane | staged on release-0.3.21-node at the sweep-end word |
| sub-version 2 (the hash lane's 07a809a7, byte 7, id a788661687db4bb3), pending the F8 census (about 15:00 BST; pass = all 64 seeds under 1.2x of the window model plus the suite and G1 lines); if it fails or slips past 20:00 BST the node ships byte 5 again with the re-pin dropped | the Counter lane, the node lane | held |
| the fork gate from horizon-node (6eb21fc9, six commits on finality.rs) | the node lane | dry-merged clean into c4459193 |
| the node's own rust-toolchain.toml | the node lane | in place on its worktree |
| the under-12 GB prove-instead switch (section 2) | the shipper | in: settings.prove_instead, Cmd::ProveHold, the Prove page's row, provedefault helpers and tests; app gate on build-2 230 + 32 + 8, UI 74 |
| the per-architecture prover server (section 3) | the reliability lane (the app's capability check, MF-10 in cbd6f3a4), the fleet (the kit's server per arch) | the app half in |
| pool-finish 434e8b9b (the pool crate: the TLS 1.3 member port with the authorize binding, the open pool, pool.md 10; fork pool-finish-node b0444f51 off 8097d600: the pool-mode miner, the IGNS/IGNP codec, pool_split_activation_daa at never on every network, so the digest does not move) and pool-mf-row 343dd83b (MF-11 in the register) | the pool lane | main's word 14:2x BST: rides 0.3.21 with the split switch at never; if it reds a 0.3.21 gate or costs more than one rebase round it moves to 0.3.22 |
| the UI branches (apps-harmony parked by the project lead's word, 14:2x BST; the Prove switch fix moved to gpu-logos): gpu-logos-21 in at fee49fc5, earnings-tidy-21 in, scene-parity-21 in, ui-overlap-fixes nothing to merge | the UI lanes | done |
| MF-11 (the app not returning after update-now; PC 2 silent since the 0.3.19 update-now at 10:34Z, its relay agent dead with it, so no job or task reaches it): the register row, the UI lane owning the cause, the relay agent as a service surviving the app as the restart path | the reliability lane, the UI lane, the relay lane | the row asked |
| the ids commit 55768f88 (`resolve_program_ids`: the override's fields, then the env or the host, then the embedded verifying keys; the exec suite 32 with the bare-node test known-failed first; built 13:35Z sha 279b1b690e854fc9): 0.3.21's FIRST node commit by main's word, with the ids gate (rule 4c) on every candidate | the node lane | on the mirror; its gates run under rule 4a |
| the 0.3.21 node order (main, 14:4x BST), each with its own gate line, one rebase round or 0.3.22: on c4459193, 55768f88; miner-reliability-20 f067f7c1 and the late-join commit 70e4601e; pool-finish-node b0444f51; horizon-node 6eb21fc9 (the deep fork-choice fix, fork_gate never set); peer-directory-node db28d331 (the peer directory prototype, peer_directory_activation_daa at never, the IGNP announce only under --announce); the sub-version-2 re-pin held for the census. The horizon lane rebases its two onto c4459193 at the sweep-end word | the node lane, the horizon lane | staged tonight |
| pool-finish-21 at 5e557171 on origin (release-0.3.21 5d153892 plus the ten pool commits, the tenth d5265fb3 the `--template-parallel` fix, default 2: the ten-member window's daemon queued ten template calls on one gRPC connection past its own timeout, 1,891 lines, no job for 36 minutes; site/miner.html carries the pool-fee sentence); igneum-pool 28 at 5e557171 on build-2, the app gate 226 + 32 + 8 at 54dd4c06 (the last commit touches pool/ and docs only); merged after the three UI branches with the app gate rerun at the merge | the pool lane | ready to merge |
| the genesis-forward lane's f95178a1 (consensus/src/consensus/services.rs, +5 −1: the difficulty manager takes `params.pow_epoch_blocks` instead of the process-wide static; names the lib-suite flake behind the horizon lane's two reds and main's "difficulty flake": two pruning-proof tests install a 60-block schedule process-wide, and a TestConsensus built in that window refuses block 61 with UnexpectedDifficulty; the lib suite 114 of 114 twice on build-2; the daemon unchanged) | the genesis-forward lane, the node lane | taken last in the node order, its own commit, the lib suite as its line |
| ui-overlap-fixes: nothing to merge for the miner (tools/ci/overlap-check.mjs read 0 overlaps on 4c89372a and on 50ffa562, the run on 3dc0832a in flight); the wallet's one finding at 927ad832 on ui-overlap-fixes-wallet off wallet-0.1.5 is the wallet's cut; the detector lands on master with the site fixes | the overlap lane | closed for 0.3.21 |
| the app reports its vote key hashes to the intake on every poll (main, 15:2x BST: the two paid devnet keys outside the fleet's registry could only be read by hand on the PCs; chainfacts keeps reading the node's keys too): the labels from prover::labels and igneum-miner key-hash once per labels change, a `keys` field on the app's intake row, the console's machine line showing the first eight of each | the shipper | to write after the 0.3.20 publish, before the 19:30 app gate |
| update-return (the MF-11 lane, bd9b4f4e off 4c89372a), main's word 15:2x BST: the app half (ota.rs: the Windows helper owns the update's return with the kept exe set, a 120 s api/state poll, a rollback and one intake line either way; engine.rs: the read-back line "update-return: app <v> up after the update from <from>", FAULT pc-restart after a power loss, the tuner ceiling and the refused-cap ladder; ember.rs; jobrun.rs the job-channel ping on wake; bootcheck.rs; platform.rs), app/windows/host.cpp (a dead engine restarted, WM_QUERYENDSESSION answered; about 60 new C++ lines) and the installer (/IGNOTA=2) ride 0.3.21 if they rebase in one round as update-return-21; the cut's windows.yml run compiling host.cpp is a NAMED GATE LINE (no host.cpp ships uncompiled). The relay half (the v3 agent as a service, playbooks, the X23 signed-run lib) lands through master on the relay lane's line with the relay deploy decision, main's | the update-return lane | rebasing |
| 0.3.20's version strings moved to 0.3.21 | the shipper | 0d7b9bd0 |
| master 819d536b merged (the 51-check gate; build-remote routes by load; the hands script's pgrep in the bracket form) | the shipper | c83ca904, 0b75cf52 |
## 2. The under-12 GB prove-instead switch (design, main's routing)
The fact (the fleet's 3080 hour, 7 October 2026): a 10 GB card completes the compressed step alone at the default threshold (8,642 to 8,729 MiB of 9,885) and never beside its miner's 1,547 MiB; no lower threshold fits (the server dies before the compressed step at 524288 and below). 0.3.20 ships the rule "proving needs a 12 GB card; mining continues" (provedefault::prove_refused_under_12gb). 0.3.21 adds the choice: a Settings switch `prove_instead` (off by default) that, on a machine whose only NVIDIA card is under 12 GB, stops that card's miner while the prover runs and restarts it when the prover is off; the tile sentence becomes "proving instead of mining on <card> (10 GB holds one, not both)"; the refusal line stays when the switch is off. Engine: the prover loop's refusal rung reads the switch; when on, it takes the miners hold for that card (the hold owner from miner-reliability) for the prover's lifetime. Test: the refusal lifts with the switch on and the card's miner reads held; a 12 GB card beside it never triggers the hold. UI: the switch under Settings > Proving with the sentence; the view test.
## 3. The per-architecture prover server (design, main's rule)
The fact (the fleet, 7 October 2026): sp1-gpu-server is built for one card's compute capability (sm_86 Ampere, sm_89 Ada, sm_120 Blackwell); a server for the wrong architecture fails every proof in 12 s with "CudaRustError: named symbol not found" and the miner never notices; the 610-series driver proves (the driver floor stays 570 or newer, no upper bound). The app's WSL2 setup (setup-wsl.sh) builds the server on the machine, so it matches by construction; the risk is a copied or stale server. 0.3.21: (1) the prover's setup records the compute capability the server was built for (nvidia-smi --query-gpu=compute_cap) beside the binary; (2) at every prover start the engine reads the card's compute_cap and the record, and refuses with "the prover was built for sm_NN; this card is sm_MM; Set up rebuilds it" when they differ (MF-10's capability check in prover.rs, owed by the reliability lane's register); (3) the fleet's kit ships one server per architecture under bin-<arch> picked by compute_cap at install (the fleet lane). Test: a record and a card that differ refuse with the sentence; equal ones pass; no record passes with a warning line.
## 4. Gate clock (rules 4a and 4b)
App: the gate on build-2 about 19:30 BST (`build-remote.sh --box 2 -- test --release` from app/igneum-app), the UI tests, the Windows cross on build-2, the reliability injector's eight steps on a one-shot pod (the fleet, about 45 minutes, after the sweep). Node: the branch commits at the sweep-end word (about 16:45 BST), suites on build-2 about 16:50 to 17:05, the first candidate binary on build-1 about 17:10 with the digest gate, the mixed-version gate, the kept start, the cases and the wipe all started from the build on the warm pods; the pin about 80 minutes after the last candidate builds. The Mac's own binaries and the DMG under the lock after the pin; the staging in a scratch copy; the publish on green with the clock time.
**gpu-logos-21 in (14:4x BST):** ba5d9cea, four commits on 8ed08dcf (1054616c the vendor marks, bca70dcd the captures, c98f2666 the Prove switch fix as its own commit: the DAG legend's bare .lg and the inspector's bare .track scoped to .legend .lg and #i-track, the switch's size class its own, the native input hidden the accessible way under a positioned label; ba5d9cea the shared marks module for the site and the re-taken shots). Lines: build-2 app gate 226 + 32 + 8 at c98f2666 (ba5d9cea touches nothing in the crate); UI 72 pass (view 41: 5 marks, 3 switch known-failed first, 1 module drift); pre-push 52 green. Captures docs/plans/gpu-logos-shots/ 01 to 12. Merged into release-0.3.21.
**earnings-tidy-21 in (14:4x BST):** a436c6f0 on 5d153892 (c7ec4573 the Earnings card tidied by the project lead's order "remove all the type a price stuff": the £ per IGN input, Use it, the remembered price, the price, balance-in-pounds, share, IGN per kWh and cost-of-hash lines gone; the card is the day rate with its reason, blocks and IGN this run, a row of three (weight, electricity, lifetime), the dev-fee switch; View.earningsLines(s, chain, pence, now, minor), FIELDS.price gone; a436c6f0 the captures docs/plans/earnings-tidy-shots/01 to 03). Lines: build-2 app gate 226 + 32 + 8; UI 75 (three new, known-failed first); pre-push 52. Merged into release-0.3.21.
**miner-reliability-21 at cbd6f3a4 in (14:5x BST):** four more commits merged: 4a28eb59 MF-10 (provedefault::server_mismatch and is_arch_failure, the test sm_86 on 8.9 and 12.0 refused, 8.6 and a fat binary pass; prover.rs server_arch_check reads the card's compute_cap and the server's sm_ words at the first probe, refuses the GPU path with the reason on the tile and a FAULT class=prover-arch line; "named symbol not found" logged as the same class) and MF-8's injector step (app-run.mjs --node-old: the previous release's node writes 150 blocks and stops, the new node opens the datadir and reads synced in one start); a751d391, 6a51c6c1, cbd6f3a4 the register (MF-11 the machine going silent with nothing reporting it, MF-12 the pool member stalled at an epoch boundary carried over from the pool lane, the rows moved from owed to code). Lines: build-2 app tests 227 + 32 + 8 on 4a28eb59; pre-push 52; UI 49 (the lane's run; the tree's UI tests re-run at the next merge). The 0.3.21 injector run: the payload staged (the app 786c3d37 from 8ed08dcf, node c4459193, miner 65d0837a, the old 0.3.17-line igneumd for --node-old), nine steps about 50 minutes on the fleet's pod after the sweep. This closes section 3's app half (the capability check); the per-architecture kit is the fleet's.
## 5. The node line's first candidate: 55768f88 (the node lane, 14:5x BST)
sha256 279b1b690e854fc9, the string read back, pairing 8c728ca3 at byte 5. The digest gate 13:35:41 to 13:37:19Z PASS (thirteen fields a89be8a7 on both binaries, sixteen fields db9a85f9 refused with no peer, the live file's digest eada4bda unmoved); the ten-minute mixed-version gate beside the 5899f603 pair 13:37:40 to 13:47:52Z PASS (digest b0afb2ee on all five, 223 new and 381 old blocks accepted, 0 rejected, plain header version 2, counts equal at 319, 486 and 604 through both joins and the restart step, no panic). The fleet's set on the same binary (the kept start with the ids gate, the cases on the warm set, the wipe) runs under rule 4a. Staging at the sweep-end word in main's order: f067f7c1 and 70e4601e, then b0444f51, then the horizon lane's rebased 6eb21fc9 and db28d331, then the re-pin on the Counter lane's word; suites on build-2 and the digest read after every merge; the next candidate's binary with every gate from its build.
**scene-parity-21 in (14:5x BST), ahead of ui-overlap-fixes-21 in the order because it was green on the exact tip (50ffa562) while ui-overlap-fixes still rebases:** 20c9153b (0d75bc1f the shared scene/ folder and live-dag.js 2.0.3, c1334faf the app side, 20c9153b the parity harness and its gate line). Lines: build-2 app gate 228 + 32 + 8; UI 72; pre-push 56 with four new chain-scene checks (sync byte-equal, the paint-on-push known-failed test, the feed contract, the parity render on build-2: home fold = /live = app Inspect at T+0, +2, +4 s). live-dag.js 2.0.2 → 2.0.3 (paint on every push whatever the visibility, the blank /live fix; the phone rule on the viewport width); proof-core.js unchanged 2.0.0; the site's copies moved on master f7743534 (igneum.network/live-dag.js reads 2.0.3); the source is scene/live-dag.js, both copies written by `node tools/scene/sync.mjs`, the gate refuses drift, so the 0.3.21 pack carries the 2.0.3 bytes. Users see: the light theme's ember at the brand package's #D0420D, the chain feed asking 300 s, Inspect 420 px tall (seven lanes), the phone layout no longer frozen at launch width, api/live keeping the key id in `miner`. Captures: build-2 /srv/builds/scene-parity/igneum-wt-scene-parity/_out and scratchpad/scene/parity-out.
**scene-parity-21-sizing in (15:0x BST):** 7e15bf2f on 3dc0832a: live-dag.js 2.0.4 (the box's height follows the lanes through onSize and autoHeight, for /live; the app passes neither, its frames byte-identical, the parity test equal on every comparison); scene/, site/ and the app copy byte-equal; no Rust change, the app gate of 20c9153b stands; pre-push 56 green. The same 2.0.4 is on master at b9017422 and served by igneum.network.
**pool-finish-21 in (15:0x BST):** 2eea335f (the ten pool commits rebased onto 3dc0832a, no conflict; the pool-fee sentence in site/miner.html). Lines at that tip on build-2: igneum-pool 28; the app gate 229 + 32 + 8. Merged after scene-parity-21-sizing (JS only, so the Rust gate stands). The app side of 0.3.21 now carries: driver-check, miner-reliability-21 (cbd6f3a4), gpu-logos-21 with the Prove switch fix, earnings-tidy-21, scene-parity-21 and its sizing (live-dag.js 2.0.4), pool-finish-21; still mine: the under-12 GB prove-instead switch (section 2). The app gate on the final tree runs on build-2 about 19:30 BST or as soon as the switch lands.
**N15 rides 0.3.21's node line (the node lane, 15:1x BST):** branch numbering-fix at d8bceca5 (a6864e36 then d8bceca5, from 52e96c94): chain_path checks the first added block's selected parent against the tip record before anything is appended and hands the orphan records above the fork point to the reorg unwind (the shape that left p1-5090 two high: a reorg's removed list one short at 15:51Z on 6 October); a start-time self-check once per process walks the records from the restart pin against the DAG's selected parents, names the first break, unwinds above it and lets the follower re-walk (the shape that left p2-3090-3 46 high from a snapshot carrying its source's break); recordsContinuous and continuityBreak on igneum_getProvingStatus and igneum_getExecStatus (null, true, or false with the break's block), box-prover claiming only on true. The number is canonical by construction from the pin; the carrier's refusal by number stands (the statement binds the number). Two unit tests known-failed first; the exec suite 35 and the kaspad check green on build-2 at 14:13Z; the live line is the fleet's restart of p1-5090 and p2-3090-3 on the 0.3.21 candidate (the self-check naming #155958 and #158875, the unwind, offset 0 after). The 0.3.21 node order, final, on byte 5: 52e96c94, f067f7c1, b0444f51, 437f0438, 2e32d5f6, f95178a1, a6864e36, d8bceca5.
**update-return-21 in (15:3x BST):** 9d838ae5, one commit on b4289c4a (the app half: ota.rs, engine.rs, jobrun.rs, ember.rs, platform.rs, main.rs, bootcheck.rs; app/windows/host.cpp; Igneum-Miner.iss /IGNOTA=2; one round, main.rs's mod list by union). Lines on the tree: build-2 app tests 247 + 32 + 8; UI 74; the Windows cross green on build-1 (igneum-app.exe 4,172,288 B sha256 5b0598ad…, system DLLs only). host.cpp compiles in the cut's windows.yml run: the named gate line. The relay half stays on update-return for the relay lane's line through master.
**first-block-21 in (16:0x BST):** 6e555d47 on 064fb02b (ladder.rs: first_block_shown persisted in ladder.json, Ladder::start_run; engine.rs start_run at load and started_at on the state; app.js staleCard, firstWait, the first rung reading the flag; the ui-mock secondblock scenario). Lines: build-2 app gate 254 + 32 + 8 (seven new ladder tests); UI 67 (view 46, one new known-failed first); pre-push 56. No installer, node or host change. Captures ~/Desktop/igneum-previews-2026-10-07/first-block/01 and 02.
**Three more in (16:2x BST):** update-return-21b a64c193f (the eGPU card kind from a USB4 or Thunderbolt router in the device's parent chain, the Power Helper's fault line and its stale-prefix fix; app 255 + 32 + 8, UI 76); first-block-21 at cc9141cb replacing 6e555d47 (main's "seen once": the first-block card waits until a window has shown it, POST api/card/seen, ladder.rs card_seen; app 255 + 32 + 8, UI 67); miner-reliability-21 at 018440ae (the register: MF-11 in the update-return lane's words, MF-12 the pool stall, MF-13 the Power Helper's stale count; code unchanged since 4a28eb59, CI success). UI tests on the merged tree 76 of 76; the app gate on build-2 at the merged tip below. GitHub refused pushes with "Internal Server Error" from about 15:25 to 16:18 BST, transient.
**The node order, final (16:3x BST):** on 55768f88: c631c64b first (the test-only fix of the two stale integration targets, 1026 → 1282; both green on build-2 at 15:34Z), then 52e96c94, f067f7c1, b0444f51, 437f0438, 2e32d5f6, f95178a1, a6864e36, d8bceca5; the tip's suite set runs the consensus crate whole (`-p kaspa-consensus` without `--lib`: the lib and both integration targets) beside consensus-core, the miner, kaspa-pow, the exec suite and the three checks (the suite rule from the 0.3.20 known-red finding). era-vdf-node 394a5902 is 0.3.22's.

View file

@ -0,0 +1,20 @@
# Release rules (the shipper's standing rules, as main set them)
Every cut of the Igneum Miner app and its node runs under these. The dated plan for each cut (docs/plans/release-<version>.md) records how each rule was met.
1. **Versions are three-part.** A hotfix takes the next number; the feature tree moves up. The app's parser returns None on a fourth part.
2. **Nothing is staged in the live downloads folder.** Stage in a scratch copy with `IGNEUM_DLSITE=<copy> publish-manifest.sh --no-deploy`; the live folder changes only in the deploy step. `publish-jobs.sh --deploy` is jobs-only. No lane removes a scratch directory it did not create.
3. **The deploy gate is a full canary on the fleet's pods:** the fresh join through the headers proof on a WIPED datadir (decisive), synced, ten minutes mining, the hub holding a block, the relay and poison cases. Main may call the deploy on the decisive read plus a diff argument.
4. **The kept-datadir start is a named gate in every release (main, 7 October 2026, after ledger N13).** Every canary runs both a wiped datadir and a KEPT one: a copy of a standing box's datadir from the live release, the pinned binary started on the copy on a scratch pod, "synced" or the store's rewrite line as the pass. The Windows shape too: the pinned Windows node once against a copy of PC 2's datadir (PC 2 only, never PC 1) before PC 1 gets the build. The miner-reliability register carries it as its own fault class. Why: every node build from 10db4b61 died at start on a kept 0.3.17 datadir (bincode ignores serde defaults) and no canary saw it because every canary wiped.
4c. **The proving ids gate (main, 7 October 2026, after the 0.3.20 blocker).** On every candidate, a node started on the LIVE override file reports both proving ids (the shard program id and the aggregator id) on its proving v1 start line, and a prover's first statement against it is accepted; a zero-id statement is the known-failed shape. It runs beside the kept-datadir read, since both share the warm pod. Why: c4459193 read the ids as unknown, built statements with zeros and refused every proof; a sweep would have stopped every prover's pay.
4a. **Every gate starts on every candidate the moment its binary builds, never after the pin (the project lead, 7 October 2026).** The digest and mixed-version gates, the kept-datadir start, the relay and poison cases and the wipe canary all begin on each candidate binary as it lands; a struck candidate's runs are stopped and its successor's begin. The post-pin wait is then the longest single form (about 80 minutes, the wipe), not the sum.
4b. **Warm pods per gate class (the project lead, 7 October 2026, ordered to the fleet lane).** The fleet keeps synced pods warm for each gate class so a case form's target starts at the tip (a kept copy of the live line, caught up), never from a kept copy far behind it; the wipe canary is the only full IBD in the set.
5. **Rollout in waves, each box read back (the project lead, 7 October 2026, replacing one-box-at-a-time):** PC 1 first, then PC 2, the Mac, the seed, the hands and the fleet in parallel waves as the lock lines allow; a lock line from the hub between waves; hold if the frozen table's signed share reads under 75; every box read back by its commit string. When the publish moves the consensus floor (a new digest), every 0.3.x node on the old file refuses the new ones as peers until it is swept, so the seed, the hands and the fleet move in the first wave with the apps' pollers, not last. Every lock line of a sweep that replaces nodes carrying a consensus floor names the date the sweep must finish (0.3.20: before 13 October 2026 09:00 UK).
6. **Read-back is by commit string plus digest plus engine:** on 0.3.18+ nodes igneum_getNodeInfo powEngine must read "igneum-pow" ("stub" = FAIL); on earlier trees `strings igneumd | grep -c igneum-pow/src/` above zero. The miner embeds no commit string; its pairing is the build line and the sha.
7. **igneum-pow pairing:** a fork build takes igneum-pow by path from the igneum worktree it sits in; build each node tree inside its own app worktree whose igneum-pow is the pinned tree; the pairing log line names it. Master's build tools need rust-toolchain.toml in the tree (the app tree's pin applies to a vendor worktree under it; a standalone node checkout is unpinned until the node line carries its own file).
8. **glibc classes:** HiveOS 2.31 (`--ship hive`, smoke in ubuntu:20.04 on the box), seeds and generic 2.35 (`--ship seed`), fleet 24.04 boxes native 2.39.
9. **The Mac builds only the macOS binaries and the DMG,** one at a time under the build lock; every other build, suite and the Windows cross-build runs on the box or a PC; the app gate is `build-remote.sh -- test --release` from the crate dir.
10. **A pin is green on its own suites and gates.** Lines taken on one binary carry to another only when the code is byte-identical, stated in the tip. No known-red pins: a stale test takes a test-only commit on top.
11. **Kill by pid, never by name,** on the shared Mac; a merge worktree never checks out master.
12. **The Discord card only when every platform is live.** Live manifest changes beyond the binaries (a moved consensus floor) go out only on the project lead's explicit word, staged beside the release with their digest and a one-line diff.
13. **Ship on green:** no calendar waits; when the gates are green, publish and state the clock time (UK). Checkpoints are for slips, not for waiting.

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

Some files were not shown because too many files have changed in this diff Show more