Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/bls/bls.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ class CBLSLazyWrapper
mutable std::mutex mutex;

mutable std::array<uint8_t, BLSObject::SerSize> vecBytes;
// Indicates if the value contained in vecBytes is valid
mutable bool bufValid{false};
mutable bool bufLegacyScheme{true};

Expand Down Expand Up @@ -462,7 +463,7 @@ class CBLSLazyWrapper
{
std::unique_lock<std::mutex> l(mutex);
s.read(AsWritableBytes(Span{vecBytes.data(), BLSObject::SerSize}));
bufValid = true;
bufValid = std::any_of(vecBytes.begin(), vecBytes.end(), [](uint8_t c) { return c != 0; });
bufLegacyScheme = specificLegacyScheme;
objInitialized = false;
hash.SetNull();
Expand Down Expand Up @@ -507,6 +508,14 @@ class CBLSLazyWrapper

bool operator==(const CBLSLazyWrapper& r) const
{
// If neither bufValid or objInitialized are set, then the object is the default object.
const bool is_default{!bufValid && !objInitialized};
const bool r_is_default{!r.bufValid && !r.objInitialized};
// If both are default; they are equal.
if (is_default && r_is_default) return true;
// If one is default and the other isn't, we are not equal
if (is_default != r_is_default) return false;

if (bufValid && r.bufValid && bufLegacyScheme == r.bufLegacyScheme) {
return vecBytes == r.vecBytes;
}
Expand Down
8 changes: 5 additions & 3 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate keyIDOwner=%s", __func__,
dmn->proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner)))));
}
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
if (dmn->pdmnState->pubKeyOperator != CBLSLazyPublicKey() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
Expand Down Expand Up @@ -578,7 +578,8 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a keyIDOwner=%s", __func__,
proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner)))));
}
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
if (dmn->pdmnState->pubKeyOperator != CBLSLazyPublicKey() &&
!DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a pubKeyOperator=%s", __func__,
proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
Expand Down Expand Up @@ -840,7 +841,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
}
if (newState->IsBanned()) {
// only revive when all keys are set
if (newState->pubKeyOperator.Get().IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
if (newState->pubKeyOperator != CBLSLazyPublicKey() && !newState->keyIDVoting.IsNull() &&
!newState->keyIDOwner.IsNull()) {
newState->Revive(nHeight);
if (debugLogs) {
LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n",
Expand Down
121 changes: 121 additions & 0 deletions src/test/bls_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <random.h>
#include <streams.h>
#include <util/irange.h>
#include <util/strencodings.h>

#include <boost/test/unit_test.hpp>

Expand Down Expand Up @@ -472,4 +473,124 @@ BOOST_AUTO_TEST_CASE(bls_threshold_signature_tests)
FuncThresholdSignature(false);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix clang-format issue.

The CI pipeline indicates a formatting issue. Please run clang-format on the file to resolve this.

🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 473-473: Clang format differences found. Please run 'clang-format' to format the code.


// A dummy BLS object that satisfies the minimal interface expected by CBLSLazyWrapper.
class DummyBLS
{
public:
// Define a fixed serialization size (for testing purposes).
static const size_t SerSize = 4;
std::array<uint8_t, SerSize> data{};

DummyBLS() { data.fill(0); }

// A dummy validity check: valid if any byte is non-zero.
bool IsValid() const
{
return std::any_of(data.begin(), data.end(), [](uint8_t c) { return c != 0; });
}

// Convert to bytes; ignore the legacy flag for simplicity.
std::array<uint8_t, SerSize> ToBytes(bool /*legacy*/) const { return data; }

// Set from bytes; again, ignore the legacy flag.
void SetBytes(const std::array<uint8_t, SerSize>& bytes, bool /*legacy*/) { data = bytes; }

// A dummy malleability check: simply compares the stored data to the given bytes.
bool CheckMalleable(const std::array<uint8_t, SerSize>& bytes, bool /*legacy*/) const { return data == bytes; }

// Reset the object to an "empty" state.
void Reset() { data.fill(0); }

// Produce a string representation.
std::string ToString(bool /*legacy*/) const { return HexStr(data); }

// Equality operator.
bool operator==(const DummyBLS& other) const { return data == other.data; }
};

// Define a type alias for our lazy wrapper instantiated with DummyBLS.
using LazyDummyBLS = CBLSLazyWrapper<DummyBLS>;

// Test 1: Two default (unset) wrappers should compare equal.
BOOST_AUTO_TEST_CASE(test_default_equality)
{
LazyDummyBLS lazy1;
LazyDummyBLS lazy2;
// Neither instance has been set, so they represent the default/null object.
BOOST_CHECK(lazy1 == lazy2);
}

// Test 2: A default wrapper and one initialized with a nonzero DummyBLS should compare unequal.
BOOST_AUTO_TEST_CASE(test_non_default_vs_default)
{
LazyDummyBLS lazy_default;
LazyDummyBLS lazy_set;
DummyBLS obj;
obj.data = {1, 0, 0, 0}; // nonzero data makes the object valid
lazy_set.Set(obj, false);
BOOST_CHECK(!(lazy_default == lazy_set));
BOOST_CHECK(lazy_default != lazy_set);
}

// Test 2: A default wrapper and one initialized with a nonzero DummyBLS should compare unequal.
BOOST_AUTO_TEST_CASE(test_non_default_vs_different)
{
LazyDummyBLS lazy_a;
LazyDummyBLS lazy_b;
DummyBLS obj;
obj.data = {1, 2, 3, 4}; // nonzero data makes the object valid
lazy_a.Set(obj, false);
obj.data = {2, 2, 3, 4}; // nonzero data makes the object valid
lazy_b.Set(obj, false);
BOOST_CHECK(lazy_a != lazy_b);
}

// Test 3: Two wrappers set with the same underlying DummyBLS value compare equal.
BOOST_AUTO_TEST_CASE(test_equality_same_value)
{
LazyDummyBLS lazy1;
LazyDummyBLS lazy2;
BOOST_CHECK(lazy1 == lazy2);
DummyBLS obj;
obj.data = {5, 6, 7, 8};
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: maybe make only 1 byte difference here, to catch error "any_of" vs "all_of".

such as:

obj.data = {1, 2, 3, 5};

lazy1.Set(obj, false);
BOOST_CHECK(lazy1 != lazy2);
lazy2.Set(obj, false);
BOOST_CHECK(lazy1 == lazy2);
}

// Test 4: Serialization and unserialization preserve the wrapped value.
BOOST_AUTO_TEST_CASE(test_serialization_unserialization)
{
LazyDummyBLS lazy1;
DummyBLS obj;
obj.data = {9, 10, 11, 12};
// Set with a specific legacy flag (true in this case)
lazy1.Set(obj, true);

// Serialize the lazy object into a data stream.
CDataStream ds(SER_DISK, CLIENT_VERSION);
lazy1.Serialize(ds, true);

// Create a new instance and unserialize the data into it.
LazyDummyBLS lazy2;
lazy2.Unserialize(ds, true);
BOOST_CHECK(lazy1 == lazy2);
BOOST_CHECK(lazy2.Get() == obj);
}

// Test 5: Two wrappers wrapping the same object should have the same hash.
BOOST_AUTO_TEST_CASE(test_get_hash_consistency)
{
LazyDummyBLS lazy1;
LazyDummyBLS lazy2;
DummyBLS obj;
obj.data = {13, 14, 15, 16};
lazy1.Set(obj, false);
lazy2.Set(obj, false);
uint256 hash1 = lazy1.GetHash();
uint256 hash2 = lazy2.GetHash();
BOOST_CHECK(hash1 == hash2);
}

BOOST_AUTO_TEST_SUITE_END()
Loading