Skip to content

feat: build, chain-validate, and seed evo snapshot state - #7593

Draft
PastaPastaPasta wants to merge 4 commits into
dashpay:developfrom
PastaPastaPasta:feat/evo-snapshot-chain
Draft

feat: build, chain-validate, and seed evo snapshot state#7593
PastaPastaPasta wants to merge 4 commits into
dashpay:developfrom
PastaPastaPasta:feat/evo-snapshot-chain

Conversation

@PastaPastaPasta

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Stacked on #7592 — the first two commits shown here belong to that PR; this one adds the last three. It will be rebased once #7592 merges.

Part of the AssumeUTXO M4 decomposition (#7579 — see the series map there); stacked on the format-core PR and reviewable as one question: given the v3 format, how does snapshot content get produced from chain state, verified against it, and seeded back into EvoDB? The dumptxoutset/load lifecycle wiring is deliberately absent — it is the next and final PR of the series.

What was done?

Three layers, one commit each:

  1. Build & chain-validate (evo/snapshot.cpp): BuildEvoSnapshot() collects active/safety commitments, rotation snapshots, historical MN-list diffs, and exact score modifiers from chain state; ValidateEvoSnapshotAgainstChain() verifies a decoded snapshot against the block index, deployments, work-block ancestry, rotation horizons, and per-entry seeded modifiers.
  2. EvoDB seeding surface (deterministicmns, llmq/blockprocessor, llmq/snapshot, creditpool, mnhftx, evodb): Seed* writers that store exactly the state reconstruction later reads, without publishing into the shared NORMAL-chainstate caches. GetHashModifier() prefers a seeded exact modifier when the work block's data is unavailable and cross-checks it against recomputation when it is; a mismatch throws SnapshotStateMismatchError, which is unreachable in production until the load integration seeds modifiers (the next PR routes it into the controlled invalid-snapshot path — the plumbing is deliberately not wired here). GetDeterministicMNListHash() switches to the canonical codec hash so completion markers and payload-derived hashes share one identity.
  3. Partial-history support: BuildEvoSnapshot() originally required every enabled LLMQ type to carry exactly signingActiveQuorumCount actives plus full safety and rotation horizons, which a young chain, a freshly activated type, or a rotation type with pre-activation horizon cycles cannot satisfy — dumptxoutset would fail on valid chain state (flagged as blocking by review on backport: assumeutxo M4 — evo snapshot format v3 and LLMQ reconstruction #7579). The builder now emits the history that exists, context-free validation treats parameter counts as maxima, and chain validation requires every carried entry to sit at a derived horizon position while tolerating absent ones. This does not make withholding easier: the completion-time CbTx quorum merkle root pins the active set exactly, historical-diff and modifier tallies must mirror the carried commitments, and completion-time quorum reconstruction establishes sufficiency. Two regression tests (young-chain builder, partial-history validation) fail against the previous exact-count enforcement.

One accepted dev-channel caveat: the GetDeterministicMNListHash() encoding switch means a develop node that activated a snapshot before this change and completes validation after it would fail completion conservatively (marker hash mismatch) and re-sync from the base. No released version carries the old markers.

How Has This Been Tested?

Full unit suite green on a --enable-werror build; the chain-aware test block exercises reconstruction from seeded state only, pre/post-DIP3 validation matrices, rotation bitset/work-list binding, and the two new partial-history regressions. Snapshot suites also run under a --with-sanitizers=undefined,integer build.

Breaking Changes

None on any release branch (see the dev-channel caveat above).

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

PastaPastaPasta and others added 2 commits August 12, 2026 22:56
…e validation

First code PR of the assumeutxo M4 series (dashpay#7579 decomposition): the versioned interchange format for Dash's evo state alongside a UTXO snapshot - canonical serialization, DoS-bounded validating decode, and every validation invariant that needs no chain context. Chain-aware building/validation and dump/load integration follow in the next PRs of the series.

Canonical ordering exists because snapshot content is hashed and cross-checked; per-object serializers are reused through a bounded stream wrapper, with bespoke code only at container level (ordering, bounds, per-entry budgets); decode-time checks deliberately stay out of the trusted hot EvoDB deserializers. AssumeutxoData gains the EvoSnapshotHash anchor the format is pinned by. Includes the aggregate rotation skip-list bound (lists accumulate across every quorum index and wrap the combined MN list), the CRangesSet bounded unserializer, and a vendored-immer shift-base ubsan suppression reachable only through the deliberately hash-colliding test fixtures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… sign change

The mask for rejecting out-of-range trailing bits promotes through operator~ to a negative int before its implicit conversion back to uint8_t, which clang's implicit-integer-sign-change check reports for every bitset whose size is not a multiple of eight. The evo snapshot unit tests are the first to deserialize such bitsets under the sanitizer job. Same bits, stated explicitly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@PastaPastaPasta
PastaPastaPasta force-pushed the feat/evo-snapshot-chain branch from 67f4773 to 0a41edc Compare August 13, 2026 05:13
The codec PR shipped UnserializeBounded without its unit coverage; add the malformed/canonical decode matrix and the round-trip checks from the original series.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Second code PR of the assumeutxo M4 series: BuildEvoSnapshot() collects commitments, rotation snapshots, historical MN-list diffs, and exact score modifiers from chain state; ValidateEvoSnapshotAgainstChain() verifies a decoded snapshot against the block index, deployments, and quorum reconstruction; and the EvoDB seeding surface writes exactly the state reconstruction later reads. The chain-aware implementations live in the header-less evo/snapshot_chain.cpp unit so the codec in evo/snapshot.cpp never includes validation.h (no evo/snapshot -> validation cycle); ChainstateManager code is untouched here and stays in validation.cpp later in the series.

Parameter-derived quorum counts are carried as maxima rather than exact requirements: a young chain, a freshly activated type, or a rotation type whose horizon predates activation legitimately has less history, and requiring the full horizon made dumptxoutset fail on valid chain state (flagged as blocking by review on the original M4 PR). Completeness is anchored by the completion-time CbTx quorum merkle root, the historical-diff and modifier tallies, and completion-time quorum reconstruction; two regression tests fail against exact-count enforcement. GetHashModifier() prefers a seeded exact modifier when work-block data is unavailable and cross-checks it when present; the mismatch error stays unreachable until the load integration seeds modifiers. GetDeterministicMNListHash() switches to the canonical codec hash (dev-channel-only marker-compat caveat in the PR description).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@PastaPastaPasta
PastaPastaPasta force-pushed the feat/evo-snapshot-chain branch from 0a41edc to 707bd8d Compare August 13, 2026 16:48
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