tools/txgen/run.mjs funds generated wallets from the devnet dev-fee key and sends transfers at a steady rate through
one node (nonce tracking from the pool's pending nonce, two-strike drop verdict, pool back-pressure counted as
deferred, spend cap, clean stop, summary JSON); tools/txgen/proving-watch.mjs watches the proving layer and builds the
per-block report. Two runs through the Mac node: 2,275 sent at 2/s, 2,161 included at 1.86/s (run 1), 1,650 sent,
1,633 included at 1.71/s with no failure (run 2, fixed code). Block 72704 shard 0 (29 transfers, 5,800 pgas) proven on
PC 2 in 34 s, verified on the Mac in 0.297 s, paid 1.7623 IGN.
Block 72803 (seven skipped copies, no executed transaction) failed the native-execution veto: the exporter rebuilt the
including blocks from an export that names no block, position or skipped copy's miner, sorting skipped copies out
of their block, merging consecutive blocks of one miner, dropping empty blocks (the node counts them in the link's
block index) and guessing the zero address. blocks_of now rebuilds from the 0.3.9 export's "blocks", "block" and
"position" fields (the fork change on vendor/igneum-node-txgen branch txgen-export), keeps an old export in its
order and refuses a skipped-only block without a miner. Fixtures block-72803-skipped-copies and
block-72854-empty-block-first with the node's shard plan beside each; the fixture test now checks the cut's links,
roots, gas, pgas and counts against the node's plan (shown failing on the old 72854 cut). No change under core/.
Bench-log entry and evidence rows 15 and 21.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The 13:19:41Z refusal on PC 2: fetch_jobs took igneum-jobs.json and .sig in two requests while the edge was
still serving the previous deployment for one of them. The signer wraps the verified pair into one object and
reads it back; the app fetches that object (the pair only when none is published); publish-jobs.sh writes and
mirrors all three files and verifies every folder after the deploy; tools/jobs.mjs reads the envelope.
Tests: jobs.rs signed_envelope_binds_file_and_signature, packaging/ota/test-publish-jobs.sh (24 checks).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
logs.mjs --rotation reads the old fingerprints from the dated .old-* files once the .next files are gone; fresh-repo.sh scrubs the dated secret files too; ship-app.mjs mirrors payload-inputs.json.sig.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On 5 October 2026 the Mac's host (shard program id 0x0559759b...) rejected every
proof from PC 2's host (0x05db1aca...). Both were built from the same guest
sources: host/build.rs compiled the guests on each machine and the ELF depends
on where it is built (cargo's -C metadata for a path crate includes the checkout
path; a worktree on the same Mac gave a third id, 0x0dfade07...). The node's
verifier also spent 114 s to 138 s per proof in the prover client and both key
setups before a 0.1 s to 0.4 s verify.
- elf/: both guest ELFs, their verifying keys and manifest.json (sha256, ids);
host/src/pinned.rs embeds and checks them at every start; the prove modes
refuse when SP1's setup does not derive the manifest's id
- --mode verify: LightProver with the pinned key, no prover client, no key
setup; prints the proof's own program id next to ours ("IS NOT OURS")
- --mode id; igneum-prove-pin and pin-guests.sh to re-pin; build.rs builds a
guest only under IGNEUM_BUILD_GUESTS=1
- tools/ci/pinned-guests-check.sh: elf/ must match its manifest, no script
builds a guest outside pin-guests.sh; make-package.sh and build-dmg.sh print
the pinned ids
- unit tests on the pinned set; bench-log entry with the three ids, the cause
and the timing: 127.0 s wall per verify before, 1.8 s to 2.4 s after
- rollout order in proving/README.md: every prover and verifier moves together
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The fact: PC 2's exporter failed blocks 58,752 to 58,843 (and the task's 58,584 to 58,984) at
shard.rs:175, "shard 0 post-root from the witness", while three empty shards were later proven, verified and
paid. Every failing block is empty: one blue block, one reward, the pool credit, no transactions, no payouts
(igneum_exportSegments 0x0..0xe738 from the Mac node, fork 2b6d23ef). The proven block 59,507 has the same
shape and the same miner as the failing 58,927. So the difference is not block content.
The rule that differed: the planner's post-root of an empty segment. The exporter on PC 2 was a stale build
whose core predated commit 1251f0a (the assertion sits at line 175 there and at 179 since). That planner
returned root_at(end) for a segment with no transactions, which is the pre-root; the statement applied the
rewards and the pool credit, as the node does (vendor/igneum-node-036/igneum/exec/src/executor.rs,
execute_segment) and as spec 7.7 item 8 says. Left = the root after the rewards (the node's), right = the root
before them; PC 2's export log for 58,752 shows exactly that pair. Reproduced here: master's core with that one
rule put back fires the same assertion on 58,927 with left 0x7886b9cf (the node's root) and right 0xea9db302
(the pre-root). Master's core as it is reproduces 58,927 and 59,192 with the node's roots, the host's native
mode matches the fixture, and the SP1 executor runs shard 0 to post-root 0x7886b9cf.
So the prover core needs no rule change: the fix is commit 1251f0a, which PC 2 received with the 10:49 and
10:52 UTC rebuilds (job-rebuild-prover-pc2-037 and 037b), after which its proofs were paid. What this commit
adds is the regression and the guard for the class:
- proving/fixtures/block-58927-empty-reward.json: the failing shape cut from the devnet (33 KB).
- proving/igneum-prove/export/tests/fixtures.rs: every fixture in proving/fixtures reproduces (block
statement, plan, every shard statement from its witness, the chain of roots and links), and the empty
segment's shard ends at the root after the rewards, never the pre-root. With the pre-1251f0a rule put back
the test fails. The test lives in the export crate so the core's manifest, part of the guest build, stays
untouched.
- export/build.rs and host/build.rs stamp each binary with a hash of the native sources it was built from,
printed on the first line of every run, so a stale build names itself in the log instead of in a line
number (the stale-build class of 4 and 5 October).
- docs/bench-log.md: the row under the first paid proofs.
The guest is unchanged: built in one directory, this branch and master give byte-identical loadable segments
for the shard program and the aggregator, so no prover needs a rebuild for this commit. Noted on the way and
left open: the same sources built in three directories on this Mac gave two different guest ELFs, so the
program id is not yet a pure function of the sources on a native build.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
publish-jobs.sh --deploy POSTs the new stamp (published_at plus 8 hex of the file's sha256) and the added id to the
relay's /wake once the live file verifies. The relay token goes in a 600-mode header file, never on the command line
or the screen. Prints "woke the apps (stamp ...)" or a one-line warning; the apps' 2-minute poll still catches it.
tools/jobs.mjs status reads relay_wake (one row per publish with the ids it added) and prints "woken +N s after the
publish" for a machine's latest job that a publish added; nothing when the table does not exist yet.
docs/plans/release-0.3.6.md: "Instant jobs" section with the design, the expected latency and a TODO row per machine
for the measured number once 0.3.6 is live. packaging/ota/README.md: the 10-minute poll is history.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>