Skip to content

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

Closed
PastaPastaPasta wants to merge 5 commits into
developfrom
assumeutxo/m1-persistence
Closed

backport: assumeutxo M1 — snapshot persistence (bitcoin#25308, #24513, #25667, #26828)#50
PastaPastaPasta wants to merge 5 commits into
developfrom
assumeutxo/m1-persistence

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Issue being fixed or feature implemented

Dash's assumeutxo support predates upstream Bitcoin Core's post-bitcoin#25667 work: we
have multi-chainstate/ActivateSnapshot/dumptxoutset, but nothing from
bitcoin#25667 onward (no persisted chainstate_snapshot dir, no crash-safe
snapshot bookkeeping). This is milestone 1 (of 7) of a plan to bring Dash's
assumeutxo implementation up to a modern, loadtxoutset-capable baseline
while preserving Dash's masternode/LLMQ/EvoDB/credit-pool extensions. The
execution plan and per-unit ledger (M1 section) are tracked outside the repo
in a private gist, not checked in: https://gist.github.com/PastaPastaPasta/aa52b1f89fb74a0566ba3b5e15afab6a

This is PR 1/7 in a stacked series (M1 → M7), based on develop at d9364c15e0.

What was done?

Backported the snapshot-persistence layer that later milestones build on:

  • bitcoin#25308 — snapshot chainstate persistence to disk (chainstate_snapshot
    dir, options/status/result plumbing). Dash extras (dash_dbs_in_memory,
    bls_threads, worker_count, max_recsigs_age, notify_bls_state) folded
    into the same options struct; all Dash error strings preserved.
  • bitcoin#24513 — mechanical Chainstate rename (scripted diff, 52 files, 205
    insertions/205 deletions, zero non-rename lines) so the rest of the upstream
    series applies against matching identifiers. Inserted as a discovered
    prerequisite not in the original plan.
  • bitcoin#25667ActivateExistingSnapshot, following Dash's existing
    shared-EvoDB pattern (the EvoDB-role redesign itself is deferred to M2 by
    design). utxo_snapshot.{h,cpp} are byte-identical to upstream post-state.
  • bitcoin#26828 — small snapshot-path fix, verbatim vs. upstream.
  • bitcoin#21585 audited and found already-present (arrived via an earlier
    batched backport without its own merge commit); documented in the ledger
    rather than re-applied.
  • bitcoin#27862 was originally planned for M1 but depends on
    SnapshotCompletionResult from bitcoin#25740, so it moved to M3.

How Has This Been Tested?

  • make check: exit 0.
  • Functional subset: feature_init, feature_reindex, feature_pruning,
    feature_abortnode, feature_utxo_set_hash, rpc_blockchain ×2,
    wallet_hd ×2 — all passed.
  • Lint: circular-dependencies and whitespace PASS (python lint deferred to CI).
  • Two-round review process per unit (gate1 hunk/diff audit, gate2 prerequisite
    review) recorded per-commit in the ledger; milestone gate PASS on 2026-07-10.

Breaking Changes

None. This is unreleased assumeutxo groundwork; no user-facing RPC or format
changes ship in this PR (loadtxoutset itself stays unexposed until M4/M5).

Checklist:

  • I have performed a self-review of my own code
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation (docs rewrite lands in M7/B8)
  • I have assigned this pull request to a milestone

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PastaPastaPasta, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3548e3b1-2c13-4836-9867-027a687ff984

📥 Commits

Reviewing files that changed from the base of the PR and between dbcad8c 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
📝 Walkthrough

Walkthrough

Changes

The pull request refactors Dash Core around the Chainstate type, introduces structured chainstate loading results and snapshot-chainstate persistence, and adds snapshot activation and restart coverage. It also migrates CoinJoin access to callbacks, bounds network vector deserialization, improves locking and pruning behavior, updates quorum and ChainLock handling, and expands regression tests and documentation.

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

Sequence Diagram(s)

sequenceDiagram
  participant AppInitMain
  participant LoadChainstate
  participant VerifyLoadedChainstate
  participant ChainstateManager
  participant SnapshotStorage

  AppInitMain->>LoadChainstate: pass CacheSizes and ChainstateLoadOptions
  LoadChainstate->>SnapshotStorage: initialize or load chainstate databases
  LoadChainstate-->>AppInitMain: return ChainstateLoadResult
  AppInitMain->>VerifyLoadedChainstate: verify loaded chainstate with options
  VerifyLoadedChainstate-->>AppInitMain: return status and bilingual error
  AppInitMain->>ChainstateManager: activate loaded chainstates
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.36% 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main assumeutxo M1 snapshot-persistence backport, even though the PR also includes several supporting refactors.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch assumeutxo/m1-persistence

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.

