Repository navigation
refactor: the unreached receipt verifier is deleted; its checks run on production's decisions - #1073
Merged
cryptskii merged 3 commits intoSep 30, 2026
Conversation
…n production's decisions `verify_stitched_receipt` and its `ParentConsumptionTracker` had no production caller. Production decides a bilateral step with `bilateral::offline` (decide_prepare, decide_confirm, decide_commit_ack), and a second child of one tip is refused because the receiver holds the tip it committed. The verifier's only users were tests and the vertical- validation tool, so the tool's attack suite, fork-exclusion property, Tripwire traces and TLA replays reported on code production never runs. Owner ruling (2026-09-30): port the checks onto the production path, then delete. Ported (tools/vertical_validation): - A step is production's offline step. The sender proposes on the shared tip it holds, signs the step commitment, and signs its receipt with a per-step EK its chain head certifies. The receiver decides with decide_prepare and decide_confirm against its held tip and the keys its contact pins, including a real Kyber identity binding. Both commit. - Attacks: double spend, replay and a receiver behind are refused as StaleTip; wrong-key, garbage and uncertified-EK signatures and forged post-states are refused by the decisions. - The fork-exclusion property and the receiver_tripwire, first-contact and fork-divergence traces run on the same step. - The TLA DSM replay delivers each step through the decisions. The Tripwire replay maps the model's tips onto the shared tips real devices hold. Deleted (dsm): verify_stitched_receipt and its signature helper, ReceiptVerificationContext, ReceiptAcceptance and ParentConsumptionTracker, with the tests that exercised only them. theorem2_two_successors_same_ parent_rejected now asserts the refusal through decide_prepare. Evidence: - adversarial 6/6, property-tests 6/6, implementation-traces 15/15, tla-check 91 specs; Tripwire and DSM trace replays literal and direct PASS. - dsm and dsm_vertical_validation: 1,630 passed, 0 failed (release). - Mutation: disabling decide_prepare's stale-tip arm turns theorem2 and a_stale_proposal_says_whether_its_claimed_tip_recomputes red, and fails the double-spend, replay and receiver-behind attacks. Restored. Records: CONFORMANCE §6.45 (and the three earlier findings marked resolved), MR-DSM-0092's note, and the verification matrix. The matrix row for the verifier's rule 2c is deleted; that rule has no production counterpart. The row for a second child now names decide_prepare.
…ched-stitched-receipt-verifier # Conflicts: # specs/requirements/CONFORMANCE_GAPS.md
This was referenced Sep 30, 2026
cryptskii
added a commit
that referenced
this pull request
Sep 30, 2026
#1073 merged with 64 pins stale, so main's Code map fails at 7cee0be (run 36674764146): 525 PINNED, 64 PIN_STALE. Every move is code only: the closures of the rows' evidence tests (and, for some, their production code) changed with the receipt verifier's deletion. Each key is repinned with a plain `ci/intent_pins.py repin`, which accepts code alone, at 7cee0be against CI's code map of that commit; its tree fingerprint equals this tree's. Their evidence, 58 tests, passed here in release: dsm_sdk lib 25/0, dsm lib 28/0, and the economic_lineage_register, economic_write_set, native_reserve_wire and economic_admission_lifecycle suites. Those tests ran on a Postgres server of their own. On the shared local server, another session's dsm_sdk tests were running at the same time. The SDK's node harness recreates fixed database names (dsm_sdk_test_node_0..4) with DROP DATABASE ... WITH (FORCE), so each process dropped the other's node databases mid-test, and a different node-backed test failed on each shared run. Each passed alone. The comparator reads 589 of 589 pins PINNED and 0 failing rows. Only INTENT_PINS.tsv changes.
cryptskii
added a commit
that referenced
this pull request
Sep 30, 2026
* fix(online): a receipt binds only when its state rules hold The online receiver bound a sender's receipt to its transfer on the child tip alone. It checked the signatures and that the child tip is the operation's successor, but never that the receipt's writes fold from its parent root to its child root. The BLE receiver checks this, and so does every producer before it signs. MR-DSM-0092's online order has it as "recompute hashes and roots". Probe (2026-09-30, on main after #1073, run on isolated node databases): a sender whose receipt claimed a child root one bit off what its writes produce was credited by the receiver. Binding now runs the one production verifier, verify_receipt_state, against the Device Tree root and genesis this device pinned for the sender: - holds: the pair binds, as before; - fails: the receipt is refused. It never binds, nothing is credited, and nothing negative is recorded; the sync reports it; - the sender's root is not pinned yet: the pair waits, staged and unbound, and binds on a later copy. Tests: - a_signed_receipt_whose_state_writes_fail_binds_nothing: three forgeries re-signed by the sender's real per-step EK, every signature valid. A child root the writes do not fold to, a write whose path is not its leaf's, and a write the operation does not imply are each refused, and the honest receipt still binds and credits once. - a_receipt_waits_while_its_senders_device_tree_root_is_not_pinned. Mutation controls: a failing state check that binds anyway, and an unpinned root that binds, each turned its test red. * test(online): the new tests' assertion messages are fixed text CodeQL flagged rust/cleartext-logging (alerts #733, #734) on the two assertion messages in a_signed_receipt_whose_state_writes_fail_binds_nothing and a_receipt_waits_while_its_senders_device_tree_root_is_not_pinned. They formatted `out.unbound`, whose reason text carries the sender's Base32 id, and a count of it still counted as tainted: the taint starts at load_cert_chain_head_pubkey, read while the receipt is recognized. Both messages are now fixed text. The assertions (`matches!` on `[Unbound::Refused(_)]` and `[Unbound::Pending(_)]`) are unchanged, and both tests pass. * chore(pins): repin the 64 rows #1078's change moves Code-class only: rows whose closures reach recipient_dispatch.rs, storage_routes.rs and core_sdk.rs, including #1079's six route_seats rows for MR-DSM-0034 and 0083. Taken from CI's code map of 9db43e0, whose tree fingerprint equals this tree's; their 56 evidence tests ran and passed at that commit. make requirement-map-intent against that map reads 1,111 rows, 0 failing, and 598 pins, 0 failing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Owner ruling (2026-09-30): the unreached receipt verifier goes. Its checks are ported onto the production path first, then it is deleted.
Why
verification::receipt_verification::verify_stitched_receiptand itsParentConsumptionTrackerhad no production caller. That is recorded in CONFORMANCE §6.3 and §6.11, and in #1068's §6.44. Production decides a bilateral step withbilateral::offline:decide_prepareanddecide_confirmon the receiver's side,decide_commit_ackon the sender's;The verifier's only users were tests and
tools/vertical_validation. So CI's attack suite, fork-exclusion property, Tripwire traces and TLA replays were reporting on code production never runs.What changes
The validation harness runs production's step (
tools/vertical_validation/src/live_device.rs):initial_chain_tip_from_device_ids, then each committed successor) and signs the step commitment (σ_A);decide_prepareanddecide_confirm, against its held tip and the keys its contact pins, including a real Kyber identity binding;Every judge is ported onto it.
decide_prepare→StaleTipStaleTipStaleTipdecide_prepare: "is not signed over its commitment by the pinned AK"decide_confirm: "does NOT chain"decide_confirm: "do not fold"fork_exclusionproperty;receiver_tripwire,tripwire_first_contact_binding,state_machine_fork_divergencetracesStaleTipnet_deliver)Deleted from
dsm:verify_stitched_receiptand its signature helper;ReceiptVerificationContext,ReceiptAcceptanceandParentConsumptionTracker;tripwire_parent_consumptiontrace.dsm/tests/smt_tripwire_theorem.rs::theorem2_two_successors_same_parent_rejectednow asserts the refusal throughdecide_prepare:StaleTiponce h₁ is held,Considerwhile h₀ is held.Evidence
vertical-validation adversarial: 6/6;property-tests --iterations 5 --seed 42: 6/6;implementation-traces: 15/15;tla-check: all 91 specs. The Tripwire, DSM_tiny, DSM_small and DSM_system trace replays pass literal and direct.cargo test -p dsm -p dsm_vertical_validation --release: 1,630 passed, 0 failed.decide_prepare's stale-tip arm disabled turnstheorem2_two_successors_same_parent_rejectedanda_stale_proposal_says_whether_its_claimed_tip_recomputesred, and fails the double-spend, replay and receiver-behind attacks. Restored.cargo fmt --check,clippy --all-targets -D warnings(1.98.0),scripts/real_code_guard.py,ci/conformance_evidence.py: clean.Records
decide_prepare, with the mutation result above.Not driven by the tool
A forged countersignature (σ_B) is judged by the sender's
decide_commit_ack. Core'sdsm::bilateral::offline::tests::an_ack_is_only_the_receivers_counter_signed_receiptrefuses it.