jcp: read back the five published vector files nothing was reading #98

Open
dcharlot wants to merge 1 commit from ci/jcp-pack into main
Owner

JCP publishes seven conformance vector files. Two were read by a test. The
other five were read by nothing at all.

jcp-conformance's main() — the binary CI runs — checks the implementation
against in-code expectations and never opens the pack directory. So
vectors.json, dual_ceilings.json, standing.json, meta.json and
legacy_jcp1.json could drift from the implementation with only the generator
any the wiser. That is how joulehook and jouledesk ended up shipping stale
JCR-1 envelopes after the receipt payload gained a field.

file before now
action_receipt.json ✅ read by a test unchanged
action_receipt_meta.json ✅ read by a test unchanged
vectors.json ✗ generator only grant + both receipts re-derived
dual_ceilings.json ✗ generator only grant + 3 receipts + spend re-decided
standing.json ✗ generator only 6 ids/signatures re-derived
meta.json ✗ generator only key must verify the pack beside it
legacy_jcp1.json ✗ nothing named it both receipts verified

Nothing was stale — all five reproduce exactly. This closes the gap before
it costs something, rather than after.

legacy_jcp1.json is the interesting one

Those envelopes were sealed under the superseded jcp/1 profile before
payload_hash existed. The current implementation cannot re-mint them and
should not be able to, so they are verified instead — which is the actual
guarantee the file exists to defend: if they stop verifying, the jcp/2 bump
silently invalidated every receipt already in the wild.

This was only partly covered. jcp-receipt's published_v1_receipts_still_verify
checks the allow receipt from a hex constant pasted into the test body. The
published file carries two. So the deny receipt was published and never
verified, and nothing tied the in-code copy to the file a third party
downloads. They are byte-identical today (checked); now the file is what is
checked, and both receipts are.

An orphaned doc comment, fixed

The paragraph beginning "The migration guarantee" sat between two tests
with no item of its own, so Rust merged it into the doc of
an_absent_environment_leaves_the_bytes_untouched — a test about something
else entirely. It read as documentation for a test that had been deleted, which
is what sent me looking. It now documents published_v1_receipts_still_verify,
and names legacy_jcp1.json rather than vectors.json, where those bytes
actually live.

Falsified

change result
corrupt a vectors.json envelope ✅ fails
corrupt a standing.json signature ✅ fails
corrupt a legacy_jcp1.json envelope ✅ fails
corrupt meta.json's pubkey ✅ fails
change spent_after.minor to 301 ✅ fails
neuter the tamper flip to ^= 0x00 ✅ fails
disable the foreign-currency refusal in jcp-runtime ✅ fails

The last is the one that matters: inventing an exchange rate breaks the
dual-ceiling test, so these bind to what the implementation does, not merely
to file-against-file agreement. The pack was restored afterwards and is
byte-identical to main.

Two pre-existing issues found, not touched here

Both confirmed on a branch that does not touch jcp, so neither is from this
change, and neither is reached by CI:

  1. jcp-energy's cumulative_advances_after_cpu_burn fails on this Mac —
    before=0 after=0, consistently across runs. IoReport::open() succeeds but
    the SoC Energy Model counter never advances, so Meter::detect() selects a
    backend that measures nothing in preference to the time×power rung that would
    at least produce a number. It reports Provenance::ModelBased, not HwShunt,
    so this is not a false hardware claim — the crate is honest about the tier.
    CI cannot see it: the test is cfg(target_os = "macos") and jcp-rs runs on
    the linux runner, so the macOS energy backend is tested nowhere automated.
  2. jcp-schema does not pass clippy -D warnings (infallible TryFrom at
    flow.rs:121). There is no clippy step in CI at all.

🤖 Generated with Claude Code