…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
@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m1-persistence branch from dbcad8c to 01795d3 Compare July 12, 2026 13:58

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
doc/design/assumeutxo.md (1)

117-118: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale chainstate_[hash] reference to match new chainstate_snapshot naming.

Lines 79-81 were updated to use chainstate_snapshot as the snapshot chainstate directory name, but line 117 still references the old chainstate_[hash] naming when describing ValidatedSnapshotShutdownCleanup(). This should say chainstate_snapshot for consistency.

📝 Proposed fix
-the background chainstate is cleaned up with
-`ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_[hash]` datadir as
-`chainstate`.
+the background chainstate is cleaned up with
+`ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_snapshot` datadir as
+`chainstate`.
🤖 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 `@doc/design/assumeutxo.md` around lines 117 - 118, Update the
`ValidatedSnapshotShutdownCleanup()` description in the documentation to replace
the stale `chainstate_[hash]` directory reference with `chainstate_snapshot`,
matching the naming established elsewhere in the document.
🧹 Nitpick comments (2)
src/test/evo_deterministicmns_tests.cpp (1)

261-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated tip-accessor lambdas into a shared helper.

tip_index/tip_height/tip_hash/sync_dmn_tip are redefined nearly identically in ~10 functions in this file. A single helper (e.g. a small struct or free function taking chainman/dmnman by reference) would remove the duplication and centralize the locking pattern for future changes.

♻️ Example helper
struct TipAccessors {
    ChainstateManager& chainman;
    CDeterministicMNManager& dmnman;
    const CBlockIndex* tip_index() const { return WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()); }
    int tip_height() const { return WITH_LOCK(::cs_main, return chainman.ActiveChain().Height()); }
    uint256 tip_hash() const { return WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->GetBlockHash()); }
    void sync_dmn_tip() const { dmnman.UpdatedBlockTip(tip_index()); }
};

Also applies to: 299-301, 440-441, 539-540, 598-599, 706-708, 941-942, 989-991, 1180-1182, 1329-1332

🤖 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/test/evo_deterministicmns_tests.cpp` around lines 261 - 264, Introduce
one shared helper for the repeated tip accessors, such as a TipAccessors struct
holding ChainstateManager and CDeterministicMNManager references, with methods
for tip_index, tip_height, tip_hash, and sync_dmn_tip that preserve the existing
locking and update behavior. Replace the local lambdas in all affected test
functions with this helper and update their call sites accordingly.
src/dbwrapper.h (1)

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

Consider making StoragePath() const.

It only reads m_is_memory/m_path, so marking it const lets it be called through a const CDBWrapper& and better expresses intent.

♻️ Proposed change
-    //! `@returns` filesystem path to the on-disk data.
-    std::optional<fs::path> StoragePath() {
+    //! `@returns` filesystem path to the on-disk data.
+    std::optional<fs::path> StoragePath() const {
         if (m_is_memory) {
             return {};
         }
         return m_path;
     }
🤖 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 CDBWrapper::StoragePath()
method const, since it only reads m_is_memory and m_path. Preserve its existing
return behavior for memory-backed and on-disk storage.
🤖 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/coinjoin/interfaces.cpp`:
- Line 35: Align the runtime default in CCoinJoinClientOptions initialization
with the documented disabled default by changing the fallback passed to
gArgs.GetBoolArg for -enablecoinjoin from true to false. Leave the existing help
text and option behavior unchanged otherwise.

In `@src/node/utxo_snapshot.h`:
- Around line 9-16: Remove the unnecessary <validation.h> include from the
header and provide only the forward declaration of Chainstate required by
WriteSnapshotBaseBlockhash. Remove the redundant local cs_main declaration, or
retain it only with the established NOLINT(readability-redundant-declaration)
annotation matching src/node/blockstorage.h.

In `@src/rpc/coinjoin.cpp`:
- Around line 281-286: Update the salt validation callbacks in both the
`coinjoinsalt generate` and `coinjoinsalt set` handlers to check the boolean
returned by `node.coinjoin_loader->WithClient`. Assert the wallet lookup
succeeds using the file’s established `CHECK_NONFATAL` pattern, so a failed
lookup cannot bypass the `client.isMixing()` guard.

