Repository navigation
feat(dlv): settle against the composed vault state — delegated liquidity across generations - #672
Merged
Conversation
…ity across generations Multi-generation settlement was not wired at all, LP online or offline: a vault was effectively one-settlement-ever. dlv.unlockRouted demanded an OWNER-published reserve proof at the exact hop generation (fetch_verified_reserve_proof(vault, hop.vault_state_anchor_seq)), but the owner publishes anchor/state/reserve proofs only in dlv.create at generation 0 and dlv.reconcile publishes nothing, so the second trade on any vault was refused "no verified reserve proof … at sequence 1". The mirror's LimboVault.current_sequence was pinned at 0 (bump_sequence had no callers), so the anchor gate would have refused a Required vault at generation 1 regardless. Meanwhile the QUOTE side already composed the vault's state from the seq-0 baseline plus verified trader receipts and bound hops at that generation — the router quoted generation 1 and the settle side refused it. The settle side now derives the vault's reserves at the hop's generation from the COMPOSED state (compose_vault_state): the owner's baseline — signed anchor, state-inclusion proof and reserve-inclusion proof, published once at creation — plus every verified trader generation folded on (trader-signed pointer, SMT-verified trader receipt matching the pointer's committed hash, RouteCommit bound to X and eligible, digest matching the fold cursor, AMM re-simulation). This is exactly the authority the quote side already trusts, and it is what lets the market advance a vault while the LP is offline: no owner signature on any transition after the baseline. The delegation guard, policy-independent, before anything moves: composed.sequence == hop.vault_state_anchor_seq. Behind it, the parent was already consumed (the trader-side twin of the owner's consume-once claim). Ahead of it, the trader is pre-settling a generation that does not exist — a probe that computes its amounts against the CURRENT reserves while naming a future parent passes every other check and would emit a receipt for a parent it never consumed, a self-credit no owner fold could honour. Mutation-tested: dropping the guard lets that probe settle (RED); the behind case stays caught by the AMM re-simulation and the slot claim (defense in depth). - ComposedVaultState now carries baseline_reserve_root / owner_devid / owner_genesis / owner_public_key from the verified baseline reserve proof, so the DlvSettle op records the baseline root and owner without a second fetch (one verification path). - enforce_anchor_binding is fed the composed sequence + digest instead of the dead mirror value; bump_sequence deleted (zero callers). - Test harness: install_identity() now resets the process-global in-memory object store — vault ids are deterministic, so one test's published pointers/receipts leaked into the next test's composition of the same vault (the guard correctly saw "already at generation 3"). Test: lp_offline_market_advances_three_generations_and_lp_reconciles_each_once — owner funds a Required vault and goes offline; three independent traders settle 0->1->2->3 through the production route (composition checkpoint at each generation, receipts (N, N+1)); stale-behind and future-ahead hops refused; the LP returns: out-of-order fold refused (parent-consumption CAS), in-order folds each consume exactly one parent with reserves equal to the composed state and consumption rows naming each trader's receipt, LP spendable balance untouched (no second debit), replay idempotent.
This was referenced Aug 18, 2026
cryptskii
added a commit
to cryptskii/dsm
that referenced
this pull request
Sep 10, 2026
… proof Hardware-proved 2026-08-18 on the 3-phone rig against the live Alibaba fleet (schema v4, APK carrying deterministicstatemachine#670 + deterministicstatemachine#672 + the deterministicstatemachine#673 SoFi UI ports): LP (8XK) created SOFI (CPTA), funded a Required SOFI/ERA vault (25,000,000 / 100, 30 bps) — head: SOFI 100,000,000 -> 75,000,000, ERA 290 -> 190, exactly the two reserve leaves at gen 0 — then was force-stopped. With the LP process dead, T1 (9FF) and T2 (5GN) settled four generations through the production SwapTab (0->1 T1 20 ERA -> 4,156,244; 1->2 T2 15 ERA -> 2,309,794; 2->3 T1 10 -> 1,274,633; 3->4 T1 8 -> 899,882), every quote exact against the COMPOSED reserves the trader derived from the prior receipts alone. T2 then confirmed a quote bound to gen 3 after T1 had consumed it: refused by the delegation guard ("is at generation 4 but the route binds generation 3 — that parent is already consumed"), T2's root byte-identical, one unreceipted pointer left on the fleet and nothing else. The LP relaunched, saw its vault (persisted-state listing) with "4 settled trades to reconcile", tapped Reconcile: leaves at gen 4, reserves 16,359,447 / 153 == funding + Σ inputs − Σ outputs, consumption rows 0..3 by four distinct receipts, LP spendable untouched (no second debit). scripts: - rig_dlv_market.py — step-wise CDP driver over the PRODUCTION UI (create-token, anchors, create-vault, vaults, swap [quote|execute|full], reconcile, balances, offline/online). CDP substitutes for finger taps only; every protocol step is the real route. - dsm_head_decode.py — host-side DeviceState v0x06 decoder for a pulled dsm_client.db: balances + reserve leaves, legs named via amm_vault_records + BLAKE3 leaf keys (b3sum). No hex. - rig_settle_foreground.py — clears Android permission dialogs + the lock prompt so the WebView owns the foreground (fresh-onboarding trap). - dlv_market_rig_proof.sh — read-only proof over the three pulled DBs + the fleet's public object listings; 17 assertions, all PASS.
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.
The finding
Multi-generation settlement was not wired at all — LP online or offline. A vault was effectively one-settlement-ever:
dlv.unlockRouteddemanded an owner-published reserve proof at the exact hop generation (fetch_verified_reserve_proof(vault, hop.vault_state_anchor_seq),dlv_routes.rs:1605). The owner publishes anchor/state/reserve proofs only indlv.createat generation 0;dlv.reconcilepublishes nothing. So the second trade on any vault was refused "no verified reserve proof … at sequence 1".LimboVault.current_sequencewas pinned at 0 (bump_sequencehad zero callers), so the anchor gate would have refused aRequiredvault at generation 1 regardless.route.findAndBindBestPath→compose_vault_state) and bound hops at that generation. The router quoted generation 1; the settle side refused it.The change
The settle side now derives the vault's reserves at the hop's generation from the composed state — the owner's seq-0 baseline (signed anchor + state-inclusion + reserve-inclusion proof) plus every verified trader generation folded on (trader-signed pointer, SMT-verified trader receipt matching the pointer's committed hash, RouteCommit bound to X and eligible, digest match, AMM re-simulation). That is exactly the authority the quote side already trusts, and it is what lets the market advance a vault while the LP is offline: no owner signature on any transition after the baseline. Storage nodes remain dumb mirrors — every artifact is signature- and SMT-verified on the trader.
The delegation guard, policy-independent, before anything moves:
composed.sequence == hop.vault_state_anchor_seq.Also:
ComposedVaultStatecarries the baseline root + owner ids (one verification path, no second fetch);enforce_anchor_bindinggets the composed sequence/digest instead of the dead mirror value; deadbump_sequencedeleted; the DLV test module now resets the process-global object store per test (vault ids are deterministic — one test's pointers leaked into the next's composition, and the guard correctly saw "already at generation 3").Proof
lp_offline_market_advances_three_generations_and_lp_reconciles_each_once— owner funds aRequiredvault and goes offline; three independent traders settle 0→1→2→3 through the production route (composition checkpoint at each generation, receipts(N, N+1)); stale-behind and future-ahead hops refused; the LP returns: out-of-order fold refused (#670 CAS), in-order folds each consume exactly one parent with reserves equal to the composed state and consumption rows naming each trader's receipt, LP spendable balance untouched (no second debit), replay idempotent.Mutation: dropping the sequence guard lets the ahead probe settle → RED. The behind case stays caught by AMM re-sim + slot claim (defense in depth) — documented in the test.
Gates
dlv_routes 15/15 (serial), composition 25/25, route_routes 5/5, settlement_slot 10/10, consumption 3/3, dsm vault 126/126, device_state 42/42, integration suites green;
make lint(fmt --check+clippy --all-targets -D warnings) clean;no_clock_and_no_json+production_safety_checks(prod clippy + TLA+) pass.Closes invariant 2 (delegated liquidity: the market progresses while the owner is offline). Builds on #670 (invariant 3) and #671 (serial CI).