From f0b3355a7a8e74ea4e47a27351859daf5efe30a1 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:10:00 +0000 Subject: [PATCH] Counter ASIC 3.0 gates (node): G4, G4b, G5 and G6 GREEN for class v4 (the three fast-time summaries incl. the known-failed case, node-gates.md, counter-asic-3-node.md) Co-Authored-By: Claude Fable 5.1 --- .../class-v4-20261006-1553Z-cpu.json | 384 +++++++++++++++ .../class-v4-20261006-metal.json | 447 ++++++++++++++++++ .../class-v4-20261006-never-failed-case.json | 245 ++++++++++ docs/plans/counter-asic-3-gate/node-gates.md | 12 + docs/plans/counter-asic-3-node.md | 77 +++ 5 files changed, 1165 insertions(+) create mode 100644 docs/plans/counter-asic-3-gate/class-v4-20261006-1553Z-cpu.json create mode 100644 docs/plans/counter-asic-3-gate/class-v4-20261006-metal.json create mode 100644 docs/plans/counter-asic-3-gate/class-v4-20261006-never-failed-case.json create mode 100644 docs/plans/counter-asic-3-gate/node-gates.md create mode 100644 docs/plans/counter-asic-3-node.md diff --git a/docs/plans/counter-asic-3-gate/class-v4-20261006-1553Z-cpu.json b/docs/plans/counter-asic-3-gate/class-v4-20261006-1553Z-cpu.json new file mode 100644 index 00000000..b81cb997 --- /dev/null +++ b/docs/plans/counter-asic-3-gate/class-v4-20261006-1553Z-cpu.json @@ -0,0 +1,384 @@ +{ + "pass": true, + "checks": { + "v4_switch_line_on_every_node": true, + "v4_switch_line_names_the_rounded_epoch": true, + "v3_switch_line_on_every_node": true, + "template_switched_at_the_first_v4_epoch": true, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": true, + "v3_and_v4_programs_seen": true, + "v2_programs_seen_when_v3_starts_later": true, + "program_ids_differ_across_the_switch": true, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true + }, + "activation": 150, + "v3_activation": 60, + "epoch_blocks": 60, + "first_v3_epoch": 1, + "first_v4_epoch": 3, + "boundary_daa": 180, + "secs": 420, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-ca3v4/target-ca3v4/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-ca3v4/target-ca3v4/release/igneum-miner", + "metal_worker": null, + "genesis_bits": "0x1f010000", + "template_switch_v3": { + "epoch": 1, + "daa": 60, + "at": 25.7 + }, + "template_switch": { + "epoch": 3, + "daa": 180, + "at": 163.9 + }, + "run_ended_at_s": 285, + "final_daa": 304, + "blocks": { + "total": 309, + "before_boundary": 181, + "after_boundary": 128, + "chain_before": 177, + "chain_after": 127 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "8f8806638d59850f", + "seed": "234e082d653dc69d", + "miners": 3, + "disagree": false, + "ready_ms": [ + 198, + 199, + 199 + ] + }, + { + "epoch": 1, + "class": "v3", + "program_id": "29ced0d031cc121b", + "seed": "bb01d0cccbc2ea2e", + "miners": 3, + "disagree": false, + "ready_ms": [ + 187, + 186, + 187 + ] + }, + { + "epoch": 2, + "class": "v3", + "program_id": "341fd4076256cd7b", + "seed": "060caf3a3e006592", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 3, + "class": "v4", + "program_id": "070ca0f94e7f8f88", + "seed": "cc9d1bb4443c3028", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 4, + "class": "v4", + "program_id": "b8cbe1ef7ab1d748", + "seed": "54e2edcfe251360c", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 5, + "class": "v4", + "program_id": "9321cbd9c54c0b83", + "seed": "53a2fe6ce1308fb3", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + } + ], + "accepted_per_miner": [ + 103, + 103, + 102 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "4da9ac669effdfab", + "4da9ac669effdfab", + "4da9ac669effdfab" + ], + "block_counts": [ + 308, + 308, + 308 + ], + "tips_per_node": [ + 1, + 1, + 1 + ], + "switch_lines": [ + "Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)" + ], + "v3_switch_lines": [ + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.6, + "daa": 0, + "epoch": 0, + "class": 2, + "nodes": [ + "0/234e082d", + "0/234e082d", + "0/234e082d" + ] + }, + { + "t": 19.7, + "daa": 51, + "epoch": 0, + "class": 2, + "nodes": [ + "51/b6699ac7", + "51/b6699ac7", + "51/b6699ac7" + ] + }, + { + "t": 34.7, + "daa": 62, + "epoch": 1, + "class": 3, + "nodes": [ + "62/50a9cf7a", + "62/50a9cf7a", + "62/50a9cf7a" + ] + }, + { + "t": 49.7, + "daa": 68, + "epoch": 1, + "class": 3, + "nodes": [ + "68/e0145a0c", + "68/e0145a0c", + "68/e0145a0c" + ] + }, + { + "t": 64.7, + "daa": 76, + "epoch": 1, + "class": 3, + "nodes": [ + "76/c23ad948", + "76/c23ad948", + "76/c23ad948" + ] + }, + { + "t": 79.8, + "daa": 97, + "epoch": 1, + "class": 3, + "nodes": [ + "97/b039ce4c", + "97/b039ce4c", + "97/b039ce4c" + ] + }, + { + "t": 94.8, + "daa": 113, + "epoch": 1, + "class": 3, + "nodes": [ + "113/ae890f91", + "113/ae890f91", + "113/ae890f91" + ] + }, + { + "t": 109.8, + "daa": 128, + "epoch": 2, + "class": 3, + "nodes": [ + "128/3545bd70", + "128/3545bd70", + "128/3545bd70" + ] + }, + { + "t": 124.8, + "daa": 138, + "epoch": 2, + "class": 3, + "nodes": [ + "138/5e342f39", + "138/5e342f39", + "138/5e342f39" + ] + }, + { + "t": 139.8, + "daa": 152, + "epoch": 2, + "class": 3, + "nodes": [ + "152/f376cfe3", + "152/f376cfe3", + "152/f376cfe3" + ] + }, + { + "t": 154.9, + "daa": 168, + "epoch": 2, + "class": 3, + "nodes": [ + "168/18974e58", + "168/18974e58", + "168/18974e58" + ] + }, + { + "t": 169.9, + "daa": 189, + "epoch": 3, + "class": 4, + "nodes": [ + "189/2efab18a", + "189/2efab18a", + "189/2efab18a" + ] + }, + { + "t": 184.9, + "daa": 203, + "epoch": 3, + "class": 4, + "nodes": [ + "203/024556df", + "203/024556df", + "203/024556df" + ] + }, + { + "t": 199.9, + "daa": 215, + "epoch": 3, + "class": 4, + "nodes": [ + "215/6aa4e4c9", + "215/6aa4e4c9", + "215/6aa4e4c9" + ] + }, + { + "t": 214.9, + "daa": 230, + "epoch": 3, + "class": 4, + "nodes": [ + "230/54e2edcf", + "230/54e2edcf", + "230/54e2edcf" + ] + }, + { + "t": 230, + "daa": 245, + "epoch": 4, + "class": 4, + "nodes": [ + "245/f5fc54f8", + "245/f5fc54f8", + "245/f5fc54f8" + ] + }, + { + "t": 245, + "daa": 265, + "epoch": 4, + "class": 4, + "nodes": [ + "265/7fe10368", + "265/7fe10368", + "265/7fe10368" + ] + }, + { + "t": 260, + "daa": 278, + "epoch": 4, + "class": 4, + "nodes": [ + "278/d50d4e8e", + "278/d50d4e8e", + "278/d50d4e8e" + ] + }, + { + "t": 275, + "daa": 287, + "epoch": 4, + "class": 4, + "nodes": [ + "287/83dc90ba", + "287/83dc90ba", + "287/83dc90ba" + ] + } + ], + "metal": null +} \ No newline at end of file diff --git a/docs/plans/counter-asic-3-gate/class-v4-20261006-metal.json b/docs/plans/counter-asic-3-gate/class-v4-20261006-metal.json new file mode 100644 index 00000000..7848160e --- /dev/null +++ b/docs/plans/counter-asic-3-gate/class-v4-20261006-metal.json @@ -0,0 +1,447 @@ +{ + "pass": true, + "checks": { + "v4_switch_line_on_every_node": true, + "v4_switch_line_names_the_rounded_epoch": true, + "v3_switch_line_on_every_node": true, + "template_switched_at_the_first_v4_epoch": true, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": true, + "v3_and_v4_programs_seen": true, + "v2_programs_seen_when_v3_starts_later": true, + "program_ids_differ_across_the_switch": true, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true, + "metal_prepare_sent_for_v4": true, + "metal_worker_prepared_v4_pack": true, + "metal_no_need_or_mismatch": true, + "metal_accepted_blocks_after_switch": true, + "metal_cpu_recheck_clean": true, + "metal_swapped_without_pause": true + }, + "activation": 150, + "v3_activation": 60, + "epoch_blocks": 60, + "first_v3_epoch": 1, + "first_v4_epoch": 3, + "boundary_daa": 180, + "secs": 480, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-ca3v4/target-ca3v4/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-ca3v4/target-ca3v4/release/igneum-miner", + "metal_worker": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/proto-metal/igneum-bench", + "genesis_bits": "0x1e010000", + "template_switch_v3": { + "epoch": 1, + "daa": 60, + "at": 83.1 + }, + "template_switch": { + "epoch": 3, + "daa": 180, + "at": 191.7 + }, + "run_ended_at_s": 317.5, + "final_daa": 300, + "blocks": { + "total": 304, + "before_boundary": 182, + "after_boundary": 122, + "chain_before": 93, + "chain_after": 63 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "3e974094b5ce543f", + "seed": "130e3b7d568db07a", + "miners": 2, + "disagree": false, + "ready_ms": [ + 179, + 180 + ] + }, + { + "epoch": 1, + "class": "v3", + "program_id": "8ef0132ff054616a", + "seed": "b515430faf46fda8", + "miners": 2, + "disagree": false, + "ready_ms": [ + 291, + 290 + ] + }, + { + "epoch": 2, + "class": "v3", + "program_id": "d3da91ec49682ce1", + "seed": "9cbe1982e1eaf9df", + "miners": 2, + "disagree": false, + "ready_ms": [ + 7, + 7 + ] + }, + { + "epoch": 3, + "class": "v4", + "program_id": "2bcecca6f867c3b5", + "seed": "ca4245b598fcbeb4", + "miners": 2, + "disagree": false, + "ready_ms": [ + 7, + 7 + ] + }, + { + "epoch": 4, + "class": "v4", + "program_id": "21b78e63fc0e9251", + "seed": "3b6cfced206edf85", + "miners": 2, + "disagree": false, + "ready_ms": [ + 8, + 7 + ] + }, + { + "epoch": 5, + "class": "v4", + "program_id": "3855570a5ba12fc9", + "seed": "343510d7149dcf50", + "miners": 2, + "disagree": false, + "ready_ms": [ + 7, + 7 + ] + } + ], + "accepted_per_miner": [ + 301, + 1, + 1 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "4e1aa3fbf1f4a54b", + "4e1aa3fbf1f4a54b", + "4e1aa3fbf1f4a54b" + ], + "block_counts": [ + 303, + 303, + 303 + ], + "tips_per_node": [ + 2, + 2, + 2 + ], + "switch_lines": [ + "Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)" + ], + "v3_switch_lines": [ + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.6, + "daa": 0, + "epoch": 0, + "class": 2, + "nodes": [ + "0/130e3b7d", + "0/130e3b7d", + "0/130e3b7d" + ] + }, + { + "t": 19.7, + "daa": 2, + "epoch": 0, + "class": 2, + "nodes": [ + "2/a26633e3", + "2/a26633e3", + "2/a26633e3" + ] + }, + { + "t": 34.7, + "daa": 4, + "epoch": 0, + "class": 2, + "nodes": [ + "4/2e80b7f2", + "4/2e80b7f2", + "4/2e80b7f2" + ] + }, + { + "t": 49.7, + "daa": 14, + "epoch": 0, + "class": 2, + "nodes": [ + "14/b6297491", + "14/b6297491", + "14/b6297491" + ] + }, + { + "t": 64.9, + "daa": 33, + "epoch": 0, + "class": 2, + "nodes": [ + "33/41f3c99e", + "33/41f3c99e", + "33/41f3c99e" + ] + }, + { + "t": 80.1, + "daa": 54, + "epoch": 0, + "class": 2, + "nodes": [ + "54/e14baafe", + "54/e14baafe", + "54/e14baafe" + ] + }, + { + "t": 95.1, + "daa": 76, + "epoch": 1, + "class": 3, + "nodes": [ + "76/31410d82", + "76/31410d82", + "76/31410d82" + ] + }, + { + "t": 110.2, + "daa": 94, + "epoch": 1, + "class": 3, + "nodes": [ + "94/f17df5db", + "94/f17df5db", + "94/f17df5db" + ] + }, + { + "t": 125.3, + "daa": 111, + "epoch": 1, + "class": 3, + "nodes": [ + "111/9cbe1982", + "111/9cbe1982", + "111/9cbe1982" + ] + }, + { + "t": 140.5, + "daa": 125, + "epoch": 2, + "class": 3, + "nodes": [ + "125/185ebceb", + "125/185ebceb", + "125/185ebceb" + ] + }, + { + "t": 155.8, + "daa": 144, + "epoch": 2, + "class": 3, + "nodes": [ + "144/904395a2", + "144/904395a2", + "144/904395a2" + ] + }, + { + "t": 171.2, + "daa": 158, + "epoch": 2, + "class": 3, + "nodes": [ + "158/e5faf4c5", + "158/e5faf4c5", + "158/e5faf4c5" + ] + }, + { + "t": 186.6, + "daa": 174, + "epoch": 2, + "class": 3, + "nodes": [ + "174/1a963704", + "174/1a963704", + "174/1a963704" + ] + }, + { + "t": 201.8, + "daa": 190, + "epoch": 3, + "class": 4, + "nodes": [ + "190/51e300b8", + "190/51e300b8", + "190/51e300b8" + ] + }, + { + "t": 217, + "daa": 205, + "epoch": 3, + "class": 4, + "nodes": [ + "205/251d965c", + "205/251d965c", + "205/251d965c" + ] + }, + { + "t": 232.1, + "daa": 219, + "epoch": 3, + "class": 4, + "nodes": [ + "219/0ecdb99f", + "219/0ecdb99f", + "219/0ecdb99f" + ] + }, + { + "t": 247.3, + "daa": 234, + "epoch": 3, + "class": 4, + "nodes": [ + "234/9f6a3235", + "233/686123b5", + "233/686123b5" + ] + }, + { + "t": 262.4, + "daa": 247, + "epoch": 4, + "class": 4, + "nodes": [ + "247/da402167", + "247/da402167", + "247/da402167" + ] + }, + { + "t": 277.6, + "daa": 263, + "epoch": 4, + "class": 4, + "nodes": [ + "263/86fe7193", + "263/86fe7193", + "263/86fe7193" + ] + }, + { + "t": 292.8, + "daa": 278, + "epoch": 4, + "class": 4, + "nodes": [ + "278/6f950a2e", + "278/6f950a2e", + "278/6f950a2e" + ] + }, + { + "t": 308.3, + "daa": 293, + "epoch": 4, + "class": 4, + "nodes": [ + "293/c29bced3", + "293/c29bced3", + "293/c29bced3" + ] + } + ], + "metal": { + "worker": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/proto-metal/igneum-bench", + "prepares": [ + "PREPARE sent for epoch seed b515430faf46fda81a323a803d58c5f3ef6a6777d1ca2b83cbd41ca0e45fe51d day 1243960 class v3 (6 DAA blocks before the boundary at 60; CPU side ready in 2280 ms)", + "PREPARE sent for epoch seed 9cbe1982e1eaf9df5c1a9dbbf870ca7016987c4594cd3cd47505f9827d1bed70 day 1243960 class v3 (6 DAA blocks before the boundary at 120; CPU side ready in 2071 ms)", + "PREPARE sent for epoch seed ca4245b598fcbeb49449c5072ae0cc6d3803eeb21f75da1eed5a56e8439bdb91 day 1243960 class v4 (6 DAA blocks before the boundary at 180; CPU side ready in 2599 ms)", + "PREPARE sent for epoch seed 3b6cfced206edf85aa0e643ee81c4c01c9a21a3de16e0c2d7347da27540867fa day 1243960 class v4 (6 DAA blocks before the boundary at 240; CPU side ready in 2325 ms)", + "PREPARE sent for epoch seed 343510d7149dcf50f3d302d9e7ed8cd0526dcadf4904ceafa74506e438c49031 day 1243960 class v4 (5 DAA blocks before the boundary at 300; CPU side ready in 2168 ms)" + ], + "prepared": [ + "worker: prepared b515430faf46fda81a323a803d58c5f3ef6a6777d1ca2b83cbd41ca0e45fe51d 69676e65756d2d6461792f38fb120000000000 396.1 program 62.9 dataset 333.1 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.8 build 174.9 resident 2 programs 2 datasets (prepare answered 0.4 s after it was sent)", + "worker: prepared 9cbe1982e1eaf9df5c1a9dbbf870ca7016987c4594cd3cd47505f9827d1bed70 69676e65756d2d6461792f38fb120000000000 61.7 program 61.7 dataset 0.0 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.8 build 174.9 resident 2 programs 2 datasets (prepare answered 0.1 s after it was sent)", + "worker: prepared ca4245b598fcbeb49449c5072ae0cc6d3803eeb21f75da1eed5a56e8439bdb91 69676e65756d2d6461792f38fb120000000000 430.2 program 193.9 dataset 236.2 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1 resident 2 programs 3 datasets (prepare answered 0.4 s after it was sent)", + "worker: prepared 3b6cfced206edf85aa0e643ee81c4c01c9a21a3de16e0c2d7347da27540867fa 69676e65756d2d6461792f38fb120000000000 138.6 program 138.5 dataset 0.0 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1 resident 2 programs 3 datasets (prepare answered 0.1 s after it was sent)", + "worker: prepared 343510d7149dcf50f3d302d9e7ed8cd0526dcadf4904ceafa74506e438c49031 69676e65756d2d6461792f38fb120000000000 175.3 program 175.3 dataset 0.0 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1 resident 2 programs 3 datasets (prepare answered 0.2 s after it was sent)" + ], + "prepared_v4": [ + "worker: prepared ca4245b598fcbeb49449c5072ae0cc6d3803eeb21f75da1eed5a56e8439bdb91 69676e65756d2d6461792f38fb120000000000 430.2 program 193.9 dataset 236.2 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1 resident 2 programs 3 datasets (prepare answered 0.4 s after it was sent)", + "worker: prepared 3b6cfced206edf85aa0e643ee81c4c01c9a21a3de16e0c2d7347da27540867fa 69676e65756d2d6461792f38fb120000000000 138.6 program 138.5 dataset 0.0 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1 resident 2 programs 3 datasets (prepare answered 0.1 s after it was sent)", + "worker: prepared 343510d7149dcf50f3d302d9e7ed8cd0526dcadf4904ceafa74506e438c49031 69676e65756d2d6461792f38fb120000000000 175.3 program 175.3 dataset 0.0 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1 resident 2 programs 3 datasets (prepare answered 0.2 s after it was sent)" + ], + "prepared_v3": [ + "worker: prepared b515430faf46fda81a323a803d58c5f3ef6a6777d1ca2b83cbd41ca0e45fe51d 69676e65756d2d6461792f38fb120000000000 396.1 program 62.9 dataset 333.1 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.8 build 174.9 resident 2 programs 2 datasets (prepare answered 0.4 s after it was sent)", + "worker: prepared 9cbe1982e1eaf9df5c1a9dbbf870ca7016987c4594cd3cd47505f9827d1bed70 69676e65756d2d6461792f38fb120000000000 61.7 program 61.7 dataset 0.0 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.8 build 174.9 resident 2 programs 2 datasets (prepare answered 0.1 s after it was sent)" + ], + "need": 0, + "mismatch_lines": [], + "rebuild_lines_before_v4": [], + "refused": [], + "swaps": [ + "SEED CHANGE at daa 60: epoch seed 130e3b7d568db07aad638f67b2ac26e7cf93c51e4f564d45c6724fc4b2d3da65 -> b515430faf46fda81a323a803d58c5f3ef6a6777d1ca2b83cbd41ca0e45fe51d, day 1243960 -> 1243960 (epoch 1): swapped with no pause (prepared 2 s ago, prepare took 396 ms)", + "SEED CHANGE at daa 120: epoch seed b515430faf46fda81a323a803d58c5f3ef6a6777d1ca2b83cbd41ca0e45fe51d -> 9cbe1982e1eaf9df5c1a9dbbf870ca7016987c4594cd3cd47505f9827d1bed70, day 1243960 -> 1243960 (epoch 2): swapped with no pause (prepared 4 s ago, prepare took 62 ms)", + "SEED CHANGE at daa 180: epoch seed 9cbe1982e1eaf9df5c1a9dbbf870ca7016987c4594cd3cd47505f9827d1bed70 -> ca4245b598fcbeb49449c5072ae0cc6d3803eeb21f75da1eed5a56e8439bdb91, day 1243960 -> 1243960 (epoch 3): swapped with no pause (prepared 3 s ago, prepare took 430 ms)", + "SEED CHANGE at daa 240: epoch seed ca4245b598fcbeb49449c5072ae0cc6d3803eeb21f75da1eed5a56e8439bdb91 -> 3b6cfced206edf85aa0e643ee81c4c01c9a21a3de16e0c2d7347da27540867fa, day 1243960 -> 1243960 (epoch 4): swapped with no pause (prepared 4 s ago, prepare took 139 ms)", + "SEED CHANGE at daa 300: epoch seed 3b6cfced206edf85aa0e643ee81c4c01c9a21a3de16e0c2d7347da27540867fa -> 343510d7149dcf50f3d302d9e7ed8cd0526dcadf4904ceafa74506e438c49031, day 1243960 -> 1243960 (epoch 5): swapped with no pause (prepared 5 s ago, prepare took 175 ms)" + ], + "accepted_total": 301, + "accepted_after_switch": 123, + "found_lines": 0, + "cpu_recheck_mismatched": 0, + "last_status": "" + } +} \ No newline at end of file diff --git a/docs/plans/counter-asic-3-gate/class-v4-20261006-never-failed-case.json b/docs/plans/counter-asic-3-gate/class-v4-20261006-never-failed-case.json new file mode 100644 index 00000000..b3eb74b2 --- /dev/null +++ b/docs/plans/counter-asic-3-gate/class-v4-20261006-never-failed-case.json @@ -0,0 +1,245 @@ +{ + "pass": false, + "checks": { + "v4_switch_line_on_every_node": true, + "v4_switch_line_names_the_rounded_epoch": false, + "v3_switch_line_on_every_node": true, + "template_switched_at_the_first_v4_epoch": false, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": false, + "v3_and_v4_programs_seen": false, + "v2_programs_seen_when_v3_starts_later": true, + "program_ids_differ_across_the_switch": false, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true + }, + "activation": "never", + "v3_activation": 60, + "epoch_blocks": 60, + "first_v3_epoch": 1, + "first_v4_epoch": null, + "boundary_daa": "never", + "secs": 150, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-ca3v4/target-ca3v4/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca3-v4-node/vendor/igneum-node-ca3v4/target-ca3v4/release/igneum-miner", + "metal_worker": null, + "genesis_bits": "0x1f010000", + "template_switch_v3": { + "epoch": 1, + "daa": 60, + "at": 31.7 + }, + "template_switch": null, + "run_ended_at_s": null, + "final_daa": 164, + "blocks": { + "total": 167, + "before_boundary": 167, + "after_boundary": 0, + "chain_before": 165, + "chain_after": 0 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "8f8806638d59850f", + "seed": "234e082d653dc69d", + "miners": 3, + "disagree": false, + "ready_ms": [ + 209, + 203, + 208, + 206, + 209, + 203 + ] + }, + { + "epoch": 1, + "class": "v3", + "program_id": "89059149b7328e64", + "seed": "80127533e53a2df7", + "miners": 3, + "disagree": false, + "ready_ms": [ + 193, + 192, + 192 + ] + }, + { + "epoch": 2, + "class": "v3", + "program_id": "6da8a5eb4dff139a", + "seed": "0e93562ee5369f59", + "miners": 3, + "disagree": false, + "ready_ms": [ + 3, + 4, + 4 + ] + } + ], + "accepted_per_miner": [ + 46, + 59, + 61 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "20e729b7e4ef8637", + "20e729b7e4ef8637", + "20e729b7e4ef8637" + ], + "block_counts": [ + 166, + 166, + 166 + ], + "tips_per_node": [ + 1, + 1, + 1 + ], + "switch_lines": [ + "Program class v4 from the override file: never", + "Program class v4 from the override file: never", + "Program class v4 from the override file: never" + ], + "v3_switch_lines": [ + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 1 (DAA score 60 rounded up to the epoch boundary at 60, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.6, + "daa": 0, + "epoch": 0, + "class": 2, + "nodes": [ + "0/234e082d", + "0/234e082d", + "0/234e082d" + ] + }, + { + "t": 19.7, + "daa": 39, + "epoch": 0, + "class": 2, + "nodes": [ + "39/14b03ba6", + "39/14b03ba6", + "39/14b03ba6" + ] + }, + { + "t": 34.7, + "daa": 62, + "epoch": 1, + "class": 3, + "nodes": [ + "62/b22630fc", + "62/b22630fc", + "62/b22630fc" + ] + }, + { + "t": 49.7, + "daa": 68, + "epoch": 1, + "class": 3, + "nodes": [ + "68/c8bb2f04", + "68/c8bb2f04", + "68/c8bb2f04" + ] + }, + { + "t": 64.7, + "daa": 83, + "epoch": 1, + "class": 3, + "nodes": [ + "83/d2610654", + "83/d2610654", + "83/d2610654" + ] + }, + { + "t": 79.8, + "daa": 101, + "epoch": 1, + "class": 3, + "nodes": [ + "101/72beef65", + "101/72beef65", + "101/72beef65" + ] + }, + { + "t": 94.8, + "daa": 111, + "epoch": 1, + "class": 3, + "nodes": [ + "111/846008b8", + "111/846008b8", + "111/846008b8" + ] + }, + { + "t": 109.8, + "daa": 121, + "epoch": 2, + "class": 3, + "nodes": [ + "121/d4da8120", + "121/d4da8120", + "121/d4da8120" + ] + }, + { + "t": 124.8, + "daa": 134, + "epoch": 2, + "class": 3, + "nodes": [ + "134/c7e80324", + "134/c7e80324", + "134/c7e80324" + ] + }, + { + "t": 139.8, + "daa": 152, + "epoch": 2, + "class": 3, + "nodes": [ + "152/2a916f0d", + "152/2a916f0d", + "152/2a916f0d" + ] + } + ], + "metal": null +} \ No newline at end of file diff --git a/docs/plans/counter-asic-3-gate/node-gates.md b/docs/plans/counter-asic-3-gate/node-gates.md new file mode 100644 index 00000000..ce322a9b --- /dev/null +++ b/docs/plans/counter-asic-3-gate/node-gates.md @@ -0,0 +1,12 @@ +# Counter ASIC 3.0: the node-side gates for class v4 (`mx8+sh256x27`) + +6 October 2026, worker "ca3-v4-node". The rows in the shape of `docs/plans/counter-asic-2-rollout.md` section 7. Trees: main `ca3-v4-node` a522d04 (igneum-pow, the workers, the harness, on ca3-coord 3213ee9), fork `ca3-v4-node` 5f7e0543 (on release-0.3.13-node bb43e9a8, the current release tip, which contains release-0.3.12-node). Builds: the Mac built igneumd and igneum-miner (`target-ca3v4/release`, `--features kaspad/igneum-pow`, warm from an APFS clone of the 0.3.13 target dir, under `with-lock.sh build`, nice 19, 4 jobs) and `igneum-bench` (the Metal worker, `swiftc -O`); the suites went to PC 2. The Mac's installed app was not touched (its miner stays paused); nothing was published; the live devnet was not touched. The Mac's load average during the runs: 5 to 12 (other agents' builds); the gates here count blocks, lines and ids, not rates, so the load does not move them. + +| # | Gate | Evidence required | State | +|---|---|---|---| +| G4 | the fast-time 3-node network mining across a v4 activation | 0 rejected blocks, 0 forks, every node's first lines show the switch, blocks on both sides of the boundary; the gate fires on one finished and one failed case | GREEN. Run 1 (15:54:32 to 15:59:21Z, `node infra/fast-time/class-v4.mjs --secs 420 --activation 150 --v3-activation 60 --epochs-after 2` under `with-lock.sh run`; three nodes, one real CPU miner each with `--engine igneum-pow`, every node verifying the other two): 3 of 3 nodes print both switch lines (`Program class v4 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)` and the v3 line for epoch 1 at DAA 60); templates class 2 for epoch 0, class 3 for epochs 1 and 2, class 4 for epochs 3 to 5; the switch seen at DAA 180, 163.9 s wall; 181 blocks before and 128 after DAA 180 (309 in all, selected chain 177 / 127); program ids agree on all three miners (e0 v2 `8f8806638d59850f`, the same v2 id as 2.0's three runs; e1 v3 `29ced0d031cc121b`, e2 v3 `341fd4076256cd7b`, e3 v4 `070ca0f94e7f8f88`, e4 v4 `b8cbe1ef7ab1d748`, e5 v4 `9321cbd9c54c0b83`; no v2 or v3 id reappears under v4); rejected 0/0/0 on the miners and 0/0/0 on the nodes (103, 103, 102 accepted); one sink `4da9ac669effdfab` on all three at 308/308/308 blocks, one tip each; digest `59a3eb5383bfbf23...` on all three; program and cache ready on one core: v2 epoch 0 199 ms, the first v3 epoch 187 ms (its own day cache), the first v4 epoch 2 ms (the v3 day cache reused across the N5 boundary, no rebuild). Summary `class-v4-20261006-1553Z-cpu.json`. Run 2, the known-failed case (15:59:46 to 16:02:17Z, `--activation never --v3-activation 60 --secs 150`): every node prints `Program class v4 from the override file: never`; the harness reports `SUMMARY FAIL: NO v4 epoch seen` with five checks down (`v4_switch_line_names_the_rounded_epoch`, `template_switched_at_the_first_v4_epoch`, `blocks_after_the_boundary`, `v3_and_v4_programs_seen`, `program_ids_differ_across_the_switch`), exit 1; the v3 side of that run was clean (epochs 0 v2, 1 and 2 v3; 0 rejected; one sink 166/166/166). Summary `class-v4-20261006-never-failed-case.json` | +| G4b | the Mac mines v4: a real Metal miner across a v4 boundary through the miner's `--prepare-packs` flow, and the app passes that flag to the Metal worker | the prepare lines on both sides, the worker's `prepared` line for the v4 pack, found or accepted blocks on v4, no `need` or mismatch line, no exit 42 or 44; the app's `miner_args` pushes `--prepare-packs` for every worker | GREEN. Run 3 (16:02:37 to 16:07:58Z, `--metal proto-metal/igneum-bench --genesis-bits 0x1e010000 --secs 480 --activation 150 --v3-activation 60`; node 0's miner is `igneum-miner --worker igneum-bench --prepare-packs --exit-on-seed-change`, the app's own shape; `igneum-bench` built from this tree at a522d04's `main.swift`, sha256 30c70754...; CPU miners on nodes 1 and 2): 5 PREPARE lines, each 5 to 6 DAA before its boundary, two `class v3` (DAA 60, 120) and three `class v4` with the pack directory and `class=v4 era=` (DAA 180, 240, 300); the worker's `prepared` lines for the three v4 packs: `prepared ca4245b5... 430.2 program 193.9 dataset 236.2 race 0.0 variant base class v4 loads/hash 128 cache-fill 1.4 build 70.1` (the first, the v4 day built from the pack's memhard.metal on the GPU: 1.4 ms cache fill, 70.1 ms build), then 138.6 and 175.3 ms with the day resident (program compile alone, the 256-instruction shadow block in the kernel text; a v3 prepare was 61.7 ms); every swap `swapped with no pause` at DAA 60, 120, 180, 240, 300 (prepared 2 to 5 s earlier); 301 blocks accepted by the Metal miner, 123 after the switch, every one re-checked on the CPU: `mismatched=0`; `need` 0; no class or era mismatch line, no `PACK OUT OF DATE`, no prepare-failed, no exit 42 or 44 (0 such lines in the miner's log); the chain 182 / 122 blocks across DAA 180 (304), rejected 0/0/0 and 0/0/0, one sink `4e1aa3fbf1f4a54b` on all three at 303/303/303, 3 of 3 v4 switch lines; program ids e3 v4 `2bcecca6f867c3b5`, e4 `21b78e63fc0e9251`, e5 `3855570a5ba12fc9` agreed by the Metal miner and the CPU miners; the Metal miner's STATUS at 309 s: 18.58 MH/s wall, 293 accepted, 0 rejected (a rate under a load average of 5 to 12 with the CPU miners and other agents' builds beside it: an order of magnitude, not a measurement). The app side: `app/igneum-app/src/engine.rs` `miner_args` pushes `--prepare-packs` for every worker (line 1478, the 0.3.11 fix; unit test `every_worker_gets_the_prepare_directory_in_the_platform_form`), confirmed unchanged on ca3-coord 3213ee9. Summary `class-v4-20261006-metal.json` | +| G5 | the PC-built Windows workers and the Mac workers from the same commit | every sha256 listed; the two Windows workers carry the resource block; no packaging, no DMG, no manifest | GREEN, with one gap named: the build job builds the node and the app only (`push-build-inputs.sh` writes two build units, `jobbuild.rs` has no worker unit), so the Windows workers were cross-built on the Mac from the committed tree a522d04 with `proto-cuda/nvrtc/build-windows.sh` under the build lock (mingw-w64, static, windres resource block), as 0.3.11's G5 row was: igneum-worker-cuda.exe e563126ed1d8ab8f6803ead89acca5b6fcb03203e6b87b70e0aec85e8e1046e4 (1,536,512 bytes), igneum-worker-opencl.exe 7fce1249d443aaf73a29b7474c1ef4084f3529b5bec3a2dc6bb97e624561a5e8 (478,208), `verify-exe.py`: both carry the coin icon and the version block (a first build of the same sources before the commit gave 26400edf... and 438e608a...: mingw stamps a link time, so the pair is not bit-reproducible; the sizes match the 0.3.11 pair, 1,536,512 and 478,208, and the sha256s differ from 0.3.11's 2b3b8c92... and edc4a75d...: the generator-4 rule is in them). The Mac worker from the same tree: igneum-bench 30c70754097dafbc3144a0c0f192526a0638089e5dde49ce46abb7a948dceade (665,128 bytes, built 16:47Z from `main.swift` as committed in a522d04, the binary G4b mined with). The same build job's (build-20261006-155958) node and app outputs from fork 5f7e0543, every sha256 verified against the PC's lines on fetch: igneumd.exe 140be25e486fe1634500b6e66eedfaca9fa6561d652f3e219b5a7a2bc377e766 (51,752,448), igneum-miner.exe ceb1e691ad57f58d09d5a0ff4cb5658aea0604d23ceb90b6a847a2c36a0bf81c (11,118,080), igneum-app.exe bfa3b871f17b69fd0c0c01a6f9fcac8964efb54dc10fd25db13422ba8f96d51f (3,065,344, PE and resource block ok); Linux igneumd d44b735869b129401d68f18786b4a5f16c22d00b682bd41a8e5022a01f7d7527 (49,603,112), igneum-miner a7985111d4972f4b67a9409b831a8c7af746508ebb3b19fc927aaddcfa2e2413, igneum-app 222f30c00bb9725f3127452f3d52403b380e25abaf9fba5c925839574cf1dabc; kept under the job's --out dir with --no-place. No packaging, no DMG, no manifest | +| G6 | the node change on a fork branch from the current release tip with suites green on PC 2, with the igneum-pow feature | the build job id and its SUMMARY line; the v4 engine test named in the output | GREEN. PC 2 job `build-20261006-155958` (fork ca3-v4-node 5f7e0543, main a522d04; published 15:59:58Z after the clear file and under the mkdir lock, taken 15:58:50Z, released 16:08:06Z after the closing report; the installed app untouched, the prover left on), `node tools/build-job.mjs run --target 1ccfe586 --node vendor/igneum-node-ca3v4 --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app --no-place`: `done exit 0 after 392 s: node ca3-v4-node 5f7e0543 app 0.3.11: every stage ok; 9 files uploaded (46 MB); 6 min of 40`; the test stage 50 s, exit 0 on both units: kaspa-consensus 98 passed (3 ignored; `ban_is_decided_by_the_carrying_block_so_nodes_agree_on_every_voter_list ... ok`, so 2.0's flake did not recur and no re-run was needed), kaspa-consensus-core 109 + 7 (`override_params_carry_the_program_class_v4_activation ... ok`), igneum-exec ?, kaspa-pow 15 (`program_class_v3_seeds_hash_their_own_program_over_their_own_cache ... ok` and `program_class_v4_seeds_hash_the_shadow_program_over_the_v3_day_cache ... ok`, both `#[cfg(feature = "igneum-pow")]`), igneum-miner ?, kaspa-p2p-flows 33, igneum-app 112 + 26 + 8. The feature: `build-job.mjs` and `push-build-inputs.sh` carry no `--features` for the test units (the manifest's `tests` entries are `dir` and `packages` only; the build units have `kaspad/igneum-pow`), and the PC's `jobbuild.rs` runs `cargo test --release -p ... ` in ONE invocation per unit, where `igneum-miner` depends on `kaspa-pow` with `features = ["igneum-pow"]`, so cargo's feature unification turns the feature on for kaspa-pow's test target in that invocation: the two engine tests above ran on PC 2 with the feature, named in the report, which is the evidence main asked for. The same two suites also pass on the Mac with the flag explicit (`cargo test --release -p kaspa-pow --features igneum-pow`: 15; `-p kaspa-consensus-core`: 109 + 7). What the tool lacks: a `features` key on a test unit; not added here (a change to the PC's installed app would be needed to honour it) | + +Summary files in this directory: `class-v4-20261006-1553Z-cpu.json` (G4 run 1), `class-v4-20261006-never-failed-case.json` (G4 run 2, the failed case), `class-v4-20261006-metal.json` (G4b). diff --git a/docs/plans/counter-asic-3-node.md b/docs/plans/counter-asic-3-node.md new file mode 100644 index 00000000..3e4e0849 --- /dev/null +++ b/docs/plans/counter-asic-3-node.md @@ -0,0 +1,77 @@ +# Counter ASIC 3.0: the node side (program class v4 as a second height switch) + +6 October 2026, worker "ca3-v4-node". Branches: `ca3-v4-node` (main repository: igneum-pow, the workers, the fast-time gate) and `ca3-v4-node` (the fork, from the 0.3.13 tip bb43e9a8). The 2.0 twin of `docs/plans/counter-asic-2-node.md`: every piece of its section 1 has its v4 twin below; v2 and v3 are byte for byte what they were (the pinned packs, `igneum-pow/tests/packs.rs`). The class: `mx8+sh256x27` (`docs/analysis/latency-shadow-2026-10-06.md`), gated here as THE class v4 candidate on the project lead's word of 16:50 UTC. Nothing is published; the Mac's app is untouched (its miner stays paused); the live devnet is untouched. Gate rows: `docs/plans/counter-asic-3-gate/node-gates.md`. + +## 1. What changed, where + +| Piece | What | +|---|---| +| igneum-pow `generator.rs` | `ProgramClass::V4`, `V4_CLASS = LoadClass { shadow: Some(ShadowClass { instrs: 256, reps: 27 }), ..V3_CLASS }` (name `mx8+sh256x27`), `GENERATOR_VERSION_V4 = 4`, `generate_era_generator` (the era draw of class v3 over `V4_CLASS`, generator 4 stamped) used by `generate_from_seed_bytes_program_class` for `(V4, Some(era))`; `ProgramClass::{load_class, generator_version, from_generator, name, parse}` take v4; `ProgramClass::has_era` (v3 and v4); a v4 program's id is `program_id(4, seed, attempt)` (`Program::program_id`), its class `Program::program_class` = V4 from generator 4. Tests: `program_classes` (v4 round trip), new `program_class_v4_is_class_v3_with_the_shadow_block` (the base instructions, attempt and era draw equal class v3's of the same seed and era; shadow 256 x 27, 55,296 per hash; the id differs; the hash differs; v2's id `bcc1248b10cc90f2` unchanged) | +| igneum-pow `lib.rs` | exports `GENERATOR_VERSION_V4`, `V4_CLASS` | +| igneum-pow `emit.rs` | program.h: `IGNEUM_GENERATOR 4`, `IGNEUM_PROGRAM_CLASS "v4"`, `IGNEUM_ERA_SEED_HEX` (the v3 comment lines are byte for byte the 0.3.11 ones, so the pinned v3 packs do not change); the shadow lines (`IGNEUM_SHADOW_INSTRS`, `IGNEUM_SHADOW_REPS`, the `for (sh ...)` block in every kernel body) come from the class as before; program.json `"program_class": "v4"` | +| igneum-pow `packcheck.rs` | `verify_pack_texts_chain` accepts generator 2, 3 or 4 (`PackFault::WrongClass` otherwise, "2, 3 or 4" in the message); the era rule applies to every class with `has_era` (v3 and v4); test `program_class_and_era_are_checked` extended (a v4 pack with its era and shadow lines passes when v4 is wanted, is refused when v2 or v3 is wanted; a v3 or v2 pack is refused when v4 is wanted; the v4 era mismatch and the missing-era cases; the v3 header unchanged) | +| igneum-pow `main.rs` | `--program-class v2|v3|v4` | +| `proto-cuda/nvrtc/packfile.h` | `pf_load` accepts generator 2, 3 or 4 and names the class from it (`"v4"`); `pf_pack_class_ok` applies the era rule to every class that is not v2; `pf_class_token` unchanged (reads `class=v4`). `emu/packfile-test.c`: 7 new checks (a generator 4 pack loads as v4 with its era; v3 refuses it; it refuses another era; v4 refuses a v3 pack; the `class=v4` token; a v3 class line on a generator 4 pack is refused); `emu/packfile-test.sh` 0 failures on the Mac | +| `proto-cuda/nvrtc/worker.cpp`, `proto-opencl/host.c` | no text change: both read the class and era through `packfile.h` (`pairIsClass`, `takeClassTokens`), so a pair's identity carries `v4` and its era, a v4 job against a resident v3 pair of the same seeds answers `need` and `program class mismatch`, and the prepared v4 pair wins; `host.c` compiles clean (`cc -fsyntax-only`) against the new header | +| `proto-metal/main.swift` | `isPackClass` (v3 or v4) and `packClassOf(generator)`: `servePackProgram` and `servePackDataset` accept generator 2, 3 or 4 and refuse the rest in plain words; a `class=v4` line is compiled only from the prepared pack (its `program_bound.metal` carries the shadow block) and its day built from the pack's `memhard.metal`, keyed by (day, class, era); an inline `class=v4` job with no prepared pair answers `need` and the class-mismatch error (2.0's G4b path); a class that is not v2, v3 or v4 is refused | +| fork `consensus/core/src/igneum.rs` | `ProgramClass::V4` (generator 4, `"v4"`), `has_era`, `dataset_class` (V4 -> V3: the v4 day cache is the v3 day cache), `program_class_for_epoch_at(epoch, N4, N5, L)` with two switches (v4 from the first epoch at or above N5, else v3 from N4, else v2; N5 at or below N4 is v4 from N5), `program_class_first_epoch_at`, `program_class_v4_first_epoch_at`, the process-wide `install_program_class_v4_activation` / `program_class_v4_activation_daa` / `program_class_v4_first_epoch`; `PowEpochInfo.program_class_v4_activation_daa` (serde default never); the switch test sweeps both heights | +| fork `consensus/core/src/config/params.rs` | `program_class_v4_activation_daa` in `Params` and `OverrideParams` (never on devnet, simnet, mainnet; 0 on the testnet), `override_params`, `From`, the digest (unconditionally, right after `program_class_v3_activation_daa`), `install_program_class_v4_activation`, `program_class_v4_first_epoch`; tests: `override_params_carry_the_program_class_v4_activation`, the digest test's 15th edit, the pinned devnet digest re-pinned (section 2), `fast_time_60x_file_is_the_devnet_at_60x` (every field) | +| fork `kaspad/src/daemon.rs` | `Program class v4 from the override file: active from epoch E (DAA score N5 rounded up to the epoch boundary at L*E, epochs of L DAA)` beside the v3 line; `install_program_class_v4_activation` next to the v3 install | +| fork `consensus/pow/src/igneum.rs` | `EpochSeeds::day_key` keys the day cache on `(day, class.dataset_class())`, so a v4 epoch after a v3 one builds no new cache; `era_bytes` for every class with an era; `pow_class_of` maps V4; `build_day` builds the dataset class's day. Test `program_class_v4_seeds_hash_the_shadow_program_over_the_v3_day_cache` (one cache for v3 and v4 seeds of one day; the v4 program is generator 4, `V4_CLASS` with the era inside, the base instructions and attempt class v3's; the engine's pow equals the standalone epoch's; another era keeps the id and changes the program) | +| fork `consensus/src/consensus/mod.rs` | `get_pow_epoch_info` reports `program_class_v4_activation_daa`; the class of this and the next epoch come from `program_class_for_epoch` (both switches) | +| fork `consensus/src/pipeline/header_processor/pre_ghostdag_validation.rs`, `processes/pruning_proof/igneum_pow.rs` | unchanged: `program_class_at(daa)` answers v4 under the installed switches; the era seed is read for every class after v2 | +| fork `rpc/core/src/model/message.rs`, `rpc/grpc/core/proto/rpc.proto` (field 19), `rpc/grpc/core/src/convert/message.rs` | `RpcPowEpochInfo.program_class_v4_activation_daa` (serde default never); proto `optional uint64 programClassV4ActivationDaa = 19` so a 0.3.13 node, which sends nothing, reads as never and not as 0 (v4 from genesis); `program_class` and `next_program_class` already carry a generator number, so 4 needs no field | +| fork `igneum/miner/src/main.rs` | the v4 activation installed from the template beside the v3 one (`node program class v4 activation: ...`); `class_tokens`: a v3 or v4 epoch's job and prepare lines end with `class= era=`; `seeds.txt` carries `program_class v4` and `era_seed_hex`; `write_pack_checked` checks class and era through `verify_pack_dir_chain` (generator 4 accepted); the "program and 256 MiB cache ready" line prints `class v4` and the id; `next_pair` takes the next epoch's class from the template (`next_program_class` 4 at the boundary) | +| `app/igneum-app/src/engine.rs` | confirmed, no change: `miner_args` pushes `--prepare-packs` for every worker (line 1478, since 0.3.11; the unit test `every_worker_gets_the_prepare_directory_in_the_platform_form`), so the Mac's Metal worker takes a v4 pack the way it takes a v3 one | +| `infra/fast-time/override-60x.json` | `program_class_v4_activation_daa` at never right after the v3 field. Two faults of the base file fixed on the way: the proving v1 block was in the file TWICE (two merges each appended it; the node refuses a duplicate field, so neither class harness could start a node from the file as committed), and the four 0.3.12 and 0.3.13 fields (`proving_v1_fresh_rule_daa`, `exec_restart_number`, `exec_restart_hash`, `exec_restart_trust_daa`) were missing (the fork's every-field test skips on the PCs, where the file is not beside the node). New CI check `tools/ci/override-json-check.sh` (every override file parses with no duplicate key; self-tested on a duplicate), registered in `.github/workflows/ci.yml` | +| `infra/fast-time/class-v4.mjs`, `infra/fast-time/README.md` | the G4 gate (section 3): class-v3.mjs's shape with two switches (`--v3-activation 60`, `--activation 150|never`), ports 29660, suffix 966, `/tmp/igneum-fast-time-v4`; the known-failed case is `--activation never` | + +## 2. The epoch-boundary rule with two switches + +One epoch has one program, so both switches key on the EPOCH: epoch `e` is v4 when `L * e >= N5`, else v3 when `L * e >= N4`, else v2 (`program_class_for_epoch_at`); each height rounds UP to the next boundary and never splits an epoch. A block's class is a function of its DAA score alone (`program_class_at`). + +| N4 | N5 | L | epochs | Source | +|---|---|---|---|---| +| 60 | 150 | 60 | e0 v2; e1, e2 v3; e3 (DAA 180) v4 | the gate run, section 3; the unit test | +| 60 | 181 | 60 | e3 v3; e4 (DAA 240) v4 | the unit test | +| 154,800 | 180,001 | 3,600 | e50 v3; e51 (183,600) v4 | the unit test (the devnet shape: v3 live from epoch 43, v4 at tip + 14,400 rounded up) | +| 150 | 120 | 60 | e2 v4 (N5 below N4: v4 from N5) | the unit test | +| any | never | any | the 2.0 answers, unchanged | the unit test sweeps N4 0..399 with N5 never | +| any | 0 | any | v4 from genesis (the testnet) | the unit test | + +The digest: `program_class_v4_activation_daa` enters `consensus_digest` unconditionally right after the v3 field, so the digest flips the moment a binary carrying the field runs, as the v3 field did on 0.3.11. Measured on the fork (the pinned test `consensus_digest_keeps_the_0_3_5_value_until_the_fee_switch_is_set`): the devnet digest with no override file moves from `c562d70e1428c9789823cc40067623b4767f7c555ce7ff4ea11c1498f013ef6c` (0.3.11 to 0.3.13) to `3c50502174b031b9afe8aad3758aa55eb32e3ee20ffa6f9760b2e958547c0690`. Consequence: a node on this binary is refused at the handshake by every 0.3.13 node and refuses them, which is the rollout's order step 1 (every node carries the object before any node reaches the height); the cut that carries it is a one-sweep binary rollout like 0.3.11's, and the Mac's app, PC 2 and the seeds all move in that sweep, nothing earlier. + +The day cache: the v4 dataset is the v3 dataset (the same mixer x8 construction, growth rule and era layout; the shadow block touches no load and no item), so the node's engine keys its day caches on `(day, dataset_class)` and a v4 epoch reuses the v3 cache across the N5 boundary: no 1 GiB rebuild on the node at the switch (the kaspa-pow test: `cached_days` 1, `cache_builds` 1 for v3 and v4 seeds of one day). The Metal worker keeps separate (day, class, era) entries, so it builds the v4 day once at the boundary from the pack's own `memhard.metal` (the same bytes; about 41 ms on the GPU by the 2.0 figure). + +## 3. The gates + +See `docs/plans/counter-asic-3-gate/node-gates.md` for the rows in the rollout's section 7 shape and the summary files. The results, in short: + +| Gate | State | The line that decides it | +|---|---|---| +| G4 | GREEN | run 1: 3 of 3 switch lines, 181 / 128 blocks across DAA 180, ids agree on all three miners, rejected 0/0/0, one sink 308/308/308; run 2 (`--activation never`): `SUMMARY FAIL: NO v4 epoch seen`, exit 1 | +| G4b | GREEN | three `prepared ... class v4` lines (430, 139, 175 ms), every swap with no pause, 123 blocks accepted on v4, re-check mismatched 0, need 0, no mismatch, no exit 42 or 44 | +| G5 | GREEN | the two Windows workers e563126e... and 7fce1249... with the resource block, the Mac worker 30c70754..., the job's igneumd.exe 140be25e..., every sha256 listed | +| G6 | GREEN | PC 2 job build-20261006-155958, every stage ok, 392 s; kaspa-pow 15 with the v4 engine test, no flake | + +## 4. Tests + +| Where | What | State | +|---|---|---| +| igneum-pow `cargo test --release` | 60 unit (the v4 class test, the v4 pack checks) + 7 derive + 4 mixer + 19 packs (the pinned v2 and v3 packs byte-identical) + 7 scratch | pass (Mac, 6 Oct 2026, 15:44Z) | +| `proto-cuda/nvrtc/emu/packfile-test.sh` | 13 + 7 checks: the generator rule with 4, the v4 pack with its era, the token matcher | 0 failures (Mac) | +| `proto-opencl/host.c` | `cc -fsyntax-only` against the new packfile.h | clean (Mac) | +| `proto-metal/main.swift` | `swiftc -O` (the G4b worker) | built (Mac) | +| fork `cargo test --release -p kaspa-consensus-core` | the two-switch rounding sweep, the v4 params, the digest's 15th edit, the pinned digest, the fast-time every-field test | pass (Mac, 109 + 7, 2 ignored; PC 2 job build-20261006-155958: 109 + 7) | +| fork `cargo test --release -p kaspa-pow --features igneum-pow` | the v4 engine test beside the v3 one | pass (Mac, 15; PC 2 job: 15, the feature on through unification with igneum-miner) | +| PC 2 `build-job.mjs` suites | `kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows` + `igneum-app` | G6 row | + +## 5. Unverified, and what is owed + +- The AMD vendor (G1 on the RX 9070 XT) and G2 (1,000 random hashes per card re-hashed on the CPU) are the hash side's gates, not this worker's; the OpenCL worker's generator-4 rule was checked by the C test and a syntax check, not by a live worker on a v4 pack (as 2.0's rollout read before its G1). +- The Metal worker's rate in G4b (18.6 MH/s wall) was taken under a load average of 5 to 12 with CPU miners beside it: not a measurement (the measured v4 rate is the shadow analysis's 26.5 to 26.9 MH/s on packbench under the measure lock). +- The era walk for era >= 1 and the pruning-proof era witness: unchanged from 2.0 section 7 (the v4 switch adds nothing there). +- `next_pair` at an epoch boundary that is also an era boundary: the 2.0 note stands for v4 as for v3. +- Wire: `RpcPowEpochInfo` gained one Borsh field (wRPC, versioned by `GetBlockTemplateResponse`) and one optional proto field; a 0.3.13 miner against this node reads the v4 height as never (the field absent) and would key epochs on the v3 switch alone, so every miner moves in the same binary sweep as the node (the rollout order of 2.0). +- The build job has no `features` key for test units (G6 row): the feature was on through unification; adding the key needs the PC's installed app to honour it, a 0.3.14 app item. +- The 48 GiB APFS clone of the 0.3.13 target dir (`vendor/igneum-node-ca3v4/target-ca3v4`, untracked) can be deleted after the cut.