igneum/docs/plans/counter-asic-3-rehearsal.md

14 KiB

Counter ASIC 3.0: the class v4 rehearsal on the rented fleet (precondition 1 of the cut)

6 October 2026, worker "ca3-v4-node", on the coordinator's word of about 17:40 UTC. A precondition: no live-devnet date for class v4 is named until this rehearsal has PASSED on the rented fleet. The fleet agent owns the boxes (memory gpu-rental.md: the Vast and RunPod accounts, the fleet SSH key, the 6 October fleet plan and budget); this file is the plan it runs and the objects it runs with. The node lane touches no box. Nothing here is published to the live devnet; the rehearsal chain is a fleet-only network that the live devnet's nodes cannot join and that cannot join them.

1. What is rehearsed

The class v4 activation on a chain of real boxes, in the shape the live devnet will see, with both activation rules of the v4 seam (docs/plans/counter-asic-3-node.md): the miner-signalled flip (section 6 there, PROPOSED) and the fixed-height floor behind it; the stale-box case, where one box runs the old binary against the new object and must be refused at the handshake without forking the chain; and the live form of the G4 checks.

Fact Value Why
Network --devnet-suffix=400 (igneum-devnet-400), every box; the live devnet has no suffix and node 1, the observer and the seeds refuse any other network name at the handshake fleet-only by construction; the chain starts at its own genesis state (DAA 0) because no box carries a devnet-400 database
Binary one commit of ca3-v4-node (main) and ca3-v4-node (fork), the build job's Linux igneumd, igneum-miner, igneum-app from PC 2 (the G6 job of the signalling commit; the shas in docs/plans/counter-asic-3-gate/node-gates.md) on every box but the stale one; the stale box runs 0.3.13's Linux node (fork bb43e9a8, the 0.3.13 outputs) the same objects as the cut's step 1
Boxes 12 or more mining boxes (the 50-miner wave's shape at a tenth of the size; 1 CPU miner or 1 GPU worker each), 1 seed box (--listen, no miner), 1 stale box 12 keeps the per-box share near 8 percent, so one box's absence moves the tally by 8 percent: the 95 percent threshold is exercised, not trivially met (with 3 boxes it is 67 or 100 percent, the fast-time shape)
Override object rehearsal below, the same file on every box (the stale box too) the stale case is the digest refusal, so the file must be the same
Duration about 2 h 40 min of chain at 1 block/s: the flip by signal at DAA 7,200 (epoch 2), two epochs after it, then the floor at 14,400 is NOT reached (the run ends at DAA 10,800) the floor is the backstop; the rehearsal proves the signal path, the fast-time gate proved the floor path

2. The override objects

Both objects below are JSON text to be written verbatim; a never height is 18446744073709551615, which no JSON tool that goes through a double may rewrite (the fast-time harness's rule). The digests are what a node of the signalling commit prints at start (Consensus params digest); the fleet agent compares every box's line against them and the stale box's against its own.

2a. The live devnet publish object (NOT published by this plan; the shape the cut will use)

The live file today (/tmp/igneum-devnet/override-v3.json, 13 fields, read 16:55Z) plus the two v4 fields. N6 is the floor: DAA at the publish + 14,400 rounded UP to a multiple of 3,600 (the 2.0 rule for N4), checked at publish (N6 - DAA >= 10,800). At the live DAA of 202,919 (16:57:43Z) that would be 219,600 (epoch 61); the number is set at the publish, not here.

{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200,"program_class_v3_activation_daa":154800,"proving_v1_activation_daa":154800,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":198000,"exec_restart_number":27276,"exec_restart_hash":"bb45cf0dd2d7cc97ebfa5a2701527c09a8ede5d32de74efead9caa293b15688a","exec_restart_trust_daa":200000,"program_class_v4_activation_daa":N6,"program_class_v4_signal_window_daa":86400}

What the two fields do on the live devnet: every node of the signalling binary stamps object byte 4 into its templates from its first block, so the signal share climbs as the fleet updates; the class flips at the first epoch boundary whose window (the 86,400 DAA, one day, below that epoch's seed block) has 95 percent of its blue blocks signalling, which is about a day after the LAST box of 95 percent of the hash rate has updated; the floor N6 flips it regardless at the latest. Digest of this object: ac8e60ce205852bdda6b554f8cbfbd9dbb040187f487cbd8affe8103633dfd56 (read from the signalling node's start line, 18:05Z; the node also prints Program class v4 from the override file: active from epoch 61 (DAA score 219600 rounded up to the epoch boundary at 219600, epochs of 3600 DAA) and the window line) (with N6 = 219,600 as the worked example; any other N6 moves it).

2b. The rehearsal object (the fleet chain)

A fresh chain, every earlier switch at 0 (the testnet's shape: the chain is born on calibrated difficulty v1, proving v1, fees v1, finality v3, class v3), the v4 signal window one epoch, the floor four hours out:

{"difficulty_v2_activation_daa":0,"proving_v0_activation_daa":0,"fees_v1_activation_daa":0,"finality_v3_activation_daa":0,"program_class_v3_activation_daa":0,"proving_v1_activation_daa":0,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":0,"exec_restart_number":18446744073709551615,"exec_restart_hash":"","exec_restart_trust_daa":18446744073709551615,"program_class_v4_activation_daa":14400,"program_class_v4_signal_window_daa":3600}

The arithmetic: epochs of 3,600 DAA, lead 600. Epoch e's seed block is the last chain block below 3600 e - 600; its window is full when that block's DAA is at least 3,600: epoch 1's seed block sits at DAA 2,999 (not full), epoch 2's at 6,599 (full). With every mining box signalling 4, the tally at epoch 2's seed block is 100 percent of the blue blocks in DAA 2,999 to 6,599, so the class flips at epoch 2, DAA 7,200, about 2 hours after genesis; the floor (epoch 4, DAA 14,400) is 2 hours later and is not reached by the run. Digest: bc2142b178ff367ae84ff0699ff523760d8878f883375da21864ed8d3ad39237 (the signalling node's start line on this object, 18:05Z, with Program class v4 from the override file: active from epoch 4 (DAA score 14400 ...) and Program class v4 signal window from the override file: 3600 DAA ...); the devnet digest with no file at all is 7f2e49beabc253f327c5ac6bb457a674ea7f527af2971c95d3bdf65ef8bcf977 on this binary (the fork's pinned test), c562d70e... on 0.3.11 to 0.3.13.

2c. The re-cut for the hour (18:1x UTC, the coordinator's "run it now")

If the fleet chain runs the devnet's 1 block/s, object 2b flips at 2 hours; this object flips at 20 minutes: epochs of 600 DAA (lead 100), window 600, floor 2,400. Epoch e's seed block sits at DAA 600 e - 100; the window is full from epoch 2 (seed at 1,100), so the flip is at epoch 2, DAA 1,200, minute 20 from the chain's start; the floor is epoch 4, DAA 2,400, minute 40; the run ends at DAA 1,800 (minute 30, one epoch after the flip; the floor is not reached). If the fleet chain is made to run 10 blocks/s instead, object 2b itself gives 6 / 12 / 24 minutes (window / flip / floor) and this one 1 / 2 / 4 minutes, too fast for a 15-minute sample cadence: use 2b. File docs/plans/counter-asic-3-gate/override-v4-rehearsal-1bps.json, digest d23394e796fb542274207f1d281bc40126040e04f86882faa0b49ea3c3f1f91b (the signalling node's start line, with PoW schedule from the override file: epoch 600 DAA, lead 100 DAA, day 86400000 ms, Program class v4 from the override file: active from epoch 4 (DAA score 2400 ...), Program class v4 signal window from the override file: 600 DAA ...).

{"difficulty_v2_activation_daa":0,"proving_v0_activation_daa":0,"fees_v1_activation_daa":0,"finality_v3_activation_daa":0,"program_class_v3_activation_daa":0,"proving_v1_activation_daa":0,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":0,"exec_restart_number":18446744073709551615,"exec_restart_hash":"","exec_restart_trust_daa":18446744073709551615,"pow_epoch_blocks":600,"pow_epoch_lead":100,"program_class_v4_activation_daa":2400,"program_class_v4_signal_window_daa":600}

With this object the step-7 line reads epoch 2 (share 10000 bps over 600 DAA ...), the floor line active from epoch 4, and the sample cadence is every 5 minutes.

3. The steps the fleet agent runs

Step What Done when
1 Fetch the signalling commit's Linux binaries from the G6 build job (the shas in node-gates.md), verify every sha256, place igneumd and igneum-miner on every box; the 0.3.13 Linux igneumd on the stale box every sha matches
2 Write the rehearsal object (2b) as override.json on every box, byte for byte (sha256 the file on each box and compare) one sha on every box
3 Start the seed box: igneumd --devnet --devnet-suffix=400 --nodnsseed --disable-upnp --listen=0.0.0.0:16411 --rpclisten=127.0.0.1:16410 --rpclisten-json=127.0.0.1:16412 --override-params-file=override.json --utxoindex --enable-unsynced-mining --yes --appdir=<fresh dir> (ports of the box's choosing, never the live devnet's 26610/26611); read its first lines: Consensus params digest equals 2b's, Program class v4 from the override file: active from epoch 4 (DAA score 14400 ...), Program class v4 signal window from the override file: 3600 DAA ... the three lines
4 Start every mining box the same way with --connect=<seed>:16411, then its miner: igneum-miner mine grpc://127.0.0.1:16410 1 100000000 <label> --engine igneum-pow --no-vote --payout-label <label> (a CPU miner; a GPU box uses --worker <path> --prepare-packs packs/prepare --exit-on-seed-change, the app's shape) every box's node prints the same digest and the two switch lines; every miner prints epoch seed ... class v3 program id ... for epoch 0
5 Start the stale box last, the same command on the 0.3.13 node, --connect=<seed>:16411 its log shows the handshake refusal (a digest mismatch line or 0 peers after 60 s with connection attempts in the log) and its chain stays at its own genesis (block count 1 or its own lonely blocks if it mines; it must NOT mine: no miner on it)
6 Every 15 minutes, on every mining box: getBlockDagInfo (block count, sink, virtual DAA) and one getBlockTemplate (powEpoch.programClass, nextProgramClass, programClassV4SignalBps, programClassV4SignalEpoch); keep the lines the shares read 10,000 bps on every box from the first template; sinks agree across boxes at each sample
7 At DAA 7,200 (about 2 h): every box's node prints Program class v4 by miner signal: epoch 2 (share 10000 bps over 3600 DAA ending at seed block <hash>, threshold 9500 bps, N of N blue blocks) with the SAME seed block hash and the same N on every box; the templates read class 4 from epoch 2; every miner prints epoch seed <S2> ... class v4 program id <id> the same <S2>, the same <id> on every box
8 Run to DAA 10,800 (epoch 3, one epoch after the flip; two epochs after is 14,400, the floor, so the run stops at 10,800 plus 600) the final sample
9 Collect every miner's program and 256 MiB cache ready lines (seed, class, id) and the template's eraSeed for epochs 2 and 3, and send them to the node lane; the Mac computes `igneum-pow show --epoch-hex --program-class v3 v4 --era-hex ` for each (the id assertion of the G4 harness, no binary needed on a box)
10 Stop every node and miner; destroy the instances by the fleet plan's rule the report in section 4 is in

Never: no live-devnet port, no live override file, no manifest, no update-now; the boxes' app installs (if any) are not touched (the rule of 5 October: a job never quits or restarts an app it did not start).

4. What the fleet agent reports back (one table, one JSON)

docs/plans/counter-asic-3-gate/rehearsal-<time>.json with the fields below and a row per box in docs/plans/counter-asic-3-rehearsal.md section 5 (this file, appended by the fleet agent):

Field From Pass rule
digest per box the node's first lines one value on every signalling box, equal to 2b's; the stale box prints 0.3.13's digest of the same file (a different value, since its binary lacks the two fields)
switch_lines per box the node's first lines the floor line names epoch 4 and the window line names 3,600 DAA, on every signalling box
signal_line per box step 7 present on every signalling box, epoch 2, the same seed block hash, share 10,000 bps (at least 9,500)
blocks_before, blocks_after getBlocks from any box, split at DAA 7,200 both over 0
rejected every miner's rejected= STATUS count and every node's PoW rejected lines 0 on every box
sinks at the end getBlockDagInfo one value on every signalling box
program_ids per epoch per box the miners' lines one id per epoch across boxes; epochs 2 and 3 class v4; the Mac's CLI check (step 9) holds
stale_box its log and getBlockDagInfo refused (no peer), block count 1, no block of its ever appears on any signalling box's chain (its own genesis-state chain and the fleet's never merge)
shares per sample step 6 10,000 bps on every box at every sample

PASS = every rule above holds. Any other outcome is RED, the file says which rule failed, and the cut waits.

5. Results

(appended by the fleet agent after the run)