Skip to content

backport: assumeutxo M1 — snapshot persistence (bitcoin#25308, #24513, #25667, #26828)#7451

Merged
PastaPastaPasta merged 5 commits into
dashpay:developfrom
thepastaclaw:assumeutxo/m1-persistence
Jul 13, 2026
Merged

backport: assumeutxo M1 — snapshot persistence (bitcoin#25308, #24513, #25667, #26828)#7451
PastaPastaPasta merged 5 commits into
dashpay:developfrom
thepastaclaw:assumeutxo/m1-persistence

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jul 12, 2026

Copy link
Copy Markdown

Issue being fixed or feature implemented

This is milestone 1 of 7 in the stacked assumeutxo backport series. It restores the previously approved minimal snapshot-initialization groundwork needed by later milestones while preserving Dash-specific chainstate manager behavior.

bitcoin#27596 is intentionally deferred to M5, when snapshots become user-loadable through the completed assumeutxo flow. M1 does not expose loadtxoutset and does not include any partial snapshot-deletion/reindex lifecycle from that later upstream change.

What was done?

  • Backported bitcoin#25308, reducing LoadChainstate parameters and return values through the upstream options/status plumbing while retaining Dash-specific options and the merged -parbls clamp.
  • Backported bitcoin#24513, the mechanical CChainState to Chainstate rename across 51 files (204 additions and 204 deletions).
  • Backported the historical bitcoin#25667 snapshot-initialization implementation.
  • Retained the previously approved minimal Dash adaptation for bitcoin#25667: test-fixture/database-option plumbing, teardown/recreation of Dash managers and txindex around ChainstateManager replacement, and removal of a redundant cs_main declaration already supplied by validation.h.
  • Backported bitcoin#26828, catching and logging snapshot-path fs::remove errors.

This branch contains no partial bitcoin#27596 behavior, crash-recovery hardening, reindex cleanup, manager-rebinding workaround, or unrelated -parbls fix. M2-M7 were not restacked by this update.

How Has This Been Tested?

  • Built the unit test binary with make -C src -j"$(sysctl -n hw.ncpu)" test/test_dash.
  • Passed ./src/test/test_dash --run_test=validation_chainstatemanager_tests (5 cases).
  • Passed ./src/test/test_dash --run_test=validation_flush_tests (1 case).
  • Passed ./src/test/test_dash --run_test=coinstatsindex_tests (2 cases).
  • Passed ./src/test/test_dash --run_test=dbwrapper_tests (9 cases).
  • Passed ./src/test/test_dash --run_test=blockmanager_tests (2 cases).
  • ./src/test/test_dash --run_test=validation_chainstate_tests reproduces the same chainstate_update_tip abort on the exact previously approved head 183f168ef9d32a809fed0f8b51002f01bfb2bb77; this reconstruction intentionally does not change inherited approved-baseline behavior.
  • The range-diff maps all five logical commits one-for-one. Commits 2, 3, and 5 retain = patch identity; commit 1 differs only by preserving the merged -parbls clamp through the LoadChainstate refactor, and commit 4 differs only by removing the redundant declaration. The sole rebase conflict was resolved transformation-faithfully in src/init.cpp.

Breaking Changes

None. This is unreleased assumeutxo groundwork; loadtxoutset remains unavailable in M1.

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

@thepastaclaw

thepastaclaw commented Jul 12, 2026

Copy link
Copy Markdown
Author

⛔ Blockers found — Sonnet deferred (commit 912c108)
Canonical validated blockers: 1

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 183f168ef9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/node/chainstate.cpp
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a256d3bb-057d-4f3c-98b1-f736915e3f48

📥 Commits

Reviewing files that changed from the base of the PR and between fe6da7c and 912c108.

📒 Files selected for processing (66)
  • ci/dash/lint-tidy.sh
  • doc/design/assumeutxo.md
  • doc/developer-notes.md
  • src/Makefile.am
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chainlock/signing.cpp
  • src/chainlock/signing.h
  • src/coinjoin/client.cpp
  • src/coinjoin/client.h
  • src/coinjoin/coinjoin.cpp
  • src/coinjoin/coinjoin.h
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/index/base.cpp
  • src/index/base.h
  • src/init.cpp
  • src/instantsend/net_instantsend.cpp
  • src/instantsend/net_instantsend.h
  • src/instantsend/signing.cpp
  • src/instantsend/signing.h
  • src/kernel/checks.cpp
  • src/kernel/checks.h
  • src/kernel/coinstats.cpp
  • src/kernel/coinstats.h
  • src/kernel/mempool_persist.cpp
  • src/kernel/mempool_persist.h
  • src/llmq/blockprocessor.cpp
  • src/llmq/blockprocessor.h
  • src/llmq/signing.h
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/node/interfaces.cpp
  • src/node/miner.cpp
  • src/node/miner.h
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/txoutproof.cpp
  • src/streams.h
  • src/test/coinstatsindex_tests.cpp
  • src/test/fuzz/mempool_utils.h
  • src/test/fuzz/tx_pool.cpp
  • src/test/net_tests.cpp
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.h
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/test/validation_flush_tests.cpp
  • src/txdb.h
  • src/txmempool.h
  • src/validation.cpp
  • src/validation.h
  • test/functional/feature_init.py
  • test/lint/lint-circular-dependencies.py
  • test/sanitizer_suppressions/tsan

Walkthrough

This change renames the chainstate implementation to Chainstate, refactors chainstate loading and verification around shared option/result types, adds persistent snapshot metadata and discovery helpers, and updates snapshot activation and cleanup. It propagates the API changes through validation, node services, RPCs, integrations, tests, documentation, build configuration, and tooling. Snapshot tests cover activation, persistence, restart handling, marker failures, cleanup, and initialization.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • dashpay/dash#7330: Modifies overlapping LoadChainstate and VerifyLoadedChainstate plumbing.
  • dashpay/dash#7361: Updates IWYU source selection involving kernel and mempool persistence files.

Suggested reviewers: pastapastapasta, udjinm6, knst

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change: M1 backport work for assumeutxo snapshot persistence.
Description check ✅ Passed The description is clearly aligned with the changeset, covering the backported assumeutxo groundwork, snapshot persistence, tests, and excluded later milestones.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/dbwrapper.h (1)

338-345: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make StoragePath() const.

This read-only accessor should be callable through const CDBWrapper&; otherwise const database/view wrappers cannot expose the storage path without changing their own constness.

Proposed fix
-    std::optional<fs::path> StoragePath() {
+    std::optional<fs::path> StoragePath() const {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dbwrapper.h` around lines 338 - 345, Update the StoragePath() accessor in
CDBWrapper to be const-qualified, preserving its existing read-only behavior and
return values so it can be called through a const CDBWrapper reference.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/init.cpp`:
- Around line 2031-2037: Update the -parbls calculation in the bls_threads
initializer to use int for the threads intermediate, including the core-count
adjustment and clamp input. Clamp the value in int, then cast the final clamped
result to int8_t when returning, preserving the existing bounds and behavior.

---

Nitpick comments:
In `@src/dbwrapper.h`:
- Around line 338-345: Update the StoragePath() accessor in CDBWrapper to be
const-qualified, preserving its existing read-only behavior and return values so
it can be called through a const CDBWrapper reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bbd6e530-cc5f-4077-98be-a3ee5b67e71f

📥 Commits

Reviewing files that changed from the base of the PR and between 13b9071 and 183f168.

📒 Files selected for processing (66)
  • ci/dash/lint-tidy.sh
  • doc/design/assumeutxo.md
  • doc/developer-notes.md
  • src/Makefile.am
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chainlock/signing.cpp
  • src/chainlock/signing.h
  • src/coinjoin/client.cpp
  • src/coinjoin/client.h
  • src/coinjoin/coinjoin.cpp
  • src/coinjoin/coinjoin.h
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/index/base.cpp
  • src/index/base.h
  • src/init.cpp
  • src/instantsend/net_instantsend.cpp
  • src/instantsend/net_instantsend.h
  • src/instantsend/signing.cpp
  • src/instantsend/signing.h
  • src/kernel/checks.cpp
  • src/kernel/checks.h
  • src/kernel/coinstats.cpp
  • src/kernel/coinstats.h
  • src/kernel/mempool_persist.cpp
  • src/kernel/mempool_persist.h
  • src/llmq/blockprocessor.cpp
  • src/llmq/blockprocessor.h
  • src/llmq/signing.h
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/node/interfaces.cpp
  • src/node/miner.cpp
  • src/node/miner.h
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/txoutproof.cpp
  • src/streams.h
  • src/test/coinstatsindex_tests.cpp
  • src/test/fuzz/mempool_utils.h
  • src/test/fuzz/tx_pool.cpp
  • src/test/net_tests.cpp
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.h
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/test/validation_flush_tests.cpp
  • src/txdb.h
  • src/txmempool.h
  • src/validation.cpp
  • src/validation.h
  • test/functional/feature_init.py
  • test/lint/lint-circular-dependencies.py
  • test/sanitizer_suppressions/tsan
💤 Files with no reviewable changes (1)
  • test/functional/feature_init.py

Comment thread src/init.cpp
@thepastaclaw thepastaclaw force-pushed the assumeutxo/m1-persistence branch from ff6b46b to bb0378e Compare July 12, 2026 17:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb0378e607

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/validation.cpp
@thepastaclaw thepastaclaw force-pushed the assumeutxo/m1-persistence branch from bb0378e to 52ef36c Compare July 12, 2026 17:56

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52ef36c57a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/node/chainstate.cpp
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Policy gate: an agent-reported missing upstream prerequisite was restored as a blocking finding. This PR is a full Bitcoin backport, and omitted upstream hunks require their prerequisite PRs unless the finding is explicitly allowlisted (intentional_exclusion / policy_override). The agent's original evidence is preserved in the finding(s) below.

Prior verifier summary (overridden by policy gate): PR #7451 (assumeutxo M1/7 backport: bitcoin#25308, bitcoin#24513, bitcoin#25667, bitcoin#26828) is functionally sound at head 52ef36c — no consensus-affecting defect was found. Six non-blocking issues survive independent verification against the live source and PR body: a standalone-build hazard in the squashed bitcoin#25667 commit, a latent dangling-reference bug in a test helper, two PR-body inaccuracies, an unrelated scope-creep commit, and a narrow soft-prerequisite gap around reindex-chainstate error handling. The prior bitcoin#21009/NeedsRedownload claim was refuted from first principles as SegWit-specific and architecturally inapplicable to Dash.

Source: reviewers gpt-5.6-sol and claude-sonnet-5 (general, dash-core-commit-history, backport-reviewer); verifier claude-sonnet-5.

Note: GitHub does not allow PastaClaw to submit REQUEST_CHANGES on its own PR, so the canonical blocking verdict is posted as a COMMENT.

🔴 9 blocking | 🟡 3 suggestion(s) | 💬 3 nitpick(s)

5 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/test/util/setup_common.h`:
- [SUGGESTION] src/test/util/setup_common.h:221-224: Squashed bitcoin#25667 merge commit (3ed61a861ad) does not build standalone
  Verified directly: at commit 3ed61a861ad, src/test/validation_chainstatemanager_tests.cpp's SnapshotTestSetup constructor calls `TestChain100Setup{{}, {}, /*coins_db_in_memory=*/false, /*block_tree_db_in_memory=*/false}` (4 arguments), but that exact commit's setup_common.h:222 declares only the 2-argument `TestChain100Setup(chain_name, extra_args)`. Checking out 3ed61a861ad alone is a compile error — the following commit, cc49052fb15, is required to widen the constructor. This breaks `git bisect` across that boundary and violates the project's own commit-atomicity convention (CLAUDE.md: 'Each commit should build and make sense on its own').
- [BLOCKING] src/test/util/setup_common.h:221-224: [Carried forward from p2, STILL VALID under new SHA] 3ed61a861ad (bitcoin#25667 merge) still does not build standalone; cc49052fb15 still exists only to repair it
  The branch was rebased since the last review — the prior-reported commit 19c3ee07885 no longer exists in this branch's ancestry, replaced by 3ed61a861ad (content-diffed and confirmed near-identical plus the new review-follow-up hunks described in the finding above). The underlying defect is unchanged: at 3ed61a861ad, src/test/validation_chainstatemanager_tests.cpp's `SnapshotTestSetup() : TestChain100Setup{{}, {}, /*coins_db_in_memory=*/false, /*block_tree_db_in_memory=*/false}` calls a 4-argument constructor, but that same commit's src/test/util/setup_common.h:222 still declares only `TestChain100Setup(const std::string& chain_name = ..., const std::vector<const char*>& extra_args = {})` (2 parameters) — a compile error if checked out alone. The next commit, cc49052fb15 ('backport: adapt Dash chainstate init and tests for bitcoin#25667', the rebased successor of ea68d3b2ed0), widens the constructor to 4 parameters. Squash the constructor-signature widening into 3ed61a861ad so every commit in the stack builds independently and git bisect remains usable.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

In `src/test/validation_chainstatemanager_tests.cpp`:
- [SUGGESTION] src/test/validation_chainstatemanager_tests.cpp:387-421: SimulateNodeRestart() leaves clhandler and peerman holding dangling ChainstateManager references
  Verified directly: SimulateNodeRestart() calls DashChainstateSetupClose(m_node) (src/node/chainstate.cpp:233-245), which only resets chain_helper, llmq_ctx, and dmnman, then replaces m_node.chainman with a new ChainstateManager (line 419). It never touches m_node.clhandler or m_node.peerman. Both chainlock::ChainlockHandler::m_chainman (src/chainlock/handler.h:47) and PeerManagerImpl::m_chainman (src/net_processing.cpp:821) are ChainstateManager& reference members bound once at fixture construction — after restart, both still reference the destroyed manager. Currently latent (neither component is exercised through this path in the test), but a future test invoking clhandler/peerman after SimulateNodeRestart() would use freed memory. Recreate or reset both alongside the chainman replacement.
- [BLOCKING] src/test/validation_chainstatemanager_tests.cpp:387-421: Restart adaptation retains references to the destroyed ChainstateManager
  STILL VALID; carried forward from p2. SimulateNodeRestart() stops the txindex, resets governance and the Dash chainstate helper hierarchy, then destroys and replaces m_node.chainman. DashChainstateSetupClose() resets chain_helper, llmq_ctx, and dmnman, but does not reset m_node.clhandler or m_node.peerman. Those objects retain ChainstateManager references established against the destroyed instance. The latest delta expands restart coverage but does not repair these references. Recreate or clear both manager-dependent objects around the replacement.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

In `src/node/chainstate.cpp`:
- [SUGGESTION] src/node/chainstate.cpp:64-69: Reindex-time snapshot-deletion failure returns generic FAILURE instead of upstream's FAILURE_FATAL, with a narrow user-facing quirk for -reindex-chainstate-only runs
  Dash's ChainstateLoadStatus enum (src/node/chainstate.h:56) has no FAILURE_FATAL value — that's added upstream by bitcoin#27862, which this PR correctly defers to M3 (it depends on bitcoin#25740's SnapshotCompletionResult). The adapted code at chainstate.cpp:69 returns plain FAILURE when snapshot deletion fails during a reindex. I verified the actual behavioral impact in src/init.cpp:2076-2112: when `-reindex` is set, FAILURE and FAILURE_INCOMPATIBLE_DB both short-circuit to InitError() with no difference in practice, so the originally-claimed 'prompts to rebuild while already reindexing' UX bug does NOT occur for plain -reindex. It does occur, narrowly, when only `-reindex-chainstate` (not `-reindex`) is passed and snapshot deletion fails: `!options.reindex` is then true, so the code prompts 'restart with -reindex or -reindex-chainstate to recover' even though -reindex-chainstate was already what triggered the failure. This is a real but narrow, non-crashing edge case tied to the already-acknowledged M3 deferral, not a new defect introduced by this PR's own code.
- [BLOCKING] src/node/chainstate.cpp:173-180: Missing prerequisite: bitcoin#21009
  STILL VALID; consolidates p5 and p7. Upstream bitcoin#25308 starts from a LoadChainstate path containing Chainstate::NeedsRedownload() and the witness-insufficiently-validated result introduced by bitcoin#21009 (merge 19a56d1519f, implementation commit d831e711cab). bitcoin#25308 transforms that check into the new ChainstateLoadResult form. Dash's pre-PR and current paths instead leave the chainstate-loading loop at lines 173-180 and proceed directly to Dash database migrations, with no NeedsRedownload check or explicit PR-description exclusion for bitcoin#21009. Dash lacks SegWit machinery, but the authoritative classification rule says an absent subsystem is not by itself evidence of intentional divergence when a PR claims the complete upstream PR. Backport bitcoin#21009 or explicitly document this permanent Dash-specific exclusion.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.
- [BLOCKING] src/node/chainstate.cpp:64-69: New in this delta: reindex-time snapshot deletion (adapted from bitcoin#27596/c711ca186f8) silently downgrades FAILURE_FATAL to FAILURE, a soft prerequisite gap from bitcoin#27862
  This code block is new since the last review (it does not exist in the prior-reviewed commit; independently confirmed via `diff <(git show 3ed61a861ad) <(git show 19c3ee07885)`, which shows it introduced under a 'Review follow-ups' note in the #25667 merge commit message: 'Adapt c711ca186f8 from bitcoin#27596 so reindex modes delete a persisted snapshot before database initialization'). Upstream's actual c711ca186f8 (bitcoin#27596, 'assumeutxo: remove snapshot during -reindex{-chainstate}') returns `ChainstateLoadStatus::FAILURE_FATAL` on deletion failure: `return {ChainstateLoadStatus::FAILURE_FATAL, Untranslated("Couldn't remove snapshot chainstate.")};`. Dash's adaptation at line 69 instead returns `ChainstateLoadStatus::FAILURE` because Dash's `enum class ChainstateLoadStatus` (src/node/chainstate.h) only has `{SUCCESS, FAILURE, FAILURE_INCOMPATIBLE_DB, INTERRUPTED}` — no `FAILURE_FATAL`. Traced the origin: `FAILURE_FATAL` is added to the enum by bitcoin#27862 commit 9047337d369d800e6eca4d3b686139073a8e8905 ('validation: Stricter assumeutxo error handling in LoadChainstate'), confirmed via `--follow` history pickaxe on src/node/chainstate.h. That same commit references `SnapshotCompletionResult::SUCCESS` (from bitcoin#25740), so bitcoin#27862 genuinely cannot be cleanly backported without #25740 — consistent with, and validating, this PR's own stated reason for deferring #27862 to M3. The practical effect on Dash's current code: in src/init.cpp:2089-2097, `FAILURE_INCOMPATIBLE_DB` is special-cased to abort immediately, but plain `FAILURE` (which this new reindex-deletion failure now returns) falls through to the generic path that prompts the user 'Do you want to rebuild the block database now? ... restart with -reindex or -reindex-chainstate to recover' — even though the failure occurred while a `-reindex`/`-reindex-chainstate` request was already being processed (retrying the same flag cannot fix a stuck/locked snapshot-directory deletion). This is a real, if narrow and non-crashing, UX/behavior degradation traceable to a missing prerequisite (bitcoin#27862), not a Dash-specific design choice. Given #25740/#27862 are correctly out of M1 scope, the pragmatic fix is not to backport them here, but to document this specific known limitation (either in the PR body's existing #27862 exclusion note, or as a code comment at line 69) so it isn't mistaken for an oversight in a later milestone.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

In `(PR description)`:
- [BLOCKING] (PR description): Backport-prerequisite items (bitcoin#21009/NeedsRedownload, bitcoin#21110 chrono check) — out of this role's scope entirely
  Per review.md, prerequisite analysis is owned exclusively by the backport-reviewer specialist. Noting only that the PR body now contains new audit text ('bitcoin#21585 was audited and found already present...', 'bitcoin#27862 ... moved to M3', and the existing '### Intentional exclusion: bitcoin#21110' section) that appears responsive to some of the prior policy-restored prerequisite findings; the backport-reviewer lane should make the actual determination at this exact head.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.
- [BLOCKING] (PR description): Snapshot persistence remains attributed to the wrong upstream PR
  STILL VALID; consolidates p3, p6, and p9. The supplied PR context continues to attribute the chainstate_snapshot directory and persistence to bitcoin#25308. Upstream bitcoin#25308 only introduces the LoadChainstate options, status, and result refactor. The directory rename and persisted base_blockhash come from bitcoin#25667, including f9f1735f139. The implementation is present, so this is a metadata issue rather than a missing code prerequisite; move the persistence wording to the bitcoin#25667 entry.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

In `src/validation.cpp`:
- [BLOCKING] src/validation.cpp:5822-5829: Missing prerequisite: bitcoin#25740 before the bitcoin#27596 follow-up
  NEW IN THE LATEST DELTA. Commit 3ed61a861ad explicitly imports c711ca186f8 from bitcoin#27596. That upstream commit starts after bitcoin#25740 (merge d5e4f9a4395), which factors chainstate initialization and adds background-validation completion state. Dash has not backported that starting state, so DeleteSnapshotChainstate is rewritten to discover the directory directly because the snapshot has no initialized CoinsViews. The current comment states this difference explicitly, making it a workaround for not-yet-backported upstream content rather than a Dash architectural divergence. The adaptation is coherent and tested, so this is a soft, non-blocking per the agent (restored to blocking by policy) prerequisite gap; document the partial bitcoin#27596 backport and its bitcoin#25740 dependency, or stage it after bitcoin#25740.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

In `<commit:3ed61a861ad>`:
- [BLOCKING] <commit:3ed61a861ad>:1: The bitcoin#25667 commit is not independently buildable
  STILL VALID; carried forward from p1 with rewritten commit hashes. At 3ed61a861ad, SnapshotTestSetup calls TestChain100Setup with four arguments, while that exact commit's src/test/util/setup_common.h still declares only the two-argument constructor. The following Dash adaptation commit cc49052fb15 adds the database-option parameters. The final head contains the repair, but the upstream backport commit itself cannot compile. Move the required constructor adaptation into 3ed61a861ad or otherwise restructure the pair so every permanent commit builds independently.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

In `src/init.cpp`:
- [BLOCKING] src/init.cpp:2031-2037: The unrelated parbls fix remains outside the advertised backport scope
  STILL VALID; carried forward from p4. The tail commit 52ef36c57ab fixes pre-existing -parbls truncation but is not part of bitcoin#25308, #24513, #25667, or #26828. The fix itself introduces no prerequisite gap. Mention it in the PR description or move it to a separate change so the advertised backport ledger matches the reachable commit stack.

  ---
  **Policy gate (backport-prereq-restore):** For full upstream backport PRs, a missing prerequisite is blocking unless the finding is explicitly allowlisted (e.g. `intentional_exclusion: true` or a matching entry in `policy_overrides`). The agent's original evidence above is the basis for this block; either backport the prerequisite or annotate the intentional exclusion in the PR description.

@thepastaclaw thepastaclaw force-pushed the assumeutxo/m1-persistence branch from 52ef36c to ef11d90 Compare July 12, 2026 18:37
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ef11d9069a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/validation.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/dbwrapper.h (1)

338-344: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider marking StoragePath() const.

It's a pure accessor with no mutation, consistent with Read/Exists/etc. which are already const.

♻️ Proposed fix
-    std::optional<fs::path> StoragePath() {
+    std::optional<fs::path> StoragePath() const {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dbwrapper.h` around lines 338 - 344, Mark the StoragePath() accessor as
const, matching the existing const-qualified read-only methods such as Read and
Exists, without changing its return behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/dbwrapper.h`:
- Around line 338-344: Mark the StoragePath() accessor as const, matching the
existing const-qualified read-only methods such as Read and Exists, without
changing its return behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fdfa8010-0fea-412f-b735-5423f14bf082

📥 Commits

Reviewing files that changed from the base of the PR and between 52ef36c and ef11d90.

📒 Files selected for processing (20)
  • doc/design/assumeutxo.md
  • src/Makefile.am
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/init.cpp
  • src/node/blockstorage.cpp
  • src/node/chainstate.cpp
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/streams.h
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/txdb.h
  • src/validation.cpp
  • src/validation.h
  • test/functional/feature_init.py
  • test/lint/lint-circular-dependencies.py
💤 Files with no reviewable changes (1)
  • test/functional/feature_init.py
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/dbwrapper.cpp
  • test/lint/lint-circular-dependencies.py
  • src/txdb.h
  • doc/design/assumeutxo.md
  • src/node/blockstorage.cpp
  • src/streams.h
  • src/node/utxo_snapshot.h
  • src/node/chainstate.cpp
  • src/test/util/chainstate.h
  • src/validation.h
  • src/validation.cpp

@thepastaclaw thepastaclaw force-pushed the assumeutxo/m1-persistence branch from ef11d90 to 7d207dc Compare July 12, 2026 19:06
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d207dc5f4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/validation.cpp
Comment thread src/validation.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/test/validation_chainstatemanager_tests.cpp`:
- Around line 400-435: Update SimulateNodeRestart to reset m_node.peerman before
destroying and recreating m_node.chainman. Ensure the peer manager is torn down
while the existing ChainstateManager is still valid, before
m_node.chainman.reset(new ChainstateManager(...)), so no validation-interface
callbacks can use a dangling ChainstateManager reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0ecc9e85-2c30-4cb8-b41b-e71425f1b06f

📥 Commits

Reviewing files that changed from the base of the PR and between ef11d90 and 7d207dc.

📒 Files selected for processing (20)
  • doc/design/assumeutxo.md
  • src/Makefile.am
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/init.cpp
  • src/node/blockstorage.cpp
  • src/node/chainstate.cpp
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/streams.h
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/txdb.h
  • src/validation.cpp
  • src/validation.h
  • test/functional/feature_init.py
  • test/lint/lint-circular-dependencies.py
💤 Files with no reviewable changes (1)
  • test/functional/feature_init.py
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/Makefile.am
  • src/streams.h
  • src/txdb.h
  • doc/design/assumeutxo.md
  • src/dbwrapper.cpp
  • src/node/blockstorage.cpp
  • src/node/utxo_snapshot.h
  • src/test/validation_chainstate_tests.cpp
  • src/node/chainstate.cpp
  • src/init.cpp
  • src/validation.cpp

Comment thread src/test/validation_chainstatemanager_tests.cpp Outdated
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw thepastaclaw force-pushed the assumeutxo/m1-persistence branch from 7d207dc to 4a319cd Compare July 12, 2026 19:35
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d49910f32e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/validation.cpp Outdated
Comment on lines +5576 to +5577
if (!m_ibd_chainstate->FlushStateToDisk(rollback_state, FlushStateMode::ALWAYS)) {
AbortNode(strprintf("Failed to roll back snapshot block index metadata: %s", rollback_state.ToString()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve snapshot recovery when rollback flush fails

If the marker write fails after the prior FlushStateToDisk() has made the fake nTx/BLOCK_ASSUMED_VALID block-index metadata durable, and this rollback flush also fails (for example due to ENOSPC or an I/O error), AbortNode() only requests shutdown and execution continues into the cleanup block below, which deletes chainstate_snapshot. The next restart can then have blocks/index entries requiring an assumed-valid snapshot but no snapshot directory for recovery or -reindex-chainstate cleanup. Return before deleting the snapshot directory, or otherwise preserve a recoverable marker/dir when the rollback cannot be made durable.

Useful? React with 👍 / 👎.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe6da7c9b7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/validation.cpp
Comment on lines +5464 to +5466
if (!in_memory) {
LOCK(::cs_main);
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sync snapshot coins before publishing marker

When loading a disk-backed snapshot, this publishes chainstate_snapshot/base_blockhash immediately after PopulateAndValidateSnapshot(), but that path only writes the snapshot coins through normal LevelDB batches and never forces the snapshot coins DB durable before the marker is written. After a crash or power loss where the marker reaches disk but the snapshot LevelDB rolls back or is empty, the next startup will detect the marker and activate an unusable snapshot chainstate instead of falling back to IBD. Please force/sync the snapshot coins DB before writing the marker, or defer publishing the marker until the snapshot DB durability is guaranteed.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/validation.cpp`:
- Around line 5459-5469: Update the persistence condition in the snapshot
validation flow to require both snapshot_ok and !in_memory before calling
node::WriteSnapshotBaseBlockhash. Keep the existing failure handling that sets
snapshot_ok to false when the write fails, and ensure failed snapshot loads
never persist the base_blockhash marker.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 490be220-35a2-4b65-80e0-e04dd0b32db1

📥 Commits

Reviewing files that changed from the base of the PR and between d49910f and fe6da7c.

📒 Files selected for processing (66)
  • ci/dash/lint-tidy.sh
  • doc/design/assumeutxo.md
  • doc/developer-notes.md
  • src/Makefile.am
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chainlock/signing.cpp
  • src/chainlock/signing.h
  • src/coinjoin/client.cpp
  • src/coinjoin/client.h
  • src/coinjoin/coinjoin.cpp
  • src/coinjoin/coinjoin.h
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/index/base.cpp
  • src/index/base.h
  • src/init.cpp
  • src/instantsend/net_instantsend.cpp
  • src/instantsend/net_instantsend.h
  • src/instantsend/signing.cpp
  • src/instantsend/signing.h
  • src/kernel/checks.cpp
  • src/kernel/checks.h
  • src/kernel/coinstats.cpp
  • src/kernel/coinstats.h
  • src/kernel/mempool_persist.cpp
  • src/kernel/mempool_persist.h
  • src/llmq/blockprocessor.cpp
  • src/llmq/blockprocessor.h
  • src/llmq/signing.h
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/node/interfaces.cpp
  • src/node/miner.cpp
  • src/node/miner.h
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/txoutproof.cpp
  • src/streams.h
  • src/test/coinstatsindex_tests.cpp
  • src/test/fuzz/mempool_utils.h
  • src/test/fuzz/tx_pool.cpp
  • src/test/net_tests.cpp
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.h
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/test/validation_flush_tests.cpp
  • src/txdb.h
  • src/txmempool.h
  • src/validation.cpp
  • src/validation.h
  • test/functional/feature_init.py
  • test/lint/lint-circular-dependencies.py
  • test/sanitizer_suppressions/tsan
💤 Files with no reviewable changes (1)
  • test/functional/feature_init.py
🚧 Files skipped from review as they are similar to previous changes (49)
  • src/test/util/validation.h
  • test/lint/lint-circular-dependencies.py
  • src/test/fuzz/mempool_utils.h
  • src/test/validation_flush_tests.cpp
  • ci/dash/lint-tidy.sh
  • src/coinjoin/coinjoin.cpp
  • src/bench/load_external.cpp
  • src/instantsend/signing.cpp
  • src/Makefile.am
  • src/node/miner.h
  • src/coinjoin/coinjoin.h
  • src/node/interfaces.cpp
  • src/node/blockstorage.h
  • src/kernel/checks.h
  • src/chainlock/signing.h
  • src/chainlock/signing.cpp
  • src/test/net_tests.cpp
  • src/txdb.h
  • src/rpc/rawtransaction.cpp
  • src/llmq/blockprocessor.cpp
  • src/rpc/mining.cpp
  • src/instantsend/net_instantsend.h
  • src/streams.h
  • src/dbwrapper.cpp
  • src/node/miner.cpp
  • src/coinjoin/walletman.h
  • src/rpc/txoutproof.cpp
  • src/test/fuzz/tx_pool.cpp
  • doc/developer-notes.md
  • src/rpc/blockchain.h
  • src/coinjoin/client.cpp
  • src/coinjoin/walletman.cpp
  • src/llmq/signing.h
  • src/txmempool.h
  • src/index/base.h
  • src/coinjoin/client.h
  • src/test/validation_chainstate_tests.cpp
  • src/kernel/mempool_persist.h
  • src/kernel/mempool_persist.cpp
  • src/instantsend/net_instantsend.cpp
  • src/instantsend/signing.h
  • src/index/base.cpp
  • src/test/coinstatsindex_tests.cpp
  • src/node/chainstate.h
  • src/rpc/mempool.cpp
  • src/dbwrapper.h
  • src/kernel/coinstats.h
  • src/rpc/blockchain.cpp
  • src/init.cpp

Comment thread src/validation.cpp
Comment on lines +5459 to +5469
bool snapshot_ok = this->PopulateAndValidateSnapshot(
*snapshot_chainstate, coins_file, metadata);

// If not in-memory, persist the base blockhash for use during subsequent
// initialization.
if (!in_memory) {
LOCK(::cs_main);
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
snapshot_ok = false;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and locate related symbols.
ast-grep outline src/validation.cpp --view expanded | sed -n '1,220p'

printf '\n--- matches ---\n'
rg -n "WriteSnapshotBaseBlockhash|DetectSnapshotChainstate|DeleteCoinsDBFromDisk|PopulateAndValidateSnapshot|base_blockhash|snapshot_ok" src/validation.cpp src -S

Repository: dashpay/dash

Length of output: 18726


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant section of validation.cpp.
nl -ba src/validation.cpp | sed -n '5435,5515p'

printf '\n--- write marker implementation ---\n'
rg -n "bool WriteSnapshotBaseBlockhash|WriteSnapshotBaseBlockhash\\(" src -S

printf '\n--- detect snapshot chainstate ---\n'
rg -n "DetectSnapshotChainstate\\(" src -S

Repository: dashpay/dash

Length of output: 190


🌐 Web query:

site:github.com dash core validation.cpp DetectSnapshotChainstate WriteSnapshotBaseBlockhash base_blockhash snapshot chainstate

💡 Result:

In Dash Core and similar Bitcoin-derived codebases, the components DetectSnapshotChainstate, WriteSnapshotBaseBlockhash, and the base_blockhash itself are part of the assumeutxo (UTXO snapshot) infrastructure [1][2][3]. DetectSnapshotChainstate This is a function used during node startup to search the data directory for an existing chainstate directory derived from a UTXO snapshot [2][4]. If such a directory is found, the node loads it as the active chainstate [2][4]. This allows the node to begin operation from a pre-validated UTXO set rather than re-syncing the entire blockchain from genesis [2][3]. base_blockhash The base_blockhash represents the hash of the specific block that serves as the foundation for the snapshot [5][3]. When a snapshot is created, the resulting UTXO set reflects the state of the chain at that specific block [5]. This hash is critical for identifying the snapshot and ensuring that the node correctly validates subsequent blocks starting from that height [3]. WriteSnapshotBaseBlockhash This function is responsible for persisting the base_blockhash to a file named base_blockhash within the snapshot's chainstate directory [2][5][6]. During subsequent node restarts, the node reads this file to reconstruct the identity of the snapshot-based chainstate [5][3]. In summary, when a snapshot-based chainstate is initialized, it is stored in a specific directory [2][7]. The WriteSnapshotBaseBlockhash function saves the snapshot's base block hash into a file within that directory [3][6]. Upon restarting, the node uses DetectSnapshotChainstate to locate the directory and read this hash, ensuring the chainstate is correctly reinitialized and aligned with the intended block height [2][4][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant region with line numbers.
sed -n '5448,5498p' src/validation.cpp | cat -n

printf '\n--- surrounding failure cleanup ---\n'
sed -n '5498,5575p' src/validation.cpp | cat -n

Repository: dashpay/dash

Length of output: 6110


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- utxo_snapshot.cpp ---'
sed -n '1,140p' src/node/utxo_snapshot.cpp | cat -n

printf '\n%s\n' '--- DetectSnapshotChainstate ---'
sed -n '5830,5865p' src/validation.cpp | cat -n

printf '\n%s\n' '--- DeleteCoinsDBFromDisk ---'
sed -n '5358,5398p' src/validation.cpp | cat -n

Repository: dashpay/dash

Length of output: 7117


Persist the base_blockhash marker only after snapshot load succeeds. Guard the write with snapshot_ok && !in_memory; otherwise a failed load can leave chainstate_snapshot/base_blockhash behind, and DetectSnapshotChainstate() will trust the leftover snapshot dir on the next startup if the process dies before cleanup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/validation.cpp` around lines 5459 - 5469, Update the persistence
condition in the snapshot validation flow to require both snapshot_ok and
!in_memory before calling node::WriteSnapshotBaseBlockhash. Keep the existing
failure handling that sets snapshot_ok to false when the write fails, and ensure
failed snapshot loads never persist the base_blockhash marker.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

PastaPastaPasta and others added 5 commits July 13, 2026 15:10
…ters and return values

Upstream commits:

- 3b91d4b refactor: Reduce number of LoadChainstate parameters
- b3e7de7 refactor: Reduce number of LoadChainstate return values
- 6db6552 refactor: Reduce number of SanityChecks return values
- 1e761a0 ci: Enable IWYU in src/kernel directory
Upstream verification script:

sed -i 's/CChainState/Chainstate/g' $(git grep -l CChainState ':(exclude)doc/release-notes*')

Verification: PASS. Applying the replacement to the parent tree reproduces this commit exactly across all 51 affected files, with no missing, unexpected, or non-mechanical changes.
Upstream commits:

d14bebf db: add StoragePath to CDBWrapper/CCoinsViewDB
f9f1735 validation: rename snapshot chainstate dir
252abd1 init: add utxo snapshot detection
34d1590 add utilities for deleting on-disk leveldb data
ad67ff3 validation: remove snapshot datadirs upon validation failure
8153bd9 blockmanager: avoid undefined behavior during FlushBlockFile
3a29dfb move-only: test: make snapshot chainstate setup reusable
00b357c validation: add ResetChainstates()
3c36139 test: add reset_chainstate parameter for snapshot unittests
51fc924 test: allow on-disk coins and block tree dbs in tests
cced4e7 test: move-only-ish: factor out LoadVerifyActivateChainstate()
e4d7995 test: add testcases for snapshot initialization
bf95976 doc: add note about snapshot chainstate init
Thread on-disk database options through Dash's generalized TestChainSetup hierarchy, and make snapshot restart tests tear down and recreate Dash managers and txindex around ChainstateManager replacement.
@thepastaclaw thepastaclaw force-pushed the assumeutxo/m1-persistence branch from fe6da7c to 912c108 Compare July 13, 2026 20:19

@PastaPastaPasta PastaPastaPasta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 912c108

@PastaPastaPasta

Copy link
Copy Markdown
Member

I believe some of the automated code review from rabbit / openai are flagging issues bitcoin fixes later; and we fix in later prs in the stacked series.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@PastaPastaPasta PastaPastaPasta merged commit f153015 into dashpay:develop Jul 13, 2026
44 checks passed
@UdjinM6 UdjinM6 added this to the 24 milestone Jul 13, 2026

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preliminary review — Codex only

At exact HEAD 912c108, one blocking commit-history defect remains: cc4903f uses constructor parameters added only by a1a2448. SimulateNodeRestart() also leaves ChainlockHandler and PeerManager bound to the destroyed ChainstateManager; the PeerManager regression was introduced by the latest rebase. The historical review artifact was read, the checkout and commit boundaries were verified, and git diff --check passed. No local test binary was available.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (failed), gpt-5.6-sol — dash-core-commit-history (failed), gpt-5.6-sol — backport-reviewer (failed), gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed), gpt-5.6-sol — backport-reviewer (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 1 blocking | 🟡 2 suggestion(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/test/validation_chainstatemanager_tests.cpp`:
- [BLOCKING] src/test/validation_chainstatemanager_tests.cpp:189-194: bitcoin#25667 cherry-pick is not independently buildable
  Commit cc4903f2b85 constructs SnapshotTestSetup with four TestChain100Setup arguments, but that commit's setup_common.h declares only the two-argument constructor. The required coins_db_in_memory and block_tree_db_in_memory plumbing first appears in a1a24480661. Upstream merge 6912a28f08c included the fixture call and constructor widening in one transformation, so checking out or bisecting to the permanent bitcoin#25667 import produces a compile error. Move the constructor-plumbing hunks into cc4903f2b85 or place them in a coherent preparatory commit before it.
- [SUGGESTION] src/test/validation_chainstatemanager_tests.cpp:387-419: Dash restart adaptation leaves ChainlockHandler bound to the destroyed manager
  SimulateNodeRestart() destroys m_node.chainman without destroying or recreating m_node.clhandler. ChainTestingSetup constructs that handler with the original ChainstateManager&, ChainlockHandler stores it as the direct reference m_chainman, and DashChainstateSetupClose() does not own the handler. The current test does not exercise a chainstate-dependent ChainLocks operation after restart, but the fixture retains a dangling reference. Stop and reset the handler before replacing the manager, then recreate it against the replacement if the restarted fixture needs ChainLocks.
- [SUGGESTION] src/test/validation_chainstatemanager_tests.cpp:387-419: Latest rebase restores a dangling PeerManager reference
  The 5686926e92e..912c10871b1 delta removes m_node.peerman.reset() before m_node.chainman is destroyed and replaced. PeerManagerImpl stores the original manager as the direct reference m_chainman, and LoadVerifyActivateChainstate() does not recreate PeerManager. The current test does not invoke peer processing after restart, but the fixture leaves PeerManager attached to freed state. Reset PeerManager while the old manager is alive and recreate it, including the connection-manager message-processor binding, if later fixture operations require peer processing.

GitHub does not permit an author to request changes on its own PR. This review is posted as a comment; the preliminary gate remains blocked by the validated blocker above.

Comment on lines +189 to +194
SnapshotTestSetup() : TestChain100Setup{
CBaseChainParams::REGTEST,
{},
/*coins_db_in_memory=*/false,
/*block_tree_db_in_memory=*/false,
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: bitcoin#25667 cherry-pick is not independently buildable

Commit cc4903f constructs SnapshotTestSetup with four TestChain100Setup arguments, but that commit's setup_common.h declares only the two-argument constructor. The required coins_db_in_memory and block_tree_db_in_memory plumbing first appears in a1a2448. Upstream merge 6912a28 included the fixture call and constructor widening in one transformation, so checking out or bisecting to the permanent bitcoin#25667 import produces a compile error. Move the constructor-plumbing hunks into cc4903f or place them in a coherent preparatory commit before it.

source: ['codex']

Comment on lines +387 to +419
ChainstateManager& SimulateNodeRestart()
{
ChainstateManager& chainman = *Assert(m_node.chainman);

BOOST_TEST_MESSAGE("Simulating node restart");
// TestChainSetup owns a txindex connected to the old chainstate. Stop it
// before destroying that chainstate, just as TestChainSetup's destructor does.
IndexWaitSynced(*g_txindex);
g_txindex->Interrupt();
g_txindex->Stop();
SyncWithValidationInterfaceQueue();
g_txindex.reset();
{
LOCK(::cs_main);
for (Chainstate* cs : chainman.GetAll()) {
cs->ForceFlushStateToDisk();
}
// Tear down Dash managers connected to the mempool and old chainstate
// before LoadVerifyActivateChainstate() recreates them below.
m_node.govman.reset();
DashChainstateSetupClose(m_node);
chainman.ResetChainstates();
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
const ChainstateManager::Options chainman_opts{
.chainparams = ::Params(),
.adjusted_time_callback = GetAdjustedTime,
};
// For robustness, ensure the old manager is destroyed before creating a
// new one.
m_node.chainman.reset();
m_node.chainman.reset(new ChainstateManager(chainman_opts));
}
return *Assert(m_node.chainman);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Dash restart adaptation leaves ChainlockHandler bound to the destroyed manager

SimulateNodeRestart() destroys m_node.chainman without destroying or recreating m_node.clhandler. ChainTestingSetup constructs that handler with the original ChainstateManager&, ChainlockHandler stores it as the direct reference m_chainman, and DashChainstateSetupClose() does not own the handler. The current test does not exercise a chainstate-dependent ChainLocks operation after restart, but the fixture retains a dangling reference. Stop and reset the handler before replacing the manager, then recreate it against the replacement if the restarted fixture needs ChainLocks.

source: ['codex']

Comment on lines +387 to +419
ChainstateManager& SimulateNodeRestart()
{
ChainstateManager& chainman = *Assert(m_node.chainman);

BOOST_TEST_MESSAGE("Simulating node restart");
// TestChainSetup owns a txindex connected to the old chainstate. Stop it
// before destroying that chainstate, just as TestChainSetup's destructor does.
IndexWaitSynced(*g_txindex);
g_txindex->Interrupt();
g_txindex->Stop();
SyncWithValidationInterfaceQueue();
g_txindex.reset();
{
LOCK(::cs_main);
for (Chainstate* cs : chainman.GetAll()) {
cs->ForceFlushStateToDisk();
}
// Tear down Dash managers connected to the mempool and old chainstate
// before LoadVerifyActivateChainstate() recreates them below.
m_node.govman.reset();
DashChainstateSetupClose(m_node);
chainman.ResetChainstates();
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
const ChainstateManager::Options chainman_opts{
.chainparams = ::Params(),
.adjusted_time_callback = GetAdjustedTime,
};
// For robustness, ensure the old manager is destroyed before creating a
// new one.
m_node.chainman.reset();
m_node.chainman.reset(new ChainstateManager(chainman_opts));
}
return *Assert(m_node.chainman);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Latest rebase restores a dangling PeerManager reference

The 5686926..912c108 delta removes m_node.peerman.reset() before m_node.chainman is destroyed and replaced. PeerManagerImpl stores the original manager as the direct reference m_chainman, and LoadVerifyActivateChainstate() does not recreate PeerManager. The current test does not invoke peer processing after restart, but the fixture leaves PeerManager attached to freed state. Reset PeerManager while the old manager is alive and recreate it, including the connection-manager message-processor binding, if later fixture operations require peer processing.

source: ['codex']

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.

4 participants