class v5 on the amended v4 (ca3-v4-amend 8d9d6859 merged): the source rule's class key sets the state flag aside so V5_CLASS and its rungs draw under it; the test class_v5_chain_draw_is_the_amended_v4_draw (the v5 chain draw equals the amended v4's instruction for instruction at rung 0 and 1 with no lossy-sourced load; the unamended v3 stream of the same seeds carries them: the known-failed case); the pinned v4 control pack re-exported with its sub-version line and id a21e93f6988dce82 (the string-seed packs carry no era, so the rule touches neither draw; the v5 pack is unchanged); generator 5 keeps program_id(5, seed, attempt) with no sub-version, the hash lane's reading

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 12:10:11 +00:00
parent 2618ea3acf
commit dc92bb46cc
3 changed files with 49 additions and 2 deletions

View file

@ -539,6 +539,11 @@ impl LoadClass {
LoadClass { shadow: Some(ShadowClass { instrs, reps }), ..self }
}
/// This class with another class's era draw (tests: a rung's class composed with the chain's era).
pub fn with_era_of(self, other: &LoadClass) -> LoadClass {
LoadClass { era: other.era, ..self }
}
/// The class with the state leaves of class v5 folded into every item ("mx8+sh256x27+state").
pub fn with_state(self) -> LoadClass {
LoadClass { state: true, ..self }
@ -1885,6 +1890,46 @@ mod tests {
assert!(!ProgramClass::V4.has_state() && ProgramClass::V5.has_state());
}
/// Class v5 (docs/design/class-v5-stored-state.md) takes the amended class v4 draw (AP-F8-1) as the chain draws it:
/// with an era present every load's source was last written by an injecting op or a rotate, the base program and
/// the shadow block equal the amended v4's of the same seed, and the same holds at a ladder rung; the state flag
/// changes the id and the dataset, never the draw. The known-failed case first: a v5 draw with a lossy-sourced
/// load would fail the same scan the amended v4 passes.
#[test]
fn class_v5_chain_draw_is_the_amended_v4_draw() {
let era = [7u8; 32];
let scan = |p: &Program| {
let mut kept = [false; 8];
let mut lossy = 0;
for i in &p.instrs {
if i.op.is_load() && !kept[i.src as usize] {
lossy += 1;
}
kept[i.dst as usize] = i.op.injects() || matches!(i.op, Op::Rotl | Op::Rotr);
}
lossy
};
for seed in ["igneum-genesis", "igneum-epoch-7", "igneum-epoch-99"] {
let v4 = generate_from_seed_bytes_program_class(seed, seed.as_bytes(), ProgramClass::V4, Some(&era));
let v5 = generate_from_seed_bytes_program_class(seed, seed.as_bytes(), ProgramClass::V5, Some(&era));
assert_eq!(v5.instrs, v4.instrs, "{seed}: the base program is the amended v4's");
assert_eq!(v5.shadow, v4.shadow, "{seed}: and the shadow block");
assert_eq!((v5.seed, v5.attempt), (v4.seed, v4.attempt));
assert_eq!(scan(&v5), 0, "{seed}: no lossy-sourced load under the rule");
assert_eq!(v5.generator, GENERATOR_VERSION_V5);
assert!(v5.class.state && !v4.class.state);
assert_ne!(v5.program_id(), v4.program_id());
let r1 = generate_from_seed_bytes_program_class_shadow(seed, seed.as_bytes(), ProgramClass::V5, Some(&era), 35);
let r1v4 = generate_from_seed_bytes_program_class_shadow(seed, seed.as_bytes(), ProgramClass::V4, Some(&era), 35);
assert_eq!(r1.instrs, r1v4.instrs, "{seed}: rung 1 too");
assert_eq!(scan(&r1), 0);
assert_eq!(r1.class, v5_class_at(35).with_era_of(&r1v4.class));
}
// the stream of 6 October (no rule) differs: a v3 program of the same seed is lossy-sourced somewhere in three seeds
let lossy_v3: usize = ["igneum-genesis", "igneum-epoch-7", "igneum-epoch-99"].iter().map(|seed| scan(&generate_from_seed_bytes_program_class(seed, seed.as_bytes(), ProgramClass::V3, Some(&era)))).sum();
assert!(lossy_v3 > 0, "the known-failed case: the unamended stream carries lossy-sourced loads");
}
/// Counter ASIC 3.0 (6 October 2026): class v4 is class v3 with the latency-shadow block `sh256x27`, drawn after
/// 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

View file

@ -15,7 +15,7 @@
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973"
#define IGNEUM_GENERATOR 4
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x853a971d7813fd5cull
#define IGNEUM_PROGRAM_ID 0xa21e93f6988dce82ull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
@ -32,6 +32,7 @@
// latency-shadow block (IGNEUM_SHADOW_INSTRS x IGNEUM_SHADOW_REPS per iteration); a worker that runs another class
// refuses this pack, and a job line names the class it wants (class=v4 era=<hex>).
#define IGNEUM_PROGRAM_CLASS "v4"
#define IGNEUM_PROGRAM_SUBVERSION 1
// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item
// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule.
#define IGNEUM_LOAD_CLASS "mx8+sh256x27"

View file

@ -2,7 +2,7 @@
"format": "igneum-program-pack-3",
"generator": 4,
"attempt": 0,
"program_id": "0x853a971d7813fd5c",
"program_id": "0xa21e93f6988dce82",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-genesis",
@ -16,6 +16,7 @@
"instruction_count": 64,
"loads_per_hash": 128,
"program_class": "v4",
"sub_version": 1,
"load_class": "mx8+sh256x27",
"mixer_mult": 8,
"cache_growth": true,