JCP publishes **seven** conformance vector files. Two were read by a test. The other five were read by nothing at all. `jcp-conformance`'s `main()` — the binary CI runs — checks the implementation against in-code expectations and never opens the pack directory. So `vectors.json`, `dual_ceilings.json`, `standing.json`, `meta.json` and `legacy_jcp1.json` could drift from the implementation with only the generator any the wiser. That is how `joulehook` and `jouledesk` ended up shipping stale JCR-1 envelopes after the receipt payload gained a field. | file | before | now | |---|---|---| | `action_receipt.json` | ✅ read by a test | unchanged | | `action_receipt_meta.json` | ✅ read by a test | unchanged | | `vectors.json` | ✗ generator only | grant + both receipts re-derived | | `dual_ceilings.json` | ✗ generator only | grant + 3 receipts + spend re-decided | | `standing.json` | ✗ generator only | 6 ids/signatures re-derived | | `meta.json` | ✗ generator only | key must verify the pack beside it | | `legacy_jcp1.json` | ✗ **nothing named it** | both receipts verified | **Nothing was stale** — all five reproduce exactly. This closes the gap before it costs something, rather than after. ### `legacy_jcp1.json` is the interesting one Those envelopes were sealed under the superseded `jcp/1` profile before `payload_hash` existed. The current implementation cannot re-mint them and should not be able to, so they are **verified** instead — which is the actual guarantee the file exists to defend: if they stop verifying, the `jcp/2` bump silently invalidated every receipt already in the wild. This was only partly covered. `jcp-receipt`'s `published_v1_receipts_still_verify` checks the **allow** receipt from a hex constant pasted into the test body. The published file carries **two**. So the deny receipt was published and never verified, and nothing tied the in-code copy to the file a third party downloads. They are byte-identical today (checked); now the *file* is what is checked, and both receipts are. ### An orphaned doc comment, fixed The paragraph beginning "**The migration guarantee**" sat between two tests with no item of its own, so Rust merged it into the doc of `an_absent_environment_leaves_the_bytes_untouched` — a test about something else entirely. It read as documentation for a test that had been deleted, which is what sent me looking. It now documents `published_v1_receipts_still_verify`, and names `legacy_jcp1.json` rather than `vectors.json`, where those bytes actually live. ### Falsified | change | result | |---|---| | corrupt a `vectors.json` envelope | ✅ fails | | corrupt a `standing.json` signature | ✅ fails | | corrupt a `legacy_jcp1.json` envelope | ✅ fails | | corrupt `meta.json`'s pubkey | ✅ fails | | change `spent_after.minor` to 301 | ✅ fails | | neuter the tamper flip to `^= 0x00` | ✅ fails | | **disable the foreign-currency refusal in `jcp-runtime`** | ✅ **fails** | The last is the one that matters: inventing an exchange rate breaks the dual-ceiling test, so these bind to what the implementation *does*, not merely to file-against-file agreement. The pack was restored afterwards and is byte-identical to main. ### Two pre-existing issues found, not touched here Both confirmed on a branch that does not touch jcp, so neither is from this change, and neither is reached by CI: 1. **`jcp-energy`'s `cumulative_advances_after_cpu_burn` fails on this Mac** — `before=0 after=0`, consistently across runs. `IoReport::open()` succeeds but the SoC Energy Model counter never advances, so `Meter::detect()` selects a backend that measures nothing in preference to the time×power rung that would at least produce a number. It reports `Provenance::ModelBased`, not `HwShunt`, so this is **not** a false hardware claim — the crate is honest about the tier. CI cannot see it: the test is `cfg(target_os = "macos")` and `jcp-rs` runs on the **linux** runner, so the macOS energy backend is tested nowhere automated. 2. **`jcp-schema` does not pass `clippy -D warnings`** (infallible `TryFrom` at `flow.rs:121`). There is no clippy step in CI at all. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jcp: read back the five published vector files nothing was reading
All checks were successful
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 1m28s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m50s
standards workspaces build + test / aac-rs (pull_request) Successful in 33s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m58s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 33s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 31s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m17s
standards workspaces build + test / map-rs (pull_request) Successful in 30s
standards workspaces build + test / mesh-rs (pull_request) Successful in 24s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m19s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 2m1s
standards workspaces build + test / openpay (pull_request) Successful in 1m14s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 2m4s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m9s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m59s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 2m29s
standards workspaces build + test / sense-rs (pull_request) Successful in 2m9s
8c0751bc89
JCP publishes seven conformance vector files. Two were read by a test
(`action_receipt.json` and `action_receipt_meta.json`, from
`jcp-action-receipt/tests/conformance.rs`). The other five were read by
nothing: `vectors.json`, `dual_ceilings.json`, `standing.json`, `meta.json`
and `legacy_jcp1.json`.