---

Outside diff comments:
In `@doc/design/assumeutxo.md`:
- Around line 117-118: Update the `ValidatedSnapshotShutdownCleanup()`
description in the documentation to replace the stale `chainstate_[hash]`
directory reference with `chainstate_snapshot`, matching the naming established
elsewhere in the document.

---

Nitpick comments:
In `@src/dbwrapper.h`:
- Around line 338-344: Mark the CDBWrapper::StoragePath() method const, since it
only reads m_is_memory and m_path. Preserve its existing return behavior for
memory-backed and on-disk storage.

In `@src/test/evo_deterministicmns_tests.cpp`:
- Around line 261-264: Introduce one shared helper for the repeated tip
accessors, such as a TipAccessors struct holding ChainstateManager and
CDeterministicMNManager references, with methods for tip_index, tip_height,
tip_hash, and sync_dmn_tip that preserve the existing locking and update
behavior. Replace the local lambdas in all affected test functions with this
helper and update their call sites accordingly.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b15f044-a36c-41eb-9673-00b35b954b77

📥 Commits

Reviewing files that changed from the base of the PR and between 2bbf4a4 and dbcad8c.

📒 Files selected for processing (129)
  • ci/dash/lint-tidy.sh
  • doc/backports/assumeutxo-ledger.md
  • doc/design/assumeutxo-dash-plan.md
  • doc/design/assumeutxo.md
  • doc/developer-notes.md
  • src/Makefile.am
  • src/Makefile.test.include
  • src/bench/duplicate_inputs.cpp
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chainlock/clsig.cpp
  • src/chainlock/clsig.h
  • src/chainlock/handler.cpp
  • src/chainlock/handler.h
  • 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/interfaces.cpp
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/dummywallet.cpp
  • src/governance/net_governance.cpp
  • src/governance/signing.cpp
  • src/governance/vote.cpp
  • src/governance/vote.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/interfaces/coinjoin.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/ehf_signals.cpp
  • src/llmq/net_quorum.cpp
  • src/llmq/quorumsman.cpp
  • src/llmq/quorumsman.h
  • src/llmq/signing.h
  • src/llmq/signing_shares.cpp
  • src/net_processing.cpp
  • 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/qt/bitcoingui.cpp
  • src/qt/optionsdialog.cpp
  • src/qt/overviewpage.cpp
  • src/qt/test/wallettests.cpp
  • src/qt/walletmodel.cpp
  • src/qt/walletmodel.h
  • src/rest.cpp
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/coinjoin.cpp
  • src/rpc/governance.cpp
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/quorums.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/txoutproof.cpp
  • src/serialize.h
  • src/spork.h
  • src/streams.h
  • src/test/block_reward_reallocation_tests.cpp
  • src/test/blockmanager_tests.cpp
  • src/test/bls_tests.cpp
  • src/test/coinstatsindex_tests.cpp
  • src/test/evo_cbtx_tests.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/test/evo_mnhf_tests.cpp
  • src/test/evo_trivialvalidation.cpp
  • src/test/evo_utils_tests.cpp
  • src/test/fuzz/mempool_utils.h
  • src/test/fuzz/tx_pool.cpp
  • src/test/governance_vote_wire_tests.cpp
  • src/test/interfaces_tests.cpp
  • src/test/llmq_chainlock_tests.cpp
  • src/test/miner_tests.cpp
  • src/test/net_tests.cpp
  • src/test/serialize_tests.cpp
  • src/test/util/chainstate.h
  • src/test/util/mining.cpp
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.h
  • src/test/validation_block_tests.cpp
  • 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
  • src/wallet/init.cpp
  • src/wallet/test/availablecoins_tests.cpp
  • src/wallet/test/coinjoin_tests.cpp
  • src/wallet/test/spend_tests.cpp
  • src/wallet/test/util.cpp
  • src/wallet/test/util.h
  • src/wallet/test/wallet_tests.cpp
  • src/wallet/wallet.cpp
  • src/wallet/wallet.h
  • src/walletinitinterface.h
  • test/functional/feature_init.py
  • test/functional/feature_remove_pruned_files_on_startup.py
  • test/functional/feature_sporks.py
  • test/functional/p2p_quorum_data.py
  • test/functional/rpc_coinjoin.py
  • test/functional/test_runner.py
  • test/lint/lint-circular-dependencies.py
  • test/sanitizer_suppressions/tsan
💤 Files with no reviewable changes (1)
  • test/functional/feature_init.py

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
doc/design/assumeutxo.md (1)

