backport: assumeutxo M7 — params polish and docs rewrite (series complete)#56
backport: assumeutxo M7 — params polish and docs rewrite (series complete)#56PastaPastaPasta wants to merge 14 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughChangesAssumeUTXO 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
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
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
1d7c4f0 to
c1a0aa7
Compare
4885d35 to
332142c
Compare
acf6ad7 to
ee0bce7
Compare
There was a problem hiding this comment.
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 winHandle the full-domain range without wrapping to zero.
For
{begin = 0, end = 0}, Line 86 evaluatesUINT64_MAX + 1inuint64_t, soSize()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 valueConsider using
std::ranges::find_iffor idiomatic C++20.As per coding guidelines, C++20 is the target standard. You can leverage the
<algorithm>library andstd::ranges::find_ifto make the implementation more idiomatic. Additionally, taking the predicate by forwarding reference (L&&) instead ofconst Lcan 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
FindFirstfunction:-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 | 🔵 TrivialReminder: Address the TODO comment.
The TODO comment outlines pending work to recreate/rebind
ActiveContextsigners andNetInstantSendobjects for runtimeloadtxoutsetwhen theChainstateManagerswitches 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 valueUse
%" PRIu64 "or%llufor 64-bit unsigned integers instrprintf.Although
tinyformatprocesses type-safety gracefully and often ignores the format character byte size, passing auint64_tto a%d(signed 32-bit int) format specifier is technically incorrect and may trigger-Wformatwarnings with modern compiler-level format checkers. Consider using%" PRIu64 "or%lluacross these methods.
src/evo/deterministicmns.cpp#L376-L387: update the%dformat specifiers foridandp.firstinApplyDiff.src/evo/deterministicmns.cpp#L389-L415: update the%dformat specifiers foridandp.firstinApplyDiffForSnapshot.🤖 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 valueFix late-binding lint warning.
The linter flags that
rpcis a loop variable captured in a lambda. While this is not functionally a bug here (sincewait_until_helper_internalexecutes 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 valueFix late-binding lint warning.
The linter flags that
rpcis a loop variable captured in a lambda. While this is not functionally a bug here (sincewait_until_helper_internalexecutes 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 valueAvoid copying the
Coinobject and use a structured binding.Inside the loop,
Coin coin = it->second;creates an unnecessary copy of theCoinobject (which includes aCTxOutcontaining aCScriptvector). You can avoid this allocation and make the loop more readable by using a structured binding with aconst 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 valueRemove duplicate forward declaration.
The forward declaration for
enum class RBFTransactionStateis 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
📒 Files selected for processing (142)
contrib/devtools/utxo_snapshot.shdoc/assumeutxo.mddoc/design/assumeutxo.mddoc/release-notes-27596.mddoc/release-notes-28685.mddoc/release-notes-assumeutxo-m7-params-polish.mddoc/release-process.mddoc/zmq.mdsrc/Makefile.amsrc/Makefile.test.includesrc/active/context.cppsrc/active/context.hsrc/active/dkgsessionhandler.cppsrc/bench/load_external.cppsrc/bitcoin-chainstate.cppsrc/chain.hsrc/chainlock/handler.cppsrc/chainlock/handler.hsrc/chainlock/signing.cppsrc/chainlock/signing.hsrc/chainparams.cppsrc/chainparams.hsrc/dbwrapper.cppsrc/dbwrapper.hsrc/dsnotificationinterface.cppsrc/dsnotificationinterface.hsrc/evo/assetlocktx.cppsrc/evo/assetlocktx.hsrc/evo/chainhelper.cppsrc/evo/chainhelper.hsrc/evo/creditpool.cppsrc/evo/creditpool.hsrc/evo/deterministicmns.cppsrc/evo/deterministicmns.hsrc/evo/evodb.cppsrc/evo/evodb.hsrc/evo/mnhftx.cppsrc/evo/mnhftx.hsrc/evo/smldiff.cppsrc/evo/smldiff.hsrc/evo/snapshot.cppsrc/evo/snapshot.hsrc/evo/snapshot_load.cppsrc/evo/snapshot_types.hsrc/evo/specialtxman.cppsrc/evo/specialtxman.hsrc/index/base.cppsrc/index/base.hsrc/index/coinstatsindex.cppsrc/init.cppsrc/instantsend/net_instantsend.cppsrc/instantsend/net_instantsend.hsrc/interfaces/chain.hsrc/kernel/chain.cppsrc/kernel/chain.hsrc/kernel/coinstats.cppsrc/kernel/coinstats.hsrc/kernel/types.hsrc/llmq/blockprocessor.cppsrc/llmq/blockprocessor.hsrc/llmq/context.cppsrc/llmq/quorumsman.cppsrc/llmq/quorumsman.hsrc/llmq/signing_shares.cppsrc/llmq/signing_shares.hsrc/llmq/snapshot.cppsrc/llmq/snapshot.hsrc/llmq/utils.cppsrc/llmq/utils.hsrc/net.cppsrc/net.hsrc/net_processing.cppsrc/node/blockstorage.cppsrc/node/blockstorage.hsrc/node/chainstate.cppsrc/node/chainstate.hsrc/node/context.hsrc/node/interfaces.cppsrc/node/miner.cppsrc/node/utxo_snapshot.cppsrc/node/utxo_snapshot.hsrc/rpc/blockchain.cppsrc/rpc/blockchain.hsrc/rpc/client.cppsrc/rpc/masternode.cppsrc/rpc/quorums.cppsrc/streams.hsrc/test/blockmanager_tests.cppsrc/test/coinstatsindex_tests.cppsrc/test/evo_db_tests.cppsrc/test/evo_deterministicmns_tests.cppsrc/test/evo_netinfo_tests.cppsrc/test/evo_snapshot_tests.cppsrc/test/fuzz/chain.cppsrc/test/fuzz/deserialize.cppsrc/test/fuzz/load_external_block_file.cppsrc/test/fuzz/rpc.cppsrc/test/fuzz/utxo_snapshot.cppsrc/test/util/chainstate.hsrc/test/util/setup_common.cppsrc/test/util/setup_common.hsrc/test/util/validation.cppsrc/test/util/validation.hsrc/test/util_tests.cppsrc/test/validation_block_tests.cppsrc/test/validation_chainstate_tests.cppsrc/test/validation_chainstatemanager_tests.cppsrc/test/validation_tests.cppsrc/txdb.hsrc/util/ranges_set.cppsrc/util/ranges_set.hsrc/util/vector.hsrc/validation.cppsrc/validation.hsrc/validationinterface.cppsrc/validationinterface.hsrc/versionbits.hsrc/wallet/rpc/backup.cppsrc/wallet/rpc/transactions.cppsrc/wallet/test/fuzz/notifications.cppsrc/wallet/wallet.cppsrc/wallet/wallet.hsrc/zmq/zmqnotificationinterface.cppsrc/zmq/zmqnotificationinterface.htest/functional/feature_assumeutxo.pytest/functional/feature_assumeutxo_dash.pytest/functional/feature_coinstatsindex.pytest/functional/feature_dbcrash.pytest/functional/feature_reindex.pytest/functional/feature_utxo_set_hash.pytest/functional/rpc_blockchain.pytest/functional/rpc_dumptxoutset.pytest/functional/test_framework/compressor.pytest/functional/test_framework/messages.pytest/functional/test_framework/p2p.pytest/functional/test_framework/test_framework.pytest/functional/test_framework/test_node.pytest/functional/test_framework/util.pytest/functional/test_runner.pytest/functional/wallet_assumeutxo.pytest/lint/lint-shell.pytest/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
There was a problem hiding this comment.
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 winHandle the full-domain range without wrapping to zero.
For
{begin = 0, end = 0}, Line 86 evaluatesUINT64_MAX + 1inuint64_t, soSize()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 valueConsider using
std::ranges::find_iffor idiomatic C++20.As per coding guidelines, C++20 is the target standard. You can leverage the
<algorithm>library andstd::ranges::find_ifto make the implementation more idiomatic. Additionally, taking the predicate by forwarding reference (L&&) instead ofconst Lcan 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
FindFirstfunction:-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 | 🔵 TrivialReminder: Address the TODO comment.
The TODO comment outlines pending work to recreate/rebind
ActiveContextsigners andNetInstantSendobjects for runtimeloadtxoutsetwhen theChainstateManagerswitches 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 valueUse
%" PRIu64 "or%llufor 64-bit unsigned integers instrprintf.Although
tinyformatprocesses type-safety gracefully and often ignores the format character byte size, passing auint64_tto a%d(signed 32-bit int) format specifier is technically incorrect and may trigger-Wformatwarnings with modern compiler-level format checkers. Consider using%" PRIu64 "or%lluacross these methods.
src/evo/deterministicmns.cpp#L376-L387: update the%dformat specifiers foridandp.firstinApplyDiff.src/evo/deterministicmns.cpp#L389-L415: update the%dformat specifiers foridandp.firstinApplyDiffForSnapshot.🤖 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 valueFix late-binding lint warning.
The linter flags that
rpcis a loop variable captured in a lambda. While this is not functionally a bug here (sincewait_until_helper_internalexecutes 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 valueFix late-binding lint warning.
The linter flags that
rpcis a loop variable captured in a lambda. While this is not functionally a bug here (sincewait_until_helper_internalexecutes 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 valueAvoid copying the
Coinobject and use a structured binding.Inside the loop,
Coin coin = it->second;creates an unnecessary copy of theCoinobject (which includes aCTxOutcontaining aCScriptvector). You can avoid this allocation and make the loop more readable by using a structured binding with aconst 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 valueRemove duplicate forward declaration.
The forward declaration for
enum class RBFTransactionStateis 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
📒 Files selected for processing (142)
contrib/devtools/utxo_snapshot.shdoc/assumeutxo.mddoc/design/assumeutxo.mddoc/release-notes-27596.mddoc/release-notes-28685.mddoc/release-notes-assumeutxo-m7-params-polish.mddoc/release-process.mddoc/zmq.mdsrc/Makefile.amsrc/Makefile.test.includesrc/active/context.cppsrc/active/context.hsrc/active/dkgsessionhandler.cppsrc/bench/load_external.cppsrc/bitcoin-chainstate.cppsrc/chain.hsrc/chainlock/handler.cppsrc/chainlock/handler.hsrc/chainlock/signing.cppsrc/chainlock/signing.hsrc/chainparams.cppsrc/chainparams.hsrc/dbwrapper.cppsrc/dbwrapper.hsrc/dsnotificationinterface.cppsrc/dsnotificationinterface.hsrc/evo/assetlocktx.cppsrc/evo/assetlocktx.hsrc/evo/chainhelper.cppsrc/evo/chainhelper.hsrc/evo/creditpool.cppsrc/evo/creditpool.hsrc/evo/deterministicmns.cppsrc/evo/deterministicmns.hsrc/evo/evodb.cppsrc/evo/evodb.hsrc/evo/mnhftx.cppsrc/evo/mnhftx.hsrc/evo/smldiff.cppsrc/evo/smldiff.hsrc/evo/snapshot.cppsrc/evo/snapshot.hsrc/evo/snapshot_load.cppsrc/evo/snapshot_types.hsrc/evo/specialtxman.cppsrc/evo/specialtxman.hsrc/index/base.cppsrc/index/base.hsrc/index/coinstatsindex.cppsrc/init.cppsrc/instantsend/net_instantsend.cppsrc/instantsend/net_instantsend.hsrc/interfaces/chain.hsrc/kernel/chain.cppsrc/kernel/chain.hsrc/kernel/coinstats.cppsrc/kernel/coinstats.hsrc/kernel/types.hsrc/llmq/blockprocessor.cppsrc/llmq/blockprocessor.hsrc/llmq/context.cppsrc/llmq/quorumsman.cppsrc/llmq/quorumsman.hsrc/llmq/signing_shares.cppsrc/llmq/signing_shares.hsrc/llmq/snapshot.cppsrc/llmq/snapshot.hsrc/llmq/utils.cppsrc/llmq/utils.hsrc/net.cppsrc/net.hsrc/net_processing.cppsrc/node/blockstorage.cppsrc/node/blockstorage.hsrc/node/chainstate.cppsrc/node/chainstate.hsrc/node/context.hsrc/node/interfaces.cppsrc/node/miner.cppsrc/node/utxo_snapshot.cppsrc/node/utxo_snapshot.hsrc/rpc/blockchain.cppsrc/rpc/blockchain.hsrc/rpc/client.cppsrc/rpc/masternode.cppsrc/rpc/quorums.cppsrc/streams.hsrc/test/blockmanager_tests.cppsrc/test/coinstatsindex_tests.cppsrc/test/evo_db_tests.cppsrc/test/evo_deterministicmns_tests.cppsrc/test/evo_netinfo_tests.cppsrc/test/evo_snapshot_tests.cppsrc/test/fuzz/chain.cppsrc/test/fuzz/deserialize.cppsrc/test/fuzz/load_external_block_file.cppsrc/test/fuzz/rpc.cppsrc/test/fuzz/utxo_snapshot.cppsrc/test/util/chainstate.hsrc/test/util/setup_common.cppsrc/test/util/setup_common.hsrc/test/util/validation.cppsrc/test/util/validation.hsrc/test/util_tests.cppsrc/test/validation_block_tests.cppsrc/test/validation_chainstate_tests.cppsrc/test/validation_chainstatemanager_tests.cppsrc/test/validation_tests.cppsrc/txdb.hsrc/util/ranges_set.cppsrc/util/ranges_set.hsrc/util/vector.hsrc/validation.cppsrc/validation.hsrc/validationinterface.cppsrc/validationinterface.hsrc/versionbits.hsrc/wallet/rpc/backup.cppsrc/wallet/rpc/transactions.cppsrc/wallet/test/fuzz/notifications.cppsrc/wallet/wallet.cppsrc/wallet/wallet.hsrc/zmq/zmqnotificationinterface.cppsrc/zmq/zmqnotificationinterface.htest/functional/feature_assumeutxo.pytest/functional/feature_assumeutxo_dash.pytest/functional/feature_coinstatsindex.pytest/functional/feature_dbcrash.pytest/functional/feature_reindex.pytest/functional/feature_utxo_set_hash.pytest/functional/rpc_blockchain.pytest/functional/rpc_dumptxoutset.pytest/functional/test_framework/compressor.pytest/functional/test_framework/messages.pytest/functional/test_framework/p2p.pytest/functional/test_framework/test_framework.pytest/functional/test_framework/test_node.pytest/functional/test_framework/util.pytest/functional/test_runner.pytest/functional/wallet_assumeutxo.pytest/lint/lint-shell.pytest/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.hRepository: 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 100Repository: 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.cppRepository: 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/**' || trueRepository: 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.hRepository: 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.cppRepository: 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.cppRepository: 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]}') PYRepository: 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.cppRepository: PastaPastaPasta/dash
Length of output: 10301
Exclude
base_block_hashbefore comparing historical work targets
CEvoSnapshot::Validate()should mirrorValidateEvoSnapshotAgainstChain()and erasebase_block_hashfrom both sets before thehistorical_hashes != required_work_hashescheck.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.cppRepository: 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.cppRepository: 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.
CheckCbTxBestChainlockcan 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 declarationkernel::MakeBlockInfoare duplicated. In C++, specifying a default argument for a parameter multiple times in the same scope (as done withconst CBlock* data = nullptron 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*\(' srcRepository: 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)" srcRepository: 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.cppRepository: PastaPastaPasta/dash
Length of output: 9546
🏁 Script executed:
#!/bin/bash set -euo pipefail rg -n "using ServiceFlags|typedef.*ServiceFlags|enum class ServiceFlags|enum ServiceFlags" srcRepository: 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.cppRepository: PastaPastaPasta/dash
Length of output: 4462
Make local-service updates atomic RMWs.
AddLocalServices/RemoveLocalServicesstill do separate load/store operations onstd::atomic<ServiceFlags>, so concurrent updates can drop bits and advertise the wrong handshake services. Usefetch_or/fetch_andor 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.
ParseHashOrHeightcan 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::Paramsmutation isn't restored if an assertion in between fails — risk of cascading unrelated test failures.
mutable_consensus.DIP0003Heightis set to1on the process-wideParams().GetConsensus()singleton, and only restored by an unconditional statement placed afterMaybeValidateSnapshot(...)(Line 1107). Between the mutation and the restore sitsBOOST_REQUIRE(m_node.evodb->CommitRootTransaction(...))(Line 1103) and theMaybeValidateSnapshotcall itself — if either throws or theREQUIREfails, Boost.Test aborts the test case and the restore is skipped, permanently corruptingDIP0003Heightfor 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
linser_varint.Both Ruff and Flake8 flag
las 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 referencedserialize_tests.cpp:varint_bitpatternsvectors).🔧 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
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.
ccd744b to
7e68400
Compare
ee0bce7 to
52195cf
Compare
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?
#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 deliberatedecoupling 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 everyM2/M5 Dash guard (subsystem gating, role-tagged notifications).
#32746's prerequisite slice of#30666included.#30807/#33604(net_processing block-download changes) composed withM5's
#29519download-priority reconciliation.#31940— release-process doc, including a from-genesisevo_hashreproduction procedure for generating real testnet/mainnet
AssumeutxoDataat release time (deliberately not done in this series —it requires synced from-genesis nodes and is a release action, not a
code change).
identity bug: out-of-transaction derived writes weren't routing to the
active snapshot identity.
doc/design/assumeutxo.mdrewritten for Dash reality (evov3, 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)
AssumeutxoDatavalues onfrom-genesis nodes — a release-time action per
doc/release-process.md,not a code change.
loadtxoutseton 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.feature_assumeutxo_dash.pysolo(load-sensitive, per M6's finding): all passed.
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: 8MNs/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 filesproduced 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/dumptxoutsetare new RPC surface as of thisseries; no existing release behavior changes.
Checklist:
Summary by CodeRabbit
New Features
loadtxoutsetandgetchainstatesRPCs; enhanceddumptxoutsetwith snapshot details and rollback options.Behavior Changes
gettxoutsetinfonow reportshash_serialized_3instead ofhash_serialized_2.Bug Fixes