Skip to content

backport: assumeutxo M7 — params polish and docs rewrite (series complete)#56

Open
PastaPastaPasta wants to merge 14 commits into
assumeutxo/m6-hardeningfrom
assumeutxo/m7-params-polish
Open

backport: assumeutxo M7 — params polish and docs rewrite (series complete)#56
PastaPastaPasta wants to merge 14 commits into
assumeutxo/m6-hardeningfrom
assumeutxo/m7-params-polish

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Issue being fixed or feature implemented

Final milestone of the assumeutxo-for-Dash series: pick up the remaining
upstream polish PRs, resync the M2 role/state gating against upstream's own
later role work, and rewrite the design/user docs to describe the tree as it
actually is after seven milestones — rather than the pre-implementation plan.
The execution plan and per-unit ledger (M7 section, series summary) are
tracked outside the repo in a private gist, not checked in:
https://gist.github.com/PastaPastaPasta/aa52b1f89fb74a0566ba3b5e15afab6a

PR 7/7 (final) in the stacked series — base is M6 (assumeutxo/m6-hardening).
Merging this PR is the last step of the whole assumeutxo-for-Dash plan
(M1–M7).

What was done?

  • M7 polish batch: 12 upstream PRs (#30598, #30636, #30807, #30880,
    #30909, #30214, #31907, #31940, #32033, #32746, #30455,
    #33604) plus a Dash adaptation commit.
    • #30598 — new v2 metadata container (magic/version/Dash network magic,
      height dropped from the header). Dash's evo v3 section stays
      independently versioned inside EVO_SNAPSHOT_MARKER — a deliberate
      decoupling decision so evo format and metadata-container format can
      revision independently. Post-M5 regtest snapshot artifacts on disk break
      across this change (expected and tested; assumeutxo is unreleased).
    • #30214 — role/state re-sync, carried through while preserving every
      M2/M5 Dash guard (subsystem gating, role-tagged notifications).
    • #32746's prerequisite slice of #30666 included.
    • #30807/#33604 (net_processing block-download changes) composed with
      M5's #29519 download-priority reconciliation.
    • #31940 — release-process doc, including a from-genesis evo_hash
      reproduction procedure for generating real testnet/mainnet
      AssumeutxoData at release time (deliberately not done in this series —
      it requires synced from-genesis nodes and is a release action, not a
      code change).
    • Adaptation commit also fixed a Dash-only credit-pool checkpoint cache
      identity bug: out-of-transaction derived writes weren't routing to the
      active snapshot identity.
    • Gate2 review: ACCEPT, no findings.
  • B8 docs rewrite: doc/design/assumeutxo.md rewritten for Dash reality (evo
    v3, dual-chainstate lifecycle, D2 union convergence, subsystem gating,
    crash recovery); new user-facing doc/assumeutxo.md; release-notes entry.
    Gate1: orchestrator accuracy review against the tree, RPC surface
    grep-verified. Gate2 waived (docs-only). Pre-existing doc-args linter
    failures (-statsport/-printcrashinfo) are unrelated to this change.

Deliberately out of scope (by design, documented in the ledger)

  • Generating real testnet-then-mainnet AssumeutxoData values on
    from-genesis nodes — a release-time action per doc/release-process.md,
    not a code change.
  • Runtime masternode-rebinding for loadtxoutset on masternode-mode nodes —
    documented follow-up, tracked separately from this series.