117-118: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale chainstate_[hash] reference to match new chainstate_snapshot naming.

Lines 79-81 were updated to use chainstate_snapshot as the snapshot chainstate directory name, but line 117 still references the old chainstate_[hash] naming when describing ValidatedSnapshotShutdownCleanup(). This should say chainstate_snapshot for consistency.

📝 Proposed fix
-the background chainstate is cleaned up with
-`ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_[hash]` datadir as
-`chainstate`.
+the background chainstate is cleaned up with
+`ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_snapshot` datadir as
+`chainstate`.
🤖 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 `@doc/design/assumeutxo.md` around lines 117 - 118, Update the
`ValidatedSnapshotShutdownCleanup()` description in the documentation to replace
the stale `chainstate_[hash]` directory reference with `chainstate_snapshot`,
matching the naming established elsewhere in the document.
🧹 Nitpick comments (2)
src/test/evo_deterministicmns_tests.cpp (1)

261-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated tip-accessor lambdas into a shared helper.

tip_index/tip_height/tip_hash/sync_dmn_tip are redefined nearly identically in ~10 functions in this file. A single helper (e.g. a small struct or free function taking chainman/dmnman by reference) would remove the duplication and centralize the locking pattern for future changes.

♻️ Example helper
struct TipAccessors {
    ChainstateManager& chainman;
    CDeterministicMNManager& dmnman;
    const CBlockIndex* tip_index() const { return WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()); }
    int tip_height() const { return WITH_LOCK(::cs_main, return chainman.ActiveChain().Height()); }
    uint256 tip_hash() const { return WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->GetBlockHash()); }
    void sync_dmn_tip() const { dmnman.UpdatedBlockTip(tip_index()); }
};

Also applies to: 299-301, 440-441, 539-540, 598-599, 706-708, 941-942, 989-991, 1180-1182, 1329-1332

🤖 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/test/evo_deterministicmns_tests.cpp` around lines 261 - 264, Introduce
one shared helper for the repeated tip accessors, such as a TipAccessors struct
holding ChainstateManager and CDeterministicMNManager references, with methods
for tip_index, tip_height, tip_hash, and sync_dmn_tip that preserve the existing
locking and update behavior. Replace the local lambdas in all affected test
functions with this helper and update their call sites accordingly.
src/dbwrapper.h (1)

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

Consider making StoragePath() const.

It only reads m_is_memory/m_path, so marking it const lets it be called through a const CDBWrapper& and better expresses intent.

♻️ Proposed change
-    //! `@returns` filesystem path to the on-disk data.
-    std::optional<fs::path> StoragePath() {
+    //! `@returns` filesystem path to the on-disk data.
+    std::optional<fs::path> StoragePath() const {
         if (m_is_memory) {
             return {};
         }
         return m_path;
     }
🤖 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 CDBWrapper::StoragePath()
method const, since it only reads m_is_memory and m_path. Preserve its existing
return behavior for memory-backed and on-disk storage.
🤖 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/coinjoin/interfaces.cpp`:
- Line 35: Align the runtime default in CCoinJoinClientOptions initialization
with the documented disabled default by changing the fallback passed to
gArgs.GetBoolArg for -enablecoinjoin from true to false. Leave the existing help
text and option behavior unchanged otherwise.

In `@src/node/utxo_snapshot.h`:
- Around line 9-16: Remove the unnecessary <validation.h> include from the
header and provide only the forward declaration of Chainstate required by
WriteSnapshotBaseBlockhash. Remove the redundant local cs_main declaration, or
retain it only with the established NOLINT(readability-redundant-declaration)
annotation matching src/node/blockstorage.h.

In `@src/rpc/coinjoin.cpp`:
- Around line 281-286: Update the salt validation callbacks in both the
`coinjoinsalt generate` and `coinjoinsalt set` handlers to check the boolean
returned by `node.coinjoin_loader->WithClient`. Assert the wallet lookup
succeeds using the file’s established `CHECK_NONFATAL` pattern, so a failed
lookup cannot bypass the `client.isMixing()` guard.

---

Outside diff comments:
In `@doc/design/assumeutxo.md`:
- Around line 117-118: Update the `ValidatedSnapshotShutdownCleanup()`
description in the documentation to replace the stale `chainstate_[hash]`
directory reference with `chainstate_snapshot`, matching the naming established
elsewhere in the document.

---

