Skip to content

feat(economic): sender-side economic admission — generalized producer seam, held outbox, wire locators - #737

Merged
cryptskii merged 1 commit into
mainfrom
feat/transfer-sender-economic-admission
Aug 27, 2026
Merged

feat(economic): sender-side economic admission — generalized producer seam, held outbox, wire locators#737
cryptskii merged 1 commit into
mainfrom
feat/transfer-sender-economic-admission

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

3.5b PR3 — sender-side economic admission: one generalized producer seam, held outbox, wire locators

Third of the four 3.5b PRs (after #735 write-set semantics, #736 peer-lineage validation). Every ordinary sender-side value operation — online Transfer debit, Burn, CreateToken fee — now runs the FULL economic admission (prepare → accept → publish → register → validate → admit) through ONE generalized seam, before anything is deliverable. Recipient wiring and the core credit gate are PR4.

The seam (no parallel paths)

  • execute_on_relationship_inner (and its staged/reserve/guarded wrappers) gains admission: Option<AdmissionPlan>: attach Prepared under the state-machine lock, build the witness/manifest/coords from the exact prepared successor at the existing staged seam, commit successor + pending + frozen evidence + caller writers in ONE transaction. faucet_claim_advance is now a thin wrapper over it; the faucet hand-roll is deleted.
  • New substrate-neutral economic_admission_flow.rs: build_dsm_admission (uses PR1's build_write_set — the SAME table the verifier checks), generic finish_admission, kind-driven resume_pending_admission. faucet_claim_flow keeps only the ticket machinery.

Owner corrections A–D, as landed

  • A — the committed outbox is NON-DELIVERABLE until ECON_ADMITTED. New durable status economic_admission_pending (schema v11): never returned by unsettled_sender_outbox(), promoted to pending_submit in the SAME terminal admission transaction. The race is tested live: a resubmit sweep forced into the held window emits ZERO transfer bytes; removing the held state turns both named tests red (mutation-verified, restored).
  • B — admission serialization under the lock. A new admission refuses an existing pending one and CAS-checks the admitted predecessor (prepared.position − 1, pre_root) under the lock; the commit transaction re-asserts it. A stale prepared snapshot is refused, never overwritten (a_stale_admission_snapshot_is_refused_not_committed).
  • C — locators are OUTPUTS of the built admission. OnlineTransferRequest gains fields 13/14 (sender_economic_position, sender_debit_mutation_index) carrying the owner's untrusted-locator sentence verbatim in the proto docs. The envelope is assembled in the staged build phase from the builder's returned position and THE debit index. The flagship harness test decodes the frozen envelope and requires locator == admitted position; freezing position+1 instead turns it red (mutation-verified, restored). Wire round-trip test included (the tag-31 lesson).
  • D — no unadmitted Burn once a lineage exists. token.burn routes through the admission seam; the TokenSDK/Bitcoin Burn arm hard-refuses when an admitted lineage exists rather than stranding it.

Two rules the tests forced into the design

  • Admission pre-states come from R_econ itself, never the device head. The head can carry credits no admission backs (a received transfer awaiting PR4 recipient admission); a debit built on the head snapshot pairs a mutation pre-state the validated pre-root cannot prove. producer_tree_and_balances now derives the balance map from the same admitted leaf-cache rows that recompute the validated root. Consequence, intended: received-but-unadmitted value is simply unspendable until PR4 — the economic-root guarantee working.
  • ECON_EVIDENCE_PUBLISHED is a quorum fact, not a best-effort pass. The republish sweep returns Ok while leaving below-quorum rows PublicationPending; finish_admission previously advanced the state anyway (latent since the faucet PR). It now requires the frozen backlog to be EMPTY before EvidencePublished — otherwise the admission stays held for resume. Exposed by the failed-finish e2e (red before the fix, green after).

CreateToken (owner ruling: fee-only validates)

initial_supply > 0 is refused at the route with the named CreateTokenInitialSupplyRequiresIssuancePredicate error before anything commits; the fee debit is admitted with insert_registry composed into the same transaction via the also slot.

Test infrastructure

  • two_device harness migrated to REAL v3 dsm-testnet identities and admitted funding (fund_admitted = live faucet claims over the fake fleet); Pair::boot resets the process-global fake registers (their persistence across suite-ordered tests replayed identical identities into already-claimed positions — the source of register-conflict failures that no single-test run could reproduce).
  • New sender_admission_tests (8): admitted Burn + foreign walk of positions 1..2 across two operation kinds; stale-CAS refusal; held-row invisibility + promotion; monotonic multi-kind lineage; wire round-trip; failed-finish/held/resume e2e; route-level fee-only create + reconciliation + anchor contract + burn e2e (the coverage the integration suites lost).
  • Integration suites rewritten (integration tests have no fake fleet, so no admitted operation can run there): refusal contracts in token_mint_burn_routes, token_create_fee_atomicity, token_create_reconciliation, token_create_anchor_integrity, token_decimal_scaling; read-path fixtures that install the durable created shape directly in balance_list_metadata, policy_rehydration, token_add_by_anchor, token_forget, token_registry_persistence, token_resolution. Custom-token mint/burn lifecycle tests deleted with the removed capability named (creator supply waits for issuance predicate 0x0029); the cap-scaling contract (display cap → base-unit registry cap) is pinned in the lib e2e.
  • r1/r2b fund B admitted for the reverse leg; r9/r12 unchanged.

Mutation controls (each red-then-restored)

  1. Held-status exclusion removed from the sweep query → a_held_outbox_row_is_invisible… AND the live race in a_failed_finish_holds_the_outbox… both red (the sweep actually emitted bytes).
  2. Wire locator frozen as position+1 → flagship harness locator assert red (11 vs 12).
  3. Evidence-quorum gate: demonstrated by the failed-finish e2e being red before the gate existed.

Boards (exact CI commands, final tree, pinned 1.98.0)

  • Workspace: cargo test --locked --workspace --exclude dsm_storage_node -- --nocapture --test-threads=1 — 3845 passed / 0 failed (dsm lib 1664/0, dsm_sdk lib 1783/0 + 7 ignored), exit 0
  • Node (repo root): cargo test --locked -p dsm_storage_node --no-default-features --features local-dev,strict -- --nocapture — 270/0, exit 0
  • make lint (repo root) — exit 0; pinned repo-root cargo clippy --all-targets — exit 0
  • bash ci/production_safety_checks.sh — exit 0

Residuals (named): non-builtin Mint remains un-admitted (purely local self-strand); recipient credits are unadmitted until PR4 (and consequently unspendable — see above).

…ucer seam, held outbox, wire locators

3.5b PR3. Every ordinary sender-side value operation (online Transfer debit,
Burn, CreateToken fee) runs the full economic admission through ONE seam on
execute_on_relationship_inner before anything is deliverable; the faucet
hand-roll is deleted onto the same machinery. Owner corrections A-D landed:
the committed outbox is held (economic_admission_pending, schema v11) and
promoted in the terminal admission transaction; a new admission refuses an
existing pending one and CAS-checks the admitted predecessor under the
state-machine lock and again in the commit tx; OnlineTransferRequest fields
13/14 carry the sender economic locators as OUTPUTS of the built admission
(untrusted locators, never authority); token.burn routes through the seam and
the TokenSDK Burn arm hard-refuses once a lineage exists. Admission
pre-states now come from R_econ itself (the admitted leaf cache), never the
device head; finish_admission requires the frozen evidence backlog empty
(quorum-durable) before EvidencePublished. CreateToken initial_supply > 0
gets the named issuance-predicate refusal; the fee debit is admitted with the
registry write composed in-tx. two_device migrated to real v3 dsm-testnet
identities with admitted faucet funding and per-boot fake-register reset;
integration suites that could no longer commit creations move to refusal/
read-path contracts, with the admitted-path coverage in the lib e2e over the
fake fleet.
@cryptskii
cryptskii merged commit 32f51f0 into main Aug 27, 2026
18 checks passed
@cryptskii
cryptskii deleted the feat/transfer-sender-economic-admission branch August 27, 2026 20:56
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