How Has This Been Tested?

  • make check: exit 0, 0 failures. Lint: circular-deps, whitespace PASS.
  • Functional battery (15 parallel) + feature_assumeutxo_dash.py solo
    (load-sensitive, per M6's finding): all passed.
  • 1 review round (ACCEPT) for the polish batch; docs gate1 PASS, gate2 waived
    for the docs-only change; final M7 milestone gate PASS on 2026-07-12.

Series-level testing (M1–M7, full stack)

Full lifecycle proven end to end by feature_assumeutxo_dash.py: 8
MNs/EvoNodes past DIP3/DIP8/DIP0024/v19/v20, rotated + non-rotated quorums,
dump→loadtxoutset→deep-state verify, ChainLock/InstantSend verification
pre-completion, signing/MN-mode refusals, background completion with
independent evo-state comparison, 4-phase restarts (crash-safe recovery),
and UTXO-only/tampered-evo rejection — all passing at every milestone gate
from M4 onward.

Breaking Changes

Snapshot metadata container moves to v2 (#30598); any snapshot files
produced by earlier commits in this series (M1–M6, pre-M7) are no longer
loadable — expected, since assumeutxo has not shipped in a Dash release.
loadtxoutset/getchainstates/dumptxoutset are new RPC surface as of this
series; no existing release behavior changes.

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
  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Added AssumeUTXO support for faster node startup using validated UTXO and Evo snapshots.
    • Added loadtxoutset and getchainstates RPCs; enhanced dumptxoutset with snapshot details and rollback options.
    • Added background validation with automatic recovery and cleanup handling.
  • Behavior Changes

    • Masternode DKG participation and quorum signing remain disabled until snapshot validation completes.
    • Historical blocks no longer trigger standard notifications or indexing.
    • gettxoutsetinfo now reports hash_serialized_3 instead of hash_serialized_2.
  • Bug Fixes

    • Improved pruning, wallet rescans, range handling, and snapshot validation error reporting.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4323cb49-0d94-4439-b69d-633377401fcb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

AssumeUTXO support is implemented across snapshot serialization, Evo-state validation, dual-chainstate lifecycle management, identity-scoped EvoDB transactions, role-aware notifications, storage/pruning, RPCs, documentation, and functional/unit tests.

AssumeUTXO and dual-chainstate integration

Layer / File(s) Summary
Snapshot format and validation
src/evo/snapshot.*, src/node/utxo_snapshot.*
Defines bounded canonical snapshot serialization, Evo-state reconstruction and validation, network/version metadata checks, and snapshot hash/commitment verification.
Snapshot loading and lifecycle
src/evo/snapshot_load.cpp, src/node/chainstate.cpp, src/validation.*, src/evo/evodb.*
Loads snapshot UTXOs and Evo state, maintains NORMAL/SNAPSHOT EvoDB identities, validates the background chainstate, and promotes or discards snapshot state with recovery handling.
Chainstate-aware processing
src/llmq/*, src/evo/*, src/validationinterface.*, src/interfaces/chain.h, src/kernel/*
Propagates chainstate roles and explicit chain context through validation, quorum, special-transaction, index, wallet, ChainLock, InstantSend, ZMQ, and notification paths.
RPCs, storage, and networking
src/rpc/blockchain.*, src/node/blockstorage.*, src/net*.{h,cpp}, src/init.cpp
Adds snapshot creation/loading and monitoring RPC behavior, snapshot-aware block storage and pruning, historical block download, service transitions, and index startup coordination.
Validation and regression coverage
src/test/*, test/functional/*, doc/*
Adds unit, fuzz, functional, wallet, Evo snapshot, EvoDB, lifecycle, serialization, and operational documentation coverage.
UTXO hash and range updates
src/kernel/coinstats.*, src/index/coinstatsindex.cpp, src/util/ranges_set.*
Reworks coin hashing, renames the serialized UTXO hash output to hash_serialized_3, and adds bounded canonical range serialization with sentinel handling.

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

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant RPC
  participant ChainstateManager
  participant SnapshotFile
  participant EvoDB
  participant BackgroundValidation
  Operator->>RPC: dumptxoutset
  RPC->>SnapshotFile: write UTXO and Evo snapshot
  Operator->>RPC: loadtxoutset
  RPC->>SnapshotFile: read and validate metadata
  RPC->>ChainstateManager: activate snapshot chainstate
  ChainstateManager->>EvoDB: write NORMAL and SNAPSHOT markers
  ChainstateManager->>BackgroundValidation: validate from genesis
  BackgroundValidation->>ChainstateManager: completion result
  ChainstateManager->>EvoDB: promote or discard snapshot markers
Loading

Suggested reviewers: udjinm6, knst

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.81% 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 correctly identifies this as an Assumeutxo backport series completion and reflects the params/docs-focused portion of the changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch assumeutxo/m7-params-polish

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.

@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m6-hardening branch from 1d7c4f0 to c1a0aa7 Compare July 12, 2026 14:44
@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m7-params-polish branch from 4885d35 to 332142c Compare July 12, 2026 15:09
@PastaPastaPasta PastaPastaPasta changed the base branch from assumeutxo/m6-hardening to develop July 12, 2026 15:23
@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m7-params-polish branch 2 times, most recently from acf6ad7 to ee0bce7 Compare July 14, 2026 05:10
@PastaPastaPasta PastaPastaPasta changed the base branch from develop to assumeutxo/m6-hardening July 14, 2026 05:10

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

Caution

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

⚠️ Outside diff range comments (1)
src/util/ranges_set.cpp (1)

80-86: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle the full-domain range without wrapping to zero.

For {begin = 0, end = 0}, Line 86 evaluates UINT64_MAX + 1 in uint64_t, so Size() reports zero. Since the deserializer accepts this canonical range, use a checked/saturating result or revise the return contract for an unrepresentable cardinality.

🤖 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/util/ranges_set.cpp` around lines 80 - 86, Update CRangesSet::Size so the
canonical full-domain range {begin = 0, end = 0} does not wrap to zero when
calculating UINT64_MAX + 1. Use a checked or saturating accumulation strategy,
or revise the return contract to represent this cardinality, while preserving
correct sizes for all other ranges.
🧹 Nitpick comments (7)
src/util/vector.h (1)

72-82: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using std::ranges::find_if for idiomatic C++20.

As per coding guidelines, C++20 is the target standard. You can leverage the <algorithm> library and std::ranges::find_if to make the implementation more idiomatic. Additionally, taking the predicate by forwarding reference (L&&) instead of const L can avoid unnecessary copies of the callable and support mutable lambdas if ever needed.

♻️ Proposed refactor

Make sure to include <algorithm> at the top of the file:

+#include <algorithm>

Then update the FindFirst function:

-template<typename V, typename L>
-inline std::optional<V> FindFirst(const std::vector<V>& vec, const L fnc)
-{
-    for (const auto& el : vec) {
-        if (fnc(el)) {
-            return el;
-        }
-    }
-    return std::nullopt;
-}
+template<typename V, typename L>
+inline std::optional<V> FindFirst(const std::vector<V>& vec, L&& fnc)
+{
+    auto it = std::ranges::find_if(vec, std::forward<L>(fnc));
+    return it != vec.end() ? std::make_optional(*it) : std::nullopt;
+}
🤖 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/util/vector.h` around lines 72 - 82, Update the FindFirst template to use
C++20 std::ranges::find_if, include the required <algorithm> header, and accept
the predicate as L&& so callable state is preserved without an unnecessary copy.
Return the matching element as an optional value, or std::nullopt when no
element matches.

Source: Coding guidelines

src/init.cpp (1)

2168-2171: 📐 Maintainability & Code Quality | 🔵 Trivial

Reminder: Address the TODO comment.

The TODO comment outlines pending work to recreate/rebind ActiveContext signers and NetInstantSend objects for runtime loadtxoutset when the ChainstateManager switches active chainstates.
Do you want me to track this task by opening a new issue, or provide assistance with implementing the rebinding logic?

🤖 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/init.cpp` around lines 2168 - 2171, Address the TODO in the runtime
loadtxoutset path by recreating or rebinding the Chainstate&-holding
ActiveContext signers and NetInstantSend after ChainstateManager switches active
chainstates. Preserve the existing startup detection and completion-time
ResetChainstates behavior, and ensure these objects reference the newly active
chainstate before runtime processing continues.
src/evo/deterministicmns.cpp (1)

376-387: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use %" PRIu64 " or %llu for 64-bit unsigned integers in strprintf.

Although tinyformat processes type-safety gracefully and often ignores the format character byte size, passing a uint64_t to a %d (signed 32-bit int) format specifier is technically incorrect and may trigger -Wformat warnings with modern compiler-level format checkers. Consider using %" PRIu64 " or %llu across these methods.

  • src/evo/deterministicmns.cpp#L376-L387: update the %d format specifiers for id and p.first in ApplyDiff.
  • src/evo/deterministicmns.cpp#L389-L415: update the %d format specifiers for id and p.first in ApplyDiffForSnapshot.
🤖 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/evo/deterministicmns.cpp` around lines 376 - 387, Update the format
specifiers for the uint64_t identifiers in ApplyDiff and ApplyDiffForSnapshot,
replacing each %d used for id and p.first with a 64-bit unsigned format such as
PRIu64 or %llu. Apply the change at src/evo/deterministicmns.cpp lines 376-387
and 389-415; no other behavior should change.
test/functional/test_framework/test_node.py (2)

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

Fix late-binding lint warning.

The linter flags that rpc is a loop variable captured in a lambda. While this is not functionally a bug here (since wait_until_helper_internal executes synchronously and the lambda doesn't outlive the iteration), you can bind it as a default argument to resolve the static analysis warning and prevent future fragility.

💡 Proposed fix
-                    wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'])
+                    wait_until_helper_internal(lambda rpc=rpc: rpc.getmempoolinfo()['loaded'])
🤖 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 `@test/functional/test_framework/test_node.py` at line 288, Update the lambda
passed to wait_until_helper_internal in the loop so the current rpc value is
bound through a default argument, eliminating late binding while preserving the
existing getmempoolinfo()['loaded'] check.

Source: Linters/SAST tools


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

Fix late-binding lint warning.

The linter flags that rpc is a loop variable captured in a lambda. While this is not functionally a bug here (since wait_until_helper_internal executes synchronously and the lambda doesn't outlive the iteration), you can bind it as a default argument to resolve the static analysis warning.

💡 Proposed fix
-                    wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'])
+                    wait_until_helper_internal(lambda rpc=rpc: rpc.getmempoolinfo()['loaded'])
🤖 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 `@test/functional/test_framework/test_node.py` at line 288, Update the lambda
passed to wait_until_helper_internal in the loop to bind the current rpc value
as a default argument, preventing late-binding lint warnings while preserving
the existing loaded-state check.

Source: Linters/SAST tools

src/kernel/coinstats.cpp (1)

92-100: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid copying the Coin object and use a structured binding.

Inside the loop, Coin coin = it->second; creates an unnecessary copy of the Coin object (which includes a CTxOut containing a CScript vector). You can avoid this allocation and make the loop more readable by using a structured binding with a const auto& reference.

♻️ Proposed refactor
 template <typename T>
 static void ApplyHash(T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
 {
-    for (auto it = outputs.begin(); it != outputs.end(); ++it) {
-        COutPoint outpoint = COutPoint(hash, it->first);
-        Coin coin = it->second;
-        ApplyCoinHash(hash_obj, outpoint, coin);
+    for (const auto& [n, coin] : outputs) {
+        COutPoint outpoint(hash, n);
+        ApplyCoinHash(hash_obj, outpoint, coin);
     }
 }
🤖 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/kernel/coinstats.cpp` around lines 92 - 100, Update ApplyHash to iterate
over outputs using a const structured-binding reference, reusing the referenced
output index and Coin directly when constructing COutPoint and calling
ApplyCoinHash; remove the local Coin copy while preserving the existing hashing
behavior.
src/interfaces/chain.h (1)

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

Remove duplicate forward declaration.

The forward declaration for enum class RBFTransactionState is duplicated.

♻️ Proposed refactor
 enum class RBFTransactionState;
-enum class RBFTransactionState;
🤖 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/interfaces/chain.h` at line 34, Remove the duplicate enum class
RBFTransactionState forward declaration from the interface, retaining the
existing declaration elsewhere and leaving all dependent references unchanged.
🤖 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/evo/snapshot.cpp`:
- Around line 232-316: Update CEvoSnapshot::Validate() so base_block_hash is
removed from both historical_hashes and required_work_hashes before comparing
them. Keep the existing reconstruction and required-target collection unchanged,
and perform the exclusion immediately before the historical_hashes !=
required_work_hashes check to match ValidateEvoSnapshotAgainstChain().

In `@src/evo/specialtxman.cpp`:
- Around line 783-784: Update the ChainLock validation around
CheckCbTxBestChainlock in the special transaction processing path so non-active
chainstates cannot accept a ChainLock from a divergent chain. Require the
ChainLock block hash to match the candidate block’s previous hash before
treating validation as successful, or disable the height/signature shortcut
whenever the chainstate is not active.

In `@src/kernel/chain.h`:
- Around line 8-47: Remove the redundant forward declarations of CBlock,
CBlockIndex, and interfaces::BlockInfo, and keep only one kernel::MakeBlockInfo
declaration. Ensure the default nullptr argument for MakeBlockInfo appears
exactly once, while preserving the remaining BlockInfo definition and
ChainstateRole operator declaration.

In `@src/net.h`:
- Around line 1506-1510: Update AddLocalServices and RemoveLocalServices to
perform atomic read-modify-write operations on nLocalServices using fetch_or and
fetch_and (or equivalent CAS loops), preserving the existing bitwise add/remove
semantics without separate loads and stores.

In `@src/rpc/blockchain.cpp`:
- Around line 2870-2871: Validate the result of ActiveChain().Next(target_index)
in the rollback handling flow before calling GetBlockHash. If invalidate_index
is null because target_index is outside the active chain, reject the RPC request
using the existing error-reporting path; only invoke InvalidateBlock after
confirming a valid active-chain block.

In `@src/test/validation_chainstatemanager_tests.cpp`:
- Around line 1085-1108: Restore the process-wide
Consensus::Params::DIP0003Height mutation with an RAII guard immediately after
saving the original value, so restoration occurs even when BOOST_REQUIRE or
MaybeValidateSnapshot exits the test early. Update the setup around
mutable_consensus and remove the later manual restore, preserving the existing
temporary value and validation behavior.

In `@src/wallet/rpc/backup.cpp`:
- Around line 1955-1970: Update the error message constructed in the descriptor
rescan failure path to say “descriptor creation” instead of “key creation,” and
correct the grammar to “could potentially be caused.” Keep the surrounding error
handling and message content unchanged.

In `@test/functional/test_framework/messages.py`:
- Around line 119-136: Rename the ambiguous parameter l in ser_varint to a
descriptive name such as value, and update all references to it within that
function while preserving the existing varint encoding logic.

---

Outside diff comments:
In `@src/util/ranges_set.cpp`:
- Around line 80-86: Update CRangesSet::Size so the canonical full-domain range
{begin = 0, end = 0} does not wrap to zero when calculating UINT64_MAX + 1. Use
a checked or saturating accumulation strategy, or revise the return contract to
represent this cardinality, while preserving correct sizes for all other ranges.

---

Nitpick comments:
In `@src/evo/deterministicmns.cpp`:
- Around line 376-387: Update the format specifiers for the uint64_t identifiers
in ApplyDiff and ApplyDiffForSnapshot, replacing each %d used for id and p.first
with a 64-bit unsigned format such as PRIu64 or %llu. Apply the change at
src/evo/deterministicmns.cpp lines 376-387 and 389-415; no other behavior should
change.

In `@src/init.cpp`:
- Around line 2168-2171: Address the TODO in the runtime loadtxoutset path by
recreating or rebinding the Chainstate&-holding ActiveContext signers and
NetInstantSend after ChainstateManager switches active chainstates. Preserve the
existing startup detection and completion-time ResetChainstates behavior, and
ensure these objects reference the newly active chainstate before runtime
processing continues.

In `@src/interfaces/chain.h`:
- Line 34: Remove the duplicate enum class RBFTransactionState forward
declaration from the interface, retaining the existing declaration elsewhere and
leaving all dependent references unchanged.

In `@src/kernel/coinstats.cpp`:
- Around line 92-100: Update ApplyHash to iterate over outputs using a const
structured-binding reference, reusing the referenced output index and Coin
directly when constructing COutPoint and calling ApplyCoinHash; remove the local
Coin copy while preserving the existing hashing behavior.

In `@src/util/vector.h`:
- Around line 72-82: Update the FindFirst template to use C++20
std::ranges::find_if, include the required <algorithm> header, and accept the
predicate as L&& so callable state is preserved without an unnecessary copy.
Return the matching element as an optional value, or std::nullopt when no
element matches.

In `@test/functional/test_framework/test_node.py`:
- Line 288: Update the lambda passed to wait_until_helper_internal in the loop
so the current rpc value is bound through a default argument, eliminating late
binding while preserving the existing getmempoolinfo()['loaded'] check.
- Line 288: Update the lambda passed to wait_until_helper_internal in the loop
to bind the current rpc value as a default argument, preventing late-binding
lint warnings while preserving the existing loaded-state check.
🪄 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: 24216dfc-e467-4a63-95fc-dd6914a02547

📥 Commits

Reviewing files that changed from the base of the PR and between f153015 and ee0bce7.

📒 Files selected for processing (142)
  • contrib/devtools/utxo_snapshot.sh
  • doc/assumeutxo.md
  • doc/design/assumeutxo.md
  • doc/release-notes-27596.md
  • doc/release-notes-28685.md
  • doc/release-notes-assumeutxo-m7-params-polish.md
  • doc/release-process.md
  • doc/zmq.md
  • src/Makefile.am
  • src/Makefile.test.include
  • src/active/context.cpp
  • src/active/context.h
  • src/active/dkgsessionhandler.cpp
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chain.h
  • src/chainlock/handler.cpp
  • src/chainlock/handler.h
  • src/chainlock/signing.cpp
  • src/chainlock/signing.h
  • src/chainparams.cpp
  • src/chainparams.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/dsnotificationinterface.cpp
  • src/dsnotificationinterface.h
  • src/evo/assetlocktx.cpp
  • src/evo/assetlocktx.h
  • src/evo/chainhelper.cpp
  • src/evo/chainhelper.h
  • src/evo/creditpool.cpp
  • src/evo/creditpool.h
  • src/evo/deterministicmns.cpp
  • src/evo/deterministicmns.h
  • src/evo/evodb.cpp
  • src/evo/evodb.h
  • src/evo/mnhftx.cpp
  • src/evo/mnhftx.h
  • src/evo/smldiff.cpp
  • src/evo/smldiff.h
  • src/evo/snapshot.cpp
  • src/evo/snapshot.h
  • src/evo/snapshot_load.cpp
  • src/evo/snapshot_types.h
  • src/evo/specialtxman.cpp
  • src/evo/specialtxman.h
  • src/index/base.cpp
  • src/index/base.h
  • src/index/coinstatsindex.cpp
  • src/init.cpp
  • src/instantsend/net_instantsend.cpp
  • src/instantsend/net_instantsend.h
  • src/interfaces/chain.h
  • src/kernel/chain.cpp
  • src/kernel/chain.h
  • src/kernel/coinstats.cpp
  • src/kernel/coinstats.h
  • src/kernel/types.h
  • src/llmq/blockprocessor.cpp
  • src/llmq/blockprocessor.h
  • src/llmq/context.cpp
  • src/llmq/quorumsman.cpp
  • src/llmq/quorumsman.h
  • src/llmq/signing_shares.cpp
  • src/llmq/signing_shares.h
  • src/llmq/snapshot.cpp
  • src/llmq/snapshot.h
  • src/llmq/utils.cpp
  • src/llmq/utils.h
  • src/net.cpp
  • src/net.h
  • src/net_processing.cpp
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/node/context.h
  • src/node/interfaces.cpp
  • src/node/miner.cpp
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/client.cpp
  • src/rpc/masternode.cpp
  • src/rpc/quorums.cpp
  • src/streams.h
  • src/test/blockmanager_tests.cpp
  • src/test/coinstatsindex_tests.cpp
  • src/test/evo_db_tests.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/test/evo_netinfo_tests.cpp
  • src/test/evo_snapshot_tests.cpp
  • src/test/fuzz/chain.cpp
  • src/test/fuzz/deserialize.cpp
  • src/test/fuzz/load_external_block_file.cpp
  • src/test/fuzz/rpc.cpp
  • src/test/fuzz/utxo_snapshot.cpp
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.cpp
  • src/test/util/validation.h
  • src/test/util_tests.cpp
  • src/test/validation_block_tests.cpp
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/test/validation_tests.cpp
  • src/txdb.h
  • src/util/ranges_set.cpp
  • src/util/ranges_set.h
  • src/util/vector.h
  • src/validation.cpp
  • src/validation.h
  • src/validationinterface.cpp
  • src/validationinterface.h
  • src/versionbits.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/rpc/transactions.cpp
  • src/wallet/test/fuzz/notifications.cpp
  • src/wallet/wallet.cpp
  • src/wallet/wallet.h
  • src/zmq/zmqnotificationinterface.cpp
  • src/zmq/zmqnotificationinterface.h
  • test/functional/feature_assumeutxo.py
  • test/functional/feature_assumeutxo_dash.py
  • test/functional/feature_coinstatsindex.py
  • test/functional/feature_dbcrash.py
  • test/functional/feature_reindex.py
  • test/functional/feature_utxo_set_hash.py
  • test/functional/rpc_blockchain.py
  • test/functional/rpc_dumptxoutset.py
  • test/functional/test_framework/compressor.py
  • test/functional/test_framework/messages.py
  • test/functional/test_framework/p2p.py
  • test/functional/test_framework/test_framework.py
  • test/functional/test_framework/test_node.py
  • test/functional/test_framework/util.py
  • test/functional/test_runner.py
  • test/functional/wallet_assumeutxo.py
  • test/lint/lint-shell.py
  • test/sanitizer_suppressions/ubsan
💤 Files with no reviewable changes (2)
  • src/txdb.h
  • contrib/devtools/utxo_snapshot.sh
👮 Files not reviewed due to content moderation or server errors (8)
  • src/wallet/rpc/transactions.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/dsnotificationinterface.cpp
  • doc/assumeutxo.md
  • src/test/evo_snapshot_tests.cpp
  • src/test/evo_db_tests.cpp
  • src/evo/snapshot_load.cpp
  • src/llmq/utils.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.

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: 8

Caution

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

⚠️ Outside diff range comments (1)
src/util/ranges_set.cpp (1)

80-86: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle the full-domain range without wrapping to zero.

For {begin = 0, end = 0}, Line 86 evaluates UINT64_MAX + 1 in uint64_t, so Size() reports zero. Since the deserializer accepts this canonical range, use a checked/saturating result or revise the return contract for an unrepresentable cardinality.

🤖 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/util/ranges_set.cpp` around lines 80 - 86, Update CRangesSet::Size so the
canonical full-domain range {begin = 0, end = 0} does not wrap to zero when
calculating UINT64_MAX + 1. Use a checked or saturating accumulation strategy,
or revise the return contract to represent this cardinality, while preserving
correct sizes for all other ranges.
🧹 Nitpick comments (7)
src/util/vector.h (1)

72-82: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using std::ranges::find_if for idiomatic C++20.

As per coding guidelines, C++20 is the target standard. You can leverage the <algorithm> library and std::ranges::find_if to make the implementation more idiomatic. Additionally, taking the predicate by forwarding reference (L&&) instead of const L can avoid unnecessary copies of the callable and support mutable lambdas if ever needed.

♻️ Proposed refactor

Make sure to include <algorithm> at the top of the file:

+#include <algorithm>

Then update the FindFirst function:

-template<typename V, typename L>
-inline std::optional<V> FindFirst(const std::vector<V>& vec, const L fnc)
-{
-    for (const auto& el : vec) {
-        if (fnc(el)) {
-            return el;
-        }
-    }
-    return std::nullopt;
-}
+template<typename V, typename L>
+inline std::optional<V> FindFirst(const std::vector<V>& vec, L&& fnc)
+{
+    auto it = std::ranges::find_if(vec, std::forward<L>(fnc));
+    return it != vec.end() ? std::make_optional(*it) : std::nullopt;
+}
🤖 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/util/vector.h` around lines 72 - 82, Update the FindFirst template to use
C++20 std::ranges::find_if, include the required <algorithm> header, and accept
the predicate as L&& so callable state is preserved without an unnecessary copy.
Return the matching element as an optional value, or std::nullopt when no
element matches.

Source: Coding guidelines

src/init.cpp (1)

2168-2171: 📐 Maintainability & Code Quality | 🔵 Trivial

Reminder: Address the TODO comment.

The TODO comment outlines pending work to recreate/rebind ActiveContext signers and NetInstantSend objects for runtime loadtxoutset when the ChainstateManager switches active chainstates.
Do you want me to track this task by opening a new issue, or provide assistance with implementing the rebinding logic?

🤖 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/init.cpp` around lines 2168 - 2171, Address the TODO in the runtime
loadtxoutset path by recreating or rebinding the Chainstate&-holding
ActiveContext signers and NetInstantSend after ChainstateManager switches active
chainstates. Preserve the existing startup detection and completion-time
ResetChainstates behavior, and ensure these objects reference the newly active
chainstate before runtime processing continues.
src/evo/deterministicmns.cpp (1)

376-387: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use %" PRIu64 " or %llu for 64-bit unsigned integers in strprintf.

Although tinyformat processes type-safety gracefully and often ignores the format character byte size, passing a uint64_t to a %d (signed 32-bit int) format specifier is technically incorrect and may trigger -Wformat warnings with modern compiler-level format checkers. Consider using %" PRIu64 " or %llu across these methods.

  • src/evo/deterministicmns.cpp#L376-L387: update the %d format specifiers for id and p.first in ApplyDiff.
  • src/evo/deterministicmns.cpp#L389-L415: update the %d format specifiers for id and p.first in ApplyDiffForSnapshot.
🤖 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/evo/deterministicmns.cpp` around lines 376 - 387, Update the format
specifiers for the uint64_t identifiers in ApplyDiff and ApplyDiffForSnapshot,
replacing each %d used for id and p.first with a 64-bit unsigned format such as
PRIu64 or %llu. Apply the change at src/evo/deterministicmns.cpp lines 376-387
and 389-415; no other behavior should change.
test/functional/test_framework/test_node.py (2)

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

Fix late-binding lint warning.

The linter flags that rpc is a loop variable captured in a lambda. While this is not functionally a bug here (since wait_until_helper_internal executes synchronously and the lambda doesn't outlive the iteration), you can bind it as a default argument to resolve the static analysis warning and prevent future fragility.

💡 Proposed fix
-                    wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'])
+                    wait_until_helper_internal(lambda rpc=rpc: rpc.getmempoolinfo()['loaded'])
🤖 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 `@test/functional/test_framework/test_node.py` at line 288, Update the lambda
passed to wait_until_helper_internal in the loop so the current rpc value is
bound through a default argument, eliminating late binding while preserving the
existing getmempoolinfo()['loaded'] check.

Source: Linters/SAST tools


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

Fix late-binding lint warning.

The linter flags that rpc is a loop variable captured in a lambda. While this is not functionally a bug here (since wait_until_helper_internal executes synchronously and the lambda doesn't outlive the iteration), you can bind it as a default argument to resolve the static analysis warning.

💡 Proposed fix
-                    wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'])
+                    wait_until_helper_internal(lambda rpc=rpc: rpc.getmempoolinfo()['loaded'])
🤖 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 `@test/functional/test_framework/test_node.py` at line 288, Update the lambda
passed to wait_until_helper_internal in the loop to bind the current rpc value
as a default argument, preventing late-binding lint warnings while preserving
the existing loaded-state check.

Source: Linters/SAST tools

src/kernel/coinstats.cpp (1)

92-100: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid copying the Coin object and use a structured binding.

Inside the loop, Coin coin = it->second; creates an unnecessary copy of the Coin object (which includes a CTxOut containing a CScript vector). You can avoid this allocation and make the loop more readable by using a structured binding with a const auto& reference.

♻️ Proposed refactor
 template <typename T>
 static void ApplyHash(T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
 {
-    for (auto it = outputs.begin(); it != outputs.end(); ++it) {
-        COutPoint outpoint = COutPoint(hash, it->first);
-        Coin coin = it->second;
-        ApplyCoinHash(hash_obj, outpoint, coin);
+    for (const auto& [n, coin] : outputs) {
+        COutPoint outpoint(hash, n);
+        ApplyCoinHash(hash_obj, outpoint, coin);
     }
 }
🤖 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/kernel/coinstats.cpp` around lines 92 - 100, Update ApplyHash to iterate
over outputs using a const structured-binding reference, reusing the referenced
output index and Coin directly when constructing COutPoint and calling
ApplyCoinHash; remove the local Coin copy while preserving the existing hashing
behavior.
src/interfaces/chain.h (1)

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

Remove duplicate forward declaration.

The forward declaration for enum class RBFTransactionState is duplicated.

♻️ Proposed refactor
 enum class RBFTransactionState;
-enum class RBFTransactionState;
🤖 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/interfaces/chain.h` at line 34, Remove the duplicate enum class
RBFTransactionState forward declaration from the interface, retaining the
existing declaration elsewhere and leaving all dependent references unchanged.
🤖 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/evo/snapshot.cpp`:
- Around line 232-316: Update CEvoSnapshot::Validate() so base_block_hash is
removed from both historical_hashes and required_work_hashes before comparing
them. Keep the existing reconstruction and required-target collection unchanged,
and perform the exclusion immediately before the historical_hashes !=
required_work_hashes check to match ValidateEvoSnapshotAgainstChain().

In `@src/evo/specialtxman.cpp`:
- Around line 783-784: Update the ChainLock validation around
CheckCbTxBestChainlock in the special transaction processing path so non-active
chainstates cannot accept a ChainLock from a divergent chain. Require the
ChainLock block hash to match the candidate block’s previous hash before
treating validation as successful, or disable the height/signature shortcut
whenever the chainstate is not active.

In `@src/kernel/chain.h`:
- Around line 8-47: Remove the redundant forward declarations of CBlock,
CBlockIndex, and interfaces::BlockInfo, and keep only one kernel::MakeBlockInfo
declaration. Ensure the default nullptr argument for MakeBlockInfo appears
exactly once, while preserving the remaining BlockInfo definition and
ChainstateRole operator declaration.

In `@src/net.h`:
- Around line 1506-1510: Update AddLocalServices and RemoveLocalServices to
perform atomic read-modify-write operations on nLocalServices using fetch_or and
fetch_and (or equivalent CAS loops), preserving the existing bitwise add/remove
semantics without separate loads and stores.

In `@src/rpc/blockchain.cpp`:
- Around line 2870-2871: Validate the result of ActiveChain().Next(target_index)
in the rollback handling flow before calling GetBlockHash. If invalidate_index
is null because target_index is outside the active chain, reject the RPC request
using the existing error-reporting path; only invoke InvalidateBlock after
confirming a valid active-chain block.

In `@src/test/validation_chainstatemanager_tests.cpp`:
- Around line 1085-1108: Restore the process-wide
Consensus::Params::DIP0003Height mutation with an RAII guard immediately after
saving the original value, so restoration occurs even when BOOST_REQUIRE or
MaybeValidateSnapshot exits the test early. Update the setup around
mutable_consensus and remove the later manual restore, preserving the existing
temporary value and validation behavior.

In `@src/wallet/rpc/backup.cpp`:
- Around line 1955-1970: Update the error message constructed in the descriptor
rescan failure path to say “descriptor creation” instead of “key creation,” and
correct the grammar to “could potentially be caused.” Keep the surrounding error
handling and message content unchanged.

In `@test/functional/test_framework/messages.py`:
- Around line 119-136: Rename the ambiguous parameter l in ser_varint to a
descriptive name such as value, and update all references to it within that
function while preserving the existing varint encoding logic.

---

Outside diff comments:
In `@src/util/ranges_set.cpp`:
- Around line 80-86: Update CRangesSet::Size so the canonical full-domain range
{begin = 0, end = 0} does not wrap to zero when calculating UINT64_MAX + 1. Use
a checked or saturating accumulation strategy, or revise the return contract to
represent this cardinality, while preserving correct sizes for all other ranges.

---

Nitpick comments:
In `@src/evo/deterministicmns.cpp`:
- Around line 376-387: Update the format specifiers for the uint64_t identifiers
in ApplyDiff and ApplyDiffForSnapshot, replacing each %d used for id and p.first
with a 64-bit unsigned format such as PRIu64 or %llu. Apply the change at
src/evo/deterministicmns.cpp lines 376-387 and 389-415; no other behavior should
change.

In `@src/init.cpp`:
- Around line 2168-2171: Address the TODO in the runtime loadtxoutset path by
recreating or rebinding the Chainstate&-holding ActiveContext signers and
NetInstantSend after ChainstateManager switches active chainstates. Preserve the
existing startup detection and completion-time ResetChainstates behavior, and
ensure these objects reference the newly active chainstate before runtime
processing continues.

In `@src/interfaces/chain.h`:
- Line 34: Remove the duplicate enum class RBFTransactionState forward
declaration from the interface, retaining the existing declaration elsewhere and
leaving all dependent references unchanged.

In `@src/kernel/coinstats.cpp`:
- Around line 92-100: Update ApplyHash to iterate over outputs using a const
structured-binding reference, reusing the referenced output index and Coin
directly when constructing COutPoint and calling ApplyCoinHash; remove the local
Coin copy while preserving the existing hashing behavior.

In `@src/util/vector.h`:
- Around line 72-82: Update the FindFirst template to use C++20
std::ranges::find_if, include the required <algorithm> header, and accept the
predicate as L&& so callable state is preserved without an unnecessary copy.
Return the matching element as an optional value, or std::nullopt when no
element matches.

In `@test/functional/test_framework/test_node.py`:
- Line 288: Update the lambda passed to wait_until_helper_internal in the loop
so the current rpc value is bound through a default argument, eliminating late
binding while preserving the existing getmempoolinfo()['loaded'] check.
- Line 288: Update the lambda passed to wait_until_helper_internal in the loop
to bind the current rpc value as a default argument, preventing late-binding
lint warnings while preserving the existing loaded-state check.
🪄 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: 24216dfc-e467-4a63-95fc-dd6914a02547

📥 Commits

Reviewing files that changed from the base of the PR and between f153015 and ee0bce7.

📒 Files selected for processing (142)
  • contrib/devtools/utxo_snapshot.sh
  • doc/assumeutxo.md
  • doc/design/assumeutxo.md
  • doc/release-notes-27596.md
  • doc/release-notes-28685.md
  • doc/release-notes-assumeutxo-m7-params-polish.md
  • doc/release-process.md
  • doc/zmq.md
  • src/Makefile.am
  • src/Makefile.test.include
  • src/active/context.cpp
  • src/active/context.h
  • src/active/dkgsessionhandler.cpp
  • src/bench/load_external.cpp
  • src/bitcoin-chainstate.cpp
  • src/chain.h
  • src/chainlock/handler.cpp
  • src/chainlock/handler.h
  • src/chainlock/signing.cpp
  • src/chainlock/signing.h
  • src/chainparams.cpp
  • src/chainparams.h
  • src/dbwrapper.cpp
  • src/dbwrapper.h
  • src/dsnotificationinterface.cpp
  • src/dsnotificationinterface.h
  • src/evo/assetlocktx.cpp
  • src/evo/assetlocktx.h
  • src/evo/chainhelper.cpp
  • src/evo/chainhelper.h
  • src/evo/creditpool.cpp
  • src/evo/creditpool.h
  • src/evo/deterministicmns.cpp
  • src/evo/deterministicmns.h
  • src/evo/evodb.cpp
  • src/evo/evodb.h
  • src/evo/mnhftx.cpp
  • src/evo/mnhftx.h
  • src/evo/smldiff.cpp
  • src/evo/smldiff.h
  • src/evo/snapshot.cpp
  • src/evo/snapshot.h
  • src/evo/snapshot_load.cpp
  • src/evo/snapshot_types.h
  • src/evo/specialtxman.cpp
  • src/evo/specialtxman.h
  • src/index/base.cpp
  • src/index/base.h
  • src/index/coinstatsindex.cpp
  • src/init.cpp
  • src/instantsend/net_instantsend.cpp
  • src/instantsend/net_instantsend.h
  • src/interfaces/chain.h
  • src/kernel/chain.cpp
  • src/kernel/chain.h
  • src/kernel/coinstats.cpp
  • src/kernel/coinstats.h
  • src/kernel/types.h
  • src/llmq/blockprocessor.cpp
  • src/llmq/blockprocessor.h
  • src/llmq/context.cpp
  • src/llmq/quorumsman.cpp
  • src/llmq/quorumsman.h
  • src/llmq/signing_shares.cpp
  • src/llmq/signing_shares.h
  • src/llmq/snapshot.cpp
  • src/llmq/snapshot.h
  • src/llmq/utils.cpp
  • src/llmq/utils.h
  • src/net.cpp
  • src/net.h
  • src/net_processing.cpp
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/node/context.h
  • src/node/interfaces.cpp
  • src/node/miner.cpp
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/rpc/blockchain.cpp
  • src/rpc/blockchain.h
  • src/rpc/client.cpp
  • src/rpc/masternode.cpp
  • src/rpc/quorums.cpp
  • src/streams.h
  • src/test/blockmanager_tests.cpp
  • src/test/coinstatsindex_tests.cpp
  • src/test/evo_db_tests.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/test/evo_netinfo_tests.cpp
  • src/test/evo_snapshot_tests.cpp
  • src/test/fuzz/chain.cpp
  • src/test/fuzz/deserialize.cpp
  • src/test/fuzz/load_external_block_file.cpp
  • src/test/fuzz/rpc.cpp
  • src/test/fuzz/utxo_snapshot.cpp
  • src/test/util/chainstate.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util/validation.cpp
  • src/test/util/validation.h
  • src/test/util_tests.cpp
  • src/test/validation_block_tests.cpp
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/test/validation_tests.cpp
  • src/txdb.h
  • src/util/ranges_set.cpp
  • src/util/ranges_set.h
  • src/util/vector.h
  • src/validation.cpp
  • src/validation.h
  • src/validationinterface.cpp
  • src/validationinterface.h
  • src/versionbits.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/rpc/transactions.cpp
  • src/wallet/test/fuzz/notifications.cpp
  • src/wallet/wallet.cpp
  • src/wallet/wallet.h
  • src/zmq/zmqnotificationinterface.cpp
  • src/zmq/zmqnotificationinterface.h
  • test/functional/feature_assumeutxo.py
  • test/functional/feature_assumeutxo_dash.py
  • test/functional/feature_coinstatsindex.py
  • test/functional/feature_dbcrash.py
  • test/functional/feature_reindex.py
  • test/functional/feature_utxo_set_hash.py
  • test/functional/rpc_blockchain.py
  • test/functional/rpc_dumptxoutset.py
  • test/functional/test_framework/compressor.py
  • test/functional/test_framework/messages.py
  • test/functional/test_framework/p2p.py
  • test/functional/test_framework/test_framework.py
  • test/functional/test_framework/test_node.py
  • test/functional/test_framework/util.py
  • test/functional/test_runner.py
  • test/functional/wallet_assumeutxo.py
  • test/lint/lint-shell.py
  • test/sanitizer_suppressions/ubsan
💤 Files with no reviewable changes (2)
  • src/txdb.h
  • contrib/devtools/utxo_snapshot.sh
👮 Files not reviewed due to content moderation or server errors (8)
  • src/wallet/rpc/transactions.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/dsnotificationinterface.cpp
  • doc/assumeutxo.md
  • src/test/evo_snapshot_tests.cpp
  • src/test/evo_db_tests.cpp
  • src/evo/snapshot_load.cpp
  • src/llmq/utils.cpp
🛑 Comments failed to post (8)
src/evo/snapshot.cpp (1)

232-316: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository root: '
pwd

echo '--- outline snapshot.cpp ---'
ast-grep outline src/evo/snapshot.cpp --view expanded || true

echo '--- line counts ---'
wc -l src/evo/snapshot.cpp src/evo/snapshot.h || true

echo '--- targeted ranges snapshot.cpp ---'
sed -n '300,520p' src/evo/snapshot.cpp

echo '--- targeted ranges snapshot.h ---'
sed -n '1,260p' src/evo/snapshot.h

Repository: PastaPastaPasta/dash

Length of output: 23253


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- search for base_block_hash comparisons and historical_hashes ---'
rg -n "base_block_hash|historical_hashes|required_work_hashes|ValidateEvoSnapshotAgainstChain|BuildEvoSnapshot|register_work_block" src/evo/snapshot.cpp src/evo/snapshot.h

echo '--- search for Validate() calls ---'
rg -n "\.Validate\(" src/evo/snapshot.cpp src/evo/snapshot.h src | head -n 100

Repository: PastaPastaPasta/dash

Length of output: 8699


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '232,316p' src/evo/snapshot.cpp
printf '\n---\n'
sed -n '340,500p' src/evo/snapshot.cpp
printf '\n---\n'
sed -n '580,640p' src/evo/snapshot.cpp
printf '\n---\n'
rg -n "Validate\\(true\\)|Validate\\(\\)" src/evo/snapshot.h src/evo/snapshot.cpp

Repository: PastaPastaPasta/dash

Length of output: 18340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for comments/tests around evo snapshot construction and validation edge cases.
rg -n "snapshot.*base_block_hash|work_block_hash.*base_block_hash|historical.*base_block_hash|assumeutxo|non-rotated|V20|WORK_DIFF_DEPTH" src test contrib -g '!**/node_modules/**' || true

Repository: PastaPastaPasta/dash

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- snapshot.cpp (240-520) ---'
sed -n '240,520p' src/evo/snapshot.cpp

echo '--- snapshot.cpp (580-635) ---'
sed -n '580,635p' src/evo/snapshot.cpp

echo '--- snapshot.h Validate declarations ---'
rg -n "Validate\\(" src/evo/snapshot.h

Repository: PastaPastaPasta/dash

Length of output: 19088


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- snapshot.h around serializer/unserializer ---'
sed -n '500,590p' src/evo/snapshot.h

echo '--- targeted tests around base/work hash edge cases ---'
sed -n '600,690p' src/test/evo_snapshot_tests.cpp

echo '--- any commentary around work/base equality in quorum utils ---'
sed -n '1110,1135p' src/rpc/quorums.cpp

Repository: PastaPastaPasta/dash

Length of output: 11338


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- reconstruction helper ---'
sed -n '190,230p' src/evo/snapshot.cpp

echo '--- test cases near line 653 and rotation/work hash behavior ---'
sed -n '640,760p' src/test/evo_snapshot_tests.cpp

echo '--- search for historical diff target mismatch tests ---'
rg -n "missing or extra historical|base_block_hash|work_block_hash = entry.quorum_base_block_hash|ValidateEvoSnapshotAgainstChain" src/test/evo_snapshot_tests.cpp

Repository: PastaPastaPasta/dash

Length of output: 11882


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
text = Path('src/evo/snapshot.cpp').read_text()

# Print the exact helper and the chain-validation historical comparison area.
for start, end, label in [
    (190, 230, 'reconstruct helper'),
    (530, 620, 'chain validation historical area'),
]:
    print(f'--- {label} ---')
    lines = text.splitlines()
    for i in range(start-1, min(end, len(lines))):
        print(f'{i+1}: {lines[i]}')
PY

Repository: PastaPastaPasta/dash

Length of output: 8126


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- evo snapshot tests around manual construction ---'
sed -n '620,700p' src/test/evo_snapshot_tests.cpp

echo '--- evo snapshot tests around ValidateEvoSnapshotAgainstChain historical comparison ---'
sed -n '760,860p' src/test/evo_snapshot_tests.cpp

Repository: PastaPastaPasta/dash

Length of output: 10301


Exclude base_block_hash before comparing historical work targets
CEvoSnapshot::Validate() should mirror ValidateEvoSnapshotAgainstChain() and erase base_block_hash from both sets before the historical_hashes != required_work_hashes check. BuildEvoSnapshot() can produce a work block at the snapshot base for non-rotated LLMQs before V20, and that snapshot will otherwise fail validation on build or load.

🤖 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/evo/snapshot.cpp` around lines 232 - 316, Update CEvoSnapshot::Validate()
so base_block_hash is removed from both historical_hashes and
required_work_hashes before comparing them. Keep the existing reconstruction and
required-target collection unchanged, and perform the exclusion immediately
before the historical_hashes != required_work_hashes check to match
ValidateEvoSnapshotAgainstChain().
src/evo/specialtxman.cpp (1)

783-784: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant implementation and call sites.
rg -n "CheckCbTxBestChainlock|best ChainLock|ChainLock" src/evo/specialtxman.cpp src -g '!**/node_modules/**'

# Show the surrounding context for the validator and the call site.
sed -n '80,150p' src/evo/specialtxman.cpp
printf '\n----\n'
sed -n '760,810p' src/evo/specialtxman.cpp

Repository: PastaPastaPasta/dash

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '97,175p' src/evo/specialtxman.cpp
printf '\n----\n'
sed -n '770,800p' src/evo/specialtxman.cpp
printf '\n----\n'
sed -n '1,120p' src/chainlock/clsig.cpp

Repository: PastaPastaPasta/dash

Length of output: 7496


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the helper and the existing cbtx tests.
sed -n '1,140p' src/evo/specialtxman.cpp
printf '\n----\n'
sed -n '1,140p' src/test/evo_cbtx_tests.cpp
printf '\n----\n'
rg -n "GetNonNullCoinbaseChainlock|cached_chainlock|bestCLHeightDiff|bestCLSignature" src/evo src/test -g '!**/node_modules/**'

Repository: PastaPastaPasta/dash

Length of output: 14085


Don't short-circuit ChainLock validation on non-active chainstates.

CheckCbTxBestChainlock can return true from the height/signature fast path without checking that the ChainLock matches this block’s previous hash. A divergent chainstate can then accept the active chain’s best ChainLock instead of validating its own ancestor. Require the block-hash match here, or disable this shortcut off the active chain.

🤖 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/evo/specialtxman.cpp` around lines 783 - 784, Update the ChainLock
validation around CheckCbTxBestChainlock in the special transaction processing
path so non-active chainstates cannot accept a ChainLock from a divergent chain.
Require the ChainLock block hash to match the candidate block’s previous hash
before treating validation as successful, or disable the height/signature
shortcut whenever the chainstate is not active.
src/kernel/chain.h (1)

8-47: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove duplicate declarations and prevent compiler errors.

The forward declarations for CBlock, CBlockIndex, interfaces::BlockInfo, and the function declaration kernel::MakeBlockInfo are duplicated. In C++, specifying a default argument for a parameter multiple times in the same scope (as done with const CBlock* data = nullptr on lines 18 and 45) will cause a compilation error.

Please remove the redundant declarations.

🐛 Proposed fix
 `#include`<iostream>
 
-class CBlock;
-class CBlockIndex;
-namespace interfaces {
-struct BlockInfo;
-} // namespace interfaces
-
-namespace kernel {
-//! Return data from block index.
-interfaces::BlockInfo MakeBlockInfo(const CBlockIndex* block_index, const CBlock* data = nullptr);
-
-} // namespace kernel
-
 class CBlock;
 class CBlockIndex;
📝 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.

`#include`<iostream>

class CBlock;
class CBlockIndex;
class CBlock;
class CBlockIndex;
class CBlockUndo;
class uint256;
namespace interfaces {
//! Block data sent with blockConnected and blockDisconnected notifications.
struct BlockInfo {
    const uint256& hash;
    const uint256* prev_hash{nullptr};
    int height{-1};
    int file_number{-1};
    unsigned data_pos{0};
    const CBlock* data{nullptr};
    const CBlockUndo* undo_data{nullptr};
    unsigned int chain_time_max{0};

    explicit BlockInfo(const uint256& block_hash) : hash(block_hash) {}
};
} // namespace interfaces

namespace kernel {
struct ChainstateRole;
//! Return data from block index.
interfaces::BlockInfo MakeBlockInfo(const CBlockIndex* block_index, const CBlock* data = nullptr);
std::ostream& operator<<(std::ostream& os, const ChainstateRole& role);
} // namespace kernel
🤖 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/kernel/chain.h` around lines 8 - 47, Remove the redundant forward
declarations of CBlock, CBlockIndex, and interfaces::BlockInfo, and keep only
one kernel::MakeBlockInfo declaration. Ensure the default nullptr argument for
MakeBlockInfo appears exactly once, while preserving the remaining BlockInfo
definition and ChainstateRole operator declaration.
src/net.h (1)

1506-1510: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
ast-grep outline src/net.h --match 'AddLocalServices|RemoveLocalServices' --view expanded
rg -nP -C3 '\b(AddLocalServices|RemoveLocalServices)\s*\(' src

Repository: PastaPastaPasta/dash

Length of output: 2054


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== src/net.h: around local-services helpers and member =="
sed -n '1490,1525p' src/net.h
echo
sed -n '1835,1865p' src/net.h

echo
echo "== search for lock annotations / related fields =="
rg -n "nLocalServices|EXCLUSIVE_LOCKS_REQUIRED|GUARDED_BY|LOCKS_EXCLUDED|Mutex|mutex" src/net.h src/net.cpp src/init.cpp src/rpc/blockchain.cpp

echo
echo "== search for all writes to nLocalServices =="
rg -n "nLocalServices\s*([=|&]|\.store|\.fetch|compare_exchange|load)" src

Repository: PastaPastaPasta/dash

Length of output: 41172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== src/init.cpp around nLocalServices mutations =="
sed -n '1075,1115p' src/init.cpp
echo
sed -n '1188,1220p' src/init.cpp
echo
sed -n '1390,1415p' src/init.cpp
echo
sed -n '2355,2375p' src/init.cpp
echo
sed -n '2605,2630p' src/init.cpp

echo
echo "== src/rpc/blockchain.cpp around AddLocalServices/RemoveLocalServices =="
sed -n '3148,3165p' src/rpc/blockchain.cpp

echo
echo "== src/net.cpp GetLocalServices implementation and any locks nearby =="
sed -n '4660,4690p' src/net.cpp

Repository: PastaPastaPasta/dash

Length of output: 9546


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "using ServiceFlags|typedef.*ServiceFlags|enum class ServiceFlags|enum ServiceFlags" src

Repository: PastaPastaPasta/dash

Length of output: 306


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 "GetLocalServices\(" src/net.cpp src/net.h src/init.cpp src/rpc/blockchain.cpp src/net_processing.cpp

Repository: PastaPastaPasta/dash

Length of output: 4462


Make local-service updates atomic RMWs. AddLocalServices/RemoveLocalServices still do separate load/store operations on std::atomic<ServiceFlags>, so concurrent updates can drop bits and advertise the wrong handshake services. Use fetch_or/fetch_and or a CAS loop here.

🤖 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/net.h` around lines 1506 - 1510, Update AddLocalServices and
RemoveLocalServices to perform atomic read-modify-write operations on
nLocalServices using fetch_or and fetch_and (or equivalent CAS loops),
preserving the existing bitwise add/remove semantics without separate loads and
stores.

Source: Coding guidelines

src/rpc/blockchain.cpp (1)

2870-2871: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Reject rollback targets outside the active chain before dereferencing invalidate_index.

ParseHashOrHeight can return a block on a stale fork. ActiveChain().Next(target_index) then returns null, and Line 2871 dereferences it, allowing an RPC request to crash the node.

Proposed guard
 invalidate_index = WITH_LOCK(::cs_main, return node.chainman->ActiveChain().Next(target_index));
+if (!invalidate_index) {
+    throw JSONRPCError(
+        RPC_INVALID_PARAMETER,
+        "Rollback target must be an ancestor of the active chain tip.");
+}
 InvalidateBlock(*node.chainman, invalidate_index->GetBlockHash());
📝 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.

        invalidate_index = WITH_LOCK(::cs_main, return node.chainman->ActiveChain().Next(target_index));
        if (!invalidate_index) {
            throw JSONRPCError(
                RPC_INVALID_PARAMETER,
                "Rollback target must be an ancestor of the active chain tip.");
        }
        InvalidateBlock(*node.chainman, invalidate_index->GetBlockHash());
🤖 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/blockchain.cpp` around lines 2870 - 2871, Validate the result of
ActiveChain().Next(target_index) in the rollback handling flow before calling
GetBlockHash. If invalidate_index is null because target_index is outside the
active chain, reject the RPC request using the existing error-reporting path;
only invoke InvalidateBlock after confirming a valid active-chain block.
src/test/validation_chainstatemanager_tests.cpp (1)

1085-1108: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Global Consensus::Params mutation isn't restored if an assertion in between fails — risk of cascading unrelated test failures.

mutable_consensus.DIP0003Height is set to 1 on the process-wide Params().GetConsensus() singleton, and only restored by an unconditional statement placed after MaybeValidateSnapshot(...) (Line 1107). Between the mutation and the restore sits BOOST_REQUIRE(m_node.evodb->CommitRootTransaction(...)) (Line 1103) and the MaybeValidateSnapshot call itself — if either throws or the REQUIRE fails, Boost.Test aborts the test case and the restore is skipped, permanently corrupting DIP0003Height for every subsequent test case in the same binary run.

🛡️ Proposed fix: restore via RAII regardless of how the test case exits
     Consensus::Params& mutable_consensus{
         const_cast<Consensus::Params&>(Params().GetConsensus())};
     const int old_dip3_height{mutable_consensus.DIP0003Height};
     mutable_consensus.DIP0003Height = 1;
+    struct DIP3HeightGuard {
+        Consensus::Params& params;
+        int original_height;
+        ~DIP3HeightGuard() { params.DIP0003Height = original_height; }
+    } dip3_height_guard{mutable_consensus, old_dip3_height};
 
     {
         auto tx = m_node.evodb->BeginTransaction(EvoDbIdentity::SNAPSHOT);
@@
     const auto result = WITH_LOCK(::cs_main,
         return chainman.MaybeValidateSnapshot(*validation_chainstate, *snapshot_chainstate, [](bilingual_str) {}));
-    mutable_consensus.DIP0003Height = old_dip3_height;
     BOOST_CHECK_EQUAL(result, SnapshotCompletionResult::EVO_STATE_MISMATCH);
📝 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.

    m_node.dmnman->SetListForBlockForTesting(incorrect_base_list);

    // The regtest assumeutxo fixture predates DIP3. Temporarily make DIP3 active
    // so the legacy completion lookup would consume the poisoned shared cache.
    Consensus::Params& mutable_consensus{
        const_cast<Consensus::Params&>(Params().GetConsensus())};
    const int old_dip3_height{mutable_consensus.DIP0003Height};
    mutable_consensus.DIP0003Height = 1;
    struct DIP3HeightGuard {
        Consensus::Params& params;
        int original_height;
        ~DIP3HeightGuard() { params.DIP0003Height = original_height; }
    } dip3_height_guard{mutable_consensus, old_dip3_height};

    {
        auto tx = m_node.evodb->BeginTransaction(EvoDbIdentity::SNAPSHOT);
        // Seed an incorrect full base list, not merely an incorrect comparison hash.
        m_node.evodb->Write(
            std::make_pair(std::string{"dmn_S3"}, base_index->GetBlockHash()),
            incorrect_base_list);
        m_node.evodb->WriteSnapshotBaseMNListHash(incorrect_hash);
        tx->Commit();
    }
    BOOST_REQUIRE(m_node.evodb->CommitRootTransaction(EvoDbIdentity::SNAPSHOT, /*sync=*/true));

    const auto result = WITH_LOCK(::cs_main,
        return chainman.MaybeValidateSnapshot(*validation_chainstate, *snapshot_chainstate, [](bilingual_str) {}));
    BOOST_CHECK_EQUAL(result, SnapshotCompletionResult::EVO_STATE_MISMATCH);
