feat(sofi): route-wide unlock and completion, amendment 2c-H B2 - #891
Merged
Merged
Conversation
dlv.unlockRouted settles a signed route of up to two hops as ONE settlement: one grammar-33 DlvRouteSettle, one bundle over every consumed vault, one QuorumBind over K(B), one trader advance whose deltas are the route's two ends, and one completion that releases the trader's fence only after every leg certifies and every vault receipt is at quorum. A one-hop route settles under grammar 26 through the same settlement tail. - profile (hops, transitions, fanout) = (2, 2, 1); hops == transitions pinned by test, never aliased; the compile-time guard bounds hops by the core bundle cardinality - per-hop vault gates extracted (gate_route_hop); a multi-hop route needs an empty vault_id (H1), verifies its chain once, and each signed hop must equal the trade its gates verified, fee included (SAT.4-R) - one settlement tail: adoption of the final output only, every leg asset rooted, occupancy at every vault, one settlement domain (H15), signed total fee == sum of vault fees (SAT.6-R), route conservation before signing - admission takes one reserve-consumption evidence per vault in route-leg order (0x0026 for one leg, 0x0035 for a route) - completion per leg, release after every receipt; resume decodes 26 or 33 - composition, SofiReceipt recovery, the core acceptance verifier and lineage classification read route settles - bind_settlement refuses a bundle whose successors commit another storage set or quorum (H15); bind/occupancy/composition fixtures now commit the set they bind under - frontend prose no longer describes the withdrawn per-hop settle
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.
What this does
Amendment 2c-H, step B2. B1 (#890) put the route-wide settlement object in the core; until now the SDK still refused any route deeper than one hop. This PR makes
dlv.unlockRoutedsettle a signed two-hop route as one settlement:DlvRouteSettle, oneSettlementBundlecarrying every consumed vault'sT_v,QuorumBindover the completeK(B)(one binding transaction, both vault keys, onetx_id),T_v, every vault receipt reaches quorum, and only then does the one fence release. Resume finishes every leg of the same settlement.A one-hop route still settles under grammar 26, through the same code path it always did.
The invariant
In code: every step before the route-wide binding COMMITS reads state and writes nothing to the trader's chain, the fleet, a fence or a register. The only trader advance is the one admitted successor after
Committed, and the trader's fence is released only after every leg is certified and every receipt is durable. A receipt below quorum on any vault leaves the whole settlement bound and unrealized.Changes
Dispatcher and profile (
dlv_routes.rs,sofi_profile.rs,route_routes.rs)BETA_MAX_HOPS = 2by the H12 ruling.(hops, transitions, fanout) = (2, 2, 1)is pinned by a test, and a second test pins hops == transitions without aliasing one constant to the other. The compile-time guard now assertsBETA_MAX_HOPS <= ccb::MAX_TRANSITIONS.unlock_routed_route.Route entry (
unlock_routed_route)vault_idmust be empty for a multi-hop route. Naming any vault, even one on the route, is refused.verify_route_commit_chain(signature, hop chain, ends, no repeated vault or parent), andXmust be visible.gate_route_hop, extracted unchanged from the single-hop path.One settlement tail (
settle_gated_legs, shared by one-hop and route)X.Admission (
economic_admission_flow.rs)admitted_market_settletakes one reserve-consumption evidence per consumed vault, in route-leg order:0x0026for one leg,0x0035for a route.Completion and resume (
dlv_routes.rs)MarketCompletion { legs }: per leg, recover or sign the receipt, certify through the walk, and freeze. The receipt closure is built once, and the release waits for every receipt at quorum.Readers that assumed one vault
vault_state_composition.rsreads the settler and route bytes from either settle.sofi_receipt_publication.rsrequires every transition to commit the fence's set.acceptance_verify.rsaccepts the settler of a route settle, with a new test.lineage.rsclassifies a route settle asSettle.Bind (
settlement_bind.rs)in_settlement_domain. The core fixtures commit a fixed three-node set.Frontend prose
unlockVaultRouteddoc and one error string still described the withdrawn per-hop settle, and so did a Jest test title and a SwapTab comment. No behaviour change; the wallet already submits an emptyvault_id.Tests
New:
a_two_hop_route_settles_as_one_bundle_one_binding_and_one_trader_advance, which checks:a −1000,c +out,bunchanged;a_route_is_refused_before_any_bind_for_its_addressing_adoption_and_signed_fees: a named vault (H1, both vaults), an unadopted final output, a hop signed at 31 bps against a 30 bps vault (SAT.4-R), and a total of 59 against 30+30 (SAT.6-R). Each refusal names its rule, with no publication, CAS, fence or movement.a_route_with_one_vault_bound_by_a_rival_takes_neither_vault: a rival is bound at b/c. The route is refused at occupancy, and a/b stays unmoved with its parent stillFree.a_route_one_receipt_short_of_quorum_stays_held_until_resume_finishes_every_leg: only the b/c receipt prefix fails. The a/b receipt is durable, the b/c receipt is not, and the fence is held. A resume while failing realizes 0. After healing, a resume realizes 1 with both receipts durable, the fence released, no CAS and no further advance.settlement_bind::a_bundle_outside_the_binding_domain_is_refused_before_publication: another set, and another quorum.settlement_bind::a_route_conflicting_at_one_parent_takes_neither: the route is CONFLICT_FINAL at the held parent, and the other parent still commits for a single bundle afterwards.a_route_deeper_than_the_profile_is_refused_before_any_bind: replaces the one-hop refusal test at depthBETA_MAX_HOPS + 1.a_route_settle_acceptance_yields_the_witness.Targeted runs (release,
--test-threads=1):economic::acceptance_verify+economic::lineage: 7/0.sofi_profile,settlement_bind,binding_occupancy,vault_state_composition,sofi_receipt_publication,settlement_resume, the new route tests, and the single-hop settle, resume, catch-up, publication-miss and binder tests: 59/0.Mutation controls
Each mutation is applied alone and the named test is run. The file is restored byte-for-byte and verified before the next mutation.
a_two_hop_route_settles_as_one_bundle_one_binding_and_one_trader_advancea_route_is_refused_before_any_bind_for_its_addressing_adoption_and_signed_feesa_route_one_receipt_short_of_quorum_stays_held_until_resume_finishes_every_legroute_commit_bytesfromDlvSettleonlya_two_hop_route_settles_as_one_bundle_one_binding_and_one_trader_advance(the walk cannot certify; bound-unrealized)settler_devidfromDlvSettleonlya_two_hop_route_settles_as_one_bundle_one_binding_and_one_trader_advance(the walk cannot certify; bound-unrealized)settlement_bind::a_bundle_outside_the_binding_domain_is_refused_before_publicationDlvSettleonlyeconomic::acceptance_verify::a_route_settle_acceptance_yields_the_witnessa_route_with_one_vault_bound_by_a_rival_takes_neither_vaulta_route_deeper_than_the_profile_is_refused_before_any_binda_two_hop_route_settles_as_one_bundle_one_binding_and_one_trader_advance(admission refuses)BETA_MAX_HOPS = 3BETA_MAX_HOPS <= ccb::MAX_TRANSITIONSguardThe tree was verified byte-identical after every control. The first cut of M8/M9 did not compile (a duplicated
DlvSettlealternative is an unreachable pattern under#![deny(warnings)]); they were rerun with a local allow and went red.Gates no fixture reaches:
Not in this PR
DlvUnlockRoutedV1/DlvReconcileV1are detached and render onDlvCloseV1.Verification
make linton the pinned 1.98.0 toolchain:cargo fmt --checkandcargo clippy --all-targets -D warningsare clean.npm run lintexits 0 with 0 errors. Its one warning is pre-existing, inRecoveryPipelineScreen.tsx, a file this PR does not touch.route_commit.test.tspasses 42/42.ci/production_safety_checks.sh: clippy production safety checks and TLA+ formal verification passed.. CI is the board.