igneum/proto-cuda/nvrtc/emu/packfile-test.c
igneum-labs 41631431c0 Workers: a pack's seed words are its program attempt's words, not the bare seed's (epoch 34 incident, 5 October 2026)
From 18:23Z both Windows workers (CUDA on PC 1 and PC 2, OpenCL on PC 1 after the 18:34Z node restart)
refused every pack for epoch 34 with "the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT", and
the miner and the app restarted them every 5 to 60 s until 18:44Z and beyond. The packs were correct.
The generator retries a rejected candidate with seed || k_le32 (attempt_words); epoch 34's attempt 0 was
rejected (246 of 16384 final register values saturated, limit 163) and attempt 1 accepted, so the pack
carried attempt 1's words while pf_load (proto-cuda/nvrtc/packfile.h) derived the expected words from
the bare seed. Both workers also matched jobs to pairs by those bare-seed words, so even a loaded pack
of a retried program would have answered "epoch seed mismatch" on every job.

The rule, in one place per language:
- packfile.h: pf_program_words(bytes, attempt); pf_load reads IGNEUM_PROGRAM_ATTEMPT and checks the
  attempt's words; the refusal says "program pack and its seeds disagree: IGNEUM_SEEDW_INIT is not
  attempt N of the epoch seed ..." in plain words.
- worker.cpp and proto-opencl/host.c: a job belongs to a pair when the seed hex the node sent is the
  pair's (pairIs); the compiled-in placeholder pack keeps the word comparison.
- igneum-pow/src/packcheck.rs: verify_pack_texts / verify_pack_dir, the same rule in Rust; the miner
  checks every pack it writes with it before a worker sees it (vendor/igneum-node pack-loop branch).
  Tests pin the attempt vectors of epoch 34 on both sides (one vector, two implementations), that
  epoch 34 is attempt 1 and epoch 33 attempt 0, a known-good pack of a later attempt, a known-mismatched
  (out of date) pack, and self-contradicting packs.
- proto-cuda/nvrtc/emu/packfile-test.c (+ .sh, in CI): pf_load on a known-good attempt-1 pack, the
  checked-in attempt-0 pack, and the known-mismatched bare-words pack.

The app (app/igneum-app):
- watchdog.rs: PACK_OUT_OF_DATE_CODE 44, PackRebuilds (at most 3 pack exports per epoch, then the card
  shows the reason), pack_refusal (the worker's "error 0 pack" line and the miner's "PACK OUT OF DATE"
  line), pack_epoch_of; tests on the incident lines, known-good and known-mismatched.
- engine.rs: exit 44 exports the pack again before the restart instead of a blind restart, the strip
  says "program pack out of date, rebuilding", the card and the log name the condition; at the cap the
  card is marked failed with the reason and tries again in 10 minutes.

The relay (relay/lib/parse.mjs): PACK_MISMATCH; the card reads "pack mismatch, rebuilding (N refusals
in the tail, M restarts)" in `node tools/console.mjs machines` instead of a bare restart count; tests
on the PC 2 tail of 18:27Z and a healthy tail.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 19:10:07 +00:00

115 lines
7 KiB
C