🤖 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/validation_chainstatemanager_tests.cpp` around lines 1085 - 1108,
Restore the process-wide Consensus::Params::DIP0003Height mutation with an RAII
guard immediately after saving the original value, so restoration occurs even
when BOOST_REQUIRE or MaybeValidateSnapshot exits the test early. Update the
setup around mutable_consensus and remove the later manual restore, preserving
the existing temporary value and validation behavior.
src/wallet/rpc/backup.cpp (1)

1955-1970: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the descriptor rescan error wording.

Use “descriptor creation” instead of “key creation,” and fix “could potentially caused” to “could potentially be caused.”

Proposed fix
-                            "of key creation, and could contain transactions pertaining to the desc. As a "
-                            "result, transactions and coins using this desc may not appear in the wallet.",
+                            "of descriptor creation, and could contain transactions pertaining to the descriptor. As a "
+                            "result, transactions and coins using this descriptor may not appear in the wallet.",
...
-                        error_msg += strprintf(" This error could potentially caused by data corruption. If "
+                        error_msg += strprintf(" This error could potentially be caused by data corruption. If "
📝 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.

                    std::string error_msg{strprintf("Rescan failed for descriptor with timestamp %d. There "
                            "was an error reading a block from time %d, which is after or within %d seconds "
                            "of descriptor creation, and could contain transactions pertaining to the descriptor. As a "
                            "result, transactions and coins using this descriptor may not appear in the wallet.",
                            GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)};
                    if (pwallet->chain().havePruned()) {
                        error_msg += strprintf(" This error could be caused by pruning or data corruption "
                                "(see dashd log for details) and could be dealt with by downloading and "
                                "rescanning the relevant blocks (see -reindex option and rescanblockchain RPC).");
                    } else if (pwallet->chain().hasAssumedValidChain()) {
                        error_msg += strprintf(" This error is likely caused by an in-progress assumeutxo "
                                "background sync. Check logs or getchainstates RPC for assumeutxo background "
                                "sync progress and try again later.");
                    } else {
                        error_msg += strprintf(" This error could potentially be caused by data corruption. If "
                                "the issue persists you may want to reindex (see -reindex option).");
🤖 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/wallet/rpc/backup.cpp` around lines 1955 - 1970, Update the error message
constructed in the descriptor rescan failure path to say “descriptor creation”
instead of “key creation,” and correct the grammar to “could potentially be
caused.” Keep the surrounding error handling and message content unchanged.
test/functional/test_framework/messages.py (1)

