Skip to content

Conversation

@PastaPastaPasta
Copy link
Member

Issue being fixed or feature implemented

Replace heap-allocated std::vector with stack-allocated std::array for BLS cryptographic element serialization to improve cache efficiency and eliminate unnecessary allocations.

Key changes:

  • Add std::array serialization support to serialize.h with optimized byte array handling (direct write without size prefix)
  • Update CoinJoin signature storage (vchSig) to use std::array
  • Optimize BLS IES encryption to use ToBytes() for stack allocation
  • Simplify CoinJoin signing code to use SignBasic() helper
  • Add Span-based SetSignature() overload for unified interface
  • Replace hardcoded sizes with BLS constants (BLS_CURVE_SIG_SIZE, BLS_CURVE_PUBKEY_SIZE, BLS_CURVE_ID_SIZE)
  • Simplify CBLSWrapper::Serialize() to use MakeByteSpan()

What was done?

Performance improvements (Apple M4 Pro, 500ms benchmark):

  • Signature ToBytes: 201.90 ns/op vs ToByteVector: 216.54 ns/op (6.8% faster)
  • PubKey ToBytes: 129.13 ns/op vs ToByteVector: 145.75 ns/op (11.4% faster)
  • Direct serialization: 222.93 ns/op vs vector path: 291.00 ns/op (23.4% faster)

Files modified:

  • src/serialize.h: Add std::array serialization templates
  • src/bls/bls.h: Use MakeByteSpan, maintain vector/array naming convention
  • src/bls/bls_ies.cpp: Use ToBytes() for symmetric keys
  • src/coinjoin/coinjoin.h: Change vchSig to std::array with zero-init
  • src/coinjoin/server.cpp: Use SignBasic() for cleaner signing
  • src/masternode/node.{h,cpp}: SignBasic() returns std::array
  • src/governance/vote.h: Add Span overload for SetSignature
  • src/governance/object.h: Update SetSignature to accept Span
  • src/governance/signing.cpp: Use SignBasic() with ToBytes()
  • src/wallet/wallet.cpp: Simplify SetSignature call
  • src/test/serialize_tests.cpp: Add array serialization tests
  • src/test/bls_tests.cpp: Add ToBytes comparison tests
  • src/test/coinjoin_queue_tests.cpp: Use SignBasic()
  • src/bench/bls.cpp: Add ToBytes vs ToByteVector benchmarks

How Has This Been Tested?

  • Unit tests passing
  • Added array serialization tests (bytes, integers, bools, empty, BLS sizes)
  • Added BLS ToBytes/ToByteVector comparison tests
  • Added BLS signature array serialization test

Breaking Changes

None

Checklist:

Go over all the following points, and put an x in all the boxes that apply.

  • 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)

Replace heap-allocated std::vector with stack-allocated std::array
for BLS cryptographic element serialization to improve cache efficiency
and eliminate unnecessary allocations.

Key changes:
- Add std::array serialization support to serialize.h with optimized
  byte array handling (direct write without size prefix)
- Update CoinJoin signature storage (vchSig) to use std::array
- Optimize BLS IES encryption to use ToBytes() for stack allocation
- Simplify CoinJoin signing code to use SignBasic() helper
- Add Span-based SetSignature() overload for unified interface
- Replace hardcoded sizes with BLS constants (BLS_CURVE_SIG_SIZE,
  BLS_CURVE_PUBKEY_SIZE, BLS_CURVE_ID_SIZE)
- Simplify CBLSWrapper::Serialize() to use MakeByteSpan()

Performance improvements (Apple M4 Pro, 500ms benchmark):
- Signature ToBytes: 201.90 ns/op vs ToByteVector: 216.54 ns/op (6.8% faster)
- PubKey ToBytes: 129.13 ns/op vs ToByteVector: 145.75 ns/op (11.4% faster)
- Direct serialization: 222.93 ns/op vs vector path: 291.00 ns/op (23.4% faster)

Testing:
- Split test cases for better organization (686 tests, all passing)
- Added array serialization tests (bytes, integers, bools, empty, BLS sizes)
- Added BLS ToBytes/ToByteVector comparison tests
- Added BLS signature array serialization test

Files modified:
- src/serialize.h: Add std::array serialization templates
- src/bls/bls.h: Use MakeByteSpan, maintain vector/array naming convention
- src/bls/bls_ies.cpp: Use ToBytes() for symmetric keys
- src/coinjoin/coinjoin.h: Change vchSig to std::array with zero-init
- src/coinjoin/server.cpp: Use SignBasic() for cleaner signing
- src/masternode/node.{h,cpp}: SignBasic() returns std::array
- src/governance/vote.h: Add Span overload for SetSignature
- src/governance/object.h: Update SetSignature to accept Span
- src/governance/signing.cpp: Use SignBasic() with ToBytes()
- src/wallet/wallet.cpp: Simplify SetSignature call
- src/test/serialize_tests.cpp: Add array serialization tests
- src/test/bls_tests.cpp: Add ToBytes comparison tests
- src/test/coinjoin_queue_tests.cpp: Use SignBasic()
- src/bench/bls.cpp: Add ToBytes vs ToByteVector benchmarks
@PastaPastaPasta PastaPastaPasta added this to the 23.1 milestone Oct 24, 2025
@github-actions
Copy link

github-actions bot commented Oct 24, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@coderabbitai
Copy link

coderabbitai bot commented Oct 24, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces dynamic BLS byte vectors with fixed-size std::array buffers and adds std::array<T,N> serialization support. Signature-related storage and APIs were changed to fixed-size arrays (coinjoin signature fields, CActiveMasternodeManager::SignBasic return type). CBLS serialization now writes a single MakeByteSpan(ToBytes(...)); bls_ies switched ToByteVector(...)+resize to ToBytes(...). CGovernanceVote::SetSignature now accepts Span. Wallet sign path now forwards decoded signature directly. New unit tests and benchmarks for ToBytes/ToByteVector and array serialization were added; the benchmark block is duplicated in src/bench/bls.cpp.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Caller
    participant BLSObj as BLS object (Sig/Pub/Sec)
    participant ToBytes as ToBytes()
    participant Stream as Serializer (Stream / MakeByteSpan)
    Note over BLSObj,ToBytes: New flow returns fixed-size std::array
    Caller->>BLSObj: request serialization
    BLSObj->>ToBytes: ToBytes(false)
    ToBytes-->>Caller: std::array<uint8_t, N>
    Caller->>Stream: Serialize(MakeByteSpan(array))
    Stream-->>Caller: bytes written
    Note right of Stream: Deserialization reads into std::array and reconstructs via Span
Loading
sequenceDiagram
    autonumber
    participant Caller
    participant Old as Old flow (ToByteVector)
    participant New as New flow (ToBytes + array)
    Note over Old,New: Comparison of old dynamic vs new fixed-size flow
    Caller->>Old: ToByteVector() -> std::vector -> optional resize/assign
    Caller->>New: ToBytes() -> std::array -> direct Serialize/assign via Span
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • src/serialize.h: template overloads for std::array (unsigned char fast-path, bool handling, generic element serialization, N==0).
    • src/coinjoin/coinjoin.h: migration from std::vector to std::array<uint8_t, BLS_CURVE_SIG_SIZE> and serialization under SER_GETHASH.
    • CActiveMasternodeManager::SignBasic signature change (src/masternode/node.h / node.cpp) and all callers.
    • CGovernanceVote::SetSignature signature change and callsites.
    • src/bls/bls.h and src/bls/bls_ies.cpp: correct usage of ToBytes vs ToByteVector and preserved key sizes.
    • src/wallet/wallet.cpp: change in how decoded signature is forwarded to vote.SetSignature.
    • Tests (src/test/*): added BLS and serialize tests correctness and any build implications.
    • src/bench/bls.cpp: duplicate benchmark definitions/registrations — deduplication required.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.26% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "refactor: optimize BLS serialization with stack-allocated arrays" directly and accurately captures the primary objective of the changeset. It clearly identifies the main change—replacing heap-allocated std::vector with stack-allocated std::array for BLS serialization—and emphasizes the performance optimization goal. The title is concise, specific, and provides sufficient context for a teammate reviewing the commit history to immediately understand the core purpose of the PR without requiring additional details.
Description Check ✅ Passed The PR description is comprehensive and directly related to the changeset. It clearly articulates the motivation (cache efficiency and eliminating unnecessary allocations), details the key changes across multiple components, provides concrete performance benchmarks from actual testing, and includes information about test coverage. The description is not vague or generic; it substantively describes specific modifications to files like src/serialize.h, src/coinjoin/coinjoin.h, src/masternode/node.{h,cpp}, and others, with measurable improvements demonstrated.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • 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

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

🧹 Nitpick comments (2)
src/serialize.h (1)

1081-1121: std::array (de)serialization looks correct; consider fast-paths and clearer failure for char-arrays

  • OK as-is. Two optional improvements:
    • Add a raw-byte fast-path for std::array<std::byte, N> to avoid element-wise loops.
    • Add a static_assert to reject std::array<char, N> with a clearer message than template substitution failure.

Example additions:

+template<typename Stream, size_t N>
+inline void Serialize(Stream& s, const std::array<std::byte, N>& a) {
+    if constexpr (N > 0) s.write(AsBytes(Span{a}));
+}
+template<typename Stream, size_t N>
+inline void Unserialize(Stream& s, std::array<std::byte, N>& a) {
+    if constexpr (N > 0) s.read(AsWritableBytes(Span{a}));
+}
+
+template<typename T, size_t N>
+struct _RejectCharArray : std::bool_constant<std::is_same_v<T, char>> {};
+
+template<typename Stream, typename T, size_t N>
+inline void Serialize(Stream& s, const std::array<T, N>& a)
+{
+    static_assert(!_RejectCharArray<T, N>::value, "std::array<char,N> serialization is forbidden; use uint8_t/std::byte");
+    /* … existing body … */
+}
src/bls/bls_ies.cpp (1)

47-51: Make AES key length intent explicit

AES256 uses 32 bytes; ToBytes(false) for a public key returns 48 bytes. We currently pass symKey.data(), which works (first 32 bytes are read), but it’s non‑obvious. Add a guard and optionally slice for clarity.

For example:

+#include <array>
+static_assert(BLS_CURVE_PUBKEY_SIZE >= 32, "BLS pubkey must be >= 32 bytes for AES-256 key derivation");
@@
-    auto symKey = pk.ToBytes(false);
+    auto symKey = pk.ToBytes(false); // 48 bytes
+    // AES256 uses the first 32 bytes
+    const unsigned char* key32 = symKey.data();
@@
-    return DecryptBlob(data.data(), data.size(), decryptedDataRet, symKey.data(), iv.begin());
+    return DecryptBlob(data.data(), data.size(), decryptedDataRet, key32, iv.begin());

Apply similarly in Encrypt() and multi‑recipient Decrypt().

Also applies to: 82-85, 103-106

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e71a9a and 6b41290.

📒 Files selected for processing (11)
  • src/bench/bls.cpp (2 hunks)
  • src/bls/bls.h (2 hunks)
  • src/bls/bls_ies.cpp (3 hunks)
  • src/coinjoin/coinjoin.h (3 hunks)
  • src/governance/vote.h (1 hunks)
  • src/masternode/node.cpp (1 hunks)
  • src/masternode/node.h (1 hunks)
  • src/serialize.h (2 hunks)
  • src/test/bls_tests.cpp (1 hunks)
  • src/test/serialize_tests.cpp (2 hunks)
  • src/wallet/wallet.cpp (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/masternode/node.cpp
  • src/bls/bls.h
  • src/bls/bls_ies.cpp
  • src/governance/vote.h
  • src/masternode/node.h
  • src/serialize.h
  • src/coinjoin/coinjoin.h
  • src/test/serialize_tests.cpp
  • src/test/bls_tests.cpp
  • src/wallet/wallet.cpp
  • src/bench/bls.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/masternode/node.cpp
  • src/masternode/node.h
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/serialize_tests.cpp
  • src/test/bls_tests.cpp
src/bench/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

Performance benchmarks should be placed in src/bench/ and use nanobench

Files:

  • src/bench/bls.cpp
🧬 Code graph analysis (8)
src/masternode/node.cpp (2)
src/bls/bls.h (1)
  • nodiscard (291-332)
src/masternode/node.h (2)
  • nodiscard (73-74)
  • cs (46-81)
src/governance/vote.h (1)
src/governance/object.cpp (2)
  • SetSignature (247-250)
  • SetSignature (247-247)
src/masternode/node.h (2)
src/bls/bls.h (2)
  • nodiscard (291-332)
  • BLS_CURVE_SIG_SIZE (39-170)
src/masternode/node.cpp (5)
  • nodiscard (262-267)
  • nodiscard (273-277)
  • nodiscard (279-285)
  • nodiscard (289-293)
  • SignBasic (279-279)
src/serialize.h (1)
src/bls/bls.h (5)
  • Serialize (161-164)
  • Serialize (435-438)
  • bool (395-476)
  • bool (398-470)
  • bool (399-459)
src/coinjoin/coinjoin.h (1)
src/bls/bls.h (1)
  • BLS_CURVE_SIG_SIZE (39-170)
src/test/serialize_tests.cpp (1)
src/bls/bls.h (2)
  • BLS_CURVE_SIG_SIZE (39-170)
  • BLS_CURVE_PUBKEY_SIZE (38-176)
src/test/bls_tests.cpp (2)
src/evo/mnhftx.h (1)
  • sig (40-40)
src/bls/bls.h (3)
  • BLS_CURVE_SIG_SIZE (39-170)
  • BLS_CURVE_PUBKEY_SIZE (38-176)
  • BLS_CURVE_SECKEY_SIZE (37-204)
src/bench/bls.cpp (1)
src/test/bls_tests.cpp (5)
  • bytes (496-496)
  • bytes (496-496)
  • bytes (499-499)
  • bytes (499-499)
  • ss (656-656)
🪛 GitHub Actions: Clang Diff Format Check
src/masternode/node.h

[error] 1-1: Clang format differences found in src/masternode/node.h. Run the formatting script to fix differences. Command failed with exit code 1.

src/test/bls_tests.cpp

[error] 1-1: Clang format differences found in src/test/bls_tests.cpp. Run the formatting script to fix differences. Command failed with exit code 1.

src/bench/bls.cpp

[error] 1-1: Clang format differences found in src/bench/bls.cpp. Run the formatting script to fix differences. Command failed with exit code 1.

🔇 Additional comments (12)
src/bls/bls.h (1)

169-170: LGTM: direct write from ToBytes

Writing s.write(MakeByteSpan(ToBytes(...))) avoids extra allocations and is safe wrt temporary lifetime.

src/wallet/wallet.cpp (1)

2115-2116: LGTM

Passing the decoded bytes directly to SetSignature(Span) removes an unnecessary copy.

src/masternode/node.cpp (1)

279-285: LGTM

Returning sig.ToBytes(false) matches the new std::array<uint8_t, BLS_CURVE_SIG_SIZE> API and avoids heap allocation.

src/masternode/node.h (1)

70-71: API return type change verified as type-compatible with all call sites

Good change toward fixed-size signatures. All six call sites are confirmed compatible: coinjoin structures now have matching std::array<uint8_t, BLS_CURVE_SIG_SIZE> fields, and governance structures properly use SetSignature(Span<const uint8_t>) which accepts std::array implicitly.

Address the clang-format issues flagged by CI for src/masternode/node.h.

src/bench/bls.cpp (1)

8-8: Cannot verify formatting compliance in sandbox; recommend local verification

The code at line 8 and surrounding sections (362-443) appear visually well-formatted with consistent indentation and spacing. However, clang-format is unavailable in the sandbox environment, preventing conclusive verification of compliance with Dash project standards.

To resolve the original CI formatting failure, run clang-format locally using your repository's configuration:

clang-format -i src/bench/bls.cpp

Then verify the changes before committing.

src/test/serialize_tests.cpp (2)

5-5: LGTM!

The BLS header inclusion is necessary for the BLS size constants used in the new test cases.


310-400: Excellent test coverage for array serialization!

The six new test cases comprehensively validate std::array serialization across different element types (uint8_t, int32_t, bool), sizes (empty, small, BLS-sized), and scenarios (single vs. multiple arrays). The verification that byte arrays serialize without a size prefix (line 318) and that BLS signature arrays serialize to exactly BLS_CURVE_SIG_SIZE (line 371) are particularly important correctness checks.

src/coinjoin/coinjoin.h (3)

10-10: LGTM!

The BLS header inclusion is necessary for the BLS constants and types used in this file.


186-186: Excellent refactor to fixed-size array!

The change from std::vector<unsigned char> to std::array<uint8_t, BLS_CURVE_SIG_SIZE> is semantically correct since BLS signatures are always 96 bytes. This eliminates unnecessary heap allocations and improves cache locality, aligning with the PR's performance optimization goals.


239-239: Consistent with CCoinJoinQueue changes!

This change mirrors the vchSig refactor in CCoinJoinQueue, providing the same performance and semantic correctness benefits for BLS signatures in broadcast transactions.

src/test/bls_tests.cpp (2)

596-644: Comprehensive validation of ToBytes vs ToByteVector equivalence!

These three test cases effectively verify that the new array-based ToBytes() method produces identical results to the existing ToByteVector() method across all BLS types (signatures, public keys, secret keys). This is crucial for ensuring correctness of the optimization.


646-676: Remove this review comment - the CBLSSignature constructor exists and is correctly used.

The constructor CBLSSignature(Span<const unsigned char>, bool) is explicitly defined at line 345 of src/bls/bls.h. The test code at line 674 correctly uses this constructor signature. No changes are needed.

Likely an incorrect or invalid review comment.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR optimizes BLS cryptographic serialization throughout Dash Core by replacing heap-allocated std::vector<uint8_t> with stack-allocated std::array<uint8_t, N> for fixed-size BLS elements (signatures, public keys, IDs). The changes add std::array serialization support to serialize.h with optimized byte array handling that omits the CompactSize prefix used by vectors, enabling direct writes for known-size cryptographic data. Key affected subsystems include CoinJoin signature storage, masternode signing operations (SignBasic() now returns std::array), governance vote signatures, and BLS IES encryption. The refactoring leverages compile-time size constants (BLS_CURVE_SIG_SIZE=96, BLS_CURVE_PUBKEY_SIZE=48) to eliminate dynamic allocations in cryptographic hot paths while maintaining wire format compatibility. Benchmarks demonstrate 6.8-23.4% performance improvements across different BLS operations, with better cache locality and reduced allocation overhead.

Important Files Changed

Filename Score Overview
src/serialize.h 5/5 Adds std::array<T, N> serialization with optimized byte array handling (no size prefix)
src/masternode/node.h 5/5 Changes SignBasic() return type from std::vector to std::array<uint8_t, BLS_CURVE_SIG_SIZE>
src/masternode/node.cpp 5/5 Updates SignBasic() implementation to call ToBytes() instead of ToByteVector()
src/coinjoin/coinjoin.h 5/5 Converts signature fields (vchSig) from std::vector to std::array with zero-init
src/bls/bls.h 5/5 Simplifies Serialize() using MakeByteSpan() and replaces hardcoded sizes with BLS constants
src/bls/bls_ies.cpp 5/5 Switches BLS IES encryption to use ToBytes() for stack-allocated symmetric keys
src/governance/vote.h 2/5 Adds Span-based SetSignature() overload but internal storage remains std::vector
src/wallet/wallet.cpp 5/5 Simplifies SetSignature() call by passing decoded value directly using new Span overload
src/test/bls_tests.cpp 5/5 Adds comprehensive tests comparing ToBytes() vs ToByteVector() and array serialization
src/bench/bls.cpp 5/5 Adds benchmarks validating 6.8-23.4% performance improvements claimed in PR

Confidence score: 4/5

  • This PR is safe to merge with minimal risk, as it maintains wire format compatibility and includes comprehensive test coverage.
  • Score lowered by one point because governance/vote.h adds a Span-based interface but retains std::vector internal storage (inconsistent with the stack-allocation optimization goal), and the PR description lists files (src/governance/object.h, src/governance/signing.cpp, src/coinjoin/server.cpp, src/test/coinjoin_queue_tests.cpp) that are not present in the changed files, suggesting potential documentation/implementation mismatch.
  • Pay close attention to src/governance/vote.h, which may need follow-up work to convert internal storage to std::array for consistency with the rest of the refactoring.

Sequence Diagram

sequenceDiagram
    participant User
Loading

Context used:

  • Context from dashboard - CLAUDE.md (source)

11 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile


std::vector<unsigned char> symKey = pk.ToByteVector(false);
symKey.resize(32);
auto symKey = pk.ToBytes(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: I think better to write directly type of symKey; it's not something super complicated but make code easier to read.

also consider {} initialization instead =

Copy link
Member Author

@PastaPastaPasta PastaPastaPasta Oct 25, 2025

Choose a reason for hiding this comment

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

is it being std::array<uint8_t, BLS_PUBLIC_KEY_SIZE> really more readable?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, because it's easy to see, that there's no allocation, and it's raw bytes, no any wrappers or overhead. I guess it's still subjective and anyway it's an opinion

if constexpr (N > 0) {
os.write(MakeByteSpan(a));
}
} else if constexpr (std::is_same_v<T, bool>) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need a case of std::array<bool, N> ? I don't see any usage in code

Copy link
Member Author

Choose a reason for hiding this comment

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

I think to just keep compatibility with the vector api.

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 overall;

though I would suggest to remove bool-specialization that is not used at the moment - it is dead code that increase scope and it could be re-introduced when it's really needed

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.

linter fails; whitespaces:

diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
@@ -308,0 +310,92 @@ BOOST_AUTO_TEST_CASE(class_methods)
+
+
+
+
+
+
+
+
+
+
+
+

Also strange one:
In file included from /usr/include/x86_64-linux-gnu/c++/11/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/11/bits/c++config.h:586,
from /usr/include/c++/11/cstdint:38,
from ./compat/byteswap.h:8,
from ./compat/endian.h:8,
from ./crypto/common.h:8,
from ./hash.h:11,
from ./bls/bls.h:8,
from test/bls_tests.cpp:5:
/usr/include/features.h:425:5: warning: #warning _FORTIFY_SOURCE > 2 is treated like 2 on this platform [-Wcpp]
425 | # warning _FORTIFY_SOURCE > 2 is treated like 2 on this platform
| ^~~~~~~
test/bls_tests.cpp: In member function ‘void bls_tests::bls_signature_array_serialization::test_method()’:
test/bls_tests.cpp:674:24: error: class template placeholder ‘Span’ not permitted in this context
674 | CBLSSignature sig3(Span{sig_array}, false);
| ^~~~
test/bls_tests.cpp:674:24: note: use ‘auto’ for an abbreviated function template

@PastaPastaPasta
Copy link
Member Author

@knst latest should resolve that

@PastaPastaPasta PastaPastaPasta requested a review from knst October 26, 2025 16:55
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 (2)
src/bench/bls.cpp (1)

362-442: Stabilize microbench loops for lower variance.

Optional: set minEpochIterations on these new benches (like elsewhere in this file) to reduce noise in CI and local runs.

Example:

-    bench.run([&] {
+    bench.minEpochIterations(bench.output() ? 10000 : 100).run([&] {
         auto bytes = sig.ToBytes(false);
         ankerl::nanobench::doNotOptimizeAway(bytes);
     });

(Apply similarly to the 5 other new benches.)

src/serialize.h (1)

1079-1123: Unify byte-span helpers and add std::byte fast-path; improve char diagnostics.

  • Prefer AsBytes(Span{a})/AsWritableBytes(Span{a}) for std::array to avoid relying on MakeByteSpan overloads.
  • Add a fast-path for std::array<std::byte, N> to write/read in a single call (parity with uint8_t).
  • Optionally, add a static_assert for std::array<char, N> to produce a clear error (current path hits deleted char Serialize and is less obvious).
 template<typename Stream, typename T, size_t N>
 void Serialize(Stream& os, const std::array<T, N>& a)
 {
+    static_assert(!std::is_same_v<T, char>, "std::array<char,N> not supported; use std::array<uint8_t,N> or std::array<std::byte,N>");
     if constexpr (std::is_same_v<T, unsigned char>) {
-        // Directly write the byte data without writing the size
-        if constexpr (N > 0) {
-            os.write(MakeByteSpan(a));
-        }
+        if constexpr (N > 0) os.write(AsBytes(Span{a}));
+    } else if constexpr (std::is_same_v<T, std::byte>) {
+        if constexpr (N > 0) os.write(AsBytes(Span{a}));
     } else if constexpr (std::is_same_v<T, bool>) {
         // Serialize each bool individually
         for (const bool& elem : a) {
             ::Serialize(os, elem);
         }
     } else {
         // Serialize each element using the default Serialize function
         for (const T& elem : a) {
             ::Serialize(os, elem);
         }
     }
 }
 
 template<typename Stream, typename T, size_t N>
 void Unserialize(Stream& is, std::array<T, N>& a)
 {
+    static_assert(!std::is_same_v<T, char>, "std::array<char,N> not supported; use std::array<uint8_t,N> or std::array<std::byte,N>");
     if constexpr (std::is_same_v<T, unsigned char>) {
-        // Directly read the byte data without reading the size
-        if constexpr (N > 0) {
-            is.read(AsWritableBytes(Span{a}));
-        }
+        if constexpr (N > 0) is.read(AsWritableBytes(Span{a}));
+    } else if constexpr (std::is_same_v<T, std::byte>) {
+        if constexpr (N > 0) is.read(AsWritableBytes(Span{a}));
     } else if constexpr (std::is_same_v<T, bool>) {
         // Unserialize each bool individually
         for (bool& elem : a) {
             ::Unserialize(is, elem);
         }
     } else {
         // Unserialize each element using the default Unserialize function
         for (T& elem : a) {
             ::Unserialize(is, elem);
         }
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83d8f0a and 990e239.

📒 Files selected for processing (6)
  • src/bench/bls.cpp (2 hunks)
  • src/governance/vote.h (2 hunks)
  • src/masternode/node.h (1 hunks)
  • src/serialize.h (3 hunks)
  • src/test/bls_tests.cpp (2 hunks)
  • src/test/serialize_tests.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/bls_tests.cpp
  • src/test/serialize_tests.cpp
🧰 Additional context used
📓 Path-based instructions (3)
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/governance/vote.h
  • src/masternode/node.h
  • src/serialize.h
  • src/bench/bls.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/masternode/node.h
src/bench/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

Performance benchmarks should be placed in src/bench/ and use nanobench

Files:

  • src/bench/bls.cpp
🧬 Code graph analysis (3)
src/governance/vote.h (2)
src/governance/object.cpp (2)
  • SetSignature (247-250)
  • SetSignature (247-247)
src/script/interpreter.cpp (1)
  • vchSig (1550-1550)
src/masternode/node.h (2)
src/masternode/node.cpp (5)
  • nodiscard (262-267)
  • nodiscard (273-277)
  • nodiscard (279-285)
  • nodiscard (289-293)
  • SignBasic (279-279)
src/bls/bls.h (2)
  • nodiscard (291-332)
  • BLS_CURVE_SIG_SIZE (39-170)
src/bench/bls.cpp (1)
src/test/bls_tests.cpp (5)
  • bytes (497-497)
  • bytes (497-497)
  • bytes (500-500)
  • bytes (500-500)
  • ss (657-657)
⏰ 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). (1)
  • GitHub Check: Build container / Build container
🔇 Additional comments (5)
src/governance/vote.h (2)

10-10: LGTM: Span header included.

The #include <span.h> is now present and correctly resolves the dependency for Span<const uint8_t> used in SetSignature.


95-95: LGTM: Span-based API is correct and consistent.

The SetSignature implementation correctly uses vchSig.assign(vchSigIn.begin(), vchSigIn.end()) to copy the span into the vector storage. This API modernization improves flexibility (accepts arrays, vectors, spans) while maintaining the intentional vector storage for variable-size support. The implementation is consistent with CGovernanceObject::SetSignature in src/governance/object.cpp.

src/serialize.h (2)

13-13: Include for std::array — OK.

Header now explicitly includes . LGTM.


842-847: Forward declarations for std::array — OK.

Placement mirrors vector/prevector decls; keeps API surface consistent.

src/masternode/node.h (1)

71-72: No ripple effects detected; review comment is incorrect.

Verification shows vchSig members in CCoinJoinQueue and CCoinJoinBroadcastTx are already typed as std::array<uint8_t, BLS_CURVE_SIG_SIZE>, matching the SignBasic() return type exactly. All call sites perform direct array-to-array assignments, and SetSignature() accepts Span<const uint8_t>, which implicitly converts from arrays. No code changes required at call sites.

Likely an incorrect or invalid review comment.

Comment on lines +417 to +418
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sig;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing include for PROTOCOL_VERSION (bench build may fail).

CDataStream uses PROTOCOL_VERSION here, but protocol.h isn’t included in this TU.

Apply near the other headers:

 #include <streams.h>
+#include <protocol.h>

Also applies to: 430-431

🤖 Prompt for AI Agents
In src/bench/bls.cpp around lines 417-418 (and similarly at 430-431), the code
uses PROTOCOL_VERSION via CDataStream but does not include protocol.h; add
#include "protocol.h" among the other header includes at the top of this TU so
PROTOCOL_VERSION is defined and the bench build will compile.

// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "span.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/"span.h"/<span.h>

Suggested change
#include "span.h"
#include <span.h>

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

🧹 Nitpick comments (2)
src/test/bls_tests.cpp (2)

597-645: Consider testing both legacy schemes for consistency.

These three test cases verify that ToBytes() and ToByteVector() produce identical results for signatures, public keys, and secret keys. The logic is correct and the tests are well-structured.

However, all three tests hardcode legacy_scheme = false, while other test cases in this file (e.g., FuncSign, FuncSerialize, FuncKeyAgg) consistently test both legacy_scheme = true and false. For consistency and comprehensive coverage, consider testing both schemes here as well.

Example pattern used elsewhere in the file:

void FuncTestToBytes(const bool legacy_scheme)
{
    bls::bls_legacy_scheme.store(legacy_scheme);
    // ... test logic ...
}

BOOST_AUTO_TEST_CASE(bls_tobytes_tests)
{
    FuncTestToBytes(true);
    FuncTestToBytes(false);
}

647-677: Constructor is correctly implemented; consider expanding to test both legacy schemes.

The Span-accepting constructor does exist in src/bls/bls.h (line 345) and is correctly used in the test. The pattern internally calls SetBytes(), so line 675 is valid.

For improved test coverage, optionally expand the test to verify both legacy_scheme = true and legacy_scheme = false cases, ensuring consistent behavior across scheme modes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 990e239 and bbca2d1.

📒 Files selected for processing (1)
  • src/test/bls_tests.cpp (2 hunks)
🧰 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/test/bls_tests.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/bls_tests.cpp
🧬 Code graph analysis (1)
src/test/bls_tests.cpp (1)
src/bls/bls.h (3)
  • BLS_CURVE_SIG_SIZE (39-170)
  • BLS_CURVE_PUBKEY_SIZE (38-176)
  • BLS_CURVE_SECKEY_SIZE (37-204)
🔇 Additional comments (1)
src/test/bls_tests.cpp (1)

5-5: LGTM! Include style fixed.

The change from "span.h" to <span.h> correctly addresses the past review feedback and follows the proper convention for system headers.

@PastaPastaPasta PastaPastaPasta requested a review from knst October 26, 2025 18:09
int64_t nTime{0};
bool fReady{false}; //ready for submit
std::vector<unsigned char> vchSig;
std::array<uint8_t, BLS_CURVE_SIG_SIZE> vchSig{};
Copy link

Choose a reason for hiding this comment

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

mixing fails with CCoinJoinQueue::CheckSignature -- VerifyInsecure() failed - should change serialization accordingly

Copy link
Member Author

Choose a reason for hiding this comment

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

Why does it fail?

COutPoint masternodeOutpoint;
uint256 m_protxHash;
std::vector<unsigned char> vchSig;
std::array<uint8_t, BLS_CURVE_SIG_SIZE> vchSig{};
Copy link

Choose a reason for hiding this comment

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

same

}

void SetSignature(const std::vector<unsigned char>& vchSigIn) { vchSig = vchSigIn; }
void SetSignature(Span<const uint8_t> vchSigIn) { vchSig.assign(vchSigIn.begin(), vchSigIn.end()); }
Copy link

Choose a reason for hiding this comment

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

so basically back to ToByteVector results

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