feat(dlv): the DLV-policy digest is derived from the signed vault state, never chosen - #760
Merged
Conversation
…te, never chosen
Two independent policy layers govern an AMM vault and both apply to every
movement of value: the token (CPTA) policy of each asset, and the vault's
own DLV policy. The creator-signed VaultStateV2 already commits all three
— member 7 market_policy is the CPTA pair, members 8 and 9 release_policy
and fee_policy are the DLV layer. What it did not have was any link to
DlvSpecV1.policy_digest: thirty-two free bytes the UI asked a human to
paste, labelled a "CPTA anchor" in the schema, length-checked by
dlv.create and compared to nothing. A token identity in the vault-policy
slot, bound by nothing.
The digest is now a deterministic VIEW of the DLV layer:
dlv_policy_digest = BLAKE3(DSM/dlv-policy-digest,
CCB(ReleasePolicy) || CCB(FeePolicy))
Member 7 deliberately does not enter it: the pair's CPTA commits stay
independent authorities over the assets. No new CCB field, no c_n change
— the value is recomputable by anyone holding CCB(V_n).
- dlv.create derives it for an AMM vault, refuses a supplied value that
disagrees, persists and echoes only the derived value, and hands it to
the draft BEFORE signing.
- LimboVault::parameters_hash folds it (presence-tagged, so "absent" and
32 zero bytes differ), and verify recomputes the same fold: the creator
now signs the vault's policy identity. Non-AMM vaults have no DLV-layer
object to derive from; their supplied digest is kept and is now at least
creator-signed.
- rehydrate_amm_vault re-derives it from the leaf-proven fee and the beta
release family and refuses a row carrying anything else
(PolicyDigestNotDerived). FeePolicy::new also refuses a fee at or above
the denominator, which the leaf pair never bounded.
- route.publishRoutingAdvertisement fills the advertised digest from the
vault record and refuses a request naming any other; the trader-signed
hop therefore repeats a value the owner signed at birth.
- The schema comments that called this a CPTA anchor are corrected; the
never-implemented spec_id derivation comment is gone; the LiquidityScreen
paste box and the frontend's policyDigest input are deleted; the
Android SoFi harness sends empty on both create and publish.
This establishes DLV-policy IDENTITY and AUTHORITY. It is not yet
behavioural enforcement: ReleasePolicy is committed but read by nothing,
and close semantics remain hardcoded in advance. Settlement and close make
the committed policy govern behaviour; that work starts next.
WIPE BOUNDARY (CHANGELOG): every pre-existing DLV's creator signature was
made over the old parameters preimage and stops verifying. No migration,
no re-signing, deliberately.
Proven: the derivation excludes the pair and distinguishes the fee (core
test); a digest altered or dropped after signing fails verify; an AMM
create with a chosen digest is refused by name, with the derived value
supplied is accepted, and with the field empty is born carrying it in the
record and in the signed vault; a record with a non-derived digest fails
rehydration; a publish with a chosen digest is refused and an empty one
advertises the record's. The two pre-existing create-then-rehydrate
tests are re-pinned from the old pasted literal to the derived value;
one now supplies it explicitly on create, a second positive control of
the accept path. Each gate mutation-proven per arm with inverse-edit
restoration. Frontend: type-check, lint and jest green; the
test of the deleted paste box is deleted with it.
Boards: workspace 74 binaries, 3951 passed / 0 failed / 17 ignored (dsm lib 1682/0, dsm_sdk lib 1810/0/7 ignored), exit 0; node 12 binaries, 276/0, exit 0; make lint exit 0;
production safety PASS (pinned 1.98.0); ci_scan PASS.
cryptskii
deleted the
feat/dlv-policy-digest-derived-from-signed-birth
branch
September 3, 2026 13:08
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.
Summary
The DLV-policy authority cut, immediately before settlement. It establishes the vault's policy identity and authority; it does not yet make that policy govern behaviour, which is settlement's and close's job.
No caller chooses that digest any more.
What was wrong
DlvSpecV1.policy_digestwas thirty-two free bytes the LiquidityScreen asked a human to paste as a "policy anchor", labelled a CPTA anchor in the schema and the frontend, length-checked bydlv.createand compared to nothing — persisted, echoed into every advertisement and signed by traders into route hops. A token identity in the vault-policy slot, bound by no creator-signed object:parameters_hash,CCB(VaultStateV2)and the create operation's canonical bytes all omitted it. Meanwhile the signed vault state already committed a DLV-layer policy as members 8 and 9, unconnected to that field.The derivation
Member 7 (
market_policy, the CPTA pair) is deliberately not an input; the core test proves two vaults over different pairs with the same release family and fee share a digest while their signed state commitments differ, and that the fee changes it. A new domain tag, becauseDSM/dlv-policyalready names a different preimage (aSmartPolicyproto) and a domain must not cover two structures. No new CCB field, no change toc_n: anyone holdingCCB(V_n)recomputes it.Where it now binds
dlv.create(AMM)LimboVault::parameters_hashandverifyverifyrehydrate_amm_vaultPolicyDigestNotDerived);FeePolicy::newalso closes the fee-above-denominator gap the leaf pair never boundedroute.publishRoutingAdvertisementspec_idcomment removed; the paste box, thepolicyDigestinput and its test deleted; the Android SoFi harness sends empty on create and publish and its synthetic-digest helper is goneNon-AMM DLVs have no DLV-layer policy object to derive from, so their supplied digest is kept — and is now at least creator-signed. Stated, not hidden.
Wipe boundary, recorded
verifyrecomputesparameters_hashfrom the vault's fields, so every pre-existing DLV's creator signature stops verifying. Recorded inCHANGELOG.mdas a second beta wipe boundary: no migration, no re-signing, no synthesis, deliberately. Beta runs ondsm-testnetwith no production user state.Proof
verifyverifytrueverifyfold)ccb,limbo_vault,dlv_routes,route_routes,vault_rehydration;amm_vault_recordsandvault_funding_routesunchanged in count (fixtures re-rooted to the derived digest). FrontendLiquidityScreen9 → 8: the test of the deleted paste box deleted with its subjectmake lintexit 0;ci/production_safety_checks.shPASS (pinned 1.98.0, incl. TLA+);scripts/ci_scan.shPASS — all on tree stamp2e4346317b3a, identical before and after every runNot in this PR
Behavioural enforcement of the committed DLV policy.
ReleasePolicyis signed but read by nothing, and close semantics are hardcoded inadvance; making the committed policy govern behaviour is the close/apply work. The owner-fold curve check is settlement blocker one and starts next.