`jcp-conformance`'s `main()` — the binary CI runs — checks the implementation
against in-code expectations and never opens the pack directory, so those five
could drift from the implementation with only the generator any the wiser.
That is how joulehook and jouledesk ended up shipping stale JCR-1 envelopes
after the receipt payload gained a field.

Everything is recomputed, not re-read: the scenarios are rebuilt from the same
fixed keys and nonces `gen-vectors` uses, and the derived ids, signatures,
wires and sealed envelopes are compared to the published bytes. Nothing was
stale — all five reproduce exactly.

`legacy_jcp1.json` is the one that could not be re-derived, and should not be.
Those envelopes were sealed under the superseded `jcp/1` profile before
`payload_hash` existed, so they are *verified* instead, which is the guarantee
the file exists to defend. This was only partly covered before:
`published_v1_receipts_still_verify` checks the **allow** receipt from a hex
constant pasted into the test body, while the file carries **two**. The deny
receipt was published and never verified, and nothing tied the in-code copy to
the file a third party downloads. They are byte-identical today; now the file
is what is checked, and both receipts are.

Also moves an orphaned doc comment in `jcp-receipt`. The paragraph beginning
"**The migration guarantee**" sat between two tests with no item of its own, so
Rust merged it into the doc of `an_absent_environment_leaves_the_bytes_untouched`
— a test about a different thing entirely. It now documents
`published_v1_receipts_still_verify`, where it belongs, and names
`legacy_jcp1.json` rather than `vectors.json`, where those bytes actually live.
Reading it as a description of a missing test is what sent me looking; it
should not send the next person the same way.

Falsified, each assertion separately: corrupting a published envelope,
signature, pubkey or spend figure fails its test, and neutering the tamper flip
fails the tamper test. The one that matters most is behavioural rather than
byte-level — disabling the foreign-currency refusal in `jcp-runtime` (i.e.
inventing an exchange rate) fails the dual-ceiling test, so these bind to what
the implementation does and not merely to file-against-file agreement. The pack
was restored afterwards and is byte-identical to main.

Unrelated and pre-existing, confirmed on a branch that does not touch jcp:
`jcp-energy`'s `cumulative_advances_after_cpu_burn` fails on this Mac
(`before=0 after=0`), and `jcp-schema` does not pass `clippy -D warnings`.
Neither is reached by CI — the first is `cfg(target_os = "macos")` while
`jcp-rs` runs on the linux runner, and there is no clippy step.

Co-Authored-By: Claude Opus 5 <[email protected]>
All checks were successful
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 1m28s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m50s
standards workspaces build + test / aac-rs (pull_request) Successful in 33s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m58s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 33s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 31s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m17s
standards workspaces build + test / map-rs (pull_request) Successful in 30s
standards workspaces build + test / mesh-rs (pull_request) Successful in 24s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m19s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 2m1s
standards workspaces build + test / openpay (pull_request) Successful in 1m14s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 2m4s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m9s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m59s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 2m29s
standards workspaces build + test / sense-rs (pull_request) Successful in 2m9s
This pull request has changes conflicting with the target branch.
  • jcp/jcp-rs/crates/jcp-conformance/tests/pack.rs
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin ci/jcp-pack:ci/jcp-pack
git switch ci/jcp-pack

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff ci/jcp-pack
git switch ci/jcp-pack
git rebase main
git switch main
git merge --ff-only ci/jcp-pack
git switch ci/jcp-pack
git rebase main
git switch main
git merge --no-ff ci/jcp-pack
git switch main
git merge --squash ci/jcp-pack
git switch main
git merge --ff-only ci/jcp-pack
git switch main
git merge ci/jcp-pack
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!98
No description provided.