diff --git a/include/xrpl/protocol/ValidatorBitset.h b/include/xrpl/protocol/ValidatorBitset.h index 1601e95d4..aebc7a39f 100644 --- a/include/xrpl/protocol/ValidatorBitset.h +++ b/include/xrpl/protocol/ValidatorBitset.h @@ -30,9 +30,44 @@ namespace ripple { -struct ValidatorBitsetInfo +class ValidatedValidatorBitset { - std::size_t selected = 0; + Slice bitset_; + std::size_t memberCount_ = 0; + std::size_t selected_ = 0; + + ValidatedValidatorBitset( + Slice bitset, + std::size_t memberCount, + std::size_t selected) + : bitset_(bitset), memberCount_(memberCount), selected_(selected) + { + } + +public: + /** Construct a validated, non-owning view over a validator bitset. + + The caller must keep the bitset storage alive while using the view. + */ + static std::optional + make(Slice bitset, std::size_t memberCount); + + std::size_t + selected() const + { + return selected_; + } + + bool + contains(std::size_t position) const + { + if (position >= memberCount_) + return false; + + auto const byte = position / 8; + return (bitset_[byte] & + static_cast(1u << (position % 8))) != 0; + } }; constexpr std::size_t @@ -47,8 +82,14 @@ validatorBitsetBytes(std::size_t memberCount) unused high bit in the final byte clear. The returned population can be used by a caller's profile-specific admission and quorum rules. */ -inline std::optional +inline std::optional validateValidatorBitset(Slice bitset, std::size_t memberCount) +{ + return ValidatedValidatorBitset::make(bitset, memberCount); +} + +inline std::optional +ValidatedValidatorBitset::make(Slice bitset, std::size_t memberCount) { if (bitset.size() != validatorBitsetBytes(memberCount)) return std::nullopt; @@ -66,22 +107,7 @@ validateValidatorBitset(Slice bitset, std::size_t memberCount) for (auto const byte : bitset) selected += std::popcount(byte); - return ValidatorBitsetInfo{selected}; -} - -inline bool -validatorBitsetContains( - Slice bitset, - std::size_t memberCount, - std::size_t position) -{ - if (position >= memberCount || - bitset.size() != validatorBitsetBytes(memberCount)) - return false; - - auto const byte = position / 8; - return (bitset[byte] & static_cast(1u << (position % 8))) != - 0; + return ValidatedValidatorBitset{bitset, memberCount, selected}; } template diff --git a/src/test/protocol/ValidatorBitset_test.cpp b/src/test/protocol/ValidatorBitset_test.cpp index fe9d35f33..ca0edd7dc 100644 --- a/src/test/protocol/ValidatorBitset_test.cpp +++ b/src/test/protocol/ValidatorBitset_test.cpp @@ -49,17 +49,15 @@ public: auto const info = validateValidatorBitset(makeSlice(bitset), 10); BEAST_EXPECT(info); if (info) - BEAST_EXPECT(info->selected == 3); - - BEAST_EXPECT(validatorBitsetContains(makeSlice(bitset), 10, 0)); - BEAST_EXPECT(validatorBitsetContains(makeSlice(bitset), 10, 7)); - BEAST_EXPECT(!validatorBitsetContains(makeSlice(bitset), 10, 8)); - BEAST_EXPECT(validatorBitsetContains(makeSlice(bitset), 10, 9)); - BEAST_EXPECT(!validatorBitsetContains(makeSlice(bitset), 10, 10)); - BEAST_EXPECT(!validatorBitsetContains(makeSlice(bitset), 10, 80)); - - Blob const wrongSize{0x81}; - BEAST_EXPECT(!validatorBitsetContains(makeSlice(wrongSize), 10, 0)); + { + BEAST_EXPECT(info->selected() == 3); + BEAST_EXPECT(info->contains(0)); + BEAST_EXPECT(info->contains(7)); + BEAST_EXPECT(!info->contains(8)); + BEAST_EXPECT(info->contains(9)); + BEAST_EXPECT(!info->contains(10)); + BEAST_EXPECT(!info->contains(80)); + } } void @@ -78,7 +76,7 @@ public: auto const info = validateValidatorBitset(makeSlice(exact), 10); BEAST_EXPECT(info); if (info) - BEAST_EXPECT(info->selected == 10); + BEAST_EXPECT(info->selected() == 10); BEAST_EXPECT(!validateValidatorBitset(makeSlice(highBitSet), 10)); BEAST_EXPECT(!validateValidatorBitset(makeSlice(tooLong), 10)); @@ -86,7 +84,7 @@ public: auto const full = validateValidatorBitset(makeSlice(fullByte), 8); BEAST_EXPECT(full); if (full) - BEAST_EXPECT(full->selected == 8); + BEAST_EXPECT(full->selected() == 8); } void diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index c2f1dd750..976e47b22 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -46,21 +47,6 @@ namespace ripple { namespace { -std::size_t -countSetBits(Blob const& bytes) -{ - std::size_t count = 0; - for (auto byte : bytes) - { - while (byte != 0) - { - byte &= static_cast(byte - 1); - ++count; - } - } - return count; -} - bool validEntropyContributorMask( std::uint8_t tier, @@ -77,19 +63,9 @@ validEntropyContributorMask( if (tier < entropyTierParticipantAligned || tier > entropyTierValidatorFull) return false; - auto const expectedSize = (denominator + 7) / 8; - if (contributors.size() != expectedSize) - return false; - - if (auto const usedBits = denominator % 8; - usedBits != 0 && !contributors.empty()) - { - auto const validBits = static_cast((1u << usedBits) - 1u); - if ((contributors.back() & ~validBits) != 0) - return false; - } - - return countSetBits(contributors) == count; + auto const bitset = + validateValidatorBitset(makeSlice(contributors), denominator); + return bitset && bitset->selected() == count; } } // namespace