Merge bitcoin/bitcoin#35896: refactor: Default uint256::operator==, add operator<=>
What changed, and why it matters
This commit is a routine code cleanup in Bitcoin Core. It switches the uint256 equality and comparison operators to use standard C++20 defaults, removes an old custom Compare() helper, and marks an internal assertion-failure function as never returning. There is no security bug being fixed and no behavior change that would affect users or attackers.
No security action required. Treat as normal refactoring/CI benchmark addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The merge refactors base_blob
Changed components
src/uint256.hsrc/primitives/transaction_identifier.hsrc/util/check.hsrc/bench/uint256_blob.cppsrc/bench/CMakeLists.txtInspect captured patch +99 / −24
### src/bench/CMakeLists.txt
@@ -52,6 +52,7 @@ add_executable(bench_bitcoin
strencodings.cpp
txgraph.cpp
txorphanage.cpp
+ uint256_blob.cpp
util_time.cpp
verify_script.cpp
)
### src/bench/uint256_blob.cpp
@@ -0,0 +1,91 @@
+// Copyright (c) The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or https://opensource.org/license/mit.
+
+#include <bench/bench.h>
+#include <random.h>
+#include <uint256.h>
+
+#include <compare>
+#include <cstddef>
+#include <utility>
+#include <vector>
+
+namespace {
+
+enum class Difference {
+ NONE,
+ FIRST_BYTE,
+ LAST_BYTE,
+};
+
+constexpr size_t NUM_PAIRS{4'096};
+
+std::vector<std::pair<uint256, uint256>> MakePairs(Difference difference)
+{
+ FastRandomContext rng{/*fDeterministic=*/true};
+ std::vector<std::pair<uint256, uint256>> pairs;
+ pairs.reserve(NUM_PAIRS);
+
+ for (size_t i{0}; i < NUM_PAIRS; ++i) {
+ uint256 lhs{rng.rand256()};
+ uint256 rhs{lhs};
+ if (difference != Difference::NONE) {
+ const size_t position{difference == Difference::FIRST_BYTE ? 0 : uint256::size() - 1};
+ lhs.begin()[position] = i % 2 == 0 ? 0 : 255;
+ rhs.begin()[position] = i % 2 == 0 ? 255 : 0;
+ }
+ pairs.emplace_back(lhs, rhs);
+ }
+ return pairs;
+}
+
+template <typename Comparator>
+void Comparison(benchmark::Bench& bench, Difference difference, Comparator comparator)
+{
+ const auto pairs{MakePairs(difference)};
+ bench.batch(pairs.size()).unit("comparison").run([&] {
+ for (const auto& [lhs, rhs] : pairs) {
+ ankerl::nanobench::doNotOptimizeAway(comparator(lhs, rhs));
+ }
+ });
+}
+
+void Uint256EqualIdentical(benchmark::Bench& bench)
+{
+ Comparison(bench, Difference::NONE, [](const uint256& lhs, const uint256& rhs) { return lhs == rhs; });
+}
+
+void Uint256EqualFirstByteDifferent(benchmark::Bench& bench)
+{
+ Comparison(bench, Difference::FIRST_BYTE, [](const uint256& lhs, const uint256& rhs) { return lhs == rhs; });
+}
+
+void Uint256EqualLastByteDifferent(benchmark::Bench& bench)
+{
+ Comparison(bench, Difference::LAST_BYTE, [](const uint256& lhs, const uint256& rhs) { return lhs == rhs; });
+}
+
+void Uint256LessIdentical(benchmark::Bench& bench)
+{
+ Comparison(bench, Difference::NONE, [](const uint256& lhs, const uint256& rhs) { return lhs < rhs; });
+}
+
+void Uint256LessFirstByteDifferent(benchmark::Bench& bench)
+{
+ Comparison(bench, Difference::FIRST_BYTE, [](const uint256& lhs, const uint256& rhs) { return lhs < rhs; });
+}
+
+void Uint256LessLastByteDifferent(benchmark::Bench& bench)
+{
+ Comparison(bench, Difference::LAST_BYTE, [](const uint256& lhs, const uint256& rhs) { return lhs < rhs; });
+}
+
+} // namespace
+
+BENCHMARK(Uint256EqualIdentical);
+BENCHMARK(Uint256EqualFirstByteDifferent);
+BENCHMARK(Uint256EqualLastByteDifferent);
+BENCHMARK(Uint256LessIdentical);
+BENCHMARK(Uint256LessFirstByteDifferent);
+BENCHMARK(Uint256LessLastByteDifferent);
### src/primitives/transaction_identifier.h
@@ -7,7 +7,6 @@
#include <attributes.h>
#include <uint256.h>
-#include <util/types.h>
#include <compare>
#include <cstddef>
@@ -28,22 +27,12 @@ class transaction_identifier
// Note: Use FromUint256 externally instead.
transaction_identifier(const uint256& wrapped) : m_wrapped{wrapped} {}
- constexpr int Compare(const transaction_identifier<has_witness>& other) const { return m_wrapped.Compare(other.m_wrapped); }
- template <typename Other>
- constexpr int Compare(const Other& other) const
- {
- static_assert(ALWAYS_FALSE<Other>, "Forbidden comparison type");
- return 0;
- }
-
public:
transaction_identifier() : m_wrapped{} {}
consteval explicit transaction_identifier(std::string_view hex_str) : m_wrapped{uint256{hex_str}} {}
- template <typename Other>
- bool operator==(const Other& other) const { return Compare(other) == 0; }
- template <typename Other>
- std::strong_ordering operator<=>(const Other& other) const { return Compare(other) <=> 0; }
+ constexpr bool operator==(const transaction_identifier&) const = default;
+ constexpr auto operator<=>(const transaction_identifier&) const = default;
const uint256& ToUint256() const LIFETIMEBOUND { return m_wrapped; }
static transaction_identifier FromUint256(const uint256& id) { return {id}; }
### src/uint256.h
@@ -59,19 +59,13 @@ class base_blob
std::fill(m_data.begin(), m_data.end(), 0);
}
+ constexpr bool operator==(const base_blob&) const = default;
+
/** Lexicographic ordering
* @note Does NOT match the ordering on the corresponding \ref
* base_uint::CompareTo, which starts comparing from the end.
*/
- constexpr int Compare(const base_blob& other) const {
- auto cmp = m_data <=> other.m_data;
- if (cmp < 0) return -1;
- if (cmp > 0) return 1;
- return 0;
- }
-
- friend constexpr bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; }
- friend constexpr bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }
+ constexpr std::strong_ordering operator<=>(const base_blob& other) const = default;
/** @name Hex representation
*
### src/util/check.h
@@ -64,8 +64,8 @@ class NonFatalCheckError : public std::runtime_error
NonFatalCheckError(std::string_view msg, const std::source_location& loc);
};
-/** Internal helper */
-void assertion_fail(const std::source_location& loc, std::string_view assertion);
+/// Internal helper. The noreturn enables optimizers to discard invalid paths.
+[[noreturn]] void assertion_fail(const std::source_location& loc, std::string_view assertion);
/** Helper for CHECK_NONFATAL() */
template <typename T>Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.