dash: wire the W4 quorum-member resolver into the MN diff-store parallel fold engine (stacked on #1261) - #1262
Closed
frstrtr wants to merge 1 commit into
Closed
dash: wire the W4 quorum-member resolver into the MN diff-store parallel fold engine (stacked on #1261)#1262frstrtr wants to merge 1 commit into
frstrtr wants to merge 1 commit into
Conversation
frstrtr
force-pushed
the
dash/mn-diff-store-fullstate-seed
branch
from
August 18, 2026 10:26
57ab73e to
8102ee5
Compare
…lel fold engine The cold MN-CKPT bridge stands up a parallel DmlFoldEngine (full ReplayMNState) to write the MN diff/snapshot store, seeded at the full-state anchor. It was created WITHOUT a quorum-member resolver, so its fold FAILED CLOSED at the first punishing qfcommit (llmqType=5 @ h=2513130, ~130 past the anchor): the PoSe-punish loop had no ordered member list to index against the commitment validMembers bitset and returned "no quorum-member resolver is installed -- PoSe punishes cannot be folded, failing closed". The store therefore covered only ~130 heights and the ban-state probe / on-demand fold fell back to the capped network getmnlistd path (payee-desync). Mirror the main replay-fold path exactly: stand up a second QuorumReplayEngine seeded at the same anchor and a second ReplayQuorumBridge whose ctor installs set_members_fn onto the store engine, seed the pre-anchor header hashes, prime_at_anchor after the deferred full-state seed lands, and drive observe()/after_fold() from the store block feed around each fold_block. The engine self-derives rotated + non-rotated quorum member sets from the same replayed blocks (dashd CalcQuorumMembers/GetAllQuorumMembers analog), self-checked against each block committed merkleRootQuorums, with no qrinfo/P2P dependency. The already-ported HandleQuorumCommitment PoSe-punish loop then applies the dashd-exact punishes so the folded list re-hashes to the committed merkleRootMNList past the qfcommit and the store covers heights to tip. Reward-safe by construction: a wrong member set / wrong penalty makes the folded root diverge from the block committed merkleRootMNList, the fold poisons (HARD STOP), the writer never sinks the row, the store stays empty, and callers fall through to the network path -- never a bad mint. Fail-closed stays the last resort.
frstrtr
force-pushed
the
dash/mn-diff-store-quorum-resolver
branch
from
August 18, 2026 11:43
1b329dc to
f480ffe
Compare
frstrtr
changed the base branch from
dash/mn-diff-store-fullstate-seed
to
master
August 18, 2026 11:43
Owner
Author
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
Wire the already-ported W4 quorum-member resolver + W1 PoSePunish loop into the parallel MN diff-store fold engine on the cold MN-CKPT bridge (
main_dash.cpp). The store-writer engine (mn_bridge_fold_engine) was stood up with noset_members_fn, so at the first punishingqfcommitit hit the fold's fail-closed:This PR mirrors the existing main replay-fold wiring onto the store engine: a second
QuorumReplayEngineseeded at the same full-state anchor and a secondReplayQuorumBridge(whose ctor installsset_members_fn), withobserve()/after_fold()driven around eachfold_blockfrom the store's block feed andprime_at_anchor()called after the deferred full-state seed lands. Stacked on #1261.No new algorithm: the dashd
HandleQuorumCommitmentPoSe-punish loop (replay_fold_engine.hpp) and the rotated/non-rotated member derivation (replay_quorum_engine.hpp, dashdCalcQuorumMembers/GetAllQuorumMembersanalog) were already ported, self-checked, and KAT-covered. This is the wiring.Reward-safety (by construction)
A wrong member set / wrong penalty → folded root diverges from the block's committed
merkleRootMNList→fold_blockpoisons (HARD STOP) → the writer never sinks the row → store stays empty → callers fall through to the network path. Never a bad mint. Fail-closed stays the last resort.KAT — red / green / reward-safe (all pass, BLS=REAL)
DashReplayQuorumSeam.Height2513130FailsClosedWithoutAResolver,DashReplayFoldReal.QfcommitWithoutMemberResolverFailsClosedDashReplayQuorumSeam.Height2513130FoldsThroughDerivedMembership(folds, punishes 4, reproducesmerkleRootMNListbyte-exact),DashReplayFoldReal.PoSePunishAndCaseDBan2516412(real ban-flip reproduces root)DashReplayFoldSynthetic.RootMismatchIsAHardStopWithNamedHeight,DashMnDiffStore.FinalRootMismatchRefusesvm905 cold daemonless MEASUREMENT (honest partial — NOT serve-ready)
Binary
g1b329dc3,/proc/exemd5 == built, BLS=REAL, flags--embedded-mainnet --embedded-null-arm --embedded-utxo --coin-p2p-discover, dashd ABSENT (no--coin-rpc).The wiring works and is proven live: the base fail-closed
no quorum-member resolver is installedis GONE (0 occurrences). The resolver is installed, seeded, consulted; the anchor full-state seed reproduces the committedmerkleRootMNListand the engine folds forward.But the cold store still caps at h=2513130 — the error changed to:
Root cause (structural, separate from the resolver wiring): the first post-anchor punishing commitment is a rotated (DIP-24)
llmqType=5quorum whose cycle base is 2513088, which is below the engine's self-contained pointself_contained_from(5, 2513000) ≈ 2513952. An anchor-seeded engine cannot self-derive a rotated cycle whose quarter-snapshot work blocks (2513080 / 2512792 / 2512504 / 2512216, three pre-anchor) predate the anchor — unlike the full--replay-bulkpath that starts at height 1 and observes them. The cold daemonless path seeds no pre-anchor cycle snapshots / work lists (0 pre-anchor header hashesat store-setup), so cycle 2513088 is underivable and the fold fails closed at h=2513130 — the same store cap as base #1261.Consequence: store covers 2513000→2513130 only; the MN-CKPT payee lane falls back to the capped network
getmnlistdprobe and hits the pre-existing payee-desync fail-closed at h=2517703 (BAN-STATE PROBE 8/8 exhausted; that is #149 territory, not this change). Final[EMBED-STATUS] arm=would-decline cause=not-populated populated=0 have_mn=0 bridge=failed-closed. Fold rate 21.5 blk/s. Not serve-ready.The resolver wiring is a correct, necessary, reward-safe prerequisite — it removes the "no resolver installed" fail-closed and folds every cycle the anchor-seeded engine can self-derive (cycle base ≳ 2513952 forward). The remaining blocker to move the cold store cap past 2513130 is a companion pre-anchor rotated-cycle seed for the store engine (the qsnapshot/work-list seed the main path takes from a capture, or an earlier anchor) — a separate piece, NOT a resolver defect.
Guardrails
NON-MONEY vm905 only; hotel untouched; dashd ABSENT; author frstrtr; zero AI attribution; DRAFT, do not merge.