Skip to content

feat(dlv): surface the owner's economic-proof locator, which no trader could reach - #835

Merged
cryptskii merged 1 commit into
mainfrom
feat/market-accepted-successor-and-admission
Sep 10, 2026
Merged

feat(dlv): surface the owner's economic-proof locator, which no trader could reach#835
cryptskii merged 1 commit into
mainfrom
feat/market-accepted-successor-and-admission

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

Step 4 requirement 6's one blocking gap, fixed on its own so the admission work that needs it is not also carrying this.

The gap

A market settle must build credit source 0x0026 DlvReserveConsumption, whose facts are owner_economic_position and reserve_consumption_evidence_addr. That evidence names the exact V_n, the owner's authority evidence, and the address of the owner's EconomicProofArtifactV1.

A settling trader could reach none of it. The route's own comment says why: the settler is usually a trader device holding no amm_vault_record, its whole knowledge of the vault having come from storage. The owner publishes the artifact at funded creation and keeps the locator locally — so the owner has it and nobody else does.

It was already on the wire, and already fetched

RoutingVaultAdvertisementV1 carries economic_proof_addr and economic_proof_position, with a comment explaining precisely why that is safe on an unsigned object:

A reader resolves the position's root from the owner's own write-once register cell and re-derives every inclusion path against it, so a wrong address or position here can only make the lookup fail — never succeed against a root the owner did not register.

And compose_discovered_vault already fetches and decodes that advertisement. It simply never surfaced the two fields. The information was present, verified-irrelevant, and unreachable at the same time.

Shape

Carried as Option<OwnerEconomicProofLocator> rather than two bare fields:

  • Absence is explicit, so a caller cannot mistake "this path carried no advertisement" for "the owner published nothing". The owner's own composition path has no ad to read and correctly yields None.
  • Both halves travel together, because neither is usable alone: the address names the artifact, the position names the register cell whose root the inclusion paths are re-derived against.

A malformed address is treated as absent, not as a refusal. Composition is about the authenticated state and this field authenticates nothing; failing a composition over a bad hint would let an unsigned advertisement deny a vault.

No consumer yet

Deliberate rather than overlooked. The consumer is the admission wrapper — modelled on the existing admitted_dlv_create_funded, which does the same job for funded creation — and it lands next. The field is inert until then.

Verification

dsm_sdk vault_state_composition 22/22 serially. make lint green; production safety checks green.

…r could reach

Step 4 requirement 6's one blocking gap, fixed on its own so the admission work that
needs it is not also carrying this.

The credit source a market settle must build is 0x0026 DlvReserveConsumption, whose
facts are owner_economic_position and reserve_consumption_evidence_addr. The evidence
itself is a ReserveConsumptionEvidenceV1 naming the exact V_n, the owner's authority
evidence, and the address of the owner's EconomicProofArtifactV1.

A settling TRADER could reach none of that. The route's own comment says why: the
settler is usually a trader device holding no amm_vault_record, its whole knowledge of
the vault having come from storage. The owner publishes the artifact at funded creation
and keeps the locator in its own local record, so the owner has it and nobody else does.

The locator IS on the wire. RoutingVaultAdvertisementV1 carries economic_proof_addr and
economic_proof_position, with a comment explaining exactly why that is safe on an
unsigned object: a reader resolves the position's root from the owner's own write-once
register cell and re-derives every inclusion path against it, so a wrong address or
position can only make the lookup FAIL, never succeed against a root the owner did not
register.

And compose_discovered_vault already fetches and decodes that advertisement. It simply
never surfaced these two fields, so the information was present, verified-irrelevant and
unreachable at the same time.

Carried as Option<OwnerEconomicProofLocator> rather than two bare fields:

- absence is EXPLICIT, so a caller cannot mistake "this path carried no advertisement"
  for "the owner published nothing". The owner's own composition path has no ad to read
  and correctly yields None.
- both halves travel together because neither is usable alone: the address names the
  artifact, the position names the register cell whose root the inclusion paths are
  re-derived against.

A malformed address is treated as absent rather than as a refusal. Composition is about
the authenticated state, and this field authenticates nothing; failing a composition over
a bad hint would let an unsigned advertisement deny a vault.

No consumer yet, and that is deliberate rather than overlooked: the consumer is the
admission wrapper, which is modelled on admitted_dlv_create_funded and lands next. The
field is inert until then.

dsm_sdk vault_state_composition 22/22 serially. make lint green; safety checks green.
@cryptskii
cryptskii merged commit 4709c06 into main Sep 10, 2026
16 checks passed
@cryptskii
cryptskii deleted the feat/market-accepted-successor-and-admission branch September 10, 2026 03:00
cryptskii added a commit that referenced this pull request Sep 10, 2026
…admission (#837)

Step 4 requirement 6's second half, and requirement 9. After a qualifying QuorumBind
COMMIT the binding is authoritative, so the trader now advances its OWN chain to the
successor the bundle names, with the economic admission attached.

admitted_dlv_settle follows admitted_dlv_create_funded, with two differences that
matter.

The credit arm is 0x0026 DlvReserveConsumption: a trader's settle output is funded by
consuming an owner vault reserve. It is NOT 0x0027, which is the owner-apply arm.
build_write_set refuses the pairing outright, and the 5c-2 plan named the wrong one
until it was corrected.

The second is the gate requirement 6 asks for in as many words. `expected_successor` is
the exact B.trader_successor the bundle was BOUND to, and the advance refuses unless it
would commit precisely that. The check runs inside `build`, after the pure prepare and
before anything is written, so a device that would diverge leaves nothing persisted,
nothing credited, and the fence untouched.

The evidence the credit source names is built from facts the trader already verified:
the exact V_n, the owner's authority evidence re-encoded from what the presentation
authenticated, and the owner's economic-proof address. A verifier REPLAYS the owner's
ancestry rather than trusting this device. The locator came from #835; without it a
trade is refused rather than admitted on an invented one, and the refusal says the
trade is bound and needs reconciliation rather than pretending it failed.

WHAT NOW MOVES, AND WHAT STILL DOES NOT. The trader's own balances move — exactly 1,000
in and 453 out in both tests, which is the constant-product output for that pool and
fee — and its head advances. That is what accepting a successor means, and the credit is
foreign-verifiable rather than a raw local one. The vault's reserves do not move, no
receipt is published, the market fold stays PartialPendingRealization, no
bundle-acceptance witness exists to construct, and THE FENCE IS NOT RELEASED.

Requirement 9 is now asserted positively rather than by absence: the test reads the
fence on the TRADER's own chain, keyed on the parent as it stood before this settle, and
requires it to still be CommittedAwaitingAcceptance. Ordinary DSM advancement is not a
certifying verdict, Ruling V3 gates release on one, and a market verdict cannot certify
until 2c-D.

The replay assertion changed direction, and the new one is the more important property.
Re-submitting an accepted settle is now REFUSED, because the trader's chain has moved
and the quorum finds the vault generation taken. The test asserts the refusal AND that
the retry moved nothing further: a refusal that had already debited would be worse than
an acceptance.

Mutation control, restored from a byte copy and verified identical after: inverting the
bound-successor comparison turns both named market tests red and reaches the
"could not admit its own advance" refusal, which shows the gate executes on the happy
path rather than being dead.

An earlier attempt at that control replaced the comparand with a constant instead. It
did not compile, because the parameter became unused and the crate denies warnings —
which is its own small evidence that the value is genuinely consumed, but not a control.

dsm_sdk: dlv_routes 37/37, vault_state_composition 22/22, settlement_bind 7/7,
binding_occupancy 10/10 — all serial. dsm: dlv::market 15/15, conformance 12/12.
make lint green; production safety checks green.

Still owed in Step 4: requirement 10's remaining controls — publication below quorum
leaving no fence and no bind, and a conflicting value for the same parent staying
excluded after COMMIT.
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