feat(dlv): an unresolved settlement fences the trader parent, and a lost commit stays fenced - #774
Merged
Merged
Conversation
…ost commit stays fenced Rev 15 Req 6.23 / 16.4 / 16.5, Theorem 18.2: the initiating-trader parent fence, durable INDETERMINATE, and the restart-recovery primitives the one-shot settlement-register path never had. Builds on the PR 2 engine. Before Class K issues the first mutating binding op for a bundle B, it durably records a fence over the initiating trader's own chain. While the DLV transaction is unresolved, no DIFFERENT successor may advance from the fenced parent, even under a fresh intent or nonce. The load-bearing rule (Req 6.23(4)): COMMITTED fixes the permitted continuation to the EXACT committed trader successor, and the quorum result alone does not consume the fence — only that exact successor, accepted through ordinary DSM bilateral advancement, does. A different successor can never consume it. Tripwire remains the underlying rule. Same split as PR 2: - Pure core (dsm::dlv::trader_fence): FenceState/FenceEvent/next_state/verdict. Holds the safety-relevant transitions, including the WrongSuccessor refusal. - Durable adapter (client_db::trader_parent_fence), modelled on dlv_close_intent: place_fence writes before anything mutates (INSERT OR IGNORE keeps the first frozen inputs); record_event applies the CORE transition and persists state, permitted successor, and ballot; active_verdict is the advancement gate; list_unresolved_fences is the restart work list. Persistence reuses next_state, so it cannot legalize a transition the core forbids. - Orchestration (quorum_bind_runner::run_fenced): places the fence before the first mutating op (FenceNotPersisted if it cannot persist), drives, and records the outcome; an unresolved run keeps the parent fenced as INDETERMINATE with the ballot persisted so restart never reuses one. bind-indeterminate (Req 21.3): a transport lands the accept on a quorum but loses the response and goes dark. Run 1 is unresolved and the fence stays FENCED; the value is chosen on the members; a resumed run above the persisted ballot discovers it and reaches COMMITTED — completion, not a second value. Rust 1.98.0: workspace board 4015/0, make lint 0, production_safety_checks 0. Wiring the gate into the settle path (PR 5) and the GetImmutable restart driver (PR 4) are not in this change.
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.
PR 3 of the QuorumBind restoration. The initiating-trader parent fence (Req 6.23), durable INDETERMINATE (Req 16.4), and the restart-recovery primitives (Req 16.5) that the one-shot settlement-register path never had. Builds on the PR 2 engine (#773).
What the fence is
Before Class K issues the first mutating binding op for a bundle
B, it durably records a local settlement fence over the initiating trader's own sovereign chain:While the DLV transaction is unresolved, no different successor may advance from the fenced trader parent, even under a fresh intent or nonce. Tripwire remains the underlying one-successor rule; the fence stops a conforming client from accidentally advancing the parent while the quorum result is in doubt (Theorem 18.2).
The load-bearing rule is Req 6.23 (4):
COMMITTED(B)fixes the permitted continuation to the exact committed trader successor, and the quorum result alone does not consume the fence — it is released only when that exact successor is accepted through ordinary DSM bilateral advancement. A different successor can never consume the fence.Shape (same split as PR 2)
dsm::dlv::trader_fence) — the fence's states, the legalnext_statetransitions, and theverdicta caller consults. No I/O, no clock. This holds the safety-relevant transitions, including the refusal (WrongSuccessor) that stops a different successor from consuming the fence.dsm_sdk::storage::client_db::trader_parent_fence) — the fence's rows, modelled ondlv_close_intent:place_fencewrites it before anything mutates (INSERT OR IGNORE, so a retry keeps the first frozen inputs);record_eventapplies the core transition and persists the new state, permitted successor, and ballot;active_verdictis the advancement gate;list_unresolved_fencesis the restart work list. The persistence reusesnext_state, so it cannot legalize a transition the core forbids.quorum_bind_runner::run_fenced) — places the fence before the first mutating op (FenceNotPersistedif it cannot persist, so the transaction never begins), drives the engine, and records the outcome as aFenceEvent:COMMITTEDfixes the successor,ABORTED/CONFLICT_FINALrelease without advancing, an unresolved run keeps the parent fenced as INDETERMINATE with the ballot persisted so restart never reuses one.Conformance
trader-parent-fence/(Req 21.6) — the fence is persisted before the first mutating call; a freshtx_id(fresh intent) on the same parent does not lift it; a committed fence permits only the exact successor; that exact acceptance releases it and the gate goes clear; abort releases without advancing and leaves no unresolved work.bind-indeterminate/(Req 21.3) — a transport that lands the accept on a quorum but loses the response and goes dark. Run 1 reports the transaction unresolved (mutated: true) and the fence staysFENCED— a fresh intent cannot pass. The value is in fact chosen on the members. A resumed run for the same transaction, above the persisted ballot, discovers the chosen value and reachesCOMMITTED— completion, not a second value (Theorem 18.4).Tests
dsm::dlv::trader_fence, 7): unresolved keeps fenced; commit permits exactly the committed successor and no other; only the exact successor consumes the fence; abort/conflict release without advancing; acceptance-before-commit is illegal; terminal states do not regress; unresolved states are the recovery work list.trader_parent_fence, 5): a fenced parent blocks every successor and a fresh intent does not lift it; commit permits only the exact successor and acceptance releases; INDETERMINATE keeps the fence and restart recovery restores it with the bumped ballot and recovery inputs; abort leaves no unresolved work; the first placement freezes the recovery inputs.quorum_bind_runner, +2):run_fencedcommits and the fence permits only that successor, then acceptance clears it; the bind-indeterminate end-to-end above.Verification (Rust 1.98.0)
make lint— exit 0--workspace --exclude dsm_storage_node --release) — 4015 passed, 0 failed (75 suites)production_safety_checks(all-features clippy + TLA+) — exit 0 (TLA+ included)Not in this PR (by design)
active_verdictis the gate a trader-successor creation must consult; the live call site is the settle path rewritten to QuorumBind — PR 5.list_unresolved_fences+run_fencedresume are the primitives; the startup loop that resolves each fence's storage set from the catalog and retrieves the immutable bundle bytes viaGetImmutablelands with the HTTP transport — PR 4.