// packfile-test.c: the pack loader's seed rule (packfile.h pf_load) on a known-good and a known-mismatched pack.
// C99, no GPU, no NVRTC: `emu/packfile-test.sh` compiles and runs it on the Mac in a second, and CI runs it too.
//
// 5 October 2026, epoch 34 on both PCs: pf_load derived the expected IGNEUM_SEEDW_INIT from the bare epoch seed, but
// a pack carries the words of its program's ATTEMPT (igneum-pow attempt_words: seed || k_le32 for k > 0), so every
// pack of a retried program was refused and the workers restarted for an hour. These cases pin the rule:
// 1. the attempt vectors of epoch 34's seed, the same constants igneum-pow/src/packcheck.rs pins (one vector, two
// implementations: a change on either side fails here or there first);
// 2. known-good: a pack of attempt 1 with seeds.txt loads, and so does the checked-in attempt-0 pack;
// 3. known-mismatched: the same pack with the bare seed's words under IGNEUM_PROGRAM_ATTEMPT 1 (what the old
// rule expected) is refused in plain words; a seeds.txt of another epoch is refused as a disagreement.
#include "../packfile.h"
#include <sys/stat.h>
#include <unistd.h>
#define EPOCH_34 "009858237e118f69abc8d096e9b1af21c24539eaecdfd1b896588825660a69ec"
#define EPOCH_33 "bed7ab62cbece66cf791485336d81d90fa1452ffed28ecd8a7416960ef64164c"
#define DAY_20731 "69676e65756d2d6461792ffb50000000000000"
static int failures = 0;
#define CHECK(cond, what) do { if (cond) printf("ok %s\n", what); else { printf("FAIL %s (%s:%d)\n", what, __FILE__, __LINE__); failures++; } } while (0)
static void write_file(const char* dir, const char* name, const char* text) {
char path[1024];
FILE* f;
snprintf(path, sizeof(path), "%s/%s", dir, name);
f = fopen(path, "wb");
if (!f) { perror(path); exit(2); }
fputs(text, f);
fclose(f);
}
static void words_hex(const uint32_t w[8], char* out) {
int i;
out[0] = 0;
for (i = 0; i < 8; ++i) sprintf(out + strlen(out), "%s0x%08x", i ? ", " : "", w[i]);
}
// A program.h with only what pf_load reads, for the given seeds, attempt and init words.
static void write_program_h(const char* dir, const char* epochHex, const char* dayHex, uint32_t attempt, const uint32_t seedw[8], const uint32_t keyw[8]) {
char sw[200], kw[200], text[2000];
words_hex(seedw, sw); words_hex(keyw, kw);
snprintf(text, sizeof(text),
"// test pack\n#pragma once\n#define IGNEUM_SEED_STRING \"test\"\n#define IGNEUM_SEED_BYTES_HEX \"%s\"\n#define IGNEUM_GENERATOR 2\n"
"#define IGNEUM_PROGRAM_ATTEMPT %u\n#define IGNEUM_DAY_BYTES_HEX \"%s\"\n#define IGNEUM_DATASET_LOG2 28\n#define IGNEUM_DATASET_MODE 1\n"
"#define IGNEUM_SEEDW_INIT { %s }\n#define IGNEUM_KEY_INIT { %s }\n#define IGNEUM_CACHE_LOG2_WORDS 26\n#define IGNEUM_CACHE_SEGMENTS 4096u\n",
epochHex, (unsigned)attempt, dayHex, sw, kw);
write_file(dir, "program.h", text);
}
int main(int argc, char** argv) {
const char* checked_in = argc > 1 ? argv[1] : NULL; // proto-cuda/packs/igneum-devnet-v4-epoch0 (attempt 0, no seeds.txt)
char dir[512];
uint8_t e34[32], day[64];
size_t n = 0, dn = 0;
uint32_t bare[8], att1[8], keyw[8];
static const uint32_t want_bare[8] = { 0x06af2a61, 0x4d67274e, 0x4ebda738, 0xad1dea73, 0x6233cd8c, 0x50371601, 0x39d0b873, 0x6af024a2 };
static const uint32_t want_att1[8] = { 0x0dcff56b, 0x6b1beb0d, 0x234dc70c, 0xe4016fa9, 0x72397152, 0xb558aa79, 0x3ffb3299, 0x72b9962e };
PfPack pk;
char err[512];
pf_unhex(EPOCH_34, e34, 32, &n);
pf_unhex(DAY_20731, day, sizeof(day), &dn);
CHECK(n == 32 && dn == 19, "the fixture seeds unhex (32 epoch bytes, 19 day bytes)");
// 1. the attempt vectors shared with igneum-pow
pf_program_words(e34, 32, 0, bare);
pf_program_words(e34, 32, 1, att1);
pf_seed_words_from_bytes(day, dn, keyw);
CHECK(memcmp(bare, want_bare, 32) == 0, "attempt 0 of epoch 34 = the bare seed words (06af2a61 4d67274e ...)");
CHECK(memcmp(att1, want_att1, 32) == 0, "attempt 1 of epoch 34 = words of seed || 01000000 (0dcff56b 6b1beb0d ...)");
CHECK(memcmp(bare, att1, 32) != 0, "the two attempts differ");
// 2. known-good: a pack of attempt 1 with seeds.txt, as igneum-miner writes it
snprintf(dir, sizeof(dir), "%s/igneum-packfile-test-%d", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp", (int)getpid());
mkdir(dir, 0755);
write_program_h(dir, EPOCH_34, DAY_20731, 1, att1, keyw);
write_file(dir, "seeds.txt", "epoch_seed_hex " EPOCH_34 "\nday_seed_hex " DAY_20731 "\nday_index 20731\n");
err[0] = 0;
CHECK(pf_load(dir, &pk, err, sizeof(err)) == 1, "known-good: the attempt-1 pack loads");
if (err[0]) printf(" (%s)\n", err);
CHECK(pk.attempt == 1 && memcmp(pk.seedw, att1, 32) == 0 && strcmp(pk.epochHex, EPOCH_34) == 0, "known-good: attempt, words and epoch hex read back");
if (checked_in) {
err[0] = 0;
CHECK(pf_load(checked_in, &pk, err, sizeof(err)) == 1, "known-good: the checked-in attempt-0 pack loads from program.h's own bytes");
if (err[0]) printf(" (%s)\n", err);
CHECK(pk.attempt == 0, "the checked-in pack is attempt 0");
}
// 3. known-mismatched: the bare words under attempt 1 (the old rule's expectation) are refused, in plain words
write_program_h(dir, EPOCH_34, DAY_20731, 1, bare, keyw);
err[0] = 0;
CHECK(pf_load(dir, &pk, err, sizeof(err)) == 0, "known-mismatched: bare words under attempt 1 are refused");
CHECK(strstr(err, "program pack and its seeds disagree: IGNEUM_SEEDW_INIT is not attempt 1 of the epoch seed 009858237e118f69") == err, "the refusal names the attempt and the epoch in plain words");
printf(" (%s)\n", err);
// and a seeds.txt of another epoch against this program.h is a disagreement, not a load
write_program_h(dir, EPOCH_34, DAY_20731, 1, att1, keyw);
write_file(dir, "seeds.txt", "epoch_seed_hex " EPOCH_33 "\nday_seed_hex " DAY_20731 "\n");
err[0] = 0;
CHECK(pf_load(dir, &pk, err, sizeof(err)) == 0, "known-mismatched: seeds.txt of epoch 33 with program.h of epoch 34 is refused");
CHECK(strstr(err, "seeds.txt epoch_seed_hex differs from program.h") != NULL, "the refusal says the files disagree");
// a pack with no IGNEUM_PROGRAM_ATTEMPT line is attempt 0 (packs before generator version 2)
{
char text[2000], sw[200], kw[200];
words_hex(bare, sw); words_hex(keyw, kw);
snprintf(text, sizeof(text), "#define IGNEUM_SEED_BYTES_HEX \"%s\"\n#define IGNEUM_DAY_BYTES_HEX \"%s\"\n#define IGNEUM_DATASET_LOG2 28\n#define IGNEUM_DATASET_MODE 1\n#define IGNEUM_SEEDW_INIT { %s }\n#define IGNEUM_KEY_INIT { %s }\n#define IGNEUM_CACHE_LOG2_WORDS 26\n#define IGNEUM_CACHE_SEGMENTS 4096u\n", EPOCH_34, DAY_20731, sw, kw);
write_file(dir, "program.h", text);
write_file(dir, "seeds.txt", "epoch_seed_hex " EPOCH_34 "\nday_seed_hex " DAY_20731 "\n");
err[0] = 0;
CHECK(pf_load(dir, &pk, err, sizeof(err)) == 1 && pk.attempt == 0, "no attempt line reads as attempt 0 and the bare words load");
}
printf("%s: %d failure(s)\n", argv[0], failures);
return failures ? 1 : 0;
}