119-136: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename ambiguous parameter l in ser_varint.

Both Ruff and Flake8 flag l as an ambiguous variable name (E741) at Lines 119 and 125, which will likely fail lint CI. The varint encode/decode logic itself is correct (verified against the referenced serialize_tests.cpp:varint_bitpatterns vectors).

🔧 Proposed fix
-def ser_varint(l):
+def ser_varint(n):
     r = b""
     while True:
-        r = bytes([(l & 0x7f) | (0x80 if len(r) > 0 else 0x00)]) + r
-        if l <= 0x7f:
+        r = bytes([(n & 0x7f) | (0x80 if len(r) > 0 else 0x00)]) + r
+        if n <= 0x7f:
             return r
-        l = (l >> 7) - 1
+        n = (n >> 7) - 1
📝 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.

def ser_varint(n):
    r = b""
    while True:
        r = bytes([(n & 0x7f) | (0x80 if len(r) > 0 else 0x00)]) + r
        if n <= 0x7f:
            return r
        n = (n >> 7) - 1


def deser_varint(f):
    n = 0
    while True:
        dat = f.read(1)[0]
        n = (n << 7) | (dat & 0x7f)
        if (dat & 0x80) > 0:
            n += 1
        else:
            return n
🧰 Tools
🪛 Flake8 (7.3.0)

