16 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 (fork 0562a7f2) prints at start (Consensus params digest); the fleet agent compares every box's line against them and the stale box's against its own. CORRECTION 18:4xZ (the rehearsal's first reading): the digest folds the NETWORK ID in, so one file prints another digest on another --devnet-suffix (the same binary and file: a15db4f0... at suffix 400, d23394e7... at 996, 45eeea99... at 401); the first values written here were read on throwaway suffixes and were wrong for the fleet's suffix 400. Every digest below now names its network id; the one the live devnet prints is the one read with no suffix.
2a. The live devnet publish object (NOT published by this plan; the shape the cut will use)
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 on the DEVNET network id (no suffix), the signalling fork 0562a7f2: 65a42ab2e63d93ac02acf761b411b31acac6c9413bbd0c6a2f4cced10509efdf (18:4xZ; the earlier ac8e60ce... was read at suffix 998 and does not apply); the no-file devnet digest on the same binary is 7f2e49be..., the fork test's pinned value. The 0.3.15 binary may move both if 0.3.14 added a digest field: the shipper reads the live value from the 0.3.15 node. The node also prints Program class v4 from the override file: active from epoch 61 (DAA score 219600 rounded up to the epoch boundary at 219600, epochs of 3600 DAA) and the window line. CUT ORDER (found by the shipper, 18:3xZ): a 0.3.13 node refuses the 15-field file at PARSE (unknown field program_class_v4_activation_daa) and exits before any handshake, so the file reaches a node only with or after its 0.3.15 binary (the app applies the manifest's override after the update; the hand nodes and the seeds get the file in the same step as the binary, never before) (with N6 = 219,600 as the worked example; any other N6 moves it).
2b. The rehearsal object (the fleet chain)
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 on igneum-devnet-400 (suffix 400): aef46983dfd121d5ecbefd056ff02d001b89315bd0547b4e1007b0b0ee7eee2c (the signalling node's start line, 18:4xZ; the earlier bc2142b1... was read at suffix 997); the node also prints Program class v4 from the override file: active from epoch 4 (DAA score 14400 ...) and Program class v4 signal window from the override file: 3600 DAA ....
2c. The re-cut for the hour (18:1x UTC, the coordinator's "run it now")
If the fleet chain runs the devnet's 1 block/s, object 2b flips at 2 hours; this object flips at 20 minutes: epochs of 600 DAA (lead 100), window 600, floor 2,400. Epoch e's seed block sits at DAA 600 e - 100; the window is full from epoch 2 (seed at 1,100), so the flip is at epoch 2, DAA 1,200, minute 20 from the chain's start; the floor is epoch 4, DAA 2,400, minute 40; the run ends at DAA 1,800 (minute 30, one epoch after the flip; the floor is not reached). If the fleet chain is made to run 10 blocks/s instead, object 2b itself gives 6 / 12 / 24 minutes (window / flip / floor) and this one 1 / 2 / 4 minutes, too fast for a 15-minute sample cadence: use 2b. File docs/plans/counter-asic-3-gate/override-v4-rehearsal-1bps.json, digest on igneum-devnet-400 (suffix 400): a15db4f0d6a5cc891582760ef5593e69014a5133261def84647cb7617528764f, the value all 16 fleet boxes printed on the Linux build of the same fork commit 0562a7f2 (PC 2 job build-20261006-174823; the earlier d23394e7... was this file at suffix 996), with PoW schedule from the override file: epoch 600 DAA, lead 100 DAA, day 86400000 ms, Program class v4 from the override file: active from epoch 4 (DAA score 2400 ...), Program class v4 signal window from the override file: 600 DAA ...).
{"difficulty_v2_activation_daa":0,"proving_v0_activation_daa":0,"fees_v1_activation_daa":0,"finality_v3_activation_daa":0,"program_class_v3_activation_daa":0,"proving_v1_activation_daa":0,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000,"proving_v1_fresh_rule_daa":0,"exec_restart_number":18446744073709551615,"exec_restart_hash":"","exec_restart_trust_daa":18446744073709551615,"pow_epoch_blocks":600,"pow_epoch_lead":100,"program_class_v4_activation_daa":2400,"program_class_v4_signal_window_daa":600}
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 on the box's GPU worker, the app's shape: igneum-miner mine grpc://127.0.0.1:16410 1 100000000 <label> --engine igneum-pow --worker <igneum-worker-cuda> --worker-args "--pack packs/devnet" --prepare-packs packs/prepare --exit-on-seed-change --no-vote --payout-label <label>. CORRECTED 18:5xZ from the run: the rehearsal object inherits the devnet's genesis bits (the fast-time harness lowers genesis_bits to 0x1f010000, 2^16 hashes a block; the object does not), so a 1-thread CPU miner (about 5 kH/s a box, 80 kH/s for 16, the fleet agent's reading) against the genesis difficulty (about 2^27 hashes a block, the fleet agent's figure) finds a block every 28 minutes for the whole fleet and the height stayed 0 for 20 minutes; the GPU workers find blocks at the chain's 1 a second |
every box's node prints the same digest and the two switch lines; every miner prints program pack checked for epoch seed <E> day <D>: attempt <A>, <dir> for epoch 0 and the worker's ready ... self-test PASS |
| 5 | Start the stale box last, the same command on the 0.3.13 node, --connect=<seed>:16411 |
its log shows the handshake refusal (a digest mismatch line or 0 peers after 60 s with connection attempts in the log) and its chain stays at its own genesis (block count 1 or its own lonely blocks if it mines; it must NOT mine: no miner on it) |
| 6 | Every 15 minutes, on every mining box: getBlockDagInfo (block count, sink, virtual DAA) and one getBlockTemplate (powEpoch.programClass, nextProgramClass, programClassV4SignalBps, programClassV4SignalEpoch); keep the lines |
the shares read 10,000 bps on every box from the first template; sinks agree across boxes at each sample |
| 7 | At DAA 7,200 (about 2 h): every box's node prints Program class v4 by miner signal: epoch 2 (share 10000 bps over 3600 DAA ending at seed block <hash>, threshold 9500 bps, N of N blue blocks) with the SAME seed block hash and the same N on every box; the templates read class 4 from epoch 2; every miner prints epoch seed <S2> ... class v4 program id <id> |
the same <S2>, the same <id> on every box |
| 8 | Run to DAA 10,800 (epoch 3, one epoch after the flip; two epochs after is 14,400, the floor, so the run stops at 10,800 plus 600) | the final sample |
| 9 | The id assertion, the same Mac-side procedure with the box's id read from the pack, not a log line (a worker-mode miner prints no program id line; the Metal run of G4b showed the same): on every box, for epochs 2 and 3, take the program pack checked for epoch seed <E> day <D>: attempt <A>, <dir> line and read <dir>/program.h's IGNEUM_GENERATOR (must be 4), IGNEUM_PROGRAM_CLASS ("v4"), IGNEUM_PROGRAM_ID, IGNEUM_SEED_BYTES_HEX and IGNEUM_ERA_SEED_HEX (the miner's PREPARE sent ... class v4 era=<hex> line carries the same era), and send the five values per box and epoch to the node lane; the Mac computes igneum-pow show --epoch-hex <IGNEUM_SEED_BYTES_HEX> --program-class v4 --era-hex <IGNEUM_ERA_SEED_HEX> and the same with v3 |
every box's IGNEUM_PROGRAM_ID equals the CLI's v4 id and differs from the CLI's v3 id of the same seed and era; one id per epoch across boxes |
| 10 | Stop every node and miner; destroy the instances by the fleet plan's rule | the report in section 4 is in |
Never: no live-devnet port, no live override file, no manifest, no update-now; the boxes' app installs (if any) are not touched (the rule of 5 October: a job never quits or restarts an app it did not start).
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 the object's suffix-400 value (2b aef46983..., 2c a15db4f0...); the stale box prints 0.3.13's digest of the same file (a different value, since its binary lacks the two fields) |
switch_lines per box |
the node's first lines | the floor line names epoch 4 and the window line names 3,600 DAA, on every signalling box |
signal_line per box |
step 7 | present on every signalling box, epoch 2, the same seed block hash, share 10,000 bps (at least 9,500) |
blocks_before, blocks_after |
getBlocks from any box, split at DAA 7,200 |
both over 0 |
rejected |
every miner's rejected= STATUS count and every node's PoW rejected lines |
0 on every box |
sinks at the end |
getBlockDagInfo |
one value on every signalling box |
program_ids per epoch per box |
the packs' program.h named by the miners' program pack checked lines (step 9) |
one id per epoch across boxes; epochs 2 and 3 generator 4, class v4; the Mac's CLI check holds |
stale_box |
its log and getBlockDagInfo |
refused (no peer), block count 1, no block of its ever appears on any signalling box's chain (its own genesis-state chain and the fleet's never merge) |
shares per sample |
step 6 | 10,000 bps on every box at every sample |
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)