diff --git a/src/bench/bls.cpp b/src/bench/bls.cpp index 9199f1f333600..7682e9f8aafd0 100644 --- a/src/bench/bls.cpp +++ b/src/bench/bls.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -358,6 +359,87 @@ static void BLS_Verify_BatchedParallel(benchmark::Bench& bench) blsWorker.Stop(); } +static void BLS_ToBytes_Signature(benchmark::Bench& bench) +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + auto sig = sk.Sign(uint256::ONE, false); + + bench.run([&] { + auto bytes = sig.ToBytes(false); + ankerl::nanobench::doNotOptimizeAway(bytes); + }); +} + +static void BLS_ToByteVector_Signature(benchmark::Bench& bench) +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + auto sig = sk.Sign(uint256::ONE, false); + + bench.run([&] { + auto bytes = sig.ToByteVector(false); + ankerl::nanobench::doNotOptimizeAway(bytes); + }); +} + +static void BLS_ToBytes_PubKey(benchmark::Bench& bench) +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + auto pk = sk.GetPublicKey(); + + bench.run([&] { + auto bytes = pk.ToBytes(false); + ankerl::nanobench::doNotOptimizeAway(bytes); + }); +} + +static void BLS_ToByteVector_PubKey(benchmark::Bench& bench) +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + auto pk = sk.GetPublicKey(); + + bench.run([&] { + auto bytes = pk.ToByteVector(false); + ankerl::nanobench::doNotOptimizeAway(bytes); + }); +} + +static void BLS_Serialize_Signature_Array(benchmark::Bench& bench) +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + auto sig = sk.Sign(uint256::ONE, false); + + bench.run([&] { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << sig; + ankerl::nanobench::doNotOptimizeAway(ss); + }); +} + +static void BLS_Serialize_Signature_Vector(benchmark::Bench& bench) +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + auto sig = sk.Sign(uint256::ONE, false); + + bench.run([&] { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + auto vec = sig.ToByteVector(false); + ss << vec; + ankerl::nanobench::doNotOptimizeAway(ss); + }); +} + +BENCHMARK(BLS_ToBytes_Signature) +BENCHMARK(BLS_ToByteVector_Signature) +BENCHMARK(BLS_ToBytes_PubKey) +BENCHMARK(BLS_ToByteVector_PubKey) +BENCHMARK(BLS_Serialize_Signature_Array) +BENCHMARK(BLS_Serialize_Signature_Vector) BENCHMARK(BLS_PubKeyAggregate_Normal) BENCHMARK(BLS_SecKeyAggregate_Normal) BENCHMARK(BLS_SignatureAggregate_Normal) diff --git a/src/bls/bls.h b/src/bls/bls.h index a99cec3114cfa..9aae112efc1fd 100644 --- a/src/bls/bls.h +++ b/src/bls/bls.h @@ -166,8 +166,7 @@ class CBLSWrapper template inline void Serialize(Stream& s, const bool specificLegacyScheme) const { - const auto bytes{ToBytes(specificLegacyScheme)}; - s.write(AsBytes(Span{bytes.data(), SerSize})); + s.write(MakeByteSpan(ToBytes(specificLegacyScheme))); } template @@ -245,7 +244,7 @@ struct CBLSIdImplicit : public uint256 { return {begin(), end()}; } - [[nodiscard]] std::array SerializeToArray(const bool fLegacy) const { return m_data; } + [[nodiscard]] std::array SerializeToArray(const bool fLegacy) const { return m_data; } }; class CBLSId : public CBLSWrapper diff --git a/src/bls/bls_ies.cpp b/src/bls/bls_ies.cpp index a99032baf8d3d..ab6eaf21b51b9 100644 --- a/src/bls/bls_ies.cpp +++ b/src/bls/bls_ies.cpp @@ -44,8 +44,7 @@ bool CBLSIESEncryptedBlob::Decrypt(size_t idx, const CBLSSecretKey& secretKey, C return false; } - std::vector symKey = pk.ToByteVector(false); - symKey.resize(32); + auto symKey = pk.ToBytes(false); uint256 iv = GetIV(idx); return DecryptBlob(data.data(), data.size(), decryptedDataRet, symKey.data(), iv.begin()); @@ -80,8 +79,7 @@ bool CBLSIESMultiRecipientBlobs::Encrypt(size_t idx, const CBLSPublicKey& recipi return false; } - std::vector symKey = pk.ToByteVector(false); - symKey.resize(32); + auto symKey = pk.ToBytes(false); return EncryptBlob(blob.data(), blob.size(), blobs[idx], symKey.data(), ivVector[idx].begin()); } @@ -97,13 +95,12 @@ bool CBLSIESMultiRecipientBlobs::Decrypt(size_t idx, const CBLSSecretKey& sk, Bl return false; } - std::vector symKey = pk.ToByteVector(false); - symKey.resize(32); - uint256 iv = ivSeed; for (size_t i = 0; i < idx; i++) { iv = ::SerializeHash(iv); } + auto symKey = pk.ToBytes(false); + return DecryptBlob(blobs[idx].data(), blobs[idx].size(), blobRet, symKey.data(), iv.begin()); } diff --git a/src/coinjoin/coinjoin.h b/src/coinjoin/coinjoin.h index b0c13beebbdbd..9b532a9ba4990 100644 --- a/src/coinjoin/coinjoin.h +++ b/src/coinjoin/coinjoin.h @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -182,7 +183,7 @@ class CCoinJoinQueue uint256 m_protxHash; int64_t nTime{0}; bool fReady{false}; //ready for submit - std::vector vchSig; + std::array vchSig{}; // memory only bool fTried{false}; @@ -235,7 +236,7 @@ class CCoinJoinBroadcastTx CTransactionRef tx; COutPoint masternodeOutpoint; uint256 m_protxHash; - std::vector vchSig; + std::array vchSig{}; int64_t sigTime{0}; CCoinJoinBroadcastTx() : tx(MakeTransactionRef(CMutableTransaction{})) diff --git a/src/governance/vote.h b/src/governance/vote.h index 18664f8b97ec5..dd76d98af5e18 100644 --- a/src/governance/vote.h +++ b/src/governance/vote.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -91,7 +92,7 @@ class CGovernanceVote UpdateHash(); } - void SetSignature(const std::vector& vchSigIn) { vchSig = vchSigIn; } + void SetSignature(Span vchSigIn) { vchSig.assign(vchSigIn.begin(), vchSigIn.end()); } bool CheckSignature(const CKeyID& keyID) const; bool CheckSignature(const CBLSPublicKey& pubKey) const; diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index 2f0f692388715..d817f548e0158 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -276,12 +276,12 @@ template bool CActiveMasternodeManager::Decrypt(const CBLSIESMultiRecipientObjec return WITH_READ_LOCK(cs, return m_info.blsKeyOperator.Sign(hash, is_legacy)); } -[[nodiscard]] std::vector CActiveMasternodeManager::SignBasic(const uint256& hash) const +[[nodiscard]] std::array CActiveMasternodeManager::SignBasic(const uint256& hash) const { AssertLockNotHeld(cs); auto sig = Sign(hash, /*is_legacy=*/false); assert(sig.IsValid()); - return sig.ToByteVector(/*specificLegacyScheme=*/false); + return sig.ToBytes(/*specificLegacyScheme=*/false); } // We need to pass a copy as opposed to a const ref because CBLSPublicKeyVersionWrapper diff --git a/src/masternode/node.h b/src/masternode/node.h index 00a526d8b4fb3..15e4e340b14d0 100644 --- a/src/masternode/node.h +++ b/src/masternode/node.h @@ -67,7 +67,8 @@ class CActiveMasternodeManager [[nodiscard]] bool Decrypt(const EncryptedObj& obj, size_t idx, Obj& ret_obj, int version) const EXCLUSIVE_LOCKS_REQUIRED(!cs); [[nodiscard]] CBLSSignature Sign(const uint256& hash, const bool is_legacy) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - [[nodiscard]] std::vector SignBasic(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); + [[nodiscard]] std::array SignBasic(const uint256& hash) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); /* TODO: Reconsider external locking */ [[nodiscard]] COutPoint GetOutPoint() const { READ_LOCK(cs); return m_info.outpoint; } diff --git a/src/serialize.h b/src/serialize.h index d0ac4e8f8917a..4cb85c6715824 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -838,6 +839,12 @@ template inline void Unserialize(St template inline void Serialize(Stream& os, const std::vector& v); template inline void Unserialize(Stream& is, std::vector& v); +/** + * array + */ +template void Serialize(Stream& os, const std::array& a); +template void Unserialize(Stream& is, std::array& a); + /** * pair */ @@ -1069,6 +1076,51 @@ void Unserialize(Stream& is, std::vector& v) } } +/** + * array + */ +template +void Serialize(Stream& os, const std::array& a) +{ + if constexpr (std::is_same_v) { + // Directly write the byte data without writing the size + if constexpr (N > 0) { + os.write(MakeByteSpan(a)); + } + } else if constexpr (std::is_same_v) { + // 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 +void Unserialize(Stream& is, std::array& a) +{ + if constexpr (std::is_same_v) { + // Directly read the byte data without reading the size + if constexpr (N > 0) { + is.read(AsWritableBytes(Span{a})); + } + } else if constexpr (std::is_same_v) { + // 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); + } + } +} + /** * pair diff --git a/src/test/bls_tests.cpp b/src/test/bls_tests.cpp index 008b245953c84..2dd8458ab35c4 100644 --- a/src/test/bls_tests.cpp +++ b/src/test/bls_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -593,4 +594,86 @@ BOOST_AUTO_TEST_CASE(test_get_hash_consistency) BOOST_CHECK(hash1 == hash2); } +BOOST_AUTO_TEST_CASE(bls_tobytes_vs_tobytevector_signature) +{ + // Test that ToBytes() and ToByteVector() produce identical results for signatures + bls::bls_legacy_scheme.store(false); + + CBLSSecretKey sk; + sk.MakeNewKey(); + auto sig = sk.Sign(uint256::ONE, false); + + auto bytes_array = sig.ToBytes(false); + auto bytes_vector = sig.ToByteVector(false); + + BOOST_CHECK_EQUAL(bytes_array.size(), BLS_CURVE_SIG_SIZE); + BOOST_CHECK_EQUAL(bytes_vector.size(), BLS_CURVE_SIG_SIZE); + BOOST_CHECK(std::equal(bytes_array.begin(), bytes_array.end(), bytes_vector.begin())); +} + +BOOST_AUTO_TEST_CASE(bls_tobytes_vs_tobytevector_pubkey) +{ + // Test that ToBytes() and ToByteVector() produce identical results for public keys + bls::bls_legacy_scheme.store(false); + + CBLSSecretKey sk; + sk.MakeNewKey(); + auto pk = sk.GetPublicKey(); + + auto bytes_array = pk.ToBytes(false); + auto bytes_vector = pk.ToByteVector(false); + + BOOST_CHECK_EQUAL(bytes_array.size(), BLS_CURVE_PUBKEY_SIZE); + BOOST_CHECK_EQUAL(bytes_vector.size(), BLS_CURVE_PUBKEY_SIZE); + BOOST_CHECK(std::equal(bytes_array.begin(), bytes_array.end(), bytes_vector.begin())); +} + +BOOST_AUTO_TEST_CASE(bls_tobytes_vs_tobytevector_seckey) +{ + // Test that ToBytes() and ToByteVector() produce identical results for secret keys + bls::bls_legacy_scheme.store(false); + + CBLSSecretKey sk; + sk.MakeNewKey(); + + auto bytes_array = sk.ToBytes(false); + auto bytes_vector = sk.ToByteVector(false); + + BOOST_CHECK_EQUAL(bytes_array.size(), BLS_CURVE_SECKEY_SIZE); + BOOST_CHECK_EQUAL(bytes_vector.size(), BLS_CURVE_SECKEY_SIZE); + BOOST_CHECK(std::equal(bytes_array.begin(), bytes_array.end(), bytes_vector.begin())); +} + +BOOST_AUTO_TEST_CASE(bls_signature_array_serialization) +{ + // Test that BLS signatures serialize correctly with std::array + bls::bls_legacy_scheme.store(false); + + CBLSSecretKey sk; + sk.MakeNewKey(); + auto sig = sk.Sign(uint256::ONE, false); + + // Serialize using standard serialization + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << sig; + + // Deserialize + CBLSSignature sig2; + ss >> sig2; + + BOOST_CHECK(sig == sig2); + + // Verify the serialized size is exactly BLS_CURVE_SIG_SIZE (no size prefix) + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << sig; + BOOST_CHECK_EQUAL(ss2.size(), BLS_CURVE_SIG_SIZE); + + // Test array assignment from ToBytes + std::array sig_array = sig.ToBytes(false); + + // Construct signature from array via Span + CBLSSignature sig3(MakeUCharSpan(sig_array), false); + BOOST_CHECK(sig == sig3); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index d0b6cc774ccf5..349027153b6e3 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -306,4 +307,96 @@ BOOST_AUTO_TEST_CASE(class_methods) } } +BOOST_AUTO_TEST_CASE(array_serialization_bytes) +{ + // Test std::array serialization (byte arrays) + std::array arr_in = {0x01, 0x02, 0x03, 0x04}; + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << arr_in; + + // Should serialize without size prefix for byte arrays + BOOST_CHECK_EQUAL(ss.size(), 4); + + std::array arr_out; + ss >> arr_out; + BOOST_CHECK(arr_in == arr_out); +} + +BOOST_AUTO_TEST_CASE(array_serialization_integers) +{ + // Test std::array serialization + std::array arr_in = {100, 200, 300}; + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << arr_in; + + std::array arr_out; + ss >> arr_out; + BOOST_CHECK(arr_in == arr_out); +} + +BOOST_AUTO_TEST_CASE(array_serialization_bools) +{ + // Test std::array serialization + std::array arr_in = {true, false, true, true, false}; + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << arr_in; + + std::array arr_out; + ss >> arr_out; + BOOST_CHECK(arr_in == arr_out); +} + +BOOST_AUTO_TEST_CASE(array_serialization_empty) +{ + // Test zero-sized array + std::array arr_in; + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << arr_in; + BOOST_CHECK_EQUAL(ss.size(), 0); + + std::array arr_out; + ss >> arr_out; +} + +BOOST_AUTO_TEST_CASE(array_serialization_bls_signature) +{ + // Test large byte array (BLS signature size) + std::array arr_in; + for (size_t i = 0; i < BLS_CURVE_SIG_SIZE; ++i) { + arr_in[i] = static_cast(i); + } + + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << arr_in; + BOOST_CHECK_EQUAL(ss.size(), BLS_CURVE_SIG_SIZE); + + std::array arr_out; + ss >> arr_out; + BOOST_CHECK(arr_in == arr_out); +} + +BOOST_AUTO_TEST_CASE(array_serialization_multiple) +{ + // Test round-trip with multiple arrays (BLS pubkey and signature sizes) + std::array arr1; + std::array arr2; + std::array arr3 = {42, 84}; + + for (size_t i = 0; i < BLS_CURVE_PUBKEY_SIZE; ++i) arr1[i] = static_cast(i); + for (size_t i = 0; i < BLS_CURVE_SIG_SIZE; ++i) arr2[i] = static_cast(255 - i); + + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << arr1 << arr2 << arr3; + + std::array arr1_out; + std::array arr2_out; + std::array arr3_out; + + ss >> arr1_out >> arr2_out >> arr3_out; + + BOOST_CHECK(arr1 == arr1_out); + BOOST_CHECK(arr2 == arr2_out); + BOOST_CHECK(arr3 == arr3_out); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1caf30747a3b0..a80ffba3948c0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2112,7 +2112,7 @@ bool CWallet::SignGovernanceVote(const CKeyID& keyID, CGovernanceVote& vote) con const auto opt_decoded = DecodeBase64(signature); CHECK_NONFATAL(opt_decoded.has_value()); // DecodeBase64 should not fail - vote.SetSignature(std::vector(opt_decoded->data(), opt_decoded->data() + opt_decoded->size())); + vote.SetSignature(*opt_decoded); return true; }