[error] 119-119: ambiguous variable name 'l'

(E741)


[error] 125-125: ambiguous variable name 'l'

(E741)

🪛 Ruff (0.15.21)

[error] 119-119: Ambiguous variable name: l

(E741)


[error] 125-125: Ambiguous variable name: l

(E741)

🤖 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 `@test/functional/test_framework/messages.py` around lines 119 - 136, Rename
the ambiguous parameter l in ser_varint to a descriptive name such as value, and
update all references to it within that function while preserving the existing
varint encoding logic.

Source: Linters/SAST tools

achow101 and others added 14 commits July 14, 2026 11:49
00618e8 assumeutxo: Drop block height from metadata (Fabian Jahr)

Pull request description:

  Fixes bitcoin#30514 which has more context and shows how the issue can be reproduced. Since the value in question is removed, there is no test to add to reproduce anything.

  This is an alternative approach to bitcoin#30516 with much of the [code being suggested there](bitcoin#30516 (comment)).

ACKs for top commit:
  maflcko:
    re-ACK 00618e8 🎌
  achow101:
    ACK 00618e8
  theStack:
    Code-review ACK 00618e8
  ismaelsadeeq:
    Re-ACK 00618e8
  mzumsande:
    ACK 00618e8

Tree-SHA512: db9575247bae838ad7742a27a216faaf55bb11e022f9afdd05752bb09bbf9614717d0ad64304ff5722a16bf41d8dea888af544e4ae26dcaa528c1add0269a4a8
…operate on snapshot chainstate

917e70a test: assumeutxo: check that UTXO-querying RPCs operate on snapshot chainstate (Sebastian Falbesoner)

Pull request description:

  Inspired by some manual testing I did for bitcoin#28553, this PR checks that RPCs which explicitly query the UTXO set database (i.e. `gettxoutsetinfo`, `scantxoutset` and `gettxout`) operate on the snapshot chainstate as expected.

ACKs for top commit:
  fjahr:
    utACK 917e70a
  achow101:
    ACK 917e70a
  tdb3:
    ACK 917e70a

Tree-SHA512: 40ecd1c5dd879234df1667fa5444a1fbbee9b7c456f597dc982d1a2bce46fe9107711b005ab829e570ef919a4914792f72f342d71d92bad2ae9434b5e68d5bd3
… nodes during IBD

992f83b test: add coverage for assumeUTXO honest peers disconnection (furszy)
6d5812e assumeUTXO: fix peers disconnection during sync (furszy)

Pull request description:

  Because AssumeUTXO nodes prioritize tip synchronization, they relay their local
  address through the network before completing the background chain sync.
  This, combined with the advertising of full-node service (`NODE_NETWORK`), can
  result in an honest peer in IBD connecting to the AssumeUTXO node (while syncing)
  and requesting an historical block the node does not have. This behavior leads to
  an abrupt disconnection due to perceived unresponsiveness from the AssumeUTXO
  node.

  This lack of response occurs because nodes ignore `getdata` requests when they do
  not have the block data available (further discussion can be found in bitcoin#30385).

  Fix this by refraining from signaling full-node service support while the
  background chain is being synced. During this period, the node will only
  signal `NODE_NETWORK_LIMITED` support. Then, full-node (`NODE_NETWORK`)
  support will be re-enabled once the background chain sync is completed.

  Thanks mzumsande for a post-bitcoin#30385 convo too.

  Testing notes:
  Just cherry-pick the second commit (bb08c22) on master.
  It will fail there, due to the IBD node requesting historical blocks to the snapshot
  node - which is bad because the snapshot node will ignore the requests and
  stall + disconnect after some time.

ACKs for top commit:
  achow101:
    ACK 992f83b
  naumenkogs:
    ACK 992f83b
  mzumsande:
    ACK 992f83b

Tree-SHA512: fef525d1cf3200c2dd89a346be9c82d77f2e28ddaaea1f490a435e180d1a47a371cadea508349777d740ab56e94be536ad8f7d61cc81f6550c58b609b3779ed3
…re_assumeutxo

19f4a7c test: Wait for local services to update in feature_assumeutxo (Fabian Jahr)

Pull request description:

  Closes bitcoin#30878

  It seems like there is a race where the block is stored locally and `getblock` does not error anymore, but `ActivateBestChain` has not finished yet, so the local services are not updated yet either. Fix this by waiting for the local services to update.

  Can be reproduced locally by adding the sleep here:

  ```cpp
  ──────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  src/validation.cpp:3567: bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< │
  ──────────────────────────────────────────────────────────────────────────────────────────────────────────┘
          }

          if (WITH_LOCK(::cs_main, return m_disabled)) {
              std::this_thread::sleep_for(std::chrono::seconds(10));
              // Background chainstate has reached the snapshot base block, so exit.

              // Restart indexes to resume indexing for all blocks unique to the snapshot
  ```

ACKs for top commit:
  maflcko:
    review-only ACK 19f4a7c
  achow101:
    ACK 19f4a7c
  pablomartin4btc:
    tACK 19f4a7c
  furszy:
    Code review ACK [19f4a7c](bitcoin@19f4a7c).

Tree-SHA512: 70dad3795988956c5e20f2d2d895fb56c5e3ce257c7547d3fd729c88949f0e24cb34594da1537bce8794ad02b2db44e7e46e995aa32539cd4dd84c4f1d4bb1c4
…t, Improve wallet RPC errors

9d2d9f7 rpc: Include assumeutxo as a failure reason of rescanblockchain (Fabian Jahr)
595edee test, assumeutxo: import descriptors during background sync (Alfonso Roman Zubeldia)
d73ae60 rpc: Improve importdescriptor RPC error messages (Fabian Jahr)
27f99b6 validation: Don't assume m_chain_tx_count in GuessVerificationProgress (Fabian Jahr)
42d5d53 interfaces: Add helper function for wallet on pruning (Fabian Jahr)

Pull request description:

  A test that is added as part of bitcoin#30455 uncovered this issue: The `GuessVerificationProgress` function is used during during descriptor import and relies on `m_chain_tx_count`. In bitcoin#29370 an [`Assume` was added](bitcoin@0fd915e) expecting the `m_chaint_tx_count` to be set. However, as the test uncovered, `GuessVerificationProgress` is called with background sync blocks that have `m_chaint_tx_count = 0` when they have not been downloaded and processed yet.

  The simple fix is to remove the `Assume`. Users should not be thrown off by the `Internal bug detected` error. The behavior of `importdescriptor` is kept consistent with the behavior for blocks missing due to pruning.

  The test by alfonsoromanz is cherry-picked here to show that the [CI errors](https://cirrus-ci.com/task/5110045812195328?logs=ci#L2535) should be fixed by this change.

  This PR also improves error messages returned by the `importdescriptors` and `rescanblockchain` RPCs. The error message now changes depending on the situation of the node, i.e. if pruning is happening or an assumutxo backgroundsync is active.

ACKs for top commit:
  achow101:
    ACK 9d2d9f7
  mzumsande:
    Code Review ACK 9d2d9f7
  furszy:
    Code review ACK 9d2d9f7

Tree-SHA512: b841a9b371e5eb8eb3bfebca35645ff2fdded7a3e5e06308d46a33a51ca42cc4c258028c9958fbbb6cda9bb990e07ab8d8504dd9ec6705ef78afe0435912b365
82be652 doc: Improve ChainstateManager documentation, use consistent terms (Ryan Ofsky)
af455dc refactor: Simplify pruning functions (TheCharlatan)
ae85c49 refactor: Delete ChainstateManager::GetAll() method (Ryan Ofsky)
6a572db refactor: Add ChainstateManager::ActivateBestChains() method (Ryan Ofsky)
491d827 refactor: Add ChainstateManager::m_chainstates member (Ryan Ofsky)
e514fe6 refactor: Delete ChainstateManager::SnapshotBlockhash() method (Ryan Ofsky)
ee35250 refactor: Delete ChainstateManager::IsSnapshotValidated() method (Ryan Ofsky)
d9e8229 refactor: Delete ChainstateManager::IsSnapshotActive() method (Ryan Ofsky)
4dfe383 refactor: Convert ChainstateRole enum to struct (Ryan Ofsky)
352ad27 refactor: Add ChainstateManager::ValidatedChainstate() method (Ryan Ofsky)
a229cb9 refactor: Add ChainstateManager::CurrentChainstate() method (Ryan Ofsky)
a9b7f56 refactor: Add Chainstate::StoragePath() method (Ryan Ofsky)
840bd2e refactor: Pass chainstate parameters to MaybeCompleteSnapshotValidation (Ryan Ofsky)
1598a15 refactor: Deduplicate Chainstate activation code (Ryan Ofsky)
9fe927b refactor: Add Chainstate m_assumeutxo and m_target_utxohash members (Ryan Ofsky)
6082c84 refactor: Add Chainstate::m_target_blockhash member (Ryan Ofsky)
de00e87 test: Fix broken chainstatemanager_snapshot_init check (Ryan Ofsky)

Pull request description:

  This PR contains the first part of bitcoin#28608, which tries to make assumeutxo code more maintainable, and improve it by not locking `cs_main` for a long time when the snapshot block is connected, and by deleting the snapshot validation chainstate when it is no longer used, instead of waiting until the next restart.

  The changes in this PR are just refactoring. They make `Chainstate` objects self-contained, so for example, it is possible to determine what blocks to connect to a chainstate without querying `ChainstateManager`, and to determine whether a Chainstate is validated without basing it on inferences like `&cs != &ActiveChainstate()` or `GetAll().size() == 1`.

  The PR also tries to make assumeutxo terminology less confusing, using "current chainstate" to refer to the chainstate targeting the current network tip, and "historical chainstate" to refer to the chainstate downloading old blocks and validating the assumeutxo snapshot. It removes uses of the terms "active chainstate," "usable chainstate," "disabled chainstate," "ibd chainstate," and "snapshot chainstate" which are confusing for various reasons.

ACKs for top commit:
  maflcko:
    re-review ACK 82be652 🕍
  fjahr:
    re-ACK 82be652
  sedited:
    Re-ACK 82be652

Tree-SHA512: 81c67abba9fc5bb170e32b7bf8a1e4f7b5592315b4ef720be916d5f1f5a7088c0c59cfb697744dd385552f58aa31ee36176bae6a6e465723e65861089a1252e5
…e with malleated snapshot

e5ff4e4 qa: use a clearer and documented amount error in malleated snapshot (Antoine Poinsot)
b34fdb5 test: introduce output amount (de)compression routines (Sebastian Falbesoner)
a7911ed test: introduce VARINT (de)serialization routines (Sebastian Falbesoner)

Pull request description:

  The `feature_assumeutxo.py` functional test checks various errors with malleated snapshots. Some of these cases are brittle or use confusing and undocumented values. Fix one of those by using a clear, documented and forward-compatible value.

  I ran across those when working on an unrelated changeset which affected the snapshot. It took me a while to understand where the seemingly magic byte string was coming from, so i figured it was worth proposing this patch on its own for the sake of making the test more maintainable.

  See commit messages for details.

ACKs for top commit:
  janb84:
    re ACK [e5ff4e4](bitcoin@e5ff4e4)
  theStack:
    ACK e5ff4e4
  fjahr:
    Code review ACK e5ff4e4
  i-am-yuvi:
    tACK e5ff4e4

Tree-SHA512: 60f022b7176836ce05e8f287b436329d7ca6460f3fcd95f78cd24e07a95a7d4d9cbbb68a117916a113fe451732b09a012d300fe860ff33d61823eca797ceddaf
02fae33 doc: add assumeutxo chainparams to release proc (willcl-ark)

Pull request description:

  This should ideally be bumped every major (and perhaps even minor?) release to avoid falling too far behind, and therefore keeping this feature as useful as it can be.

  Document in release-process.md to avoid forgetting to do this.

ACKs for top commit:
  achow101:
    ACK 02fae33
  glozow:
    ACK 02fae33

Tree-SHA512: 1c570b476a2c1369cde20965a762a4bce76fc27e7cf2704032132c9679ac1bc003d5dcc5b2bf39625f1b92b182254bec60743e52858ef89428df2b90ff4fb804
… successful

52482cb test: Check datadir cleanup after assumeutxo was successful (Fabian Jahr)

Pull request description:

  I noticed that the proper datadir cleanup after a successful restart of an assumutxo node does not seem to be covered in our tests. This is added here.

ACKs for top commit:
  l0rinc:
    utACK 52482cb
  mabu44:
    Re-ACK 52482cb
  Prabhat1308:
    re-ACK [`52482cb`](bitcoin@52482cb)
  TheCharlatan:
    Re-ACK 52482cb

Tree-SHA512: cc941afeba250050eaccf5112255d961253fec9b2683545454a0d2fbe4d542178394b301d169a9dd79edbf6b5d478d95282727dbb0aca96ee79d4cd1ff80f19b
…ack in feature_assumeutxo.py

206bc05 test: remove unnecessary m_best_header setting hack in feature_assumeutxo.py (Sebastian Falbesoner)

Pull request description:

  The mentioned bug (issue bitcoin#26245) has been fixed in PR bitcoin#30666, so the workaround is not needed anymore.

ACKs for top commit:
  fjahr:
    ACK 206bc05
  janb84:
    ACK 206bc05
  mzumsande:
    utACK 206bc05

Tree-SHA512: 92ad6c09c80318a02e00d8c2a4ca1ec8a6caa6aa310916ff705871c91fe5c960c15ed394ead8ce9c7466b592dba176aa87a635b3228ddc93f57082124f04613f
…sumeutxo.py

cb7d5bf test, assumeutxo: loading a wallet (backup) on a pruned node (Alfonso Roman Zubeldia)
7a36524 test, refactor snapshot import and background validation (Alfonso Roman Zubeldia)

Pull request description:

  Adding tests in `./test/functional/wallet_assumeutxo.py` to cover the following scenario:
  - test loading a wallet (backup) on a pruned node

ACKs for top commit:
  fjahr:
    re-ACK cb7d5bf
  theStack:
    re-ACK cb7d5bf

Tree-SHA512: 88cc419f340d31e80120e0c6cafe567efc678df27576db6e08aeab62d2b50ed1153d56f3f3343e9bae49262e38f9fb81db7769f02a4a01e4ef25c5d029c12323
…apshot block after assumeutxo validation

7d9e1a8 test: Verify peer usage after assumeutxo validation completes (stringintech)
0067abe p2p: Allow block downloads from peers without snapshot block after assumeutxo validation (stringintech)

Pull request description:

  Currently, after assumeutxo background validation finishes, the node continues to skip peers that don't have the snapshot block in their best chain until restart. This unnecessarily excludes peers from block downloads even though the background sync has completed and undo data is available.

  The restriction persists because `m_chainman.CurrentChainstate().SnapshotBase()` continues to return the snapshot base block until restart, even after validation completes. Added `m_chainman.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED` check to only apply the peer restriction while background validation is ongoing.

  Also added test coverage in `feature_assumeutxo.py` that verifies peers without the snapshot block can be used for block downloads after background validation completes. The test fails without this fix.

ACKs for top commit:
  fjahr:
    Re-ACK 7d9e1a8
  achow101:
    ACK 7d9e1a8
  sedited:
    Re-ACK 7d9e1a8

Tree-SHA512: 5515971da7bf7efc55eecdf03686f44c20c9e52dd168e7cfa119032d6a8ebccee69df7143075e4e9d0a01426cd9ae7202dce5c00919a82478ebf49a15dc0fe19
Complete the bitcoin#30214 role/state resync around synthetic target headers, carry the best-header recalculation needed by bitcoin#32746, and adapt the new functional coverage to Dash snapshot framing and test-framework paths.\n\nRoute out-of-transaction derived EvoDB cache writes to the active chainstate identity. This is required when bitcoin#30455 mines past a validated snapshot: block-template credit-pool caching must not leave the NORMAL transaction tree dirty while snapshot-identity blocks are active.
@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m6-hardening branch from ccd744b to 7e68400 Compare July 14, 2026 17:01
@PastaPastaPasta PastaPastaPasta force-pushed the assumeutxo/m7-params-polish branch from ee0bce7 to 52195cf Compare July 14, 2026 17:01
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.

6 participants