feat(economic): sender-side economic admission — generalized producer seam, held outbox, wire locators - #737
Merged
Conversation
…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.
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.
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) gainsadmission: Option<AdmissionPlan>: attachPreparedunder 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_advanceis now a thin wrapper over it; the faucet hand-roll is deleted.economic_admission_flow.rs:build_dsm_admission(uses PR1'sbuild_write_set— the SAME table the verifier checks), genericfinish_admission, kind-drivenresume_pending_admission.faucet_claim_flowkeeps only the ticket machinery.Owner corrections A–D, as landed
ECON_ADMITTED. New durable statuseconomic_admission_pending(schema v11): never returned byunsettled_sender_outbox(), promoted topending_submitin 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).(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).OnlineTransferRequestgains 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).token.burnroutes 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
R_econitself, 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_balancesnow 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_PUBLISHEDis a quorum fact, not a best-effort pass. The republish sweep returnsOkwhile leaving below-quorum rowsPublicationPending;finish_admissionpreviously advanced the state anyway (latent since the faucet PR). It now requires the frozen backlog to be EMPTY beforeEvidencePublished— 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 > 0is refused at the route with the namedCreateTokenInitialSupplyRequiresIssuancePredicateerror before anything commits; the fee debit is admitted withinsert_registrycomposed into the same transaction via thealsoslot.Test infrastructure
two_deviceharness migrated to REAL v3dsm-testnetidentities and admitted funding (fund_admitted= live faucet claims over the fake fleet);Pair::bootresets 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).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).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 inbalance_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/r2bfund B admitted for the reverse leg;r9/r12unchanged.Mutation controls (each red-then-restored)
a_held_outbox_row_is_invisible…AND the live race ina_failed_finish_holds_the_outbox…both red (the sweep actually emitted bytes).Boards (exact CI commands, final tree, pinned 1.98.0)
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 0cargo test --locked -p dsm_storage_node --no-default-features --features local-dev,strict -- --nocapture— 270/0, exit 0make lint(repo root) — exit 0; pinned repo-rootcargo clippy --all-targets— exit 0bash ci/production_safety_checks.sh— exit 0Residuals (named): non-builtin Mint remains un-admitted (purely local self-strand); recipient credits are unadmitted until PR4 (and consequently unspendable — see above).