Nitpick comments:
In `@src/dbwrapper.h`:
- Around line 338-344: Mark the CDBWrapper::StoragePath() method const, since it
only reads m_is_memory and m_path. Preserve its existing return behavior for
memory-backed and on-disk storage.

In `@src/test/evo_deterministicmns_tests.cpp`:
- Around line 261-264: Introduce one shared helper for the repeated tip
accessors, such as a TipAccessors struct holding ChainstateManager and
CDeterministicMNManager references, with methods for tip_index, tip_height,
tip_hash, and sync_dmn_tip that preserve the existing locking and update
behavior. Replace the local lambdas in all affected test functions with this
helper and update their call sites accordingly.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b15f044-a36c-41eb-9673-00b35b954b77

📥 Commits

Reviewing files that changed from the base of the PR and between 2bbf4a4 and dbcad8c.

📒 Files selected for processing (129)
  • ci/dash/lint-tidy.sh
  • doc/backports/assumeutxo-ledger.md
  • doc/design/assumeutxo-dash-plan.md
  • doc/design/assumeutxo.md
  • doc/developer-notes.md
  • src/Makefile.am
  • src/Makefile.test.include
  • src/bench/duplicate_inputs.cpp
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chainlock/clsig.cpp
  • src/chainlock/clsig.h
  • src/chainlock/handler.cpp
  • src/chainlock/handler.h
  • 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/interfaces.cpp
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/dummywallet.cpp
  • src/governance/net_governance.cpp
  • src/governance/signing.cpp
  • src/governance/vote.cpp
  • src/governance/vote.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/interfaces/coinjoin.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/ehf_signals.cpp
  • src/llmq/net_quorum.cpp
  • src/llmq/quorumsman.cpp
  • src/llmq/quorumsman.h
  • src/llmq/signing.h
  • src/llmq/signing_shares.cpp
  • src/net_processing.cpp
  • 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/qt/bitcoingui.cpp
  • src/qt/optionsdialog.cpp
  • src/qt/overviewpage.cpp
  • src/qt/test/wallettests.cpp
  • src/qt/walletmodel.cpp
  • src/qt/walletmodel.h
  • src/rest.cpp
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/coinjoin.cpp
  • src/rpc/governance.cpp
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/quorums.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/txoutproof.cpp
  • src/serialize.h
  • src/spork.h
  • src/streams.h
  • src/test/block_reward_reallocation_tests.cpp
  • src/test/blockmanager_tests.cpp
  • src/test/bls_tests.cpp
  • src/test/coinstatsindex_tests.cpp
  • src/test/evo_cbtx_tests.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/test/evo_mnhf_tests.cpp
  • src/test/evo_trivialvalidation.cpp
  • src/test/evo_utils_tests.cpp
  • src/test/fuzz/mempool_utils.h
  • src/test/fuzz/tx_pool.cpp
  • src/test/governance_vote_wire_tests.cpp
  • src/test/interfaces_tests.cpp
  • src/test/llmq_chainlock_tests.cpp
  • src/test/miner_tests.cpp
  • src/test/net_tests.cpp
  • src/test/serialize_tests.cpp
  • src/test/util/chainstate.h
  • src/test/util/mining.cpp
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.h
  • src/test/validation_block_tests.cpp
  • 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
  • src/wallet/init.cpp
  • src/wallet/test/availablecoins_tests.cpp
  • src/wallet/test/coinjoin_tests.cpp
  • src/wallet/test/spend_tests.cpp
  • src/wallet/test/util.cpp
  • src/wallet/test/util.h
  • src/wallet/test/wallet_tests.cpp
  • src/wallet/wallet.cpp
  • src/wallet/wallet.h
  • src/walletinitinterface.h
  • test/functional/feature_init.py
  • test/functional/feature_remove_pruned_files_on_startup.py
  • test/functional/feature_sporks.py
  • test/functional/p2p_quorum_data.py
  • test/functional/rpc_coinjoin.py
  • test/functional/test_runner.py
  • test/lint/lint-circular-dependencies.py
  • test/sanitizer_suppressions/tsan
💤 Files with no reviewable changes (1)
  • test/functional/feature_init.py
🛑 Comments failed to post (3)
src/coinjoin/interfaces.cpp (1)

35-35: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Default value mismatch for -enablecoinjoin between help text and runtime default.

The constructor sets CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", true)) with a default of true, but the help text in src/wallet/init.cpp line 107 documents the default as 0 (disabled):

