Skip to content

Conversation

PastaPastaPasta
Copy link
Member

Summary

This PR introduces a dedicated SignHash type to replace the generic uint256 usage for LLMQ sign hashes, improving type safety and code clarity throughout the signing subsystem.

Motivation

Previously, BuildSignHash was a standalone function returning uint256, which:

  • Required forward declarations to avoid circular dependencies
  • Lacked type safety (any uint256 could be passed where a sign hash was expected)
  • Made code intent less clear

Changes

New SignHash Type

  • Created SignHash class extending BaseHash<uint256> (following existing patterns like AssumeutxoHash)
  • Encapsulates the hash calculation logic in the constructor
  • Provides serialization support for database compatibility
  • Includes std::hash specialization for use as unordered_map keys

Refactored APIs

  • CSigBase::buildSignHash() now returns SignHash instead of uint256
  • Sign hash calculation moved from standalone function to SignHash constructor
  • Removed forward declarations from assetlocktx.cpp and quorums.cpp

Type Safety Improvements

  • SignHash is now a distinct type throughout LLMQ code
  • Conversion to uint256 via .Get() only at API boundaries (BLS verification, database operations)
  • Maintains clean abstraction - BLS library continues using uint256 as it shouldn't depend on higher-level LLMQ types

Benefits

  • Elimination of circular dependencies - No more forward declarations needed
  • Better type safety - Compiler catches type mismatches
  • Clearer code intent - Explicit when working with sign hashes vs other hashes
  • Future flexibility - Can add SignHash-specific methods if needed
  • Zero runtime overhead - All changes are compile-time type safety improvements

Test plan

  • Unit tests pass (test_dash --run_test=evo_islock_tests)
  • Code compiles without warnings
  • No circular dependencies introduced

🤖 Generated with Claude Code

@PastaPastaPasta
Copy link
Member Author

Note: this pr is currently based on #6825

Copy link

github-actions bot commented Aug 20, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

Copy link

coderabbitai bot commented Aug 20, 2025

Walkthrough

Adds a new llmq::SignHash type (src/llmq/signhash.h/cpp) and wires it into the build. Replaces the standalone BuildSignHash function with SignHash and updates CSigBase::buildSignHash() to return SignHash. Call sites across evo, instantsend, llmq (signing, quorums, signing_shares), rpc, and tests now construct SignHash and use .Get() where a uint256 was previously used. Two nested signHash members in CSigSharesNodeState were changed from uint256 to llmq::SignHash. A std::hash specialization and salted hasher for SignHash were added.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (15)
test/lint/lint-circular-dependencies.py (1)

45-45: Validate that this new cycle is expected and unavoidable.

"core_io -> evo/mnhftx -> llmq/signing -> net_processing -> evo/smldiff -> core_io" still references llmq/signing. If the intent is to reduce signing exposure by introducing SignHash, consider whether this particular cycle can also be broken (e.g., moving verification bits behind narrower interfaces).

Would you like me to propose a dependency minimization plan (file-by-file include graph diff) for the evo/mnhftx → llmq/signing path?

src/llmq/signhash.h (2)

55-65: Move std::hash specialization into namespace std and use a fixed-width read for better distribution.

Two issues:

  • The specialization should be declared in namespace std (the qualified-id form is accepted by some compilers but declaring inside std is the conventional and less contentious approach).
  • Hashing into size_t by memcpy’ing sizeof(size_t) bytes yields platform-dependent entropy (32 vs 64-bit). Prefer always reading 8 bytes (LE64) and casting to size_t.

Apply this diff:

-// Make SignHash hashable for use in unordered_map
-template<>
-struct std::hash<llmq::SignHash> {
-    std::size_t operator()(const llmq::SignHash& signHash) const noexcept {
-        // Use the first 8 bytes of the hash as the hash value
-        const unsigned char* data = signHash.data();
-        std::size_t result;
-        std::memcpy(&result, data, sizeof(result));
-        return result;
-    }
-};
+// Make SignHash hashable for use in unordered_map
+namespace std {
+template<>
+struct hash<llmq::SignHash> {
+    std::size_t operator()(const llmq::SignHash& signHash) const noexcept {
+        // Use the first 8 bytes of the hash for a simple hash seed
+        uint64_t v = 0;
+        std::memcpy(&v, signHash.data(), sizeof(v)); // data() is byte-aligned
+        return static_cast<std::size_t>(v);
+    }
+};
+} // namespace std

If you want stronger distribution, consider a lightweight mixer (e.g., xorshift or wyhash-inspired mix on v) at no appreciable cost.


28-50: Optional: leaner serialization via BaseHash utilities (if available).

If util/hash_type.h already provides Serialize/Unserialize for BaseHash-derived types (as it does for other typed hashes in Core), these explicit methods may be redundant.

If redundant in your codebase, remove the custom Serialize/Unserialize here and rely on the BaseHash-provided ones. Otherwise, keeping them is fine.

src/evo/specialtx_filter.cpp (2)

35-51: Minor: unify element-add helpers to avoid vector copies (optional).

Both AddScriptElement and AddHashElement allocate a fresh std::vector each time. If addElement ultimately copies into another container, this can be two copies per field.

Consider changing the callback to accept a Span (or similar) and only copy once at the call site requiring owning storage. That would also let you pass MakeUCharSpan(stream) directly without an intermediate vector.


52-59: Maintenance note is appreciated. Add a brief “what fields” table reference (optional).

The sync note is good. A short pointer in a comment to the exact function/section in CBloomFilter that must mirror this would further reduce drift risk.

test/functional/p2p_blockfilters.py (1)

311-357: Helper creates valid, minimal AssetLockTx; consider using zero-value OP_RETURN (optional).

The construction is straightforward and works for the purpose of filter testing. As a minor convention alignment, OP_RETURN outputs are typically zero-value.

Apply this diff to set OP_RETURN output to zero and keep the locked amount solely in the payload’s credit outputs:

-        # Add OP_RETURN output with the locked amount
-        lock_tx.vout.append(CTxOut(int(amount_locked * COIN), CScript([OP_RETURN, b""])))
+        # Add a zero-value OP_RETURN output to carry payload (conventional)
+        lock_tx.vout.append(CTxOut(0, CScript([OP_RETURN, b""])))
src/instantsend/instantsend.cpp (1)

289-291: Minor: keep the strong type until the call site for clarity

You immediately convert SignHash to a uint256. Keeping the SignHash wrapper locally improves readability and type-safety, only unwrapping at the API boundary.

Apply this small refactor:

-        uint256 signHash = llmq::SignHash(llmq_params.type, quorum->qc->quorumHash, id, islock->txid).Get();
-        batchVerifier.PushMessage(nodeId, hash, signHash, islock->sig.Get(), quorum->qc->quorumPublicKey);
+        llmq::SignHash signHash(llmq_params.type, quorum->qc->quorumHash, id, islock->txid);
+        batchVerifier.PushMessage(nodeId, hash, signHash.Get(), islock->sig.Get(), quorum->qc->quorumPublicKey);
src/Makefile.am (2)

203-204: Expose evo/specialtx_filter.h in BITCOIN_CORE_H

Publicly listing the header is fine. See below about moving the implementation to libbitcoin_common for reuse.


484-485: Consider moving evo/specialtx_filter.cpp to libbitcoin_common to avoid duplication between filters

CBloomFilter lives in libbitcoin_common. If we want both bloom and compact filters to use the exact same extraction logic, putting evo/specialtx_filter.cpp into libbitcoin_common prevents duplication and keeps behavior in sync. Today, it’s compiled into libbitcoin_node, so common can’t directly reuse it without duplication.

Apply this diff to move the implementation to libbitcoin_common:

 libbitcoin_node_a_SOURCES = \
   ...
-  evo/specialtx.cpp \
-  evo/specialtx_filter.cpp \
+  evo/specialtx.cpp \
   evo/specialtxman.cpp \
   ...
 
 libbitcoin_common_a_SOURCES = \
   base58.cpp \
   bech32.cpp \
   chainparams.cpp \
   coins.cpp \
   common/bloom.cpp \
+  evo/specialtx_filter.cpp \
   compressor.cpp \
   core_read.cpp \
   core_write.cpp \
   ...

This keeps blockfilter.cpp (node) able to call into common at link time, and lets CBloomFilter reuse the same function.

src/llmq/signing.h (1)

118-121: Opportunity: key recovered-sig caches by SignHash instead of uint256

Now that SignHash exists, consider migrating caches/maps that logically key by the sign-hash (e.g., hasSigForSessionCache) to use SignHash for additional type safety. This is a nice-to-have and can be incremental.

src/llmq/signing.cpp (3)

189-201: Use signHash.Get() consistently for DB keys to avoid type ambiguity.

Here k4 stores a SignHash object as the tuple element, while write path used uint256. While serialization currently matches, it’s safer and clearer to always pass the underlying uint256 for DB keys.

Apply:

-    auto k4 = std::make_tuple(std::string("rs_s"), signHash);
+    auto k4 = std::make_tuple(std::string("rs_s"), signHash.Get());

435-444: Avoid recomputing SignHash in logs.

buildSignHash() now constructs a hash each time. Cache it locally to reduce duplicate work in the hot path.

-    LogPrint(BCLog::LLMQ, "CSigningManager::%s -- signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
-             recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from);
+    const SignHash sign_hash{recoveredSig->buildSignHash()};
+    LogPrint(BCLog::LLMQ, "CSigningManager::%s -- signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
+             sign_hash.ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from);
@@
-        LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already pending reconstructed sig, signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
-                 recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from);
+        LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already pending reconstructed sig, signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
+                 sign_hash.ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from);

278-279: Format specifier mismatch in LogPrint.

func is a const char*, but the format string uses %d. Use %s to avoid UB and garbled logs. (Pre-existing, but nearby.)

-    LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, toDelete.size());
+    LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%s -- deleted %d entries\n", __func__, toDelete.size());
src/test/blockfilter_tests.cpp (1)

356-443: Edge-case tests are valuable; consider adding a Bloom parity check.

The compact-filter path is covered; adding analogous CBloomFilter relevance tests would guard parity with CheckSpecialTransactionMatchesAndUpdate.

If you’d like, I can draft a small Bloom filter unit test asserting that the same special-tx elements are considered relevant and update behavior is correct under BLOOM_UPDATE_ALL.

src/common/bloom.cpp (1)

200-202: Unknown type logging: reduce verbosity to avoid log spam.

Using LogPrintf here can be noisy if new types appear. Consider demoting to a category-based LogPrint or removing entirely.

-    LogPrintf("Unknown special transaction type in Bloom filter check.\n");
+    LogPrint(BCLog::NET, "Unknown special transaction type in Bloom filter check.\n");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a6f55ee and 75d6cf4.

📒 Files selected for processing (19)
  • src/Makefile.am (4 hunks)
  • src/blockfilter.cpp (2 hunks)
  • src/common/bloom.cpp (1 hunks)
  • src/evo/assetlocktx.cpp (3 hunks)
  • src/evo/mnhftx.cpp (2 hunks)
  • src/evo/specialtx_filter.cpp (1 hunks)
  • src/evo/specialtx_filter.h (1 hunks)
  • src/instantsend/instantsend.cpp (2 hunks)
  • src/llmq/quorums.cpp (2 hunks)
  • src/llmq/signhash.cpp (1 hunks)
  • src/llmq/signhash.h (1 hunks)
  • src/llmq/signing.cpp (8 hunks)
  • src/llmq/signing.h (2 hunks)
  • src/llmq/signing_shares.cpp (9 hunks)
  • src/rpc/quorums.cpp (2 hunks)
  • src/test/blockfilter_tests.cpp (2 hunks)
  • src/test/evo_islock_tests.cpp (2 hunks)
  • test/functional/p2p_blockfilters.py (3 hunks)
  • test/lint/lint-circular-dependencies.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit Inference Engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/evo/specialtx_filter.h
  • src/rpc/quorums.cpp
  • src/evo/mnhftx.cpp
  • src/blockfilter.cpp
  • src/evo/specialtx_filter.cpp
  • src/llmq/quorums.cpp
  • src/test/blockfilter_tests.cpp
  • src/llmq/signing.h
  • src/instantsend/instantsend.cpp
  • src/common/bloom.cpp
  • src/llmq/signhash.cpp
  • src/test/evo_islock_tests.cpp
  • src/llmq/signing_shares.cpp
  • src/llmq/signhash.h
  • src/evo/assetlocktx.cpp
  • src/llmq/signing.cpp
src/{masternode,evo}/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Masternode lists must use immutable data structures (Immer library) for thread safety

Files:

  • src/evo/specialtx_filter.h
  • src/evo/mnhftx.cpp
  • src/evo/specialtx_filter.cpp
  • src/evo/assetlocktx.cpp
src/{test,wallet/test,qt/test}/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Unit tests for C++ code should be placed in src/test/, src/wallet/test/, or src/qt/test/ and use Boost::Test or Qt 5 for GUI tests

Files:

  • src/test/blockfilter_tests.cpp
  • src/test/evo_islock_tests.cpp
test/functional/**/*.py

📄 CodeRabbit Inference Engine (CLAUDE.md)

Functional tests should be written in Python and placed in test/functional/

Files:

  • test/functional/p2p_blockfilters.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: dashpay/dash#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-20T18:42:49.794Z
Learning: Applies to src/evo/specialtx.h : Special transactions use payload extensions defined in src/evo/specialtx.h
📚 Learning: 2025-07-20T18:42:49.794Z
Learnt from: CR
PR: dashpay/dash#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-20T18:42:49.794Z
Learning: Applies to src/evo/specialtx.h : Special transactions use payload extensions defined in src/evo/specialtx.h

Applied to files:

  • src/evo/specialtx_filter.h
  • src/evo/mnhftx.cpp
  • src/blockfilter.cpp
  • src/evo/specialtx_filter.cpp
  • src/common/bloom.cpp
  • src/Makefile.am
  • src/evo/assetlocktx.cpp
🧬 Code Graph Analysis (14)
src/evo/specialtx_filter.h (1)
src/evo/specialtx_filter.cpp (2)
  • ExtractSpecialTxFilterElements (57-136)
  • ExtractSpecialTxFilterElements (57-58)
src/evo/mnhftx.cpp (1)
src/evo/mnhftx.h (1)
  • sig (36-36)
src/blockfilter.cpp (1)
src/evo/specialtx_filter.cpp (2)
  • ExtractSpecialTxFilterElements (57-136)
  • ExtractSpecialTxFilterElements (57-58)
src/evo/specialtx_filter.cpp (2)
src/common/bloom.cpp (2)
  • stream (67-67)
  • stream (88-88)
src/test/blockfilter_tests.cpp (1)
  • stream (120-120)
src/test/blockfilter_tests.cpp (1)
src/evo/assetlocktx.h (1)
  • CAssetLockPayload (27-66)
src/llmq/signing.h (2)
src/llmq/signing_shares.h (4)
  • nodiscard (136-167)
  • nodiscard (186-193)
  • nodiscard (228-233)
  • nodiscard (237-244)
src/llmq/signing.cpp (2)
  • buildSignHash (840-843)
  • buildSignHash (840-840)
src/instantsend/instantsend.cpp (2)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
src/llmq/signhash.cpp (1)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/test/evo_islock_tests.cpp (2)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
src/llmq/signing_shares.cpp (2)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
test/functional/p2p_blockfilters.py (3)
test/functional/test_framework/messages.py (2)
  • CAssetLockTx (1197-1222)
  • tx_from_hex (230-232)
test/functional/test_framework/script_util.py (1)
  • key_to_p2pkh_script (65-67)
test/functional/test_framework/test_framework.py (1)
  • skip_if_no_wallet (1060-1068)
src/llmq/signhash.h (1)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
src/evo/assetlocktx.cpp (1)
src/evo/assetlocktx.h (1)
  • quorumSig (82-82)
src/llmq/signing.cpp (2)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
🔇 Additional comments (47)
src/evo/specialtx_filter.h (1)

13-22: Public API looks clear and minimal — good separation.

Forward-declaring CTransaction and exposing only a callback-based extractor keeps dependencies light and prevents unnecessary header coupling. Matches the stated goal of avoiding circular deps.

test/lint/lint-circular-dependencies.py (1)

36-40: Updated circular-dependency expectations align with the refactor.

The new cycles referencing llmq/commitment instead of llmq/signing (and removal of older signing-related cycles) are consistent with moving sign-hash calculation out of signing. No red flags here.

src/evo/specialtx_filter.cpp (2)

64-82: Parity with Bloom filter: ProRegTx extraction looks correct.

  • Collateral outpoint serialized with SER_NETWORK/PROTOCOL_VERSION.
  • Owner/voting KeyIDs and payout script added.
    Matches the maintenance note’s intent to keep Bloom and compact-filter behavior in sync.

114-126: AssetLock: correct exclusion of OP_RETURN and inclusion of credit outputs.

Including creditOutputs from the payload while excluding OP_RETURN is the right choice for filter usefulness. This aligns with the functional tests.

test/functional/p2p_blockfilters.py (3)

69-71: Good guard for wallet dependency.

Ensures the functional test is skipped properly if the wallet is not compiled.


382-437: End-to-end AssetLockTx filter size check is pragmatic.

Comparing filter sizes between a block with special tx and a control block is a reasonable signal given GCS decoding isn’t exposed here. Assertions and RPC usage look correct.


438-527: Multiple credit outputs path looks solid.

Covers the multi-output case and validates increased filter size. Good complement to unit tests.

src/instantsend/instantsend.cpp (1)

20-20: Include addition aligns with the new SignHash type usage

Including llmq/signhash.h here is correct and scoped alongside other llmq headers. No concerns.

src/evo/mnhftx.cpp (2)

12-12: Header include is correct

llmq/signhash.h inclusion is necessary for the new SignHash construction below. Looks good.


106-108: LGTM: migrated to SignHash cleanly

Constructing SignHash and passing signHash.Get() to VerifyInsecure keeps the strong type locally and unwraps at the BLS boundary. No behavior change; clearer intent.

src/evo/assetlocktx.cpp (2)

10-10: Correct new dependency

Adding llmq/signhash.h is required for the SignHash initializer below. No issues.


149-151: Nice use of C++17 if-initializer with SignHash

Limiting signHash’s scope to the condition is neat and safe; VerifyInsecure only needs the underlying uint256 during the call. No lifetime issues here.

src/rpc/quorums.cpp (2)

25-25: Include added appropriately

llmq/signhash.h is needed for quorum_verify. Placement among llmq headers is consistent.


611-613: LGTM: switched quorum_verify to SignHash

Retains strong typing locally and unwraps at the BLS boundary. RPC behavior unchanged; internal clarity improved.

src/llmq/quorums.cpp (3)

11-11: New include is correct and localized

llmq/signhash.h inclusion is required for VerifyRecoveredSig migration. No concerns.


1281-1283: LGTM: VerifyRecoveredSig migrated to SignHash

Strongly-typed construction followed by Get() at the verify call site is exactly the intended pattern. No functional changes introduced.


11-11: Verification complete: no stale BuildSignHash & all SignHash call sites include <llmq/signhash.h>

All BuildSignHash references have been removed, and every file constructing llmq::SignHash (including src/test/evo_islock_tests.cpp and src/instantsend/instantsend.cpp) explicitly includes <llmq/signhash.h>.

src/blockfilter.cpp (2)

10-10: Include of specialtx_filter is appropriate and localized

Bringing in evo/specialtx_filter.h here keeps BlockFilter decoupled from specific special-tx details. No issues.


200-203: Good: delegated extraction for special-transaction elements; ensure parity with bloom filters

The delegation pattern is clean and keeps BasicFilterElements lean. One concern: changing the content of the BASIC_FILTER by adding extra elements (keys, proTxHash, AssetLock credit outputs, etc.) will produce different cfilters and cfheaders compared to older releases. This can fragment serving peers until the network converges on this version.

  • Please document this behavioral change in the release notes and confirm it’s acceptable for BIP158 light clients.
  • If strict backward-compat is a requirement for the BASIC_FILTER type, consider introducing a new filter type instead.

Would you verify network impact and client compatibility? If we want to avoid duplication and drift between compact filters and bloom filters, we should have both call the same extractor (rather than “mirror” logic). See Makefile suggestion below for moving evo/specialtx_filter.cpp to libbitcoin_common so CBloomFilter can reuse it directly.

src/test/evo_islock_tests.cpp (2)

8-8: Include of llmq/signhash.h is correct

The test now depends on the new SignHash API; header inclusion is correct.


84-85: Correct SignHash construction and usage in test

Order of parameters matches SignHash’s serialization (llmqType, quorumHash, id, msgHash), and calling .Get() for uint256 comparison is appropriate. LGTM.

src/Makefile.am (2)

256-257: Expose llmq/signhash.h in BITCOIN_CORE_H

This aligns with the API shift to the strong SignHash type. No issues.


517-518: Adding llmq/signhash.cpp to node library is fine

SignHash is primarily consumed by llmq/signing and friends in node; placement is reasonable.

src/llmq/signing.h (2)

11-11: Include dependency on signhash.h is required for the new return type

Header dependency is correct and localizes the type change.


71-71: All C++ call sites correctly use SignHash; no outstanding BuildSignHash references

I’ve verified that in the C++ codebase:

  • There are no remaining calls to the old free function BuildSignHash (only a Go binding in src/dashbls/go-bindings/threshold.go, which isn’t affected).
  • Every use of buildSignHash() in C++ either
    • stores the result in a SignHash and uses its methods directly (e.g. .ToString())
    • or calls .Get() when a raw uint256 is needed (e.g. in caches, BLS-verify, batch writes).

No further updates are required here.

src/llmq/signhash.cpp (1)

15-23: Constructor correctly reproduces previous BuildSignHash hashing semantics

The serialization order and use of CHashWriter(SER_GETHASH, 0) match the prior approach. No runtime overhead, and type safety is improved.

src/llmq/signing.cpp (6)

9-9: Include for SignHash is correct and scoped.

Bringing in llmq/signhash.h locally keeps the type usage explicit in this TU. Looks good.


165-167: DB keying by signHash now uses uint256 at the boundary — LGTM.

Using signHash.Get() when forming the LevelDB key preserves on-disk compatibility and keeps DB schema unchanged.


213-216: Cache erase uses uint256 — consistent with cache key type.

Using signHash.Get() aligns with the cache's uint256 key type. Good.


556-558: Batch verify now feeds the underlying uint256 sign hash — correct.

Switching PushMessage to recSig->buildSignHash().Get() matches the verifier’s template type.


600-614: SignHash usage in conflict detection is precise.

Comparing signHash.Get() values preserves prior behavior and clarifies intent.


840-843: API change: CSigBase::buildSignHash() now returns SignHash — clean and contained.

Encapsulating construction inside the type improves intent and type safety. No behavioral change.

src/llmq/signing_shares.cpp (8)

10-10: Include for SignHash is appropriate.

Keeps the translation unit self-sufficient with the new type.


31-33: Keying by uint256 sign hash is preserved — good.

Using buildSignHash().Get() to populate SigShareKey avoids accidental map key type drift.


128-132: Session map remains keyed by uint256 — consistent usage of Get().

Constructing SignHash once and using Get() avoids recomputation and keeps map keys uniform.


776-778: Precomputing signHash for lookups is correct.

Constructing SignHash once and taking .Get() for map access keeps lookups consistent and efficient.


833-835: Verification uses underlying uint256 — aligns with BLS API.

Passing signHash.Get() to VerifyInsecure matches library expectations.


1561-1565: Signing uses buildSignHash().Get() — correct across both single-node and threshold paths.

This keeps signature inputs stable and type-safe.

Also applies to: 1597-1601


1621-1627: ForceReAnnouncement: session keying by uint256 maintained — good.

Computing SignHash(...).Get() here avoids accidental divergence with storage/container key types.


1643-1644: Cleanup on recovered sig uses underlying uint256 — correct.

RemoveSigSharesForSession expects uint256; passing .Get() is the right boundary conversion.

src/test/blockfilter_tests.cpp (2)

10-18: Required headers added for special-tx payloads and spans — LGTM.

These cover payload construction, network info, and span utilities used in the tests.


201-354: Comprehensive coverage for special transaction fields in filters — well done.

The assertions validate inclusion of collateralOutpoint, key IDs, payout scripts, proTxHash, and AssetLock credit scripts, and exclusion of unrelated data. This anchors behavior for ExtractSpecialTxFilterElements.

src/common/bloom.cpp (5)

122-127: Maintenance note is spot on.

Explicitly calling out the need to keep this in sync with ExtractSpecialTxFilterElements will save future regressions.


133-145: Provider Register matching and update behavior looks correct.

  • Matches collateral outpoint, owner/voting key IDs, and payout script.
  • Updates the filter with tx hash under BLOOM_UPDATE_ALL.

This mirrors what clients expect for SPV relevance.


147-158: Service Update handling is consistent with expectations.

  • proTxHash match returns relevance immediately.
  • scriptOperatorPayout match triggers proTxHash insertion under BLOOM_UPDATE_ALL.

Good balance between relevance and update semantics.


160-171: Registrar Update handling is correct and symmetric with service update.

Checks proTxHash first, then key/script with appropriate update behavior.


180-191: Double-check AssetUnlock prevouts against synthetic AssetLock outpoints

We’ve confirmed that:

  • Bloom code in src/common/bloom.cpp unconditionally injects synthetic outpoints (txid, idx) for each credit output of an AssetLock via ProcessTxOut(extraOuts[i], tx.GetHash(), i).
  • The AssetUnlock logic (in src/evo/assetlocktx.cppCheckAssetUnlockTxGetDataFromUnlockTx in src/evo/creditpool.cpp) does iterate over tx.vin and expects each input’s prevout to reference exactly those synthetic outpoints (i.e. the credit outputs inserted earlier).
  • No other special prevout mapping is used—AssetUnlock inputs are matched by standard COutPoint(hash,index).

Therefore the synthetic insertion is correct and clients will see relevant matches when filtering for spends of those credit outputs.

No changes needed here.

Comment on lines 24 to 37
class SignHash : public BaseHash<uint256> {
public:
SignHash() = default;

/**
* Constructs a SignHash from the given parameters.
* This replaces the previous BuildSignHash function.
*/
SignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash);

/**
* Get the underlying uint256 hash value.
*/
const uint256& Get() const { return m_hash; }
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix inheritance: BaseHash must be tagged with the derived type, not uint256.

Using BaseHash defeats the type-tagging (and some compile-time type-safety) this PR aims to introduce. The Tag template parameter should be the wrapper type itself.

Apply this diff:

-#include <llmq/params.h>
+#include <consensus/llmq_types.h>
 #include <uint256.h>
 
-#include <cstring>
-#include <vector>
+#include <cstring>
+#include <cstdint>
+#include <functional>
 
 #include <util/hash_type.h>
 
 namespace llmq
 {
@@
-class SignHash : public BaseHash<uint256> {
+class SignHash : public BaseHash<SignHash> {
 public:
     SignHash() = default;
@@
-    SignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash);
+    explicit SignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash);

Rationale:

  • BaseHash expects T to be the tag type, not the underlying hash.
  • Include the minimal header that declares Consensus::LLMQType to avoid heavier dependencies.
  • Explicit constructor prevents unintended implicit conversions.
  • Add missing standard headers required by std::hash specialization and fixed-width integer usage.
📝 Committable suggestion

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

Suggested change
class SignHash : public BaseHash<uint256> {
public:
SignHash() = default;
/**
* Constructs a SignHash from the given parameters.
* This replaces the previous BuildSignHash function.
*/
SignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash);
/**
* Get the underlying uint256 hash value.
*/
const uint256& Get() const { return m_hash; }
#include <consensus/llmq_types.h>
#include <uint256.h>
#include <cstring>
#include <cstdint>
#include <functional>
#include <util/hash_type.h>
namespace llmq {
class SignHash : public BaseHash<SignHash> {
public:
SignHash() = default;
/**
* Constructs a SignHash from the given parameters.
* This replaces the previous BuildSignHash function.
*/
explicit SignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash);
/**
* Get the underlying uint256 hash value.
*/
const uint256& Get() const { return m_hash; }

Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

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

concept ACK

LOCK(cs_cache);
hasSigForIdCache.insert(std::make_pair(recSig.getLlmqType(), recSig.getId()), true);
hasSigForSessionCache.insert(signHash, true);
hasSigForSessionCache.insert(signHash.Get(), true);
Copy link
Collaborator

Choose a reason for hiding this comment

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

consider further refactoring by using SignHash as a type for hasSigForSessionCache, for uniqueSignHashes, as argument in PushMessage, InitSession, as a member of SignSession, etc

Copy link

This pull request has conflicts, please rebase.

PastaPastaPasta and others added 2 commits September 2, 2025 09:23
…ping

Refactored BuildSignHash function into a dedicated SignHash class that extends
BaseHash<uint256>. This change:
- Eliminates circular dependencies by removing forward declarations
- Improves type safety with dedicated SignHash type vs generic uint256
- Follows existing patterns in the codebase (similar to AssumeutxoHash)
- Centralizes sign hash calculation logic in its own translation unit

The new SignHash type encapsulates the hash calculation for LLMQ signing
operations, making the code more maintainable and type-safe.

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

Co-Authored-By: Claude <[email protected]>
Extends usage of the SignHash type to improve type safety:
- CSigBase::buildSignHash() now returns SignHash instead of uint256
- Added serialization support for database storage compatibility
- Added std::hash specialization for use in unordered_map
- Use SignHash directly where possible, only converting to uint256 at
  API boundaries (BLS verification, database operations)

This provides better type safety while maintaining clean abstraction
boundaries - the BLS library continues to work with uint256 as it
should not depend on higher-level LLMQ types.

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

Co-Authored-By: Claude <[email protected]>
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: 0

♻️ Duplicate comments (1)
src/llmq/signing_shares.cpp (1)

129-132: Consistent session map keying with uint256; consider broader SignHash use later.

Using signHash.Get() keeps wire/db compatibility. Optional: store SignHash (or a strong typedef) in Session to reduce repeated conversions.

🧹 Nitpick comments (3)
src/llmq/signing_shares.cpp (1)

10-10: Fix include order to satisfy clang-format.

Place llmq headers in alphabetical order within the group to resolve the CI formatting error.

Apply this diff:

-#include <llmq/options.h>
-#include <llmq/quorums.h>
-#include <llmq/commitment.h>
+#include <llmq/commitment.h>
+#include <llmq/options.h>
+#include <llmq/quorums.h>
 #include <llmq/signhash.h>
src/llmq/signing.cpp (2)

189-201: Use uint256 consistently for the “rs_s” key during erase.

k4 currently builds the key with SignHash (not .Get()). While serialization is likely identical, using uint256 avoids ambiguity and matches write paths.

Apply this diff:

-    auto k4 = std::make_tuple(std::string("rs_s"), signHash);
+    auto k4 = std::make_tuple(std::string("rs_s"), signHash.Get());

Also applies to: 213-216


554-558: Wrap long PushMessage call to satisfy clang-format CI.

Reflow the call as multi-line.

Apply this diff:

-            batchVerifier.PushMessage(nodeId, recSig->GetHash(), recSig->buildSignHash().Get(), recSig->sig.Get(), quorum->qc->quorumPublicKey);
+            batchVerifier.PushMessage(
+                nodeId,
+                recSig->GetHash(),
+                recSig->buildSignHash().Get(),
+                recSig->sig.Get(),
+                quorum->qc->quorumPublicKey);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 75d6cf4 and f446223.

📒 Files selected for processing (12)
  • src/Makefile.am (2 hunks)
  • src/evo/assetlocktx.cpp (2 hunks)
  • src/evo/mnhftx.cpp (2 hunks)
  • src/instantsend/instantsend.cpp (2 hunks)
  • src/llmq/quorums.cpp (2 hunks)
  • src/llmq/signhash.cpp (1 hunks)
  • src/llmq/signhash.h (1 hunks)
  • src/llmq/signing.cpp (8 hunks)
  • src/llmq/signing.h (2 hunks)
  • src/llmq/signing_shares.cpp (9 hunks)
  • src/rpc/quorums.cpp (2 hunks)
  • src/test/evo_islock_tests.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/llmq/signing.h
  • src/evo/assetlocktx.cpp
  • src/llmq/quorums.cpp
  • src/instantsend/instantsend.cpp
  • src/Makefile.am
  • src/test/evo_islock_tests.cpp
  • src/evo/mnhftx.cpp
  • src/rpc/quorums.cpp
  • src/llmq/signhash.cpp
  • src/llmq/signhash.h
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/llmq/signing.cpp
  • src/llmq/signing_shares.cpp
🧠 Learnings (1)
📓 Common learnings
Learnt from: kwvg
PR: dashpay/dash#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: kwvg
PR: dashpay/dash#6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.
🧬 Code graph analysis (2)
src/llmq/signing.cpp (2)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
src/llmq/signing_shares.cpp (2)
src/llmq/signhash.h (1)
  • SignHash (24-44)
src/llmq/signhash.cpp (1)
  • SignHash (15-23)
🪛 GitHub Actions: Clang Diff Format Check
src/llmq/signing.cpp

[error] 554-556: Clang-format differences found: wrapped long function call across lines.


[error] 837-846: Clang-format differences found: condensed buildSignHash function formatting.

src/llmq/signing_shares.cpp

[error] 4-9: Clang-format differences found: include ordering changes.

⏰ 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). (7)
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_multiprocess / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (10)
src/llmq/signing_shares.cpp (6)

31-31: Keying now uses uint256 via SignHash::Get() — correct boundary.

This aligns the in-memory key (uint256) with the new SignHash type. LGTM.


776-777: Correct materialization of SignHash for lookups.

Computing once and using the uint256 for map access is appropriate.


834-835: Verification uses underlying hash — correct.

Passing signHash.Get() to VerifyInsecure is expected.


1561-1561: Signing with SignHash::Get() — correct.

Both branches properly sign the concrete uint256.

Also applies to: 1597-1597


1621-1621: Re-announcement uses recomputed signHash — OK.

Matches the session/signature stores keyed by uint256.


1643-1644: Cleanup uses recoveredSig.buildSignHash().Get() — consistent.

Correctly erases by the underlying session key.

src/llmq/signing.cpp (4)

165-178: DB write paths now store by uint256(session) from SignHash — correct.

k4 uses signHash.Get() and the cache mirrors that. Looks good.


473-474: Unique-session tracking uses buildSignHash().Get() — correct.

Strong type at construction, plain uint256 for set keying. LGTM.


600-610: Equality via SignHash::Get() — correct.

Comparison uses the underlying uint256 to detect conflicts. LGTM.


840-843: buildSignHash now returns SignHash — good; adjust formatting per CI.

Functionality is correct. The CI flagged formatting; please run clang-format to match the project style if it still fails after other fixes.

This update enhances the consistency and type safety of the LLMQ signing code by:
- Replacing uint256 with llmq::SignHash in relevant structures and functions.
- Updating InitSession to accept SignHash directly, improving clarity.
- Ensuring serialization compatibility for SignHash in database operations.
- Adjusting includes for better organization and reducing redundancy.

These changes streamline the codebase and reinforce the use of the SignHash type, aligning with previous refactoring efforts for improved maintainability.
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: 0

♻️ Duplicate comments (1)
src/llmq/signing.cpp (1)

176-176: Broaden SignHash usage in caches and maps (follow-up).

Consider using SignHash in hasSigForSessionCache, unique sets, and function params to maximize type safety. Requires hasher support.

🧹 Nitpick comments (4)
src/llmq/signing_shares.h (1)

311-312: Storing SignHash in SessionInfo/Session is consistent with the refactor.

Good step toward stronger typing. Consider also updating APIs that take a signHash to accept SignHash for full end-to-end type safety (e.g., GetSessionBySignHash/EraseAllForSignHash helpers).

Also applies to: 324-325

src/llmq/signing_shares.cpp (3)

101-115: Avoid copying CSigBase into InitSession.

Pass by const reference to prevent copying several uint256s.

Apply:

-static void InitSession(CSigSharesNodeState::Session& s, const llmq::SignHash& signHash, CSigBase from)
+static void InitSession(CSigSharesNodeState::Session& s, const llmq::SignHash& signHash, const CSigBase& from)

121-123: Minor: reuse computed SignHash if available.

Not critical, but when UpdateKey already computed the hash, consider threading that through to avoid recomputation.


129-133: sessions keyed by uint256 while Session holds SignHash.

This is fine for now; long-term, consider keying by SignHash once a salted hasher exists for it.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f446223 and ac9e5b7.

📒 Files selected for processing (8)
  • src/llmq/quorums.cpp (2 hunks)
  • src/llmq/signhash.cpp (1 hunks)
  • src/llmq/signhash.h (1 hunks)
  • src/llmq/signing.cpp (9 hunks)
  • src/llmq/signing.h (2 hunks)
  • src/llmq/signing_shares.cpp (12 hunks)
  • src/llmq/signing_shares.h (2 hunks)
  • src/test/evo_islock_tests.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/llmq/quorums.cpp
  • src/test/evo_islock_tests.cpp
  • src/llmq/signhash.cpp
  • src/llmq/signhash.h
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/llmq/signing_shares.h
  • src/llmq/signing.h
  • src/llmq/signing_shares.cpp
  • src/llmq/signing.cpp
🧠 Learnings (1)
📓 Common learnings
Learnt from: kwvg
PR: dashpay/dash#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.
🧬 Code graph analysis (4)
src/llmq/signing_shares.h (2)
src/llmq/signhash.h (2)
  • llmq (17-52)
  • SignHash (24-46)
src/llmq/signhash.cpp (1)
  • SignHash (14-22)
src/llmq/signing.h (2)
src/llmq/signing_shares.h (4)
  • nodiscard (136-167)
  • nodiscard (186-193)
  • nodiscard (228-233)
  • nodiscard (237-244)
src/llmq/signing.cpp (2)
  • buildSignHash (841-841)
  • buildSignHash (841-841)
src/llmq/signing_shares.cpp (2)
src/llmq/signhash.cpp (1)
  • SignHash (14-22)
src/llmq/signhash.h (1)
  • SignHash (24-46)
src/llmq/signing.cpp (2)
src/llmq/signhash.cpp (1)
  • SignHash (14-22)
src/llmq/signhash.h (1)
  • SignHash (24-46)
⏰ 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). (7)
  • GitHub Check: x86_64-pc-linux-gnu_multiprocess / Build depends
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (19)
src/llmq/signing.h (3)

11-11: Include of signhash.h is appropriate.

Required for the new SignHash return type.


18-18: Include of util/threadinterrupt.h is fine.

Matches usage of CThreadInterrupt in this header.


71-71: All callers updated to use SignHash
Verified via grep that every use of buildSignHash now returns SignHash and no code still expects a raw uint256.

src/llmq/signing_shares.cpp (9)

7-7: New include is fine.

llmq/commitment.h is used in this TU; OK.


10-10: New include is required for SignHash.

All good.


31-33: UpdateKey now uses SignHash::Get() correctly.

No issues.


351-353: Correct: compare/cache lookups use signHash.Get().

Matches existing DB/cache API that expects uint256.

Also applies to: 388-390


834-841: Sporadic VerifyInsecure uses SignHash::Get() correctly.

No issues.


1561-1565: Local copy of uint256 signHash is correct for signing.

Explicit copy avoids lifetime pitfalls. LGTM.

Also applies to: 1596-1603


1621-1627: OK: Re-announce path derives uint256 signHash via SignHash wrapper.

Consistent with internal map key type.


1643-1645: Cleanup uses recoveredSig.buildSignHash().Get(); correct.

Matches Erase* methods expecting uint256.


776-781: All SignHash::Get() bindings are safe
No auto& or const auto& bindings to SignHash(...).Get() were found in the codebase; all calls use value copies.

src/llmq/signing.cpp (7)

9-9: Include signhash.h is needed.

LGTM.


165-167: DB keys now use SignHash::Get(); correct.

Keeps on-disk format as uint256.

Also applies to: 175-179


195-201: Erase paths updated to use SignHash::Get(); correct.

Cache consistency preserved.

Also applies to: 213-216


473-475: Unique set uses buildSignHash().Get() — safe copy.

No issues.


611-613: Conflict check compares SignHash::Get(); correct.

Type-safe compare while preserving wrapper semantics.


841-841: buildSignHash() implementation updated — good.

Centralizes construction in SignHash ctor; noexcept not required.


557-559: CBLSBatchVerifier::PushMessage copies the message hash by value; passing buildSignHash().Get() is safe. Internal Message stores its own uint256 copy, so there’s no risk of dangling references.

Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

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


const uint256 signHash = llmq::BuildSignHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
if (!sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash)) {
const llmq::SignHash signHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: use {}

Suggested change
const llmq::SignHash signHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
const llmq::SignHash signHash{llmqType, quorum->qc->quorumHash, requestId, msgHash};

return {};
}
uint256 signHash = BuildSignHash(llmq_params.type, quorum->qc->quorumHash, id, islock->txid);
uint256 signHash = llmq::SignHash(llmq_params.type, quorum->qc->quorumHash, id, islock->txid).Get();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
uint256 signHash = llmq::SignHash(llmq_params.type, quorum->qc->quorumHash, id, islock->txid).Get();
uint256 signHash = llmq::SignHash{llmq_params.type, quorum->qc->quorumHash, id, islock->txid}.Get();


uint256 signHash = BuildSignHash(llmqType, quorum->qc->quorumHash, id, msgHash);
const bool ret = sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash);
SignHash signHash(llmqType, quorum->qc->quorumHash, id, msgHash);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
SignHash signHash(llmqType, quorum->qc->quorumHash, id, msgHash);
SignHash signHash{llmqType, quorum->qc->quorumHash, id, msgHash};

m_hash = h.GetHash();
}

} // namespace llmq No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: \n

};


#endif // BITCOIN_LLMQ_SIGNHASH_H No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: \n here too


uint256 signHash = llmq::BuildSignHash(llmqType, quorum->qc->quorumHash, id, msgHash);
return sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash);
llmq::SignHash signHash(llmqType, quorum->qc->quorumHash, id, msgHash);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
llmq::SignHash signHash(llmqType, quorum->qc->quorumHash, id, msgHash);
llmq::SignHash signHash{llmqType, quorum->qc->quorumHash, id, msgHash};

- Replace () with {} for SignHash constructor calls across codebase
- Add missing trailing newlines in signhash files
- Addresses code review feedback for consistent initialization syntax
@PastaPastaPasta
Copy link
Member Author

@knst see latest

Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

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

LGTM fd6ad86

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

♻️ Duplicate comments (1)
src/rpc/quorums.cpp (1)

611-612: LGTM: matches prior suggestion; concise and clear.

🧹 Nitpick comments (1)
src/evo/assetlocktx.cpp (1)

149-151: Minor readability: avoid init-statement in if; construct then verify.

Apply:

-    if (const llmq::SignHash signHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
-        quorumSig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get())) {
+    const llmq::SignHash signHash{llmqType, quorum->qc->quorumHash, requestId, msgHash};
+    if (quorumSig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get())) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ac9e5b7 and fd6ad86.

📒 Files selected for processing (7)
  • src/evo/assetlocktx.cpp (2 hunks)
  • src/evo/mnhftx.cpp (2 hunks)
  • src/instantsend/instantsend.cpp (2 hunks)
  • src/llmq/quorums.cpp (2 hunks)
  • src/llmq/signhash.cpp (1 hunks)
  • src/llmq/signhash.h (1 hunks)
  • src/rpc/quorums.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/instantsend/instantsend.cpp
  • src/llmq/quorums.cpp
  • src/evo/mnhftx.cpp
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/llmq/signhash.cpp
  • src/rpc/quorums.cpp
  • src/evo/assetlocktx.cpp
  • src/llmq/signhash.h
src/{masternode,evo}/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

Masternode lists must use immutable data structures (Immer library) for thread safety

Files:

  • src/evo/assetlocktx.cpp
🧠 Learnings (1)
📓 Common learnings
Learnt from: kwvg
PR: dashpay/dash#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.
🧬 Code graph analysis (3)
src/llmq/signhash.cpp (1)
src/llmq/signhash.h (1)
  • SignHash (24-46)
src/evo/assetlocktx.cpp (1)
src/evo/assetlocktx.h (1)
  • quorumSig (82-82)
src/llmq/signhash.h (1)
src/llmq/signhash.cpp (1)
  • SignHash (14-22)
⏰ 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). (8)
  • GitHub Check: predict_conflicts
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_multiprocess / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (3)
src/evo/assetlocktx.cpp (1)

10-10: LGTM: correct dependency inclusion.

src/rpc/quorums.cpp (1)

25-25: LGTM: needed header added.

src/llmq/signhash.h (1)

24-29: Verify SignHash CRTP template, salted hashing, and includes
Ensure BaseHash is parameterized with SignHash (CRTP), std::hash uses StaticSaltedHasher for DoS resistance, and clean up includes by removing <vector> and adding <cstdint>/<functional>.

SignHash::SignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash)
{
CHashWriter h(SER_GETHASH, 0);
h << llmqType;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure deterministic encoding of enum; drop unused include.

  • Serialize LLMQType explicitly as uint8_t for stability.
  • Remove unused .

Apply:

-#include <vector>
@@
-    h << llmqType;
+    h << static_cast<uint8_t>(llmqType);

Also applies to: 10-10

🤖 Prompt for AI Agents
In src/llmq/signhash.cpp around line 17, the enum value llmqType is being
serialized without an explicit fixed-size type which risks non-deterministic
encoding; cast/convert llmqType to uint8_t (or static_cast<uint8_t>(llmqType))
when writing to h so it is serialized as a single byte, and remove the
now-unused #include <vector> from the top of the file to clean up imports.

@knst knst requested a review from UdjinM6 September 3, 2025 00:47
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK fd6ad86

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants