feat(economic): recipient-side economic admission — prevalidation before the fence, the total credit gate, the release split - #738
Open
cryptskii wants to merge 1 commit into
Conversation
…ore the fence, the total credit gate, the release split 3.5b PR4, final PR of the series. Every online credit-direction Transfer runs the full economic admission on the recipient. Core: the accepting gate beside the FaucetClaim gate at full discipline (DsmBacked + Prepared + exact digest, both gates tightened; total across transports — the BLE/USB online-credit receive fails closed until its own wiring lands, owner ruling); prevalidate_sender_debit extracted so pre- and post-accept conjuncts share one implementation; the acceptance verifier compares cleared-signature operation bytes and binds the countersigned B-side pair to the exact verified recipient successor (two latent PR2 defects); build_write_set refuses SourceAlreadyConsumed at build. SDK: prevalidation in complete_ready_split_transfers establishes every foreign dependency BEFORE any durable state — the PR2 walker with the recorder AS the PeerEvidenceFetcher, wire/receipt binding, EK-ancestry portability, and the q-durable closure via member-attributed republish of the exact recorded bytes; Invalid ⇒ TerminalReject, Quarantined ⇒ terminal, Incomplete ⇒ hold-and-retry. Durability memos are SPLIT: the economic watermark never implies EK-closure durability (per-exact-address memo). The atomic accept carries the acceptance bundle, both EK step objects, the pending admission, and the HELD reply; finish publishes, registers, validates, admits — and the terminal admit transaction promotes the reply and freezes the RecipientEconomicReleaseV1, the transport-only finalization authority: bare sig_b is provenance and cannot finalize; the sender fetches the release by content address, verifies it under the pinned contact identity, and independently quorum-reads the recipient's registered root before finalizing. EK signer chains (ek_cert_step_chain) are appended identically on both devices (accept tx / finalize tx) and at the BLE completion points (frozen offline as publication debt). Schema v11 -> v12 (beta wipe). fund_unadmitted deleted.
| sender, | ||
| incoming_transfer_op(&sdk.device_info.device_id, 5, vec![0x31u8; 32]), | ||
| { | ||
| let op = incoming_transfer_op(&sdk.device_info.device_id, 5, vec![0x31u8; 32]); |
| sender, | ||
| incoming_transfer_op(&sdk.device_info.device_id, 7, vec![0x32u8; 32]), | ||
| { | ||
| let op = incoming_transfer_op(&sdk.device_info.device_id, 7, vec![0x32u8; 32]); |
| sender, | ||
| incoming_transfer_op(&sdk.device_info.device_id, 9, vec![0x41u8; 32]), | ||
| { | ||
| let op = incoming_transfer_op(&sdk.device_info.device_id, 9, vec![0x41u8; 32]); |
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 PR4 — recipient-side economic admission: prevalidation before the fence, the total credit gate, and the release split
Final PR of the 3.5b series (#735 write-set semantics, #736 peer-lineage validation, #737 sender admission). Every online credit-direction Transfer now runs the FULL economic admission on the recipient — sender-ancestry prevalidation before any durable state, atomic accept with the acceptance bundle and pending admission, publish → register → validate → admit, and a post-admission RELEASE the sender independently verifies before finalizing. All five fifth-review corrections are implemented as specified.
The accepting gate (core, total — no transport exemption)
DeviceState::advancegains the credit-direction Transfer gate beside the FaucetClaim gate, at FULL discipline (correction 11b, both gates): the attached admission must beDsmBackedANDPreparedAND bound to exactly this operation's digest —None, wrong kind, wrong state, and digest mismatch each get a named refusal. Owner ruling: the BLE/USB receiver's online-credit path (byte-indistinguishable at this seam) fails closed until its own wiring lands; bearer-tier BLE (authority_policy: Some) is untouched. Mutation-verified: deleting the gate turns botha_credit_transfer_requires_the_full_admission_disciplineand the BLE-shaped control red (run, red, restored).Prevalidation — every foreign dependency before the fence exists (corrections 3, 5, 8)
In
complete_ready_split_transfers, per row, after the finality barrier and before the relationship lock: decode the frozen wire (locators 13/14 as untrusted hints; peer identity from the stored contact, never wire metadata), walk the sender's lineage via the PR2 walker with the recorder AS thePeerEvidenceFetcher(correction 2 — every immutable object any nested verification fetched, including EK predecessor steps, is recorded at the actual fetch boundary), runprevalidate_sender_debit(extracted into core so the pre-accept and post-accept conjuncts can never drift), bind the wire bytes to the validated operation and the receipt to the debit successor, require EK-ancestry portability, and establish the q-durable closure by member-attributed republish of the exact recorded bytes.Refusal taxonomy is load-bearing: walker
Invalid/predicate refusal ⇒ TerminalReject before ANY durable state (THE correction-3 control, tested with fabricated debit indices);Quarantined⇒ terminal with the divergence recorded;Incomplete⇒ the row holds and retries — an outage is never an attack and never a wedge (tested: quorum down ⇒ zero durable state; heal ⇒ the same row completes).Split durability memos (correction 4)
The
peer_economic_lineagewatermark covers the ECONOMIC DAG only; EK-step durability is memoized per exact immutable address (immutable_q_durable_memo), never inferred from an economic position. When the watermark does not cover the target, the walk runs from the activation root with the cache disabled so the recorder observes the full closure. Re-validation preserves the flag (INSERT OR IGNORE).The release split (fifth-review correction 1)
PeerTransferAcceptanceEvidenceV1/sig_bis acceptance PROVENANCE only — it publishes with the admission evidence beforeECON_ADMITTEDand cannot finalize the sender. The new transport-onlyRecipientEconomicReleaseV1(no CCB class) binds the receipt commitment, acceptance-evidence address, recipient(G, DevID), admitted position, post-R_econroot, and admission-manifest address, signed by the recipient. It is constructed and frozen in the accept transaction (every field an output of the build), the reply row is HELD, and the release object is frozen ONLY in the terminal admit transaction — promotion to deliverable and network publication are both admission-terminal. The countersign delta carries the release's 32-byte content address (a ~50KB inline SPHINCS+ release exceeded the reply-envelope cap — discovered live, fixed by content-addressing).Sender finalization requires the release: fetch by address (re-hash verified), verify under the pinned contact AK, bind commitment + devid + contact genesis, then INDEPENDENTLY quorum-read the recipient's register cell at the named position and require exact post-root + manifest equality — a hostile recipient's private "admitted" flag is never trusted. Mutation-verified: disabling the gate finalizes on bare
sig_band turns the named test red (run, red, restored). Companion tests: a release naming a mismatching registered root is Rejected; an unregistered root defers (outage, not attack).EK-step ancestry (correction 3 + the portability chain)
New
ek_cert_step_chaintable (per relationship, per SIGNER — never receipt role). The recipient appends both sides'EkCertStepV1objects in the accept transaction (frozen with the admission evidence); the sender appends the byte-identical objects in its finalize transaction (deterministic derivation — both mirrors converge on the same content addresses, which is what keeps role reversal walkable). The BLE bilateral path records both signers' steps at its two completion points, frozen offline through the generic publication backlog (no BLE-specific protocol); prevalidation's per-address durability check fail-closes any online bundle depending on an unpublished step.Core pre-fixes (latent PR2 defects, found during design)
verify_peer_transfer_acceptancecompared the wire's UNSIGNED canonical bytes against the walker's SIGNED operation — unsatisfiable once a real bundle existed. Fixed withwith_cleared_signature()at the comparison.(b_parent_tip, b_child_tip)now comes from the exact verified recipient successor (embedded_parent+C_dsm+, threaded throughVerifiedDsmSuccessor,AcceptedSubstrate, coords, and the provenance context) and the bundle's pair must equal it —the_bundle_cannot_self_select_its_b_side_pairpins it.build_write_setnow refusesSourceAlreadyConsumedat build (the verifier refused independently; honest producers get a named error) —the_same_sender_debit_cannot_fund_a_second_creditdrives it over the recipient's real post-admission tree.Everything else
apply_incoming_transfer_stagedgainsadmission: Option<AdmissionPlan>; the existing in-tx writes chain in the commit helper'salsoslot — no new transaction composition.find_current_payload_with_prefixgains a purpose filter; a crash-resumed recipient recovers its release from the journal by the manifest address the admission itself binds.admit_economic_positionpromotes held replies and freezes post-admit artifacts atomically withECON_ADMITTED.canonical_rebuildreplays previously-committed gated credits with a synthetic Prepared bound to the recorded operation digest, stripped after each step.fund_unadmitteddeleted (zero callers). Schema v11 → v12 (beta wipe):embedded_parenton the pending row,ek_cert_step_chain,immutable_q_durable_memo,closure_q_durable,held/release_byteson the reply row,release_byteson the acceptance journal.Tests
11/11 bilateral finality tests green with BOTH admissions live per generation (role reversal, checkpoint gating, crossing sends, relationship-local barrier). New
recipient_admission_tests(5): both-sides e2e with a register-backed release and the q-durable watermark; fabricated coordinates refused before any durable state; same-debit-cannot-fund-twice; the held-release race rule (mutation-verified); outage-holds-then-recovers. New core gate tests (2, mutation-verified). Release-gate tests (2). The finalize harness (18 tests) now runs against a full release fixture (signed release + published object + registered recipient root). Fixture sweep:core_sdk(22) andbcr(14) attach the honest Prepared precondition — the gate is total in tests exactly as in production.Boards (exact CI commands, final tree, pinned 1.98.0)
cargo test --locked --workspace --exclude dsm_storage_node -- --nocapture --test-threads=1— 3855 passed / 0 failed (dsm lib 1666/0, dsm_sdk lib 1790/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 0Named residuals: BLE/USB online-credit receive is refused fail-closed until the BLE admission wiring PR (owner ruling — no transport exemption); a BLE step that recorded before this PR (or whose post-commit step recording failed) holds later online acceptance on that relationship as
Incompleteuntil reconciled — fail-closed, logged, never corrupting.