argsman.AddArg("-enablecoinjoin", strprintf("Enable use of CoinJoin for funds stored in this wallet (0-1, default: %u)", 0), ...)

Users who omit -enablecoinjoin will have CoinJoin enabled at runtime, contradicting the documented default. Either change the runtime default to false to match the help text, or update the help text to reflect default: 1.

🔧 Proposed fix (align runtime default to help text)
-        CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", true));
+        CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", false));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", false));
🤖 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/coinjoin/interfaces.cpp` at line 35, Align the runtime default in
CCoinJoinClientOptions initialization with the documented disabled default by
changing the fallback passed to gArgs.GetBoolArg for -enablecoinjoin from true
to false. Leave the existing help text and option behavior unchanged otherwise.
src/node/utxo_snapshot.h (1)

9-16: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n '`#include`' src/validation.h | rg -i 'utxo_snapshot'
rg -n '`#include`' src/node/utxo_snapshot.h
rg -n 'class Chainstate' src/node/utxo_snapshot.h src/validation.h
rg -n 'extern RecursiveMutex cs_main' -A1 src/node/blockstorage.h src/node/utxo_snapshot.h

Repository: PastaPastaPasta/dash

Length of output: 664


🏁 Script executed:

#!/bin/bash
sed -n '1,120p' src/node/utxo_snapshot.h
printf '\n--- validation.h excerpt ---\n'
sed -n '1,120p' src/validation.h
printf '\n--- references in utxo_snapshot.h ---\n'
rg -n 'Chainstate|SnapshotMetadata|cs_main|WriteSnapshotBaseBlockhash|ReadSnapshotBaseBlockhash' src/node/utxo_snapshot.h src/validation.h

Repository: PastaPastaPasta/dash

Length of output: 19311


Drop <validation.h> here. WriteSnapshotBaseBlockhash only needs a forward declaration of Chainstate, and the extra extern RecursiveMutex cs_main; is redundant with the declaration already pulled in by validation.h. If you keep the local declaration, add the same NOLINT(readability-redundant-declaration) used in src/node/blockstorage.h.

🤖 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/node/utxo_snapshot.h` around lines 9 - 16, Remove the unnecessary
<validation.h> include from the header and provide only the forward declaration
of Chainstate required by WriteSnapshotBaseBlockhash. Remove the redundant local
cs_main declaration, or retain it only with the established
NOLINT(readability-redundant-declaration) annotation matching
src/node/blockstorage.h.
src/rpc/coinjoin.cpp (1)

281-286: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Inconsistent error handling for WithClient return value in salt RPCs.

The coinjoinsalt generate and coinjoinsalt set handlers ignore the bool return of WithClient. If the wallet is not found by the loader (returns false), the isMixing() check is silently bypassed and salt generation proceeds without verifying mixing is inactive. All other CoinJoin RPC handlers in this file use CHECK_NONFATAL to assert the wallet is found.

If the wallet was already validated by GetWalletForJSONRPCRequest, the WithClient call should always succeed — but the defensive gap means a race or unexpected loader state would allow salt changes during active mixing rather than failing safely.

Consider either asserting the return value (matching the other handlers) or returning an RPC error when WithClient returns false.

🔧 Suggested fix for `coinjoinsalt generate`
     const NodeContext& node = EnsureAnyNodeContext(request.context);
-    if (node.coinjoin_loader != nullptr) {
-        node.coinjoin_loader->WithClient(wallet->GetName(), [&](auto& client) {
+    CHECK_NONFATAL(CHECK_NONFATAL(node.coinjoin_loader)->WithClient(wallet->GetName(), [&](auto& client) {
         if (client.isMixing()) {
             throw JSONRPCError(RPC_WALLET_ERROR,
                                strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
         }
-    });
-    }
+    }));

Also applies to: 384-389

🤖 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/rpc/coinjoin.cpp` around lines 281 - 286, Update the salt validation
callbacks in both the `coinjoinsalt generate` and `coinjoinsalt set` handlers to
check the boolean returned by `node.coinjoin_loader->WithClient`. Assert the
wallet lookup succeeds using the file’s established `CHECK_NONFATAL` pattern, so
a failed lookup cannot bypass the `client.isMixing()` guard.

@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m1-persistence branch 3 times, most recently from 956367c to dc94574 Compare July 12, 2026 15:03
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.
@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m1-persistence branch from dc94574 to 35d0d60 Compare July 12, 2026 15:19
PastaPastaPasta and others added 3 commits July 12, 2026 10:42
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.
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.

2 participants