Skip to content

Conversation

@UdjinM6
Copy link

@UdjinM6 UdjinM6 commented Jan 13, 2026

Issue being fixed or feature implemented

Migrate three Dash-specific indexes (AddressIndex, SpentIndex, TimestampIndex) from synchronous operation in CBlockTreeDB to async operation using BaseIndex framework.

When enabling an index for the first time, the node will build the index in the background while remaining fully operational. Progress can be monitored via the getindexinfo RPC.

Existing nodes with indexes enabled will automatically migrate data from the old location (block index database) to new separate databases on first startup. This migration may take 20-40 minutes or longer depending on hardware specifications and index sizes. The node will log progress during migration.

What was done?

Architecture Improvements:

  • Indexes now derive from BaseIndex, enabling async catch-up during IBD
  • Each index has its own database under indexes/{addressindex,spentindex,timestampindex}/
  • No longer blocks validation during index updates
  • Can be enabled/disabled without -reindex

Migration System:

  • Template-based MigrateIndex<>() function handles data migration
  • Threshold-based incremental compaction (256 MiB) to reclaim disk space
  • Iterator reset before compaction to allow LevelDB file deletion
  • Best block locator written to ensure index consistency

How Has This Been Tested?

Run tests. Run a testnet/mainnet node and confirm migration succeeds (backup old datadir cause otherwise you'd have to reindex to go back to develop/master binaries).

Breaking Changes

  • SpentIndex and AddressIndex are incompatible with pruned nodes as they require access to undo data now

Checklist:

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

UdjinM6 and others added 10 commits January 13, 2026 00:43
Migrates TimestampIndex to follow the BaseIndex async pattern (similar to TxIndex),
enabling background syncing without blocking validation.

Key changes:
- Move data structures from src/timestampindex.h to src/index/timestampindex.h
- Implement async TimestampIndex class inheriting from BaseIndex
- Create separate database at indexes/timestampindex/ (previously in blocks/index/)
- Remove synchronous writes from validation (ConnectBlock, DisconnectBlock, RollforwardBlock)
- Update getblockhashes RPC to use async index with BlockUntilSyncedToCurrentChain()
- Remove global fTimestampIndex flag and ERROR_TIMEIDX_NEEDS_REINDEX
- Add cache allocation in node/caches.{h,cpp}
- Update initialization/shutdown in init.cpp

Benefits:
- Index can be toggled on/off without requiring -reindex
- Background syncing doesn't block block validation
- Works with pruned nodes (only stores block metadata)
- Consistent with other async indexes (txindex)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates feature_timestampindex.py to validate async index capabilities:
- Test that index can be disabled without -reindex
- Test that index can be re-enabled without -reindex (syncs in background)
- Use bump_mocktime() to ensure distinct timestamps for range queries
- Verify correct RPC behavior after toggling

These tests validate key advantages of the async migration over the
old synchronous implementation, where changing the index required a
full blockchain reindex.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit migrates SpentIndex to use the async BaseIndex pattern,
similar to the TimestampIndex migration.

Key changes:
- Create src/index/spentindex.{h,cpp} with async SpentIndex class
- Use CBlockUndo to access historical spent output data
- Incompatible with pruned nodes (requires undo data)
- Database location: indexes/spentindex/ (128MB cache)
- Remove synchronous writes from validation.cpp
- Remove database methods from txdb.{h,cpp}
- Remove global fSpentIndex flag from blockstorage.{h,cpp}
- Remove validation check from chainstate.{h,cpp}
- Update RPC handlers (getspentinfo, getrawtransaction)
- Move CAddressUnspent* structures to addressindex.h
- Fix locking: query async index before acquiring cs_main in TxToJSON

Implementation details:
- Uses undo data via UndoReadFromDisk() for historical UTXO access
- Cache size: 128MB (~1.2M entries, proportional to TxIndex)
- Properly handles both mempool and confirmed transactions
- Background syncing with BlockUntilSyncedToCurrentChain()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update the spentindex functional test to reflect that SpentIndex
now builds asynchronously and no longer requires -reindex to enable.

Changes:
- Remove test for ERROR_SPENTIDX_NEEDS_REINDEX (no longer applicable)
- Remove ErrorMatch import (no longer needed)
- Update test description to reflect async enable/disable behavior
- SpentIndex can now be enabled without -reindex like other async indexes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrates AddressIndex from synchronous writes during block validation
to asynchronous operation using the BaseIndex framework.

Key changes:
- Create async AddressIndex class in src/index/addressindex.{h,cpp}
- Dual database architecture: transaction history + unspent outputs
- Implement Rewind() and BlockDisconnected() for proper reorg handling
- Update RPC handlers (getaddressutxos, getaddressdeltas, getaddressbalance,
  getaddresstxids) to use g_addressindex global
- Remove synchronous writes from validation.cpp
- Remove database methods from txdb.{h,cpp}
- Remove global fAddressIndex flag from blockstorage
- Remove validation check from chainstate initialization
- Add 256MB cache allocation (larger than SpentIndex due to dual databases)
- Add to Makefile.am build system
Updates the AddressIndex functional test to reflect async operation:
- AddressIndex can now be enabled/disabled without -reindex
- Test already includes comprehensive reorg testing that verifies
  proper cleanup of both transaction history and unspent index
Add a template method to compact a specific key range instead of the
entire database. This enables incremental compaction during batch
operations to reclaim disk space progressively.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add CBlockTreeDB::MigrateOldIndexData() to migrate old synchronous index
data from the block index database to new async index databases during
node startup. This preserves existing index data so users don't need to
rebuild indexes after upgrading.

Key features:
- Migrates data for each index independently (timestampindex, spentindex, addressindex)
- Copies old entries from blocks/index to new databases under indexes/*
- Uses batched writes to handle millions of entries efficiently
- Writes DB_BEST_BLOCK metadata from chainstate so indexes know they're synced
- Erases old data from blocks/index after successful migration
- Compacts blocks/index database after each index to reclaim disk space progressively
- Automatically triggered during LoadBlockIndexDB()
- Only runs once - subsequent startups will find no old data

This migration significantly improves upgrade experience by avoiding
hours of index rebuilding on mainnet nodes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@UdjinM6 UdjinM6 added this to the 23.1 milestone Jan 13, 2026
@github-actions
Copy link

github-actions bot commented Jan 13, 2026

⚠️ Potential Merge Conflicts Detected

This PR has potential conflicts with the following open PRs:

Please coordinate with the authors of these PRs to avoid merge conflicts.

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Walkthrough

This PR converts address, spent, and timestamp indexes from synchronous block-tree storage to asynchronous BaseIndex-backed components. It adds AddressIndex, SpentIndex, and TimestampIndex under src/index/ with dedicated DB layers and types, exposes global pointers (g_addressindex, g_spentindex, g_timestampindex), updates build and init/shutdown flows to start/stop these indexes, removes legacy rpc/index_util and on-disk sync helpers, and adds CBlockTreeDB::MigrateOldIndexData() to migrate existing index data to the new async databases on first startup. RPCs, mempool, validation, and tests are updated to use the new async index APIs.

Sequence Diagram(s)

sequenceDiagram
    participant Chain as Chain Manager
    participant Index as Address/Spent/Timestamp Index
    participant Undo as Undo/UTXO Data
    participant DB as Index DB
    participant Disk as Disk

    Chain->>Index: WriteBlock(block, pindex)
    Index->>Undo: Read undo / prevouts
    Undo-->>Index: Undo / UTXO entries
    Index->>Index: Build index entries (spends/receipts/timestamps)
    Index->>DB: WriteBatch(entries)
    DB->>Disk: Persist entries
    Disk-->>DB: Ack
    DB-->>Index: Success
    Index-->>Chain: Return success
Loading
sequenceDiagram
    participant Node as Node Startup
    participant BlockDB as CBlockTreeDB
    participant Migrator as Migration routine
    participant NewDB as Async Index DBs
    participant OldDB as Old Block Index DB

    Node->>BlockDB: LoadBlockIndexDB()
    BlockDB->>Migrator: MigrateOldIndexData()
    Migrator->>OldDB: Read old index entries
    OldDB-->>Migrator: Old entries batch
    Migrator->>NewDB: WriteBatch to new index DBs
    NewDB->>NewDB: FinalizeMigration (best-block)
    Migrator->>OldDB: Compact/Erase migrated ranges
    Migrator-->>BlockDB: Migration result
    BlockDB-->>Node: Continue startup
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main architectural change: migrating three indexes from synchronous to asynchronous operation using the BaseIndex framework.
Description check ✅ Passed The description provides comprehensive detail on the migration architecture, data flow, testing approach, and breaking changes related to the core changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

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

⚠️ Outside diff range comments (1)
test/functional/feature_spentindex.py (1)

6-8: Incorrect file header comment.

The docstring says "Test addressindex generation and fetching" but this file is feature_spentindex.py which tests the spent index.

 #
-# Test addressindex generation and fetching
+# Test spentindex generation and fetching
 #
🤖 Fix all issues with AI agents
In @src/index/addressindex_types.h:
- Around line 8-19: The header src/index/addressindex_types.h is failing CI
formatting; run the repository's clang-format-diff.py flow to auto-apply the
project's clang-format style to this file (targeting the current patch/commit),
regenerate the diff, and commit the updated formatting so the
BITCOIN_INDEX_ADDRESSINDEX_TYPES_H header guard and the include block (e.g.,
consensus/amount.h, script/script.h, serialize.h, uint256.h, util/underlying.h
and the chrono/tuple/vector includes) conform to the project's style; ensure you
use the repo's specified clang-format binary/version when running the script
before pushing.

In @src/index/addressindex.cpp:
- Around line 61-77: The iterator loop over pcursor (checking DB_ADDRESSINDEX
and CAddressIndexKey::m_address_bytes == address_hash) lacks validation of the
address type and may include entries with the same hash but different
CAddressIndexKey::m_address_type; update the condition that currently checks
key.first == DB_ADDRESSINDEX && key.second.m_address_bytes == address_hash to
also require key.second.m_address_type == expected_type (or compare against the
provided address type variable), so only matching type+hash pairs are processed
before pushing into entries and advancing pcursor.
- Around line 90-103: The loop in the ReadAddressUnspentIndex-like logic only
checks key.first == DB_ADDRESSUNSPENTINDEX and key.second.m_address_bytes ==
address_hash, but never validates the address type; update the if condition to
also require key.second.type == type (or the actual function parameter name for
the requested address type) so only entries matching both address bytes and
address type are collected; ensure you still call pcursor->Next() on success and
return the same error on GetValue failure.

In @src/index/timestampindex.cpp:
- Around line 5-10: The file is missing the declaration for error(...), causing
build failures in TimestampIndex::Rewind and TimestampIndex::BlockDisconnected;
add the missing include by adding #include <util/error.h> alongside the existing
includes at the top of timestampindex.cpp so the calls to error(...) resolve
correctly.
- Around line 90-103: BlockDisconnected currently calls Rewind(best_block_index,
pindex->pprev) without ensuring pindex->pprev is non-null which can trigger an
assert in Rewind; add a defensive guard in TimestampIndex::BlockDisconnected to
check that pindex->pprev != nullptr before calling Rewind (if null, log an
error/warning with __func__ and GetName() and skip the Rewind), keeping existing
behavior when pprev is valid.

In @src/init.cpp:
- Around line 1213-1220: Add a runtime guard during index startup to catch
already-pruned datadir cases: when initializing address and spent indexes (the
code around the index start block at ~2277), check
chainman.m_blockman.m_have_pruned and if true return an InitError (or otherwise
abort with a clear message) indicating pruning is incompatible with enabling
-addressindex or -spentindex; ensure you reference the existing flag checks for
args.GetBoolArg("-addressindex") and args.GetBoolArg("-spentindex") so the new
check triggers even when -prune=0 but the on-disk datadir is already pruned.

In @src/rpc/node.cpp:
- Around line 814-843: Validate that outputIndex is non-negative before
constructing CSpentIndexKey: if outputIndex < 0, throw
JSONRPCError(RPC_INVALID_PARAMETER, "Invalid outpoint index"); then build
CSpentIndexKey(txid, outputIndex) and proceed with the existing async index
check (g_spentindex->GetSpentInfo) and mempool overlay (EnsureAnyMemPool +
mempool.getSpentIndex) logic unchanged; keep the current final error
throw(JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info")) when
neither source finds a value, and optionally add a distinct RPC error only if
GetSpentInfo can explicitly signal “not yet synced” (handle that case where
GetSpentInfo provides such a status).

In @src/txdb.h:
- Around line 93-98: Add the EXCLUSIVE_LOCKS_REQUIRED(::cs_main) annotation to
the public declaration of MigrateOldIndexData in src/txdb.h to enforce that
callers must hold ::cs_main (e.g., change the declaration of MigrateOldIndexData
to include EXCLUSIVE_LOCKS_REQUIRED(::cs_main)); this matches the existing
annotation on LoadBlockIndexGuts and reflects that MigrateOldIndexData reads the
coins DB (DB_BEST_BLOCK) and is only called from LoadBlockIndexDB which holds
cs_main.

In @test/util/data/non-backported.txt:
- Around line 21-23: The file lists header patterns for new index sources but
misses the corresponding source patterns; update the list (where you have
src/index/addres*.h, src/index/spent*.h, src/index/timestamp*.h) to also include
the matching .cpp patterns so clang-format covers source files too — add
src/index/addres*.cpp, src/index/spent*.cpp and src/index/timestamp*.cpp to the
same list.
🧹 Nitpick comments (13)
src/index/spentindex_types.h (1)

19-39: LGTM! Well-structured key type.

The key design with m_tx_hash and m_tx_index correctly identifies spent outputs.

Minor consideration: The codebase uses VARINT for similar index fields (e.g., CoinEntry in txdb.cpp line 65). Using VARINT for m_tx_index could reduce storage for typical low-value indices, though the current fixed-width approach is also valid.

src/index/spentindex.h (1)

76-76: Consider using const CSpentIndexKey& for the lookup parameter.

The key parameter appears to be input-only for the lookup operation. Using const CSpentIndexKey& key would better express the intent and allow callers to pass temporaries or const objects.

♻️ Suggested signature change
-    bool GetSpentInfo(CSpentIndexKey& key, CSpentIndexValue& value) const;
+    bool GetSpentInfo(const CSpentIndexKey& key, CSpentIndexValue& value) const;
src/index/spentindex.cpp (1)

148-161: Consider logging severity for rewind failures.

When Rewind fails in BlockDisconnected, the error is logged but the index may be left in an inconsistent state. This could lead to stale or incorrect data being served by GetSpentInfo.

Consider whether a more severe action (e.g., marking the index as requiring rebuild) would be appropriate, or document that the index may become inconsistent after a failed rewind.

src/txdb.cpp (1)

492-502: Consider using a small cache for chainstate read.

The CDBWrapper for reading the best block is created with cache size 0. While this works, a small cache (e.g., 8 MiB) could improve read performance, especially if the coins database needs to warm up.

-        CDBWrapper coins_db(chainstate_path, 0, false, false);
+        CDBWrapper coins_db(chainstate_path, 8 << 20 /* 8 MiB */, false, false);
src/rpc/rawtransaction.cpp (2)

