From 4aa0c665e83c7a155f313235952ca44f20a946e1 Mon Sep 17 00:00:00 2001 From: igneum-josh <337424239+igneum-josh@users.noreply.github.com> Date: Wed, 7 Oct 2026 11:07:43 +0100 Subject: [PATCH] Counter ASIC 3.0 gates (hash): the v4 unit test follows the amendment (the base program is its own stream over class v3's load slots and era draw; every load site obeys the source rule) Co-Authored-By: Claude Fable 5.1 --- igneum-pow/src/generator.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index 679a5bf9f..c29caf159 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -1739,8 +1739,9 @@ mod tests { } /// Counter ASIC 3.0 (6 October 2026): class v4 is class v3 with the latency-shadow block `sh256x27`, drawn after - /// the base program, so a v4 program's base instructions, attempt and era draw are the v3 program's of the same - /// seed and era, draw for draw; its generator is 4, its id `program_id(4, seed, attempt)`, its era recorded; + /// the base program; since the AP-F8-1 amendment (7 October 2026) its base program draws a load's source only + /// from registers whose last writer keeps entropy, so it is its own stream over class v3's load slots and era + /// draw; its generator is 4, its id `program_id(4, seed, attempt)` with the sub-version suffix, its era recorded; /// v2 and v3 are untouched. #[test] fn program_class_v4_is_class_v3_with_the_shadow_block() { @@ -1757,9 +1758,24 @@ mod tests { assert_eq!(v4.generator, GENERATOR_VERSION_V4); assert_eq!(v4.program_class(), ProgramClass::V4); assert_eq!(v4.era_bytes.as_deref(), Some(&era[..])); - assert_eq!(v4.instrs, v3.instrs, "the base program is class v3's, draw for draw"); - assert_eq!(v4.attempt, v3.attempt); + // The AP-F8-1 amendment (7 October 2026): class v4's chain draw takes a load's source only from registers + // whose last writer injects or rotates, so its base program is its own stream (the 6 October stream, equal + // to class v3's draw for draw, is sub-version 0 and never stamped); the load slots, the op draws and the era + // draw are still class v3's, and every load site obeys the rule assert_eq!(v4.seed, v3.seed); + assert_eq!( + v4.instrs.iter().map(|i| i.op.is_load()).collect::>(), + v3.instrs.iter().map(|i| i.op.is_load()).collect::>(), + "the load slots are class v3's" + ); + let mut kept = [false; 8]; + for (k, i) in v4.instrs.iter().enumerate() { + if i.op.is_load() { + assert!(kept[i.src as usize], "load #{k} reads r{} whose last writer does not keep entropy", i.src); + } + kept[i.dst as usize] = i.op.injects() || matches!(i.op, Op::Rotl | Op::Rotr); + } + assert_ne!(v4.instrs, v3.instrs, "the amended v4 base program is not class v3's (a lossy-sourced load was redrawn)"); assert!(v3.shadow.is_empty() && !v3.has_shadow()); assert_eq!(v4.shadow.len(), 256); assert_eq!(v4.shadow_reps(), 27);