Merge horizon d2b1b08c into master (gate: green on d2b1b08c, recorded by tools/ci/pre-push.sh; the full gate runs in CI on this merge)

This commit is contained in:
igneum-labs 2026-10-07 14:17:38 +00:00
commit 4e6016fc33
19 changed files with 6676 additions and 1 deletions

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,86 @@
#!/usr/bin/env node
// The gate line of mission item 4 (docs/analysis/mission/mission.md 2.4): "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", with the
// known-failed cases first. Runs the six cases of infra/fast-time/fork-gate.mjs side by side (each on its own slot:
// ports and devnet suffix), reads each case's verdict against what it MUST be, prints the table and writes
// docs/plans/mission-item-4-gate/summary.json. Exit 0 only when every case's verdict is the one it must give.
//
// node infra/fast-time/fork-gate-gate.mjs [--parallel 6] [--only attack-gate-on-expect-hold,...] [--joint 240] [--split 180]
// [--watch 150] [--window 60] [--honest-threads 1] [--attacker-threads 3] [--attacker-joint-threads 2]
// IGNEUMD and IGNEUM_MINER name the binaries; the cases' logs land beside the summaries.
//
// The fork depth: the window is 60 DAA (the devnet's 600 s at 60x is 10 DAA, too few blocks to read; the window stays
// at 60 and the split at 180 s, three windows deep; "15 min back" on the devnet is one and a half windows, which the
// split covers from its 61st second on). The honest side mines through the split on every case (the 6 October harness
// idled it, and a withheld chain's victim keeps mining).
import { spawn } from 'node:child_process';
import { mkdirSync, openSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
const ROOT = new URL('../../', import.meta.url).pathname;
const OUT_DIR = `${ROOT}docs/plans/mission-item-4-gate`;
const args = process.argv.slice(2);
const sflag = (name, dflt = null) => { const i = args.indexOf(`--${name}`); return i >= 0 ? args[i + 1] : dflt; };
const PARALLEL = Number(sflag('parallel', '6'));
const ONLY = sflag('only') ? sflag('only').split(',') : null;
const PASS_THROUGH = ['joint', 'split', 'watch', 'window', 'honest-threads', 'attacker-threads', 'attacker-joint-threads'].flatMap(n => sflag(n) != null ? [`--${n}`, sflag(n)] : []);
const log = (...a) => console.log(new Date().toISOString().slice(11, 23), 'gate', ...a);
// in the order the mission asks: the known-failed cases first, then the gate line, then the two sides the gate must leave alone
const 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" },
{ 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" },
{ 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' },
{ 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' },
{ mode: 'partition', gate: 'on', expect: 'reorg', must: 'PASS', line: 'partition heal with the gate on: the heavier side wins' },
{ mode: 'partition', gate: 'off', expect: 'reorg', must: 'PASS', line: 'partition heal with the gate off: the same outcome, so the heal is unchanged' },
].map((c, slot) => ({ ...c, slot, name: `${c.mode}-gate-${c.gate}-expect-${c.expect}` }));
const cases = ONLY ? CASES.filter(c => ONLY.includes(c.name)) : CASES;
if (!cases.length) { console.error(`no case matches --only ${ONLY}`); process.exit(2); }
mkdirSync(OUT_DIR, { recursive: true });
function run(c) {
return new Promise((resolve) => {
const logFile = `${OUT_DIR}/fork-gate-${c.name}.log`;
const out = openSync(logFile, 'w');
const a = [`${ROOT}infra/fast-time/fork-gate.mjs`, '--mode', c.mode, '--gate', c.gate, '--expect', c.expect, '--slot', String(c.slot), '--case', c.name, '--out', `${OUT_DIR}/fork-gate-${c.name}.json`, ...PASS_THROUGH];
const t0 = Date.now();
const p = spawn(process.execPath, a, { stdio: ['ignore', out, out], env: process.env });
log(`start ${c.name} (slot ${c.slot}, pid ${p.pid}): ${c.line}`);
p.on('exit', (code) => {
const secs = Math.round((Date.now() - t0) / 1000);
let summary = null;
try { summary = JSON.parse(readFileSync(`${OUT_DIR}/fork-gate-${c.name}.json`, 'utf8')); } catch { }
const lines = existsSync(logFile) ? readFileSync(logFile, 'utf8').split('\n') : [];
const summaryLine = lines.find(l => l.includes('SUMMARY ')) || lines.filter(Boolean).at(-1) || '';
const verdict = code === 0 ? 'PASS' : code === 1 ? 'FAIL' : `ERROR ${code}`;
log(`end ${c.name}: ${verdict} in ${secs} s (must ${c.must}) ${summaryLine.replace(/^.*?SUMMARY /, '')}`);
resolve({ ...c, verdict, as_it_must: verdict === c.must, secs, exit: code, summary_line: summaryLine.replace(/^\S+ fg\d+ /, ''), summary: summary ? { ...summary, samples: undefined } : null, log: logFile });
});
});
}
const results = [];
const queue = cases.slice();
async function worker() { while (queue.length) results.push(await run(queue.shift())); }
await Promise.all(Array.from({ length: Math.min(PARALLEL, cases.length) }, worker));
results.sort((x, y) => x.slot - y.slot);
const pad = (s, n) => String(s).padEnd(n);
console.log('');
console.log(`${pad('case', 34)} ${pad('must', 5)} ${pad('got', 8)} ${pad('ok', 4)} ${pad('side 2 at fork', 15)} ${pad('depth', 6)} ${pad('heavier', 8)} honest nodes`);
for (const r of results) {
const s = r.summary;
const nodes = s ? Object.entries(s.per_node).map(([n, p]) => `${n.toUpperCase()} ${p.reorged ? `reorged@${p.reorg_at_s}s` : 'held'}/${p.refusal_lines} refusals`).join(', ') : '(no summary)';
console.log(`${pad(r.name, 34)} ${pad(r.must, 5)} ${pad(r.verdict, 8)} ${pad(r.as_it_must ? 'yes' : 'NO', 4)} ${pad(s ? `${s.share_at_fork.side2_bps ?? 0} bps` : '?', 15)} ${pad(s ? s.fork_depth_daa_at_heal : '?', 6)} ${pad(s ? s.checks.side2_heavier_at_heal : '?', 8)} ${nodes}`);
}
const healUnchanged = (() => {
const on = results.find(r => r.name === 'partition-gate-on-expect-reorg'), off = results.find(r => r.name === 'partition-gate-off-expect-reorg');
if (!on || !off) return null;
return on.verdict === 'PASS' && off.verdict === 'PASS' && JSON.stringify(Object.values(on.summary?.per_node ?? {}).map(p => [p.reorged, p.refusal_lines])) === JSON.stringify(Object.values(off.summary?.per_node ?? {}).map(p => [p.reorged, p.refusal_lines]));
})();
const allGood = results.every(r => r.as_it_must) && healUnchanged !== false;
console.log('');
console.log(`GATE ${allGood ? 'GREEN' : 'RED'}: ${results.filter(r => r.as_it_must).length} of ${results.length} cases gave the verdict they must${healUnchanged == null ? '' : `; partition heal unchanged between gate on and off: ${healUnchanged}`}`);
writeFileSync(`${OUT_DIR}/summary.json`, JSON.stringify({ green: allGood, at: new Date().toISOString(), heal_unchanged: healUnchanged, node: process.env.IGNEUMD ?? null, miner: process.env.IGNEUM_MINER ?? null, cases: results }, null, 2));
process.exit(allGood ? 0 : 1);

325
infra/fast-time/fork-gate.mjs Executable file
View file

@ -0,0 +1,325 @@
#!/usr/bin/env node
// Weight-gated deep fork choice (docs/analysis/51-percent.md rank 2; fork `deep_fork_refusal`, 0.3.16): the fast-time gate
// of mission item 4 (docs/analysis/mission/mission.md 2.4, 7 October 2026). Three nodes on one fast-time network: H1 and
// H2 are honest and mine through every phase (--honest-threads CPU threads each, so the honest side never idles); B is
// the third node. The network mines together for --joint seconds (the weight table fills: 120 DAA at 60x), then B's two
// links (pass-through proxies B dials) are cut and B mines a private chain with --attacker-threads for --split seconds,
// more than the gate's window (--window DAA, 60 here against the devnet's 600 s) and heavier than the honest chain by
// blue work. Then the links are restored and every honest node's sink is watched for --watch seconds.
//
// --mode attack B's key is FRESH: it mined nothing before the cut, so it holds 0 of the weight table at the fork
// (the 51 percent renter of 51-percent.md section 1 has no history; here B holds 60 percent of the
// CPU threads, 3 of 5, so its chain is heavier for sure; the refusal does not read how much heavier)
// --mode third B mines through the joint phase too (--attacker-joint-threads, 2 against 1 + 1: about half of the
// blue blocks at the fork, above a third), so its deep fork is ACCEPTED: both honest nodes reorg to it
// --mode partition no B: H1 against H2, each with half of the weight; H2 mines heavier through the split; at the heal
// H1 takes H2's chain and H2 keeps it, with the gate on exactly as with it off (the heal is unchanged)
// --gate on|off fork_gate_activation_daa 0, or the switch at never
// --expect hold|reorg which outcome of the honest side is a PASS. `--mode attack --gate off --expect reorg` is the
// rule's known-failed case (the attack succeeds without the gate) and must PASS;
// `--mode attack --gate off --expect hold` is the harness's own failed shape and must FAIL.
//
// node infra/fast-time/fork-gate.mjs --mode attack --gate on --expect hold [--joint 240] [--split 180] [--watch 150]
// [--window 60] [--honest-threads 1] [--attacker-threads 3] [--attacker-joint-threads 2] [--slot 0] [--out <file>]
// IGNEUMD and IGNEUM_MINER name the binaries. --slot k moves the ports and the devnet suffix so cases run side by side
// (infra/fast-time/fork-gate-gate.mjs runs the mission's six at once).
//
// A "reorg" is read from the chain, never from a key: the honest node's sink at the cut (its pre-cut tip) is asked of
// getVirtualChainFromBlock, and the node has reorged when that call lists removed chain blocks (the pre-cut tip left the
// selected chain). The 6 October harness read the sink's key and misread a block B had mined into the shared chain.
//
// Leftovers of an earlier run of the same slot are stopped by PID FILE, never by name (CLAUDE.md, 6 October 2026): every
// process this harness starts is written to <tmp>/pids, and a pid is killed only when /proc/<pid>/cmdline carries this
// slot's data directory or one of its ports.
import { spawn } from 'node:child_process';
import { createServer, connect as netConnect } from 'node:net';
import { mkdirSync, rmSync, writeFileSync, readFileSync, openSync, existsSync, appendFileSync } from 'node:fs';
import { Rpc } from '../../tools/finality-attacks/lib/rpc.mjs';
const ROOT = new URL('../../', import.meta.url).pathname;
const FILE = `${ROOT}infra/fast-time/override-60x.json`;
const IGNEUMD = process.env.IGNEUMD || `${ROOT}vendor/igneum-node/target-integration/release/igneumd`;
const CPU_MINER = process.env.IGNEUM_MINER || `${ROOT}vendor/igneum-node/target-integration/release/igneum-miner`;
const args = process.argv.slice(2);
const flag = (name, dflt) => { const i = args.indexOf(`--${name}`); return i >= 0 ? Number(args[i + 1]) : dflt; };
const sflag = (name, dflt = null) => { const i = args.indexOf(`--${name}`); return i >= 0 ? args[i + 1] : dflt; };
const MODE = sflag('mode', 'attack');
const GATE = sflag('gate', 'on');
const EXPECT = sflag('expect', MODE === 'attack' && GATE === 'on' ? 'hold' : 'reorg');
const JOINT = flag('joint', 240), SPLIT = flag('split', 180), WATCH = flag('watch', 150), WINDOW = flag('window', 60);
const HT = flag('honest-threads', 1), AT = flag('attacker-threads', 3), AJT = flag('attacker-joint-threads', 2);
const SLOT = flag('slot', 0);
const GENESIS_BITS = flag('genesis-bits', 0x1f010000);
const CASE = sflag('case') || `${MODE}-gate-${GATE}-expect-${EXPECT}`;
const OUT = sflag('out') || `${ROOT}docs/plans/mission-item-4-gate/fork-gate-${CASE}.json`;
// nodes at BASE + i x 10 (gRPC, p2p, JSON-RPC, EVM RPC), the links at BASE + 31 and + 32; a slot is 40 ports wide. 30690 and up
// is clear of every other fast-time harness (difficulty 29790s, vote-or-burn 29990s, the 6 October fork gate 30090s, miner-stall
// 30290s, peer-drop 30390s, nuisance 30490s, headers-proof 30590s).
const BASE = 30690 + SLOT * 40, SUFFIX = 980 + SLOT;
const TMP = `/tmp/igneum-fast-time-fg${SLOT}`;
const NEVER = '18446744073709551615';
if (!['attack', 'third', 'partition'].includes(MODE) || !['on', 'off'].includes(GATE) || !['hold', 'reorg'].includes(EXPECT)) {
console.error('usage: --mode attack|third|partition --gate on|off --expect hold|reorg'); process.exit(2);
}
const started = [];
const log = (...a) => console.log(new Date().toISOString().slice(11, 23), `fg${SLOT}`, ...a);
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
for (const b of [IGNEUMD, CPU_MINER]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); }
// leftovers of an earlier run of this slot, by pid file (never by name)
const PIDS = `${TMP}/pids`;
function stopLeftovers() {
if (!existsSync(PIDS)) return;
const ports = Array.from({ length: 40 }, (_, k) => `127.0.0.1:${BASE + k}`);
for (const line of readFileSync(PIDS, 'utf8').split('\n').filter(Boolean)) {
const pid = Number(line);
let cmd = '';
try { cmd = readFileSync(`/proc/${pid}/cmdline`, 'utf8'); } catch { continue; }
if (!cmd.includes(TMP) && !ports.some(p => cmd.includes(p))) continue;
try { process.kill(pid, 'SIGKILL'); log(`stopped leftover pid ${pid} of an earlier run`); } catch { }
}
}
stopLeftovers();
await sleep(1000);
rmSync(TMP, { recursive: true, force: true }); mkdirSync(TMP, { recursive: true });
const track = (proc) => { started.push(proc); try { appendFileSync(PIDS, `${proc.pid}\n`); } catch { } };
const baseText = readFileSync(FILE, 'utf8');
const field = (name) => { const m = new RegExp(`"${name}":\\s*([0-9]+)`).exec(baseText); return m ? +m[1] : undefined; };
export function mergeOverrideText(text, fields) {
let out = text;
for (const k of Object.keys(fields)) out = out.replace(new RegExp(`\\s*"${k}":\\s*[^,}\\n]+,?`), '');
const extra = Object.entries(fields).map(([k, v]) => `"${k}": ${typeof v === 'string' && !/^\d+$/.test(v) ? JSON.stringify(v) : v}`).join(', ');
return out.replace(/,?\s*}\s*$/, `,\n ${extra}\n}\n`);
}
const DAY_MS = field('pow_day_ms');
const WEIGHT_WINDOW = field('weight_window') || 120;
const override = `${TMP}/override.json`;
writeFileSync(override, mergeOverrideText(baseText, {
genesis_bits: GENESIS_BITS, skip_proof_of_work: false,
fork_gate_activation_daa: GATE === 'on' ? '0' : NEVER, fork_gate_window_daa: WINDOW,
program_class_v3_activation_daa: NEVER, program_class_v4_activation_daa: NEVER,
}));
log(`case ${CASE}: mode ${MODE}, gate ${GATE} (window ${WINDOW} DAA), joint ${JOINT} s, split ${SPLIT} s, watch ${WATCH} s, honest ${HT} thread(s) each, attacker ${AT} (joint ${MODE === 'third' ? AJT : MODE === 'partition' ? HT : 0}), expect ${EXPECT}`);
// a pass-through proxy: open = the link is up; closed = the partition
class Link {
constructor(listenPort, targetPort) { this.listenPort = listenPort; this.targetPort = targetPort; this.socks = new Set(); this.up = false; }
open() {
return new Promise((resolve) => {
this.server = createServer((client) => {
const upstream = netConnect(this.targetPort, '127.0.0.1');
this.socks.add(client); this.socks.add(upstream);
client.pipe(upstream); upstream.pipe(client);
for (const s of [client, upstream]) { s.on('error', () => { }); s.on('close', () => { this.socks.delete(s); if (!client.destroyed) client.destroy(); if (!upstream.destroyed) upstream.destroy(); }); }
});
this.server.listen(this.listenPort, '127.0.0.1', () => { this.up = true; resolve(this); });
});
}
close() {
return new Promise((resolve) => {
this.up = false;
for (const s of this.socks) { try { s.destroy(); } catch { } }
this.socks.clear();
if (this.server) this.server.close(() => resolve()); else resolve();
this.server = null;
});
}
}
class Node {
constructor(name, i, peers = []) {
this.name = name; this.i = i; this.grpcPort = BASE + i * 10; this.p2pPort = BASE + i * 10 + 1; this.jsonPort = BASE + i * 10 + 2; this.evmPort = BASE + i * 10 + 3;
this.peers = peers; this.dir = `${TMP}/${name}`; this.logFile = `${this.dir}/node.log`;
}
get grpc() { return `grpc://127.0.0.1:${this.grpcPort}`; }
async start() {
mkdirSync(this.dir, { recursive: true });
const a = ['--devnet', `--devnet-suffix=${SUFFIX}`, '--nodnsseed', '--disable-upnp', '--nologfiles', '--enable-unsynced-mining', '--utxoindex',
`--appdir=${this.dir}`, `--rpclisten=127.0.0.1:${this.grpcPort}`, `--rpclisten-json=127.0.0.1:${this.jsonPort}`, `--evm-rpclisten=127.0.0.1:${this.evmPort}`,
`--listen=127.0.0.1:${this.p2pPort}`, `--override-params-file=${override}`, '--loglevel=info', '--yes'];
// --addpeer, not --connect: a dropped addpeer is dialled again, which is how a link heals
if (this.peers.length) for (const p of this.peers) a.push(`--addpeer=127.0.0.1:${p}`); else a.push('--outpeers=0');
const out = openSync(this.logFile, 'a');
this.proc = spawn(IGNEUMD, a, { stdio: ['ignore', out, out] });
track(this.proc);
await sleep(1500);
if (this.proc.exitCode != null) throw new Error(`${this.name} exited ${this.proc.exitCode}: ${this.grepLog(/ERROR|Error|error|refused|invalid/).slice(-3).join(' | ')}`);
// the RPC must answer before the node is used (the 6 October harness called it one tick after the socket opened)
for (let i = 0; i < 20; i++) {
const rpc = new Rpc(`ws://127.0.0.1:${this.jsonPort}`);
try { if (await rpc.connect()) { await rpc.call('getBlockDagInfo'); this.rpc = rpc; break; } } catch { try { rpc.close(); } catch { } }
this.rpc = null; await sleep(500);
}
if (!this.rpc) throw new Error(`${this.name}: the RPC did not answer within 10 s`);
log(`${this.name} up pid ${this.proc.pid} json ${this.jsonPort} p2p ${this.p2pPort}${this.peers.length ? ` addpeer ${this.peers.join(',')}` : ''}`);
return this;
}
grepLog(re) { try { return readFileSync(this.logFile, 'utf8').split('\n').filter(l => re.test(l)); } catch { return []; } }
async block(hash) { const b = await this.rpc.call('getBlock', { hash, includeTransactions: false }); return b.block; }
async sink() {
const d = await this.rpc.call('getBlockDagInfo');
const b = await this.block(d.sink);
const bw = b.header.blueWork;
const blueWork = typeof bw === 'string' ? BigInt(bw.startsWith('0x') ? bw : `0x${bw}`) : BigInt(bw);
return { hash: d.sink, blocks: +d.blockCount, daa: +b.header.daaScore, blue: +b.header.blueScore, blueWork, key: String(b.header.voteKeyHash) };
}
/// whether `hash` (a block that was on this node's selected chain) has left it: the chain path from it to the sink
/// lists removed chain blocks exactly when it did
async reorgedFrom(hash) {
try {
const r = await this.rpc.call('getVirtualChainFromBlock', { startHash: hash, includeAcceptedTransactionIds: false });
return (r.removedChainBlockHashes || []).length > 0;
} catch (e) { return null; }
}
async knows(hash) { try { await this.block(hash); return true; } catch { return false; } }
}
const miners = {};
function miner(name, grpc, threads, secs, label = name) {
// `label` is the vote-key label (the key); `name` the log file and the handle, so a restarted miner keeps its key
const out = openSync(`${TMP}/${name}.log`, 'a');
const p = spawn(CPU_MINER, ['mine', grpc, String(threads), String(secs), label, '--engine', 'igneum-pow', '--payout-label', label, '--status-secs', '30', '--no-vote'], { stdio: ['ignore', out, out], env: { ...process.env, IGNEUM_POW_DAY_MS: String(DAY_MS) } });
track(p); miners[name] = p;
return p;
}
const minerKey = (name) => { try { const m = /vote_key_hash=([0-9a-f]{64})/.exec(readFileSync(`${TMP}/${name}.log`, 'utf8')); return m ? m[1] : null; } catch { return null; } };
const links = [];
async function stopAll() {
for (const p of started.slice().reverse()) { try { p.kill('SIGINT'); } catch { } }
await sleep(1500);
for (const p of started) { try { p.kill('SIGKILL'); } catch { } }
for (const l of links) { try { await l.close(); } catch { } }
}
process.on('SIGINT', async () => { await stopAll(); process.exit(130); });
process.on('unhandledRejection', async (e) => { log(`FAILED: ${e?.stack || e}`); await stopAll(); process.exit(3); });
// topology: H1 listens; H2 dials H1 (directly, or through a cuttable link in partition mode); B dials H1 and H2 through
// two cuttable links. The honest set is every node whose chain the gate is meant to keep: H1 and H2, or H1 alone in
// partition mode (where H2 is the heavier side).
const h1 = await new Node('h1', 0).start();
let h2, b = null, side2;
if (MODE === 'partition') {
const l0 = await new Link(BASE + 31, h1.p2pPort).open(); links.push(l0);
h2 = await new Node('h2', 1, [l0.listenPort]).start();
side2 = h2;
} else {
h2 = await new Node('h2', 1, [h1.p2pPort]).start();
const l1 = await new Link(BASE + 31, h1.p2pPort).open(); links.push(l1);
const l2 = await new Link(BASE + 32, h2.p2pPort).open(); links.push(l2);
b = await new Node('b', 2, [l1.listenPort, l2.listenPort]).start();
side2 = b;
}
const honest = MODE === 'partition' ? [h1] : [h1, h2];
const side2Label = MODE === 'partition' ? 'fg-h2' : 'fg-b';
await sleep(3000);
const t0 = Date.now();
const since = () => ((Date.now() - t0) / 1000).toFixed(1);
const total = JOINT + SPLIT + WATCH + 60;
// phase 1: together. The honest miners run through every phase; side 2 mines in the joint phase only in third and
// partition mode (its key must hold weight at the fork), never in attack mode (its key must be fresh).
miner('fg-h1', h1.grpc, HT, total);
if (MODE !== 'partition') miner('fg-h2', h2.grpc, HT, total);
if (MODE === 'third') miner('fg-b', b.grpc, AJT, JOINT);
if (MODE === 'partition') miner('fg-h2', h2.grpc, HT, JOINT);
await sleep(JOINT * 1000);
const keys = { h1: minerKey('fg-h1'), h2: MODE === 'partition' ? null : minerKey('fg-h2'), side2: MODE === 'third' || MODE === 'partition' ? minerKey(side2Label) : null };
const joint = { h1: await h1.sink(), h2: await h2.sink(), side2: await side2.sink() };
log(`phase 1 done at ${since()} s: H1 ${joint.h1.blocks} blocks sink ${joint.h1.hash.slice(0, 8)} daa ${joint.h1.daa}; H2 ${joint.h2.blocks} sink ${joint.h2.hash.slice(0, 8)}; side 2 (${side2.name}) ${joint.side2.blocks} sink ${joint.side2.hash.slice(0, 8)}; keys H1 ${keys.h1?.slice(0, 8)} H2 ${keys.h2?.slice(0, 8)} side2 ${keys.side2?.slice(0, 8) ?? 'fresh (none yet)'}`);
// the weight at the fork: builders of the last WEIGHT_WINDOW chain blocks of H1's chain and their mergesets, by key
async function shareAt(node, upTo, key) {
let cur = upTo, mine = 0, others = 0;
for (let i = 0; i < WEIGHT_WINDOW; i++) {
const blk = await node.block(cur);
const sp = blk.verboseData?.selectedParentHash;
for (const h of [cur, ...(blk.verboseData?.mergeSetBluesHashes || []).filter(x => x !== sp)]) {
const hb = h === cur ? blk : await node.block(h);
if (key && String(hb.header.voteKeyHash) === key) mine++; else others++;
}
if (!sp || +blk.header.daaScore <= 1) break; cur = sp;
}
return { side2: mine, others, side2_bps: mine + others ? Math.round(10000 * mine / (mine + others)) : null };
}
const share = await shareAt(h1, joint.h1.hash, keys.side2);
log(`weight at the fork (last ${share.side2 + share.others} blue blocks of H1's chain): side 2 ${share.side2}, others ${share.others}, side 2 ${share.side2_bps} bps`);
// phase 2: the partition; the honest side keeps mining, side 2 mines heavier
for (const l of links) await l.close();
if (miners[side2Label]) { try { miners[side2Label].kill('SIGINT'); } catch { } await sleep(2000); }
miner(`${side2Label}-split`, side2.grpc, AT, SPLIT + WATCH + 60, side2Label);
log(`links cut at ${since()} s; side 2 (${side2.name}) now mines with ${AT} thread(s), the honest side with ${HT} each`);
await sleep(SPLIT * 1000);
if (!keys.side2) keys.side2 = minerKey(`${side2Label}-split`);
const preCut = {};
for (const n of honest) preCut[n.name] = await n.sink();
const split = { h1: await h1.sink(), h2: await h2.sink(), side2: await side2.sink() };
const forkDaa = joint.h1.daa;
const forkDepthAtHeal = Math.max(split.side2.daa, split.h1.daa) - forkDaa;
const heavier = split.side2.blueWork > split.h1.blueWork;
log(`phase 2 done at ${since()} s: H1 ${split.h1.blocks} blocks sink ${split.h1.hash.slice(0, 8)} daa ${split.h1.daa} blue ${split.h1.blue} work ${split.h1.blueWork}; side 2 ${split.side2.blocks} sink ${split.side2.hash.slice(0, 8)} daa ${split.side2.daa} blue ${split.side2.blue} work ${split.side2.blueWork} (side 2 heavier: ${heavier}); fork depth ${forkDepthAtHeal} DAA against window ${WINDOW}`);
// phase 3: heal and watch every honest node's chain
for (const l of links) await l.open();
log(`links restored at ${since()} s; watching ${honest.map(n => n.name.toUpperCase()).join(' and ')} for ${WATCH} s`);
const samples = [];
const reorgAt = {};
const healAt = Date.now();
const side2PreHeal = split.side2;
while (Date.now() - healAt < WATCH * 1000) {
await sleep(5000);
const s = { t: +since() };
for (const n of honest) {
const sk = await n.sink();
const reorged = await n.reorgedFrom(preCut[n.name].hash);
s[n.name] = { blocks: sk.blocks, sink: sk.hash.slice(0, 16), daa: sk.daa, blue: sk.blue, key: sk.key.slice(0, 8), reorged, knows_side2_tip: await n.knows(side2PreHeal.hash) };
if (reorged && reorgAt[n.name] == null) { reorgAt[n.name] = +since(); log(`${n.name.toUpperCase()} left its pre-cut chain at ${since()} s: sink ${sk.hash.slice(0, 16)} by ${sk.key.slice(0, 8)}`); }
}
const s2 = await side2.sink();
s.side2 = { blocks: s2.blocks, sink: s2.hash.slice(0, 16), blue: s2.blue, reorged: await side2.reorgedFrom(side2PreHeal.hash) };
samples.push(s);
if (samples.length % 6 === 0) log(`t=${since()} s ${honest.map(n => `${n.name.toUpperCase()} ${s[n.name].blocks} blocks sink ${s[n.name].sink.slice(0, 8)} ${s[n.name].reorged ? 'REORGED' : 'held'}`).join('; ')}; side 2 ${s2.blocks} blocks sink ${s2.hash.slice(0, 8)}`);
}
const last = samples.at(-1);
const refusals = {};
for (const n of honest) refusals[n.name] = n.grepLog(/Fork choice: block .* not a sink candidate/).length;
const perNode = {};
for (const n of honest) perNode[n.name] = { held: samples.every(s => s[n.name].reorged === false), reorged: last?.[n.name].reorged === true, reorg_at_s: reorgAt[n.name] ?? null, knows_side2_tip: last?.[n.name].knows_side2_tip === true, refusal_lines: refusals[n.name] };
const every = (f) => honest.every(n => f(perNode[n.name]));
const checks = {
side2_under_a_third_at_fork: share.side2_bps != null && share.side2_bps * 3 < 10000,
fork_deeper_than_window: forkDepthAtHeal > WINDOW,
side2_heavier_at_heal: heavier,
every_honest_node_learned_side2_chain: every(p => p.knows_side2_tip),
every_honest_node_held: every(p => p.held),
every_honest_node_reorged: every(p => p.reorged),
every_honest_node_logged_a_refusal: every(p => p.refusal_lines > 0),
no_honest_node_logged_a_refusal: every(p => p.refusal_lines === 0),
side2_kept_its_chain: samples.every(s => s.side2.reorged === false),
};
const wantUnderThird = MODE === 'attack';
const good = EXPECT === 'hold'
? checks.side2_under_a_third_at_fork === wantUnderThird && checks.fork_deeper_than_window && checks.side2_heavier_at_heal && checks.every_honest_node_learned_side2_chain && checks.every_honest_node_held && checks.every_honest_node_logged_a_refusal && checks.side2_kept_its_chain
: checks.side2_under_a_third_at_fork === wantUnderThird && checks.fork_deeper_than_window && checks.side2_heavier_at_heal && checks.every_honest_node_reorged && checks.no_honest_node_logged_a_refusal && checks.side2_kept_its_chain;
const needed = EXPECT === 'hold'
? ['side2_under_a_third_at_fork', 'fork_deeper_than_window', 'side2_heavier_at_heal', 'every_honest_node_learned_side2_chain', 'every_honest_node_held', 'every_honest_node_logged_a_refusal', 'side2_kept_its_chain']
: ['side2_under_a_third_at_fork', 'fork_deeper_than_window', 'side2_heavier_at_heal', 'every_honest_node_reorged', 'no_honest_node_logged_a_refusal', 'side2_kept_its_chain'];
const fails = needed.filter(k => (k === 'side2_under_a_third_at_fork' ? checks[k] !== wantUnderThird : !checks[k]));
const refusalExample = honest.map(n => n.grepLog(/Fork choice: block .* not a sink candidate/)[0]).find(Boolean)?.replace(/^.*?Fork choice/, 'Fork choice') ?? null;
const summary = {
pass: good, expect: EXPECT, case: CASE, mode: MODE, gate: GATE, window: WINDOW, joint: JOINT, split: SPLIT, watch: WATCH,
threads: { honest_each: HT, attacker_split: AT, attacker_joint: MODE === 'third' ? AJT : MODE === 'partition' ? HT : 0 },
keys: Object.fromEntries(Object.entries(keys).map(([k, v]) => [k, short(v)])), share_at_fork: share, fork_daa: forkDaa, fork_depth_daa_at_heal: forkDepthAtHeal,
sinks: { joint: strip(joint), split: strip(split) }, per_node: perNode, checks, failed_checks: fails, refusal_example: refusalExample,
samples, node: IGNEUMD, miner: CPU_MINER, slot: SLOT, ports: { base: BASE, suffix: SUFFIX },
};
// a key in a summary is its first 8 hex and an ellipsis, never the 64 (7 October 2026: the 6 October summaries' raw keys
// turned the no-secrets check red on every CI run of their branch; a 64-hex value next to the word key is a secret shape)
const short = (k) => (typeof k === 'string' && k.length === 64 ? `${k.slice(0, 8)}…` : k);
function strip(o) { const r = {}; for (const [k, v] of Object.entries(o)) r[k] = { ...v, blueWork: v.blueWork.toString(), key: short(v.key) }; return r; }
mkdirSync(OUT.replace(/\/[^/]+$/, ''), { recursive: true });
writeFileSync(OUT, JSON.stringify(summary, null, 2));
log(`SUMMARY ${good ? 'PASS' : 'FAIL'} (${CASE}): side 2 held ${share.side2_bps ?? 0} bps of the blue blocks at the fork; fork depth ${forkDepthAtHeal} DAA against window ${WINDOW}; side 2 blue work ${split.side2.blueWork} against H1 ${split.h1.blueWork} at the heal; ${honest.map(n => `${n.name.toUpperCase()} ${perNode[n.name].reorged ? `reorged at ${perNode[n.name].reorg_at_s} s` : 'held'} (${perNode[n.name].refusal_lines} refusal lines, knows side 2's tip: ${perNode[n.name].knows_side2_tip})`).join('; ')}; side 2 kept its chain: ${checks.side2_kept_its_chain}${fails.length ? `; FAILED CHECK ${fails.join(', ')}` : ''}`);
log(`summary: ${OUT}`);
await stopAll();
process.exit(good ? 0 : 1);

View file

@ -9,7 +9,7 @@
# tools/ci/whole-body-check.sh --self-test # the real tools pass; a copy with the block removed fails
set -euo pipefail
cd "$(dirname "$0")/../.."
TOOLS=(tools/build-remote.sh tools/cross-remote.sh tools/workers-remote.sh infra/build-server/hands/move-hand.sh)
TOOLS=(tools/build-remote.sh tools/cross-remote.sh tools/workers-remote.sh tools/fast-time-remote.sh infra/build-server/hands/move-hand.sh)
check() { # <file>
local f="$1" src
src=$(grep -nE '^\. "\$HERE/(\.\./)+(infra/build-server/)?lib\.sh"' "$f" | head -1 | cut -d: -f1)

83
tools/fast-time-remote.sh Executable file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034 # the BS_* context variables are read by lib.sh
# Run a fast-time harness (infra/fast-time/*.mjs, tools/finality-attacks) ON a build box instead of the Mac (CLAUDE.md,
# 7 October 2026: nothing heavy on the Mac), under a box slot with a holder line the workers dashboard shows and one JSONL
# line in /srv/builds/_log/builds.jsonl, the way tools/build-remote.sh runs cargo. The repo worktree's HEAD goes to the box
# through the bare mirror (checked out at /srv/builds/<worktree>), the harness directories are overlaid by rsync (uncommitted
# edits travel, every written file re-stamped with touch in lib.sh's bs_overlay_dir), the command runs at the worktree root
# on the box with IGNEUMD and IGNEUM_MINER pointing at a node build already there (a fork worktree's target/release under the
# same worktree root, built by tools/build-remote.sh from that fork worktree), and the result directory comes back.
#
# tools/fast-time-remote.sh [--box N] [--priority gate|normal] --node-bin <dir on the box, or relative to the worktree root>
# [--fetch <dir relative to the worktree root>] [--label <text>] -- <command at the worktree root>
#
# tools/fast-time-remote.sh --priority gate --node-bin vendor/igneum-node-horizon/target/release \
# --fetch docs/plans/mission-item-4-gate -- node infra/fast-time/fork-gate-gate.mjs
#
# Class: `--priority gate` (nice 0, the full core set, a slot ahead of queued suites; box 1: a release or mission gate) or
# normal (nice 10 on the last 32 cores like a suite, box 2 by lib.sh bs_route's attack class). The command never starts with
# cargo: build on the box first with tools/build-remote.sh from the fork worktree, then point --node-bin at its target.
# Added for mission item 4 (the fork-gate gate), 7 October 2026.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC2034
BS_TOOL=fast-time-remote
# shellcheck source=../infra/build-server/lib.sh
. "$HERE/../infra/build-server/lib.sh"
{ # whole-body: bash parses this block entirely before running a line of it, so an edit to this file while a run is in
# flight cannot reach the running copy (7 Oct 2026: build-remote.sh was edited mid-run and died on shifted bytes after a 4-min build)
BOX="${BOX:-}"; PRIORITY="${PRIORITY:-normal}"; NODE_BIN=""; FETCH=""; LABEL=""; CMD=()
while [ $# -gt 0 ]; do
case "$1" in
--box) BOX="$2"; shift 2 ;;
--priority) PRIORITY="$2"; shift 2 ;;
--node-bin) NODE_BIN="$2"; shift 2 ;;
--fetch) FETCH="$2"; shift 2 ;;
--label) LABEL="$2"; shift 2 ;;
--) shift; CMD=("$@"); break ;;
*) bs_die "unknown argument $1 (the command follows --)" ;;
esac
done
[ ${#CMD[@]} -gt 0 ] || bs_die "no command: tools/fast-time-remote.sh [options] -- node infra/fast-time/<harness>.mjs ..."
[ -n "$NODE_BIN" ] || bs_die "--node-bin names the directory on the box that holds igneumd and igneum-miner (a fork worktree's target/release)"
case "$PRIORITY" in gate|normal) ;; *) bs_die "--priority takes gate or normal, not '$PRIORITY'" ;; esac
case "${CMD[0]}" in cargo) bs_die "the command must not be cargo: build with tools/build-remote.sh first" ;; esac
# the class: a gate runs at nice 0 on the full set, a slot ahead of queued suites, on box 1; normal is the bounded class on box 2
if [ "$PRIORITY" = gate ]; then BR_NICE=0; BR_CORES=0; BR_JOBS_CAP=0; BR_PRIORITY=gate; BR_KIND=gate; else BR_NICE=10; BR_CORES=32; BR_JOBS_CAP=32; BR_PRIORITY=normal; BR_KIND=other; fi
export BR_NICE BR_CORES BR_JOBS_CAP BR_PRIORITY BR_KIND
if [ -z "$BOX" ]; then if [ "$PRIORITY" = gate ]; then BOX=1; else BOX=$(bs_route attack); fi; fi
bs_host "$BOX"
# the context by hand (bs_context wants a crate): the repo worktree root is the tree; lib.sh reads these
BS_TOP=$(git -C "$HERE" rev-parse --show-toplevel); BS_WT_ROOT="$BS_TOP"; BS_KIND=repo; BS_MIRROR="$BS_MIRROR_REPO"; BS_TOP_REL=.
BS_WT=$(basename "$BS_WT_ROOT"); BS_CRATE_REL=infra/fast-time; BS_REMOTE_WT="$BS_ROOT_REMOTE/$BS_WT"; BS_REMOTE_CRATE="$BS_REMOTE_WT"
BS_BRANCH=$(git -C "$BS_TOP" branch --show-current 2>/dev/null || true); BS_SHA=$(git -C "$BS_TOP" rev-parse HEAD); [ -n "$BS_BRANCH" ] || BS_BRANCH="detached-$(git -C "$BS_TOP" rev-parse --short HEAD)"
BS_LOCAL_DIRS="infra/fast-time tools/finality-attacks"; BS_VENDOR_REPOS=""
case "$NODE_BIN" in /*) ;; *) NODE_BIN="$BS_REMOTE_WT/$NODE_BIN" ;; esac
bs_log "box $BOX ($BS_HOST) for priority $PRIORITY: ${CMD[*]} from $BS_WT at $BS_SHA ($BS_BRANCH), node binaries $NODE_BIN"
bs_ssh "[ -x '$NODE_BIN/igneumd' ] && [ -x '$NODE_BIN/igneum-miner' ]" || bs_die "no igneumd and igneum-miner at $NODE_BIN on the box: build them there first (tools/build-remote.sh from the fork worktree)"
# the harness directories by overlay only, never a checkout of the worktree root: on a box where the root is no repo
# checkout (every box a node build alone has touched) remote-run's checkout mode replaces the directory, and at 13:44 UK on
# 7 October 2026 the first run of this wrapper took igneum-build-2's /srv/builds/igneum-wt-horizon/vendor (the fork sources
# and the gate binary just built there) with it
bs_wt_lock
bs_ssh "mkdir -p '$BS_REMOTE_WT'"
for d in $BS_LOCAL_DIRS; do bs_overlay_dir "$d"; done
bs_log "harness sources in place (overlay only; changed files re-stamped with touch by bs_overlay_dir)"
quoted=""; for a in "${CMD[@]}"; do quoted="$quoted $(printf '%q' "$a")"; done
cmd="export IGNEUMD=$(printf '%q' "$NODE_BIN/igneumd") IGNEUM_MINER=$(printf '%q' "$NODE_BIN/igneum-miner") IGNEUM_NODE_ROOT=$(printf '%q' "$BS_REMOTE_WT/"); $quoted"
BR_COMMAND="${CMD[*]}"; BR_TARGET=x86_64-unknown-linux-gnu; BR_ARTEFACTS=""; export BR_COMMAND BR_TARGET BR_ARTEFACTS
[ -n "$LABEL" ] || LABEL="fast-time ${CMD[*]}"
label="$BS_WT $LABEL; kind=$BR_KIND nice=$BR_NICE cores=$( [ "$BR_CORES" = 0 ] && echo 96 || echo "$BR_CORES")"
set +e
bs_remote_run "$BS_REMOTE_WT" "$label" "$cmd"
rc=$?
set -e
if [ -n "$FETCH" ]; then
mkdir -p "$BS_WT_ROOT/$FETCH"
bs_rsync -r "$BS_HOST:$BS_REMOTE_WT/$FETCH/" "$BS_WT_ROOT/$FETCH/" && bs_log "fetched $FETCH from the box" || bs_log "fetch of $FETCH failed (the run's rc was $rc)"
fi
bs_log "done: rc $rc"
[ "$rc" = 0 ] || exit "$rc"
exit 0
}