79-131: Consider consolidating duplicate loop logic.

The code iterates over tx.vin and tx.vout twice—once for the index query and once for the mempool query. While functionally correct (mempool overwrites index entries), this could be simplified by querying both sources in a single pass.

That said, the current structure clearly separates the two data sources, which aids readability and maintains the semantic that mempool data takes precedence.


86-94: Minor: Redundant coinbase check inside loop.

tx.IsCoinBase() is checked for every input iteration, but it only needs to be checked once. Consider hoisting the check outside the loop.

+    const bool is_coinbase = tx.IsCoinBase();
     // Collect spent info for inputs
     for (const auto& txin : tx.vin) {
-        if (!tx.IsCoinBase()) {
+        if (!is_coinbase) {
             CSpentIndexValue spentInfo;
src/index/addressindex_types.h (1)

235-306: Unspent key/value types look consistent; consider moving CScript to avoid copies

CAddressUnspentKey fixed-size serialization and CAddressUnspentValue null semantics look fine. Minor improvement: avoid copying CScript in the value ctor.

Proposed diff (move CScript)
-    CAddressUnspentValue(CAmount amount, CScript tx_script, int32_t block_height) :
-        m_amount{amount},
-        m_tx_script{tx_script},
+    CAddressUnspentValue(CAmount amount, CScript tx_script, int32_t block_height) :
+        m_amount{amount},
+        m_tx_script{std::move(tx_script)},
         m_block_height{block_height} {};
src/rpc/node.cpp (3)

411-436: Consider loosening the hard dependency on g_addressindex for mempool-only results, or explicitly justify it.

At Line 413-415 you reject the RPC when -addressindex is disabled, but the implementation is purely mempool.getAddressIndex(...) (Line 430) and doesn’t query g_addressindex. If the intended policy is “these RPCs are only available when addressindex is enabled” then this is fine, but it would be good to make that explicit in a short comment because the guard otherwise looks accidental.


493-509: Prefer consistent “index not synced” error when GetAddressUnspentIndex() returns false.

With async indexes, a false return may mean “not synced yet / interrupted” rather than “no address info”. Right now you throw "No information available for address" (Line 507), which can be misleading during startup/IBD. Consider emitting a dedicated message (similar to getaddressbalance’s "Address index not yet synced").


586-595: Avoid mutating start/end inside the per-address loop (surprising behavior).

At Line 590 you normalize start/end for every address iteration. For multi-address queries this can silently change query semantics after the first address. Better: normalize once before the loop (or keep per-address local copies).

src/index/timestampindex.cpp (1)

25-44: ReadRange() should either clear hashes or document “appends to output”.

Right now ReadRange() always appends to the caller-provided vector. That’s fine if intended, but it’s easy to accidentally reuse a vector and get unexpected results. A one-line comment (or hashes.clear() if callers always expect a fresh result) would prevent surprises.

src/index/addressindex.h (1)

8-14: Make the header self-contained and tighten encapsulation (final class).

  • This header uses std::unique_ptr but doesn’t include <memory>. Relying on transitive includes is fragile.
  • Since AddressIndex is final, m_db doesn’t need to be protected.
Proposed fix
 #include <index/addressindex_types.h>
 #include <index/base.h>

+#include <memory>
 #include <vector>

@@
 class AddressIndex final : public BaseIndex
 {
 protected:
     class DB;
-    const std::unique_ptr<DB> m_db;
+private:
+    const std::unique_ptr<DB> m_db;
 
+protected:
     class DB : public BaseIndex::DB

Also applies to: 23-28

src/index/addressindex.cpp (1)

115-124: Consider suppressing unused variable warning.

The value in the structured binding at line 119 is unused. While this works, using a placeholder or attribute would be cleaner.

-for (const auto& [key, value] : entries) {
+for (const auto& [key, _] : entries) {
     batch.Erase(std::make_pair(DB_ADDRESSINDEX, key));
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7482956 and d52a3cb.

📒 Files selected for processing (41)
  • doc/release-notes-7101.md
  • src/Makefile.am
  • src/addressindex.cpp
  • src/core_write.cpp
  • src/dbwrapper.h
  • src/index/addressindex.cpp
  • src/index/addressindex.h
  • src/index/addressindex_types.h
  • src/index/addressindex_util.cpp
  • src/index/addressindex_util.h
  • src/index/spentindex.cpp
  • src/index/spentindex.h
  • src/index/spentindex_types.h
  • src/index/timestampindex.cpp
  • src/index/timestampindex.h
  • src/index/timestampindex_types.h
  • src/init.cpp
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/caches.cpp
  • src/node/caches.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/rpc/blockchain.cpp
  • src/rpc/index_util.cpp
  • src/rpc/index_util.h
  • src/rpc/node.cpp
  • src/rpc/rawtransaction.cpp
  • src/spentindex.h
  • src/test/util/setup_common.cpp
  • src/timestampindex.h
  • src/txdb.cpp
  • src/txdb.h
  • src/txmempool.cpp
  • src/txmempool.h
  • src/validation.cpp
  • src/zmq/zmqpublishnotifier.cpp
  • test/functional/feature_addressindex.py
  • test/functional/feature_spentindex.py
  • test/functional/feature_timestampindex.py
  • test/util/data/non-backported.txt
💤 Files with no reviewable changes (9)
  • src/test/util/setup_common.cpp
  • src/spentindex.h
  • src/node/chainstate.cpp
  • src/node/blockstorage.h
  • src/addressindex.cpp
  • src/timestampindex.h
  • src/rpc/index_util.h
  • src/node/chainstate.h
  • src/rpc/index_util.cpp
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/core_write.cpp
  • src/index/addressindex_util.h
  • src/index/timestampindex_types.h
  • src/node/caches.cpp
  • src/node/blockstorage.cpp
  • src/dbwrapper.h
  • src/validation.cpp
  • src/rpc/blockchain.cpp
  • src/txdb.cpp
  • src/txmempool.cpp
  • src/rpc/rawtransaction.cpp
  • src/node/caches.h
  • src/index/spentindex_types.h
  • src/txmempool.h
  • src/index/spentindex.h
  • src/index/addressindex.cpp
  • src/index/timestampindex.cpp
  • src/index/addressindex_types.h
  • src/index/addressindex.h
  • src/index/addressindex_util.cpp
  • src/rpc/node.cpp
  • src/zmq/zmqpublishnotifier.cpp
  • src/index/timestampindex.h
  • src/index/spentindex.cpp
  • src/txdb.h
  • src/init.cpp
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests in test/functional/ must be written in Python (minimum version specified in .python-version) and depend on dashd and dash-node

Files:

  • test/functional/feature_timestampindex.py
  • test/functional/feature_addressindex.py
  • test/functional/feature_spentindex.py
src/node/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

NodeContext must be extended with Dash-specific managers for each subsystem during initialization

Files:

  • src/node/caches.cpp
  • src/node/blockstorage.cpp
  • src/node/caches.h
{guix-build*,releases,**/guix-build*,releases/**,.github/**,depends/**,ci/**,contrib/**,doc/**}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not make changes to build system files (guix-build*), release artifacts, or avoid changes to .github, depends, ci, contrib, and doc directories unless specifically prompted

Files:

  • doc/release-notes-7101.md
🧠 Learnings (30)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/blockprocessor.cpp:217-224
Timestamp: 2025-08-19T14:57:31.801Z
Learning: In PR #6692, knst acknowledged a null pointer dereference issue in ProcessBlock() method where LookupBlockIndex may return nullptr but is passed to gsl::not_null, and created follow-up PR #6789 to address it, consistent with avoiding scope creep in performance-focused PRs.
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/core_write.cpp
  • src/node/caches.cpp
  • src/validation.cpp
  • src/rpc/blockchain.cpp
  • src/txdb.cpp
  • src/txmempool.cpp
  • src/rpc/rawtransaction.cpp
  • test/util/data/non-backported.txt
  • src/index/spentindex_types.h
  • src/txmempool.h
  • src/Makefile.am
  • src/index/addressindex.cpp
  • src/rpc/node.cpp
  • src/index/spentindex.cpp
  • src/txdb.h
  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/**/*.{cpp,h} : Special transactions use payload serialization routines defined in src/evo/specialtx.h and must include appropriate special transaction types (ProRegTx, ProUpServTx, ProUpRegTx, ProUpRevTx)

Applied to files:

  • src/core_write.cpp
  • src/rpc/blockchain.cpp
  • src/rpc/rawtransaction.cpp
  • test/util/data/non-backported.txt
  • src/txmempool.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Applied to files:

  • src/core_write.cpp
  • src/node/caches.cpp
  • src/node/blockstorage.cpp
  • src/validation.cpp
  • src/txdb.cpp
  • test/util/data/non-backported.txt
  • src/txmempool.h
  • src/index/spentindex.h
  • src/Makefile.am
  • src/index/addressindex.cpp
  • src/rpc/node.cpp
  • src/index/spentindex.cpp
  • src/txdb.h
  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Applied to files:

  • src/core_write.cpp
  • src/node/blockstorage.cpp
  • src/rpc/blockchain.cpp
  • src/txdb.cpp
  • test/util/data/non-backported.txt
  • src/Makefile.am
  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite

Applied to files:

  • src/core_write.cpp
  • src/node/caches.cpp
  • src/txdb.cpp
  • src/txmempool.h
  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq}/**/*.{cpp,h} : BLS integration must be used for cryptographic foundation of advanced masternode features

Applied to files:

  • src/core_write.cpp
  • src/rpc/blockchain.cpp
  • src/rpc/rawtransaction.cpp
  • test/util/data/non-backported.txt
  • src/txmempool.h
  • src/Makefile.am
  • src/zmq/zmqpublishnotifier.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • test/functional/feature_timestampindex.py
  • test/functional/feature_spentindex.py
📚 Learning: 2025-12-22T15:42:48.595Z
Learnt from: kwvg
Repo: dashpay/dash PR: 7068
File: src/qt/guiutil_font.h:68-77
Timestamp: 2025-12-22T15:42:48.595Z
Learning: In C++/Qt codebases, use fail-fast asserts in setters to enforce invariants (e.g., ensuring internal maps contain necessary keys). Prefer assert() for programmer errors that should be caught in development and debugging, rather than defensive runtime checks with fallbacks. This helps catch invariant violations early during development. Apply to header and source files where invariant-driven state mutations occur, especially in setters like SetWeightBold/SetWeightNormal that assume established relationships or preconditions.

Applied to files:

  • src/index/addressindex_util.h
  • src/index/timestampindex_types.h
  • src/dbwrapper.h
  • src/node/caches.h
  • src/index/spentindex_types.h
  • src/txmempool.h
  • src/index/spentindex.h
  • src/index/addressindex_types.h
  • src/index/addressindex.h
  • src/index/timestampindex.h
  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h} : Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Applied to files:

  • src/node/caches.cpp
  • test/util/data/non-backported.txt
  • src/node/caches.h
  • src/Makefile.am
📚 Learning: 2025-01-07T18:50:44.838Z
Learnt from: knst
Repo: dashpay/dash PR: 6511
File: src/evo/deterministicmns.cpp:1369-1373
Timestamp: 2025-01-07T18:50:44.838Z
Learning: The functions `MigrateDBIfNeeded` and `MigrateDBIfNeeded2` in `src/evo/deterministicmns.cpp` are temporary and will be removed in a future version. Refactoring suggestions for these functions should be avoided.

Applied to files:

  • src/node/blockstorage.cpp
  • src/txdb.cpp
  • src/txdb.h
📚 Learning: 2025-08-19T14:57:31.801Z
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/blockprocessor.cpp:217-224
Timestamp: 2025-08-19T14:57:31.801Z
Learning: In PR #6692, knst acknowledged a null pointer dereference issue in ProcessBlock() method where LookupBlockIndex may return nullptr but is passed to gsl::not_null, and created follow-up PR #6789 to address it, consistent with avoiding scope creep in performance-focused PRs.

Applied to files:

  • src/node/blockstorage.cpp
📚 Learning: 2025-06-06T11:53:09.094Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6665
File: src/evo/providertx.h:82-82
Timestamp: 2025-06-06T11:53:09.094Z
Learning: In ProTx serialization code (SERIALIZE_METHODS), version checks should use hardcoded maximum flags (/*is_basic_scheme_active=*/true, /*is_extended_addr=*/true) rather than deployment-based flags. This is because serialization code should be able to deserialize any structurally valid ProTx up to the maximum version the code knows how to handle, regardless of current consensus validity. Validation code, not serialization code, is responsible for checking whether a ProTx version is consensus-valid based on deployment status.

Applied to files:

  • src/validation.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/validation.cpp
  • src/rpc/blockchain.cpp
  • src/txmempool.cpp
  • src/rpc/rawtransaction.cpp
  • test/util/data/non-backported.txt
  • src/txmempool.h
  • src/Makefile.am
📚 Learning: 2025-11-25T10:53:37.523Z
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: In Dash Core, `DEFAULT_SYNC_MEMPOOL` belongs with node synchronization logic (currently in src/masternode/sync.h) rather than with validation logic in src/validation.h, even though other mempool-related constants are in validation.h. The constant is conceptually tied to sync functionality, not validation.

Applied to files:

  • src/validation.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files

Applied to files:

  • src/validation.cpp
  • src/rpc/blockchain.cpp
  • src/txdb.cpp
  • src/Makefile.am
  • src/rpc/node.cpp
  • src/init.cpp
📚 Learning: 2025-07-23T09:30:34.631Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.h:5-6
Timestamp: 2025-07-23T09:30:34.631Z
Learning: Dash Core uses BITCOIN_ prefix for header guards as the standard convention, inherited from Bitcoin Core. Only a few BLS-specific files in src/bls/ use DASH_ prefix. The vast majority of files (385+) use BITCOIN_ prefix.

Applied to files:

  • src/txdb.cpp
  • src/index/addressindex_types.h
  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Dash extends Bitcoin Core through composition with minimal changes to the Bitcoin Core foundation

Applied to files:

  • src/txdb.cpp
  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : ChainLocks implementation must prevent reorganizations and provide block finality through 51% attack prevention

Applied to files:

  • src/txmempool.cpp
  • src/rpc/rawtransaction.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : InstantSend implementation must provide distributed key generation for secure transaction locking with quorum consensus

Applied to files:

  • src/txmempool.cpp
  • src/rpc/rawtransaction.cpp
  • test/util/data/non-backported.txt
  • src/txmempool.h
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Applied to files:

  • src/rpc/rawtransaction.cpp
  • src/rpc/node.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : LLMQ quorums must support multiple configurations (50/60, 400/60, 400/85) for different services (ChainLocks, InstantSend, governance voting)

Applied to files:

  • src/rpc/rawtransaction.cpp
  • src/Makefile.am
📚 Learning: 2025-10-02T18:29:54.756Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6840
File: src/net_processing.cpp:2882-2886
Timestamp: 2025-10-02T18:29:54.756Z
Learning: Across net_processing.cpp, once LLMQContext (m_llmq_ctx) is asserted non-null, its subcomponents (e.g., isman, qdkgsman, quorum_block_processor) are treated as initialized and used without extra null checks.

Applied to files:

  • src/rpc/rawtransaction.cpp
📚 Learning: 2025-01-02T21:50:00.967Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/context.cpp:42-43
Timestamp: 2025-01-02T21:50:00.967Z
Learning: LLMQContext manages concurrency for the `CInstantSendManager`. Previously, this was handled globally; now it's handled as a class member in `LLMQContext`, but the concurrency control remains consistent.

Applied to files:

  • src/rpc/rawtransaction.cpp
📚 Learning: 2025-12-01T18:13:21.314Z
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 7018
File: test/lint/lint-github-actions.py:1-9
Timestamp: 2025-12-01T18:13:21.314Z
Learning: In the Dash repository, the file test/util/data/non-backported.txt should only include C++ files (.cpp or .h) because it is used for running clang-format. Other file types (such as Python files, .ui files, etc.) should not be added to this list.

Applied to files:

  • test/util/data/non-backported.txt
📚 Learning: 2025-08-11T17:16:36.654Z
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6804
File: src/qt/proposalwizard.cpp:40-42
Timestamp: 2025-08-11T17:16:36.654Z
Learning: In the Dash repository, when a PR adds new files that are not from Bitcoin backports, these files must be added to the list in test/util/data/non-backported.txt. This applies to newly created files like qt/proposalwizard.{h,cpp} and forms/proposalwizard.ui. Limited exemptions may exist for subtrees and similar cases.

Applied to files:

  • test/util/data/non-backported.txt
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo}/**/*.{cpp,h} : Masternode lists must use immutable data structures (Immer library) for thread safety

Applied to files:

  • test/util/data/non-backported.txt
  • src/Makefile.am
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Applied to files:

  • test/util/data/non-backported.txt
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/txmempool.h
  • src/Makefile.am
  • src/txdb.h
📚 Learning: 2025-10-05T20:38:28.457Z
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.

Applied to files:

  • src/txdb.h
🧬 Code graph analysis (14)
test/functional/feature_timestampindex.py (2)
test/functional/test_framework/test_framework.py (4)
  • start_node (635-644)
  • no_op (789-790)
  • sync_all (862-864)
  • bump_mocktime (866-883)
test/functional/test_framework/util.py (1)
  • assert_equal (69-74)
src/index/addressindex_util.h (1)
src/index/addressindex_util.cpp (2)
  • AddressBytesFromScript (12-28)
  • AddressBytesFromScript (12-12)
test/functional/feature_addressindex.py (1)
test/functional/test_framework/test_framework.py (1)
  • sync_all (862-864)
src/rpc/rawtransaction.cpp (2)
src/bitcoin-tx.cpp (1)
  • txin (263-263)
src/core_write.cpp (1)
  • spentKey (269-269)
src/index/spentindex_types.h (1)
src/txdb.cpp (1)
  • SERIALIZE_METHODS (66-66)
src/txmempool.h (1)
src/txmempool.cpp (2)
  • getAddressIndex (583-595)
  • getAddressIndex (583-584)
src/index/addressindex.cpp (1)
src/index/addressindex_util.cpp (2)
  • AddressBytesFromScript (12-28)
  • AddressBytesFromScript (12-12)
test/functional/feature_spentindex.py (1)
src/rpc/blockchain.cpp (1)
  • unspent (2576-2576)
src/index/timestampindex.cpp (2)
src/index/timestampindex_types.h (1)
  • CTimestampIndexIteratorKey (18-19)
src/index/timestampindex.h (1)
  • BlockDisconnected (49-52)
src/index/addressindex.h (1)
src/index/addressindex.cpp (25)
  • AddressIndex (141-144)
  • AddressIndex (146-146)
  • DB (21-24)
  • WriteBatch (26-47)
  • WriteBatch (26-27)
  • ReadAddressIndex (49-80)
  • ReadAddressIndex (49-51)
  • ReadAddressUnspentIndex (82-113)
  • ReadAddressUnspentIndex (82-84)
  • EraseAddressIndex (115-124)
  • EraseAddressIndex (115-115)
  • UpdateAddressUnspentIndex (126-139)
  • UpdateAddressUnspentIndex (126-126)
  • WriteBlock (148-252)
  • WriteBlock (148-148)
  • Rewind (254-375)
  • Rewind (254-254)
  • BlockDisconnected (377-390)
  • BlockDisconnected (377-377)
  • GetDB (392-392)
  • GetDB (392-392)
  • GetAddressIndex (394-403)
  • GetAddressIndex (394-396)
  • GetAddressUnspentIndex (405-414)
  • GetAddressUnspentIndex (405-407)
src/index/addressindex_util.cpp (1)
src/hash.h (1)
  • Hash160 (94-99)
src/index/timestampindex.h (1)
src/index/timestampindex.cpp (19)
  • TimestampIndex (51-54)
  • TimestampIndex (56-56)
  • DB (15-18)
  • Write (20-23)
  • Write (20-20)
  • ReadRange (25-44)
  • ReadRange (25-25)
  • EraseTimestampIndex (46-49)
  • EraseTimestampIndex (46-46)
  • WriteBlock (58-68)
  • WriteBlock (58-58)
  • Rewind (70-88)
  • Rewind (70-70)
  • BlockDisconnected (90-103)
  • BlockDisconnected (90-90)
  • GetDB (105-105)
  • GetDB (105-105)
  • GetBlockHashes (107-114)
  • GetBlockHashes (107-107)
src/index/spentindex.cpp (3)
src/node/blockstorage.cpp (2)
  • UndoReadFromDisk (512-543)
  • UndoReadFromDisk (512-512)
src/index/addressindex_util.cpp (2)
  • AddressBytesFromScript (12-28)
  • AddressBytesFromScript (12-12)
src/index/spentindex.h (1)
  • BlockDisconnected (60-63)
src/txdb.h (1)
src/txdb.cpp (2)
  • MigrateOldIndexData (465-586)
  • MigrateOldIndexData (465-465)
🪛 Cppcheck (2.19.0)
src/rpc/blockchain.cpp

[error] 22-22: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)

src/txdb.cpp

[error] 10-10: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)

src/index/addressindex.cpp

[error] 10-10: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)


[error] 24-24: #error No known always_inline attribute for this platform.

(preprocessorErrorDirective)

src/index/timestampindex.cpp

[error] 16-16: #error Client version information missing

(preprocessorErrorDirective)

src/index/addressindex_util.cpp

[error] 10-10: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)

src/index/spentindex.cpp

[error] 16-16: #error Client version information missing

(preprocessorErrorDirective)

🪛 GitHub Actions: Clang Diff Format Check
src/index/addressindex_types.h

[error] 28-29: Clang format differences found between before/after formatting. Run 'git diff -U0 origin/develop -- $(git ls-files -- $(cat test/util/data/non-backported.txt)) | ./contrib/devtools/clang-format-diff.py -p1' to generate diffs and apply formatting fixes in this file. Exit code 1 from the formatting check.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_multiprocess-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
🔇 Additional comments (52)
src/zmq/zmqpublishnotifier.cpp (1)

21-21: LGTM!

The include is correctly added to support the ToUnderlying call used on line 468 for logging the governance object type. This makes the dependency explicit rather than relying on transitive includes.

src/dbwrapper.h (1)

395-410: LGTM!

The CompactRange implementation correctly follows the established pattern used by EstimateSize for key serialization. Memory safety is ensured since the CDataStream objects remain in scope for the duration of the pdb->CompactRange call, and the const qualifier is appropriate since compaction doesn't change the logical database contents.

doc/release-notes-7101.md (1)

1-12: LGTM! Clear and informative release notes.

The documentation clearly explains the migration behavior, expected duration, and breaking changes regarding pruned node incompatibility. The mention of getindexinfo RPC for progress monitoring is helpful for users.

src/index/addressindex_util.cpp (1)

12-28: LGTM! Address extraction logic is correct.

The byte offsets correctly match standard script formats:

  • P2SH: extracts the 20-byte hash at bytes [2, 22)
  • P2PKH: extracts the 20-byte hash at bytes [3, 23)
  • P2PK: hashes the pubkey (excluding length prefix and OP_CHECKSIG)

The static analysis hint about line 10 is a false positive from Cppcheck not recognizing the <vector> system header.

src/txmempool.h (2)

21-22: LGTM! Appropriate includes for the new index type headers.

The new includes for addressindex_types.h and spentindex_types.h align with the PR's migration of index types to dedicated header files under src/index/.


614-615: LGTM! Return type change is consistent with implementation.

The change from bool to void aligns with the implementation in txmempool.cpp which simply populates the results vector without any failure path. This simplifies the API since the function always succeeds.

src/index/spentindex_types.h (2)

41-79: LGTM! Comprehensive value structure.

The value type captures all relevant spending information: the spending transaction, block height, amount, and address details. The IsNull() check on m_tx_hash is a reasonable sentinel for validity.


81-89: LGTM! Clean comparison functor and type alias.

Using std::tie for lexicographic comparison is idiomatic and correct. The CSpentIndexEntry alias provides a convenient key-value pair type.

src/index/spentindex.h (3)

13-17: LGTM! Reasonable defaults and helper struct.

DEFAULT_SPENTINDEX{false} is appropriate for an opt-in feature. The CSpentIndexTxInfo struct provides a convenient container for collecting spent info during block processing.


19-29: LGTM! Clear and comprehensive documentation.

The docstring clearly explains the index purpose, the undo data dependency, and why the index is incompatible with pruned nodes. The database location is also documented.


30-77: Overall class design follows BaseIndex pattern correctly.

The SpentIndex class properly:

  • Derives from BaseIndex with appropriate overrides
  • Uses a nested DB class for database operations
  • Implements Rewind and BlockDisconnected for reorg handling
  • Returns false from AllowPrune() to enforce the undo data requirement
  • Exposes a global g_spentindex pointer consistent with other indexes (g_addressindex, g_timestampindex)
src/core_write.cpp (1)

22-22: LGTM!

The include consolidation from <addressindex.h> and <spentindex.h> to <index/spentindex.h> aligns with the new index structure. The types used in this file (CSpentIndexKey, CSpentIndexValue, CSpentIndexTxInfo) are correctly exposed through the new header path.

src/index/spentindex.cpp (5)

1-3: LGTM!

New SpentIndex implementation file with appropriate MIT license header.


22-53: LGTM!

The DB class follows the BaseIndex::DB pattern correctly. The WriteBatch method handles both writes and deletes cleanly using the null value convention (documented in line 32 comment), and EraseSpentIndex provides efficient batch deletion.


62-108: LGTM!

The WriteBlock implementation correctly:

  • Skips the genesis block (no inputs to index)
  • Reads undo data to extract spent output information
  • Validates undo data size matches input count (preventing data corruption)
  • Extracts address type/bytes using AddressBytesFromScript
  • Batches all entries for efficient DB write

110-146: LGTM!

The Rewind implementation correctly iterates backwards through the chain, reads blocks from disk, collects all spent index keys for each block's inputs, and erases them in batches. The final call to BaseIndex::Rewind ensures the best block locator is updated consistently.


165-172: LGTM!

The GetSpentInfo method correctly ensures the index is synced to the current chain tip before querying, providing consistent data to callers.

src/node/blockstorage.cpp (1)

394-398: LGTM!

The migration call is correctly placed in LoadBlockIndexDB after reindexing flag check. Returning false on migration failure ensures the node won't start with an inconsistent index state. This is the appropriate location since it runs during initial block database loading.

test/functional/feature_addressindex.py (2)

49-56: LGTM!

The test correctly validates the new async index behavior:

  1. Disables -addressindex and restarts
  2. Re-enables -addressindex without -reindex
  3. Continues with normal index operations

This confirms that the async AddressIndex can be toggled without requiring a full reindex, as expected from the migration to the BaseIndex framework.


58-342: Test coverage is comprehensive.

The existing test cases thoroughly validate address index functionality including p2pkh/p2sh queries, balance tracking, height range filtering, reorg handling, and mempool indexing. The async index behavior is implicitly validated by the successful execution of these queries after the toggle-without-reindex flow.

src/rpc/blockchain.cpp (2)

22-22: LGTM!

Correctly includes the new timestamp index header to access g_timestampindex for the async index migration.


555-565: LGTM! Good error handling for async index.

The implementation correctly:

  1. Checks if the timestamp index is enabled before use
  2. Provides a distinct error when the index is still syncing vs. not enabled
  3. Uses the global g_timestampindex pointer consistent with the async index pattern
src/node/caches.cpp (2)

7-9: LGTM!

Correctly includes the new index headers to access their default flag constants (DEFAULT_ADDRESSINDEX, DEFAULT_TIMESTAMPINDEX, DEFAULT_SPENTINDEX).


25-30: Cache allocation is well-proportioned.

The cache allocation ratios make sense:

  • Address index (256 MiB cap): Larger index with address-to-txid mappings
  • Timestamp index (8 MiB cap): Simpler timestamp-to-blockhash mappings
  • Spent index (128 MiB cap): Moderate size for spent output tracking

All three correctly reduce nTotalCache after allocation to ensure subsequent allocations work from the remaining budget.

src/node/caches.h (1)

20-22: LGTM!

The new cache size fields are correctly added with consistent int64_t type matching the existing fields. These are properly populated by CalculateCacheSizes() in caches.cpp.

src/Makefile.am (2)

248-258: LGTM!

New index headers are correctly added to BITCOIN_CORE_H in proper alphabetical order within the index/ group.


541-547: LGTM!

New index source files are correctly added to libbitcoin_node_a_SOURCES:

  • index/addressindex.cpp and index/addressindex_util.cpp for address indexing
  • index/spentindex.cpp for spent output tracking
  • index/timestampindex.cpp for timestamp-based block lookups

These are properly placed in the node library since indexes are node-specific functionality. Old root-level index files have been removed.

test/functional/feature_spentindex.py (3)

50-57: LGTM!

The test correctly validates that the async spent index can be enabled without requiring -reindex, which is the expected behavior after migrating to the async BaseIndex framework.


124-130: LGTM!

Good addition to verify that getspentinfo correctly returns mempool-backed data with height: -1 for unconfirmed transactions.


140-168: LGTM!

Comprehensive reorg handling test that validates the spent index correctly transitions between confirmed and mempool states during block invalidation and reconsideration.

test/functional/feature_timestampindex.py (3)

28-40: LGTM!

The test correctly validates that the async timestamp index can be enabled and disabled without requiring -reindex, consistent with the PR's migration to the BaseIndex framework.


42-52: LGTM!

Good test coverage for the timestamp index RPC, using bump_mocktime to ensure distinct block timestamps for reliable verification.


54-72: LGTM!

The reorg test correctly validates that the timestamp index removes invalidated blocks and restores them after reconsideration. The assertion checks are consistent with invalidating the block at index 3 (which invalidates both blocks 3 and 4).

src/txmempool.cpp (4)

36-40: LGTM!

Forward declarations for the index classes and their global pointers avoid circular header dependencies while allowing the mempool to check index availability.


538-541: LGTM!

The null guard correctly prevents address index updates when the index is not enabled, avoiding unnecessary work and potential null pointer issues.


608-611: LGTM!

Consistent null guard pattern with addAddressIndex, correctly preventing spent index updates when the index is disabled.


583-595: No changes required. The function currently returns void with no evidence in the git history of a previous bool return type. The single call site in src/rpc/node.cpp:430 correctly treats the function as void. The implementation has no failure paths and the out-parameter pattern for populating results is a valid C++ design.

Likely an incorrect or invalid review comment.

src/txdb.cpp (4)

44-48: LGTM!

Defining the deprecated key constants locally for migration purposes is appropriate. These are only needed during the one-time migration and don't pollute the public interface.


356-442: Well-structured migration template.

The implementation correctly handles:

  • Batched writes to avoid memory pressure
  • Iterator reset before compaction (required by LevelDB)
  • Periodic compaction to reclaim disk space during migration
  • Final sync writes for durability

One minor observation: after compaction and iterator reset (lines 412-413), seeking to start relies on previously-erased keys being gone. This is correct since WriteBatch with erase was called before compaction.


448-463: LGTM!

The finalization correctly writes a minimal CBlockLocator containing just the best block hash, ensuring the migrated index knows its sync point.


542-575: LGTM!

Correctly migrates both address index types (transaction history and unspent) to the same database, with a single best-block locator write covering both.

src/rpc/rawtransaction.cpp (1)

133-140: LGTM!

The lock ordering is correct: spent index queries (which may call BlockUntilSyncedToCurrentChain) must complete before acquiring cs_main. The comment at lines 80-81 explains this requirement.

src/validation.cpp (1)

1057-1062: Mempool indexing calls are properly gated—no action needed

The unconditional calls to addAddressIndex() and addSpentIndex() in src/validation.cpp are safe. Both methods in src/txmempool.cpp (lines 540 and 610) immediately return early if the corresponding global index (g_addressindex or g_spentindex) is null. These globals are only instantiated when -addressindex or -spentindex command-line flags are explicitly enabled; both default to false. The mempool methods have no performance or behavioral impact when indexes are disabled.

src/rpc/node.cpp (1)

653-670: Good: using index best-block height to avoid chain-tip/index skew.

Using g_addressindex->GetSummary() and rejecting when not synced (Line 663-668) is the right direction for async index RPCs.

src/init.cpp (1)

242-277: Index lifecycle wiring looks consistent (Interrupt/Stop/reset).

The added Interrupt() and Stop()/reset() hooks for g_addressindex, g_timestampindex, and g_spentindex (Line 264-272 and Line 389-400) match the existing g_txindex pattern and are placed after background callback flushing.

Also applies to: 384-406

src/index/timestampindex.h (1)

8-12: The timestampindex.h file does not currently contain the GetBlockHashes method at lines 58-66 that the review comment references, nor is there evidence of this method being called in the codebase. The review comment appears to be written for a future state of the code or for a different version of the file than currently exists in the repository.

To verify this review comment properly, I would need:

  • Confirmation of whether this is code being reviewed in a pull request that hasn't been applied yet
  • The actual implementation of GetBlockHashes method with the (high, low) parameter order mentioned
  • Verification that the parameter order confusion actually exists in the intended code state
  • Confirmation of which call sites need to be audited for the parameter order issue

The missing includes (<memory>, <vector>) and the parameter order confusion ([low, high] vs (high, low)) are valid concerns if the mentioned code exists, but they cannot be definitively verified against the current state of the repository.

src/index/addressindex.cpp (6)

1-19: LGTM!

The includes, constants, and global pointer declaration follow the established pattern for async indexes. The constexpr database key prefixes are well-defined.


21-47: LGTM!

The DB constructor and WriteBatch implementation are well-structured. The null-value convention for signaling deletion is applied consistently, and the batch operations ensure atomicity.


55-59: Height range filtering requires both bounds to be set.

The condition start > 0 && end > 0 means specifying only a start height (e.g., start=100, end=0 for "from block 100 onwards") will not seek to the correct position. Consider whether this is intentional or if the logic should support one-sided ranges.

If one-sided ranges should be supported:

-if (start > 0 && end > 0) {
+if (start > 0) {
     pcursor->Seek(std::make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, address_hash, start)));
 } else {

148-252: LGTM!

The WriteBlock implementation correctly:

  • Skips genesis block (no undo data available)
  • Validates undo data alignment with transaction inputs
  • Tracks spending (negative values) and receiving (positive values)
  • Uses correct transaction indices (0 for coinbase, i+1 for others)
  • Processes coinbase outputs separately since coinbase has no inputs

254-275: LGTM on Rewind structure.

The rewind logic correctly:

  • Asserts the ancestor relationship
  • Processes blocks in reverse order from current_tip to new_tip
  • Reads both block and undo data for each block being unwound
  • Delegates to base class at the end to update the best block pointer

394-414: LGTM on query methods.

Both GetAddressIndex and GetAddressUnspentIndex correctly call BlockUntilSyncedToCurrentChain() before querying, ensuring callers receive consistent data relative to the current chain tip.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/index/addressindex.cpp`:
- Around line 361-373: AddressIndex::BlockDisconnected may call Rewind with
pindex->pprev which can be nullptr for the genesis block; add a null check for
pindex->pprev before invoking Rewind (use CurrentIndex() to decide whether to
rewind and only call Rewind(best_block_index, pindex->pprev) when pindex->pprev
!= nullptr), and if pindex->pprev is nullptr log an appropriate error/warning
(including GetName()) and skip the Rewind to avoid the assertion in Rewind.

In `@src/index/spentindex.cpp`:
- Around line 148-160: In SpentIndex::BlockDisconnected, add a defensive null
check for pindex->pprev before calling Rewind: if pindex->pprev is null (e.g.,
genesis), skip calling Rewind and log or handle gracefully (use process/error
logging similar to the existing error call using GetName()); keep the existing
checks using CurrentIndex() and only call Rewind(best_block_index,
pindex->pprev) when pindex->pprev != nullptr to avoid a potential null pointer
dereference.
♻️ Duplicate comments (1)
src/index/timestampindex.cpp (1)

5-9: Missing include for error() function.

The error() function is called at lines 81 and 99, but <util/error.h> is not included. This may cause compilation failures unless transitively included.

Proposed fix
 `#include` <index/timestampindex.h>

 `#include` <chain.h>
 `#include` <logging.h>
+#include <util/error.h>
 `#include` <util/system.h>
🧹 Nitpick comments (4)
src/index/addressindex_util.cpp (1)

12-28: Consider adding bounds checks before byte extraction.

The Is* methods should ensure valid script format, but defensive bounds checking before slice operations would guard against malformed scripts or future changes to these methods.

For example, script.begin() + 22 on P2SH assumes at least 23 bytes. While IsPayToScriptHash() should only return true for valid P2SH scripts, adding explicit size checks would be more robust.

🛡️ Optional defensive check example
 bool AddressBytesFromScript(const CScript& script, AddressType& address_type, uint160& address_bytes)
 {
     if (script.IsPayToScriptHash()) {
+        if (script.size() < 23) return false;  // P2SH is exactly 23 bytes
         address_type = AddressType::P2SH;
         address_bytes = uint160(std::vector<uint8_t>(script.begin() + 2, script.begin() + 22));
     } else if (script.IsPayToPublicKeyHash()) {
+        if (script.size() < 25) return false;  // P2PKH is exactly 25 bytes
         address_type = AddressType::P2PK_OR_P2PKH;
         address_bytes = uint160(std::vector<uint8_t>(script.begin() + 3, script.begin() + 23));
     } else if (script.IsPayToPublicKey()) {
+        if (script.size() < 35) return false;  // P2PK minimum is 35 bytes (compressed)
         address_type = AddressType::P2PK_OR_P2PKH;
         address_bytes = Hash160(std::vector<uint8_t>(script.begin() + 1, script.end() - 1));
     } else {
test/functional/feature_spentindex.py (1)

148-162: Consider adding sync_all() after invalidation to ensure consistent state before verification.

The test invalidates blocks on all nodes in a loop but doesn't synchronize before checking getspentinfo on node 1. While this likely works because node 1's mempool is populated independently, adding a sync would make the test more robust.

♻️ Optional improvement
         # Invalidate the block containing the spending transaction on all nodes
         for node in self.nodes:
             node.invalidateblock(block_hash)
+        self.sync_all()
 
         # After invalidation, transactions go back to mempool
         # getspentinfo should still find the spend in mempool with height -1
src/index/addressindex.cpp (1)

266-318: Minor: Comment doesn't match implementation.

The comment at line 266 says "Process transactions in reverse to undo them", but the loop actually processes transactions in forward order (index 0 to vtxundo.size()). The operations performed are still correct reversals, so this is just a documentation inconsistency.

Suggested fix
-        // Process transactions in reverse to undo them
+        // Undo each transaction's index entries
         // blockundo.vtxundo[i] corresponds to block.vtx[i+1] (coinbase skipped)
src/rpc/node.cpp (1)

589-594: Consider moving start/end validation outside the loop.

The start/end reset at line 590 is evaluated on every iteration but only needs to happen once. Moving it before the loop would be cleaner.

Suggested refactor
     if (!g_addressindex) {
         throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
     }
+    if (start <= 0 || end <= 0) { start = 0; end = 0; }
     for (const auto& address : addresses) {
-        if (start <= 0 || end <= 0) { start = 0; end = 0; }
         if (!g_addressindex->GetAddressIndex(address.first, address.second,
                                              addressIndex, start, end)) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
         }
     }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d52a3cb and 5e6534a.

📒 Files selected for processing (10)
  • src/index/addressindex.cpp
  • src/index/addressindex.h
  • src/index/addressindex_util.cpp
  • src/index/spentindex.cpp
  • src/index/spentindex.h
  • src/index/timestampindex.cpp
  • src/rpc/node.cpp
  • src/txdb.h
  • test/functional/feature_spentindex.py
  • test/util/data/non-backported.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/util/data/non-backported.txt
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/txdb.h
  • src/index/addressindex.h
  • src/index/spentindex.h
  • src/index/spentindex.cpp
  • src/index/addressindex.cpp
  • src/index/timestampindex.cpp
  • src/rpc/node.cpp
  • src/index/addressindex_util.cpp
test/functional/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Functional tests in test/functional/ must be written in Python (minimum version specified in .python-version) and depend on dashd and dash-node

Files:

  • test/functional/feature_spentindex.py
🧠 Learnings (19)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request `#6543` is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/blockprocessor.cpp:217-224
Timestamp: 2025-08-19T14:57:31.801Z
Learning: In PR `#6692`, knst acknowledged a null pointer dereference issue in ProcessBlock() method where LookupBlockIndex may return nullptr but is passed to gsl::not_null, and created follow-up PR `#6789` to address it, consistent with avoiding scope creep in performance-focused PRs.
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Applied to files:

  • src/txdb.h
  • src/index/addressindex.h
  • src/index/spentindex.h
  • src/index/spentindex.cpp
  • src/index/addressindex.cpp
  • src/rpc/node.cpp
📚 Learning: 2025-01-07T18:50:44.838Z
Learnt from: knst
Repo: dashpay/dash PR: 6511
File: src/evo/deterministicmns.cpp:1369-1373
Timestamp: 2025-01-07T18:50:44.838Z
Learning: The functions `MigrateDBIfNeeded` and `MigrateDBIfNeeded2` in `src/evo/deterministicmns.cpp` are temporary and will be removed in a future version. Refactoring suggestions for these functions should be avoided.

Applied to files:

  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/txdb.h
  • src/index/spentindex.h
  • src/index/spentindex.cpp
  • src/rpc/node.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Applied to files:

  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/wallet/**/*.{cpp,h} : Wallet implementation must use Berkeley DB and SQLite

Applied to files:

  • src/txdb.h
  • src/index/spentindex.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : ChainLocks implementation must prevent reorganizations and provide block finality through 51% attack prevention

Applied to files:

  • src/txdb.h
📚 Learning: 2025-07-22T14:38:45.606Z
Learnt from: PastaPastaPasta
Repo: dashpay/dash PR: 6738
File: src/evo/smldiff.h:91-94
Timestamp: 2025-07-22T14:38:45.606Z
Learning: In the Dash codebase, EXCLUSIVE_LOCKS_REQUIRED annotations are Clang thread safety annotations that are enforced at compile time on supported platforms like macOS. If callers don't properly hold the required locks, the build will fail with compile-time errors, not runtime issues.

Applied to files:

  • src/txdb.h
📚 Learning: 2025-11-04T18:24:27.241Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.

Applied to files:

  • src/txdb.h
📚 Learning: 2025-07-23T09:30:34.631Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.h:5-6
Timestamp: 2025-07-23T09:30:34.631Z
Learning: Dash Core uses BITCOIN_ prefix for header guards as the standard convention, inherited from Bitcoin Core. Only a few BLS-specific files in src/bls/ use DASH_ prefix. The vast majority of files (385+) use BITCOIN_ prefix.

Applied to files:

  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Dash extends Bitcoin Core through composition with minimal changes to the Bitcoin Core foundation

Applied to files:

  • src/txdb.h
📚 Learning: 2025-10-05T20:38:28.457Z
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.

Applied to files:

  • src/txdb.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/txdb.h
📚 Learning: 2025-12-22T15:42:48.595Z
Learnt from: kwvg
Repo: dashpay/dash PR: 7068
File: src/qt/guiutil_font.h:68-77
Timestamp: 2025-12-22T15:42:48.595Z
Learning: In C++/Qt codebases, use fail-fast asserts in setters to enforce invariants (e.g., ensuring internal maps contain necessary keys). Prefer assert() for programmer errors that should be caught in development and debugging, rather than defensive runtime checks with fallbacks. This helps catch invariant violations early during development. Apply to header and source files where invariant-driven state mutations occur, especially in setters like SetWeightBold/SetWeightNormal that assume established relationships or preconditions.

Applied to files:

  • src/txdb.h
  • src/index/addressindex.h
  • src/index/spentindex.h
📚 Learning: 2025-08-19T14:57:31.801Z
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/blockprocessor.cpp:217-224
Timestamp: 2025-08-19T14:57:31.801Z
Learning: In PR `#6692`, knst acknowledged a null pointer dereference issue in ProcessBlock() method where LookupBlockIndex may return nullptr but is passed to gsl::not_null, and created follow-up PR `#6789` to address it, consistent with avoiding scope creep in performance-focused PRs.

Applied to files:

  • src/index/addressindex.cpp
  • src/index/timestampindex.cpp
  • src/rpc/node.cpp
📚 Learning: 2025-05-05T12:45:44.781Z
Learnt from: knst
Repo: dashpay/dash PR: 6658
File: src/evo/creditpool.cpp:177-185
Timestamp: 2025-05-05T12:45:44.781Z
Learning: The GetAncestor() function in CBlockIndex safely handles negative heights by returning nullptr rather than asserting, making it safe to call with potentially negative values.

Applied to files:

  • src/index/timestampindex.cpp
📚 Learning: 2025-08-08T07:01:47.332Z
Learnt from: knst
Repo: dashpay/dash PR: 6805
File: src/wallet/rpc/wallet.cpp:357-357
Timestamp: 2025-08-08T07:01:47.332Z
Learning: In src/wallet/rpc/wallet.cpp, the upgradetohd RPC now returns a UniValue string message (RPCResult::Type::STR) instead of a boolean, including guidance about mnemonic backup and null-character passphrase handling; functional tests have been updated to assert returned strings in several cases.

Applied to files:

  • src/rpc/node.cpp
📚 Learning: 2025-02-14T15:19:17.218Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6529
File: src/wallet/rpcwallet.cpp:3002-3003
Timestamp: 2025-02-14T15:19:17.218Z
Learning: The `GetWallet()` function calls in `src/wallet/rpcwallet.cpp` are properly validated with null checks that throw appropriate RPC errors, making additional validation unnecessary.

Applied to files:

  • src/rpc/node.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files

Applied to files:

  • src/rpc/node.cpp
🧬 Code graph analysis (7)
src/txdb.h (1)
src/txdb.cpp (2)
  • MigrateOldIndexData (465-586)
  • MigrateOldIndexData (465-465)
src/index/spentindex.h (1)
src/index/spentindex.cpp (9)
  • SpentIndex (55-58)
  • SpentIndex (60-60)
  • DB (22-25)
  • WriteBatch (27-39)
  • WriteBatch (27-27)
  • ReadSpentIndex (41-44)
  • ReadSpentIndex (41-41)
  • EraseSpentIndex (46-53)
  • EraseSpentIndex (46-46)
src/index/spentindex.cpp (4)
src/index/addressindex.cpp (7)
  • DB (21-24)
  • Rewind (244-359)
  • Rewind (244-244)
  • BlockDisconnected (361-373)
  • BlockDisconnected (361-361)
  • GetDB (375-375)
  • GetDB (375-375)
src/node/blockstorage.cpp (2)
  • UndoReadFromDisk (512-543)
  • UndoReadFromDisk (512-512)
src/index/addressindex_util.cpp (2)
  • AddressBytesFromScript (12-29)
  • AddressBytesFromScript (12-12)
src/index/spentindex.h (1)
  • BlockDisconnected (60-63)
test/functional/feature_spentindex.py (3)
test/functional/test_framework/test_framework.py (1)
  • sync_all (862-864)
src/rpc/node.cpp (2)
  • getspentinfo (780-857)
  • getspentinfo (780-780)
test/functional/test_framework/util.py (1)
  • assert_equal (69-74)
src/index/addressindex.cpp (2)
src/node/blockstorage.cpp (6)
  • UndoReadFromDisk (512-543)
  • UndoReadFromDisk (512-512)
  • ReadBlockFromDisk (744-767)
  • ReadBlockFromDisk (744-744)
  • ReadBlockFromDisk (769-781)
  • ReadBlockFromDisk (769-769)
src/index/addressindex_util.cpp (2)
  • AddressBytesFromScript (12-29)
  • AddressBytesFromScript (12-12)
src/index/timestampindex.cpp (5)
src/index/spentindex.cpp (5)
  • DB (22-25)
  • BlockDisconnected (148-160)
  • BlockDisconnected (148-148)
  • GetDB (162-162)
  • GetDB (162-162)
src/index/timestampindex_types.h (1)
  • CTimestampIndexIteratorKey (18-19)
src/index/addressindex.h (1)
  • BlockDisconnected (67-71)
src/index/spentindex.h (1)
  • BlockDisconnected (60-63)
src/index/timestampindex.h (1)
  • BlockDisconnected (49-52)
src/index/addressindex_util.cpp (1)
src/hash.h (1)
  • Hash160 (94-99)
🪛 Cppcheck (2.19.0)
src/index/spentindex.cpp

[error] 16-16: #error Client version information missing

(preprocessorErrorDirective)

src/index/addressindex.cpp

[error] 10-10: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)


[error] 24-24: #error No known always_inline attribute for this platform.

(preprocessorErrorDirective)

src/index/timestampindex.cpp

[error] 16-16: #error Client version information missing

(preprocessorErrorDirective)

src/index/addressindex_util.cpp

[error] 10-10: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: Lint / Run linters
🔇 Additional comments (16)
src/txdb.h (1)

95-97: LGTM!

The MigrateOldIndexData() declaration is clean with proper EXCLUSIVE_LOCKS_REQUIRED(::cs_main) annotation, matching the contract established by LoadBlockIndexGuts. The documentation comment clearly describes the method's purpose.

src/index/spentindex.cpp (3)

62-108: LGTM!

The WriteBlock implementation correctly:

  • Skips the genesis block (no undo data available)
  • Validates undo data alignment with transaction inputs
  • Extracts address information via AddressBytesFromScript
  • Batches entries efficiently before writing

110-146: LGTM!

The Rewind implementation correctly handles chain reorganizations by:

  • Validating the ancestor relationship via assertion
  • Iterating backwards through disconnected blocks
  • Reading blocks from disk to identify spent outputs to remove
  • Delegating to BaseIndex::Rewind for best-block pointer updates

27-53: LGTM!

The DB methods are well-implemented:

  • WriteBatch efficiently handles both writes and deletions using the null-value convention
  • EraseSpentIndex properly batches erasure operations
  • Clear documentation of the null-value deletion pattern
src/index/spentindex.h (2)

30-77: LGTM!

The SpentIndex class is well-designed:

  • Proper documentation explaining pruning incompatibility and undo data requirement
  • Class marked final as it's not intended for further derivation
  • AllowPrune() = false correctly enforces the undo data requirement
  • Clean separation between public API (GetSpentInfo) and internal operations

15-17: LGTM!

The CSpentIndexTxInfo struct provides a clean container for tracking spent outputs in the mempool context.

test/functional/feature_spentindex.py (2)

124-130: LGTM!

Good test coverage for mempool integration. The test correctly verifies that getspentinfo returns height=-1 for unconfirmed transactions, which matches the RPC implementation that checks mempool after the async index.


49-57: LGTM!

Good coverage for the async index behavior. This test verifies the PR's key feature that spentindex can be enabled/disabled without requiring -reindex, which is a significant usability improvement.

src/index/addressindex.h (1)

1-92: LGTM! Well-structured async index header.

The header properly:

  • Documents the dual-database design (transaction history + UTXO filtering)
  • Correctly disables pruning via AllowPrune() const override { return false; }
  • Exposes appropriate public query methods with const qualifiers
  • Uses proper encapsulation with protected/private members
src/index/timestampindex.cpp (2)

90-102: LGTM! BlockDisconnected properly guards against null pprev.

The condition at line 97 correctly checks pindex->pprev before calling Rewind, preventing potential issues when disconnecting the genesis block.


25-44: LGTM! Timestamp range query implementation is correct.

The ReadRange method properly:

  • Seeks to the lower timestamp bound
  • Iterates while checking the upper bound (m_block_time <= high)
  • Terminates cleanly when leaving the valid key range
src/index/addressindex.cpp (2)

49-80: LGTM! Type validation now correctly included.

The iterator loop at line 62 now properly validates both m_address_type == type and m_address_bytes == address_hash, addressing the previous review concern about returning entries of incorrect types.


148-242: LGTM! WriteBlock implementation is well-structured.

The implementation correctly:

  • Skips genesis block (no undo data)
  • Validates undo data matches transaction input count
  • Processes coinbase outputs separately (no inputs)
  • Uses proper index mapping (i + 1 to skip coinbase in vtx)
  • Gracefully skips unsupported script types via AddressBytesFromScript
src/rpc/node.cpp (3)

413-415: LGTM! Address index availability check is correct.

The RPC handler properly validates that g_addressindex is enabled before proceeding with mempool queries.


663-669: LGTM! Proper handling of async index state.

The implementation correctly:

  • Uses the index's best_block_height rather than chain tip (avoiding inconsistency during sync)
  • Checks synced status before proceeding
  • Documents the rationale clearly in comments

814-856: LGTM! getspentinfo correctly layers async index with mempool.

The implementation properly:

  • Validates outputIndex >= 0 (addressing previous review)
  • Checks the async index first, then mempool
  • Correctly overwrites with mempool state (may have newer data during reorg)
  • Documents the rationale for mempool priority

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

UdjinM6 and others added 2 commits January 14, 2026 17:57
- Add null check for pindex->pprev in BlockDisconnected
- Add address type validation in AddressIndex iterator loops
- Add outputIndex >= 0 validation in getspentinfo RPC
- Add EXCLUSIVE_LOCKS_REQUIRED(::cs_main) to MigrateOldIndexData
- Use const reference for SpentIndex::GetSpentInfo key parameter
- Move m_db to private section in AddressIndex
- Add missing <memory> include in addressindex.h
- Fix unused variable warning (use _ placeholder)
- Fix docstring in feature_spentindex.py
- Add .cpp patterns to non-backported.txt

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UdjinM6 and others added 2 commits January 17, 2026 01:18
Add addressindex, timestampindex, and spentindex to getindexinfo RPC
output. Update rpc_misc.py test to verify new indexes are reported.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Check sync status upfront for address, timestamp, and spent index RPCs.
Include current sync height in error messages to help users understand
progress, following the pattern used by coinstatsindex.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@UdjinM6 UdjinM6 force-pushed the feat_async_additional_indexes branch from 721ff47 to 0a7a4f1 Compare January 16, 2026 22:37
@UdjinM6
Copy link
Author

UdjinM6 commented Jan 16, 2026

@coderabbitai pls review

@UdjinM6 UdjinM6 added the RPC Some notable changes to RPC params/behaviour/descriptions label Jan 16, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@UdjinM6, I'll conduct a comprehensive review of this major refactoring. This is a substantial PR migrating three indexes to async operation. Let me examine the key areas:


🏁 Script executed:

#!/bin/bash
# Examine the migration logic in txdb.cpp to understand how existing data is migrated
cat src/txdb.cpp | grep -A 50 "MigrateOldIndexData"

Length of output: 2525


🏁 Script executed:

#!/bin/bash
# Check the AddressIndex implementation focusing on WriteBlock and Rewind
cat src/index/addressindex.cpp | head -200

Length of output: 7264


🏁 Script executed:

#!/bin/bash
# Check the SpentIndex implementation
cat src/index/spentindex.cpp | head -150

Length of output: 5305


🏁 Script executed:

#!/bin/bash
# Check the TimestampIndex implementation
cat src/index/timestampindex.cpp

Length of output: 3828


🏁 Script executed:

#!/bin/bash
# Examine RPC changes to verify proper sync checks
cat src/rpc/node.cpp | grep -B 5 -A 15 "GetSummary"

Length of output: 5229


🏁 Script executed:

#!/bin/bash
# Check how pruning compatibility is enforced
rg -n "AllowPrune" src/index/*.h

Length of output: 556


🏁 Script executed:

#!/bin/bash
# Look for potential race conditions in index operations
rg -n "cs_main\|LOCK\|mutex" src/index/addressindex.cpp src/index/spentindex.cpp src/index/timestampindex.cpp

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check test updates for async behavior
cat test/functional/feature_addressindex.py | grep -A 10 "reindex"

Length of output: 438


🏁 Script executed:

#!/bin/bash
# Verify error handling in RPC methods
cat src/rpc/blockchain.cpp | grep -B 3 -A 10 "g_timestampindex"

Length of output: 928


🏁 Script executed:

#!/bin/bash
# Check the complete migration flow including compaction and finalization
cat src/txdb.cpp | grep -A 100 "template <uint8_t DB_PREFIX"

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Verify mempool integration for address and spent indexes
cat src/txmempool.cpp | grep -B 5 -A 20 "addAddressIndex\|addSpentIndex"

Length of output: 1536


🏁 Script executed:

#!/bin/bash
# Check for proper error handling in Rewind operations
rg -n "error\(" src/index/addressindex.cpp src/index/spentindex.cpp src/index/timestampindex.cpp | head -20

Length of output: 1928


🏁 Script executed:

#!/bin/bash
# Verify the BlockDisconnected implementation across all indexes
cat src/index/addressindex.cpp | grep -A 15 "BlockDisconnected"

Length of output: 677


🏁 Script executed:

#!/bin/bash
# Check initialization and startup logic
cat src/init.cpp | grep -B 3 -A 10 "g_addressindex\|g_spentindex\|g_timestampindex"

Length of output: 2387


🏁 Script executed:

#!/bin/bash
# Examine the migration's batch size and compaction threshold
cat src/txdb.cpp | grep -B 2 -A 2 "batch_size\|COMPACT_THRESHOLD\|256.*MiB"

Length of output: 2879


🏁 Script executed:

#!/bin/bash
# Check if there are any remaining references to the old synchronous index methods
rg -n "WriteAddressIndex|ReadAddressIndex|WriteTimestampIndex|UpdateSpentIndex" --type cpp | grep -v "src/index/" | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Verify the test coverage for migration scenarios
cat test/functional/feature_addressindex.py test/functional/feature_spentindex.py test/functional/feature_timestampindex.py | grep -i "migrat\|async"

Length of output: 147


🏁 Script executed:

#!/bin/bash
# Check the MigrateIndex template implementation in detail
cat src/txdb.cpp | sed -n '/^template <uint8_t DbKey/,/^static int64_t MigrateIndex/p' | head -80

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Look for the actual MigrateIndex template function body
cat src/txdb.cpp | sed -n '/^static int64_t MigrateIndex/,/^}/p' | head -100

Length of output: 3065


🏁 Script executed:

#!/bin/bash
# Check if there's proper handling of the case where migration is partial/interrupted
rg -n "best_block_hash|FinalizeMigration" src/txdb.cpp

Length of output: 801


🏁 Script executed:

#!/bin/bash
# Verify the iterator reset logic mentioned in PR description
cat src/txdb.cpp | grep -B 5 -A 5 "NewIterator\|pcursor.reset"

Length of output: 2538


🏁 Script executed:

#!/bin/bash
# Check parameter interactions for pruning + indexes
cat src/init.cpp | grep -B 3 -A 3 "prune.*addressindex\|prune.*spentindex"

Length of output: 1707


🏁 Script executed:

#!/bin/bash
# Look for potential issues with undo data validation
cat src/index/addressindex.cpp | grep -B 2 -A 5 "vin.size.*vprevout.size"

Length of output: 306


🏁 Script executed:

#!/bin/bash
# Check if old index database keys are properly defined for migration
cat src/txdb.cpp | grep "constexpr.*DB_.*INDEX"

Length of output: 287


🏁 Script executed:

#!/bin/bash
# Verify CompactRange implementation in dbwrapper.h
cat src/dbwrapper.h | grep -B 5 -A 15 "CompactRange"

Length of output: 1282


@UdjinM6, I've completed a comprehensive review of this major refactoring. Overall, the implementation is solid and well-architected, but I have some observations and suggestions:

✅ Strengths

1. Excellent Migration Logic
The MigrateIndex template is well-designed:

  • Incremental batching with periodic 256 MiB compaction threshold
  • Critical detail handled correctly: Iterator reset before compaction allows LevelDB to delete old SST files
  • Seeks back to start after reset (works because processed keys were already erased)
  • Final sync write ensures durability
  • FinalizeMigration properly writes best_block locator

2. Robust Index Implementations
All three indexes properly implement:

  • WriteBlock: Validates undo data size matches tx inputs before processing
  • Rewind: Walks back blocks and erases entries correctly
  • BlockDisconnected: Triggers rewind appropriately
  • Genesis block (height 0) correctly skipped

3. Proper Pruning Enforcement

  • AddressIndex/SpentIndex: AllowPrune() = false ✓ (require undo data)
  • TimestampIndex: AllowPrune() = true ✓ (only needs block metadata)
  • Parameter interaction properly enforces incompatibility

4. Comprehensive RPC Guards
All RPC methods properly:

  • Check index existence before use
  • Verify sync status via GetSummary()
  • Return clear error messages with current sync height

⚠️ Concerns & Suggestions

1. Missing Migration Test Coverage
The tests verify async behavior but don't explicitly test:

  • Migration from old sync DB to new async DB
  • Interrupted migration recovery
  • Large dataset migration performance

Recommendation: Add a test that:

# Start node with old-format indexes
# Stop and restart with new code
# Verify migration completes and data accessible

2. Breaking API Change - getAddressIndex
In src/txmempool.h:

- bool getAddressIndex(...)
+ void getAddressIndex(...)

This changes error handling from boolean return to potentially silent failures (or exceptions not shown in the diff).

Question: Are all callers updated to handle the new signature? The implementation returns early if g_addressindex is null, which is safe, but this is a subtle behavioral change.

3. CompactRange Start Key Reuse
In the migration loop:

pcursor.reset(source_db.NewIterator());
pcursor->Seek(start);  // Uses original start, not updated key

This works because erased keys won't be found, but it's subtle. Consider adding a comment explaining why this is safe.

4. No Partial Migration Recovery
If migration is interrupted (crash, kill -9), there's no resume mechanism. The code will re-migrate already-migrated entries on next startup. While not incorrect, it's inefficient for large datasets.

Consideration: Could check for partial migration by looking for both old and new index data, though this might be over-engineering for a one-time migration.

5. Undo Data Validation
Good validation present:

if (tx->vin.size() != txundo.vprevout.size()) {
    return error("%s: Undo data mismatch for tx %s", __func__, txhash.ToString());
}

However, no validation that blockundo.vtxundo.size() == block.vtx.size() - 1 (accounting for coinbase). Consider adding this check before the loop.

🔍 Minor Observations

1. Database Key Consistency
Old and new DB keys are identical (e.g., both use 'a' for address index), which is good for migration but could be confusing during debugging. The separate database directories help disambiguate.

2. Memory Usage
Cache allocation in src/node/caches.cpp looks reasonable, but with three new indexes, peak memory during sync might be higher. The PR description mentions this is expected.

3. Test Updates Look Good

  • Removed error-on-enable-without-reindex checks ✓
  • Added reorg testing for spent/timestamp indexes ✓
  • Simplified node topology where possible ✓

📋 Final Verdict

This is a well-executed refactoring with proper attention to:

  • Data integrity (undo validation, sync durability)
  • Error handling (comprehensive error() calls)
  • User experience (clear error messages with sync progress)
  • Backward compatibility (automatic migration)

The main gap is test coverage for the migration path itself, which is critical given the one-time nature of this migration for existing nodes.

Recommendation: Either add migration tests or document that migration was manually tested on mainnet/testnet nodes (which the PR description mentions).

Would you like me to generate a test case for the migration scenario, or do you have additional migration testing planned?


🧠 Learnings used
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request `#6543` is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC Some notable changes to RPC params/behaviour/descriptions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant