Skip to content

fix(core): receiver policy check before any read, smt-key tag ruling (A9), and the DSM core row sweep - #1083

Merged
cryptskii merged 14 commits into
mainfrom
fix/dsm-core-beta-gaps
Oct 1, 2026
Merged

cryptskii merged 14 commits into
mainfrom
fix/dsm-core-beta-gaps

Conversation

@cryptskii

@cryptskii cryptskii commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

DSM core batch for beta: one fix, three tests, an owner ruling on the relationship-key tag, and seventeen conformance rows re-verified. CONFORMANCE §6.51 records it.

Fix

The receiver checks the token's policy before it reads the sender's register (MR-DSM-0029, G13).

  • Before: the sync prevalidated each bound pair first, walking the sender's lineage over the network. Only then did the apply check the token's committed policy, which the receiver already holds.
  • Now: the sync checks the policy first. The apply keeps its own check, under the state-machine lock.

Tests

  • a_transfer_its_policy_refuses_is_refused_before_the_senders_register_is_read
    • A hostile sender signs a transfer of its non-transferable token, advances its own head over it, and signs the step's receipt with its per-step EK.
    • Both halves bind at B, B's sync refuses by the policy, and no member is asked for a cell.
    • Mutation (check removed): red. B asks dsm-node-1 for a register cell of the sender.
  • a_non_transferable_token_refuses_its_transfer now also drives B's canonical apply with a transfer A signed.
    • Mutation (policy check skipped for transfers to this device): red.
  • a_send_to_a_device_that_is_not_a_contact_moves_nothing.

Owner ruling: DSM Amendment A9

DSM/smt-key is the canonical relationship-SMT domain tag for beta, and /v1 in the explainer was an error.

Rows

Verification

  • Targeted runs pass on this tree: the 13 sender_admission_tests::a_* tests, including both new legs, and the G13 test after merging fix(online): a receipt binds only when its state rules hold #1078.
  • make lint: Rust fmt --check and clippy --all-targets pass. The frontend eslint step did not run locally (eslint is not installed in this worktree; the branch touches no frontend code).
  • The Code map will report the rows this change moves. I repin them from CI's map in a follow-up commit on this PR.

…atever its sender checked

a_non_transferable_token_refuses_its_transfer now also drives B's own
canonical apply (apply_incoming_transfer_staged) with a transfer A
signed, of the non-transferable token B adopted, from B's pinned head
for A. It is refused by the token's operation restriction before any
acceptance is built.

A probe first showed this holds end to end: with the sender's check
removed, B's sync refused the transfer ("Token policy violation ...
Operation not permitted") and nothing was credited. The CONFORMANCE §5A
note that the recipient never checks was wrong.

Mutation: the policy check skipped only for transfers addressed to this
device turns the test red with "reached acceptance: the policy did not
refuse".
…hat SoFi is reachable

MR-DSM-0205, 0209, 0211, 0212, 0213 and 0214 are Met on the SoFi
end-to-end tests and the Core resolution tests. MR-DSM-0219, 0265 and
0267 stay Partial, their gaps restated. All nine had recorded SoFi as
unreachable, which has not been true since #1056 and #1064.
MR-DSM-0074, Amendment A3: the sender sends only over relationships it
has pre-added. wallet.sendSmart to a device A never added is refused
with "recipient must be an added contact before online send"; nothing
is debited, no position is admitted and nothing is left pending. The
check is structural (the send is built from the contact record's keys),
so it has no mutation control that leaves the send buildable.
- 0039: acceptance needs a claim final at the payer's next root cell.
- 0074: both ends only use pre-added relationships (new sender-side
  test).
- 0079: Core counts a link only once a ByteCommit following its parent
  commits it.
- 0093: only the named counterparty takes a step.
… register is read

MR-DSM-0029, G13: the receiver reads the payer's register only after
every check it can decide from what it holds. The sync prevalidated each
bound pair, which walks the sender's lineage over the network, and only
the apply then checked the token's committed policy, which this device
holds. The policy check now runs first, before prevalidation. The apply
keeps its own check under the state-machine lock. Test to follow.
…der's register is read

MR-DSM-0029, G13, for f1723e3. A hostile sender signs a transfer of
its non-transferable token to B and advances its own head over it. It
then signs the step's receipt with its per-step EK, as its wallet signs
a send's receipt. Both halves reach B's boundary and bind. B's sync
refuses the pair by the token's committed policy, and no member is asked
for a cell while it does.

Mutation: remove the in-hand policy check and the test goes red. B asks
dsm-node-1 for a register cell of the sender before it refuses.

The non-transferable token request moves into a helper, which the new
test and a_non_transferable_token_refuses_its_transfer share.
# Conflicts:
#	specs/requirements/CONFORMANCE_GAPS.md
…verified

- §6.50 records the batch: the receiver's policy check before any read,
  the receiving device's refusal of a non-transferable token, the
  contact check, and the fifteen rows re-verified on this branch.
- MR-DSM-0029 now cites the sync's in-hand policy check and its
  mutation-controlled test. It stays Partial: adoption and the
  relationship tip are still decided after the register read.
- MR-SOFI-0311 and the §5A Transferable check row no longer say the
  recipient never checks. Both stay open: offline transfers are outside
  this round, and vault creation and SoFi legs are tested in Core only.
- VERIFICATION_MATRIX gains both gates, each with its mutation control.
Owner ruling, 2026-09-30. DSM/smt-key is the canonical domain tag for
relationship SMT keys in beta. The /v1 the explainer carried in §26's
formula and §16's example was an error. The code and its golden vector
are unchanged, and no key migrates.

- Explainer: §26's formula and §16's example corrected, with Amendment
  A9 after §26.
- MASTER: §1 re-pinned, a §7.1 entry, and MR-DSM-0115 rewritten.
- CONFORMANCE: MR-DSM-0115 and MR-DSM-0249 go Partial → Met; §6.14's
  finding is marked resolved, and §6.50 records it. DSM core totals:
  90 Met, 96 Partial, 39 Missing, 0 Violated.
- 66 code-class rows, whose closures reach storage_routes.rs,
  core_sdk.rs and the sender admission tests.
- 5 status-class rows, which this batch moved Partial → Met and accepts
  with `--accept status`: MR-DSM-0115 (two symbols), MR-DSM-0209,
  MR-DSM-0212 and MR-DSM-0249.

All taken from CI's code map of 0d1228f. Their 59 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.
# Conflicts:
#	specs/requirements/CONFORMANCE_GAPS.md
#	specs/requirements/INTENT_PINS.tsv
The merge with #1084 took main's pins for MR-STOR-0146, and this
branch's changes move both rows' closures. Taken from CI's code map of
61b776a. Their 3 evidence tests passed at that commit. `make
requirement-map-intent` against that map reads 1,111 rows, 0 failing,
and 598 pins, 0 failing.
@cryptskii
cryptskii merged commit 9383759 into main Oct 1, 2026
24 checks passed
cryptskii added a commit that referenced this pull request Oct 10, 2026
- 66 code-class rows, whose closures reach storage_routes.rs,
  core_sdk.rs and the sender admission tests.
- 5 status-class rows, which this batch moved Partial → Met and accepts
  with `--accept status`: MR-DSM-0115 (two symbols), MR-DSM-0209,
  MR-DSM-0212 and MR-DSM-0249.

All taken from CI's code map of 9e4c5e3. Their 59 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant