From b17dcb10fdc5e29cdea49a275884cb2f30b00ae3 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 4 Oct 2026 12:35:42 -0400 Subject: [PATCH] Base short_id on chain type, avoid magic numbers/types. --- .../query/consensus/consensus_compact.ipp | 40 ++++++++++--------- .../impl/query/consensus/consensus_states.ipp | 5 ++- .../database/impl/query/properties_block.ipp | 4 +- include/bitcoin/database/query.hpp | 12 ++++-- .../bitcoin/database/tables/caches/pool.hpp | 10 +++-- include/bitcoin/database/tables/schema.hpp | 13 +++--- 6 files changed, 49 insertions(+), 35 deletions(-) diff --git a/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp b/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp index 0ae57a5ec..c9d0d328c 100644 --- a/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp +++ b/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp @@ -29,22 +29,21 @@ namespace database { TEMPLATE code CLASS::get_compact_links(tx_links& out, - const std::vector& short_ids, + const std::vector& short_ids, const system::siphash_key& key) const NOEXCEPT { using namespace system; - constexpr auto mask = unmask_right(48); out.assign(short_ids.size(), tx_link::terminal); if (short_ids.empty() || !store_.pool.enabled()) return error::success; // A short id duplicated within the block is ambiguous. std::vector ambiguous(short_ids.size()); - std::unordered_map positions{}; + std::unordered_map positions{}; positions.reserve(short_ids.size()); for (size_t index{}; index < short_ids.size(); ++index) { - const auto id = bit_and(short_ids.at(index), mask); + const auto id = bit_and(short_ids.at(index), chain::short_id::mask); if (const auto [it, added] = positions.emplace(id, index); !added) ambiguous.at(index) = ambiguous.at(it->second) = true; } @@ -78,12 +77,15 @@ code CLASS::get_compact_links(tx_links& out, // The columns are little-endian words, hashed in place across vector lanes. TEMPLATE bool CLASS::get_compact_matches(compact_matches& out, - const std::unordered_map& positions, + const std::unordered_map& positions, const system::siphash_key& key) const NOEXCEPT { using namespace system; - constexpr auto mask = unmask_right(48); - constexpr size_t chunk_rows = 16384; + using lane_t = schema::pool::witness_lane; + using link_t = table::pool::link::integer; + static_assert(is_same_type>); + constexpr auto chunk_rows = short_id_chunk_rows; const auto ptr0 = store_.pool.id0.get_memory(); const auto ptr1 = store_.pool.id1.get_memory(); const auto ptr2 = store_.pool.id2.get_memory(); @@ -94,7 +96,7 @@ bool CLASS::get_compact_matches(compact_matches& out, const auto rows = possible_narrow_cast(store_.pool.count().value); if constexpr (is_little_endian) { - const auto bytes = rows * sizeof(uint64_t); + const auto bytes = rows * sizeof(lane_t); if (is_lesser(ptr0.size(), bytes) || is_lesser(ptr1.size(), bytes) || is_lesser(ptr2.size(), bytes) || is_lesser(ptr3.size(), bytes)) return false; @@ -110,16 +112,16 @@ bool CLASS::get_compact_matches(compact_matches& out, [&](size_t chunk) NOEXCEPT { const auto first = chunk * chunk_rows; - std::vector ids(std::min(chunk_rows, rows - first)); + std::vector ids(std::min(chunk_rows, rows - first)); if constexpr (is_little_endian) { const auto column = [&](const memory& ptr) NOEXCEPT { - return std::span + const auto data = pointer_cast(ptr.data()); + return std::span { - std::next(pointer_cast(ptr.data()), - first), ids.size() + std::next(data, first), ids.size() }; }; @@ -135,8 +137,7 @@ bool CLASS::get_compact_matches(compact_matches& out, { const table::pool::link link { - possible_narrow_cast( - first + row) + possible_narrow_cast(first + row) }; if (!store_.pool.id0.get(ptr0, link, id0) || @@ -157,18 +158,19 @@ bool CLASS::get_compact_matches(compact_matches& out, auto& matches = found.at(chunk); for (size_t row{}; row < ids.size(); ++row) - if (const auto it = positions.find(bit_and(ids[row], mask)); - it != positions.end()) + { + const auto id = bit_and(ids[row], chain::short_id::mask); + if (const auto it = positions.find(id); it != positions.end()) matches.emplace_back(it->second, - possible_narrow_cast( - first + row)); + possible_narrow_cast(first + row)); + } }); if (fail.load(std::memory_order_relaxed)) return false; for (const auto& matches: found) - out.insert(out.end(), matches.begin(), matches.end()); + out.insert(out.end(), matches.cbegin(), matches.cend()); return true; } diff --git a/include/bitcoin/database/impl/query/consensus/consensus_states.ipp b/include/bitcoin/database/impl/query/consensus/consensus_states.ipp index 788889ae3..e9420335a 100644 --- a/include/bitcoin/database/impl/query/consensus/consensus_states.ipp +++ b/include/bitcoin/database/impl/query/consensus/consensus_states.ipp @@ -296,8 +296,9 @@ bool CLASS::set_pooled(const tx_link& link, const transaction& tx, const auto bip16 = ctx.is_enabled(chain::flags::bip16_rule); const auto bip141 = ctx.is_enabled(chain::flags::bip141_rule); const auto sigops = tx.signature_operations(bip16, bip141); - const auto words = from_little_endians(array_cast( - tx.get_hash(true))); + using lane_t = schema::pool::witness_lane; + const auto hash = tx.get_hash(true); + const auto words = from_little_endians(array_cast(hash)); // ======================================================================== const auto scope = get_transactor(); diff --git a/include/bitcoin/database/impl/query/properties_block.ipp b/include/bitcoin/database/impl/query/properties_block.ipp index 460a36027..846444852 100644 --- a/include/bitcoin/database/impl/query/properties_block.ipp +++ b/include/bitcoin/database/impl/query/properties_block.ipp @@ -290,7 +290,9 @@ hash_digest CLASS::get_wtxid(const tx_link& link) const NOEXCEPT !store_.pool.id3.get(row, id3)) return {}; - const auto words = to_little_endians(std_array + using lane_t = schema::pool::witness_lane; + using lanes_t = std_array; + const auto words = to_little_endians(lanes_t { id0.word, id1.word, id2.word, id3.word }); diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index ebbccffba..3a7ca9e92 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -58,6 +58,7 @@ class query using chain_state_cptr = system::chain::chain_state::cptr; using chain_context = system::chain::context; using prevout_spends = table::prevout::spends; + using short_id = system::chain::short_id::integer; using ec_compresseds = system::ec_compresseds; using ec_compressed = system::ec_compressed; using ec_signatures = system::ec_signatures; @@ -626,10 +627,10 @@ class query code validate_pooled(const header_link& link, const chain_context& ctx, uint64_t subsidy_interval, uint64_t initial_subsidy) NOEXCEPT; - /// Pooled tx links by compact block short id (low 48 bits of the siphash - /// of wtxid), terminal where not pooled or ambiguous. + /// Pooled tx links by compact block short id (masked siphash of wtxid), + /// terminal where not pooled or ambiguous. code get_compact_links(tx_links& out, - const std::vector& short_ids, + const std::vector& short_ids, const system::siphash_key& key) const NOEXCEPT; /// Header properties. @@ -957,7 +958,7 @@ class query /// Called by get_compact_links. using compact_matches = std::vector>; bool get_compact_matches(compact_matches& out, - const std::unordered_map& positions, + const std::unordered_map& positions, const system::siphash_key& key) const NOEXCEPT; /// Support set_strong and set_unstrong writers. @@ -1091,6 +1092,9 @@ class query // This value should never be read, but may be useful in debugging. static constexpr uint32_t unspecified_timestamp = max_uint32; + // Pool rows hashed by one task of the compact block short id scan. + static constexpr size_t short_id_chunk_rows = system::power2(14u); + // Chain objects. template static bool push_bool(std::vector& stack, diff --git a/include/bitcoin/database/tables/caches/pool.hpp b/include/bitcoin/database/tables/caches/pool.hpp index cbf4e4d58..bae2705db 100644 --- a/include/bitcoin/database/tables/caches/pool.hpp +++ b/include/bitcoin/database/tables/caches/pool.hpp @@ -37,10 +37,12 @@ struct pool_context static constexpr auto suffix = "context"_t; }; -/// One 64 bit word of the short id hash (common to the id columns). +/// One lane of the witness hash (common to the id columns). struct pool_word : public schema::pool_id0 { + using lane = schema::pool::witness_lane; + static constexpr link count() NOEXCEPT { return 1; @@ -48,21 +50,21 @@ struct pool_word inline bool from_data(reader& source) NOEXCEPT { - word = source.read_little_endian(); + word = source.read_little_endian(); BC_ASSERT(!source || source.get_read_position() == minrow); return source; } inline bool to_data(flipper& sink) const NOEXCEPT { - sink.write_little_endian(word); + sink.write_little_endian(word); BC_ASSERT(!sink || sink.get_write_position() == minrow); return sink; } inline bool operator==(const pool_word&) const NOEXCEPT = default; - uint64_t word{}; + lane word{}; }; /// Transaction identifier columns (words 0-3 of the short id hash). diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 5f128f888..98e776f2b 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -73,7 +73,7 @@ constexpr size_t prevout_ = 5; // ->prevout slab. constexpr size_t txs_ = 5; // ->txs slab. constexpr size_t tx = 4; // ->tx record. constexpr size_t block = 3; // ->header record. -constexpr size_t pool_ = 4; // ->pool record. +constexpr size_t pool_ = 4; // ->pool record. constexpr size_t filter_ = 5; // ->filter record. constexpr size_t doubles_ = 4; // doubles bucket (no actual keys). @@ -415,6 +415,8 @@ struct pool static constexpr size_t pk = schema::pool_; using link = linkage; using key = system::data_array; + static constexpr size_t witness_lanes = 4; + using witness_lane = unsigned_type; static constexpr size_t minsize = schema::flags + // context.flags schema::height_ + // context.height @@ -430,14 +432,15 @@ struct pool static_assert(minrow == 34u); static_assert(link::size == 4u); static_assert(cell == 4u); + static_assert(witness_lanes * sizeof(witness_lane) == schema::hash); bool operator==(const pool&) const NOEXCEPT = default; }; // pool columns (records aligned with the pool spine). -TABLE_COLUMN(pool_id0, sizeof(uint64_t)); -TABLE_COLUMN(pool_id1, sizeof(uint64_t)); -TABLE_COLUMN(pool_id2, sizeof(uint64_t)); -TABLE_COLUMN(pool_id3, sizeof(uint64_t)); +TABLE_COLUMN(pool_id0, sizeof(pool::witness_lane)); +TABLE_COLUMN(pool_id1, sizeof(pool::witness_lane)); +TABLE_COLUMN(pool_id2, sizeof(pool::witness_lane)); +TABLE_COLUMN(pool_id3, sizeof(pool::witness_lane)); static_assert(is_same_type); // record nomap