From 1cc64189c09bd9486d09bb4763a32900cb0d5b78 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 11 Oct 2026 18:53:42 -0400 Subject: [PATCH] Add get_thresholds. --- .../bitcoin/database/impl/query/sequences.ipp | 29 ++++++ include/bitcoin/database/query.hpp | 3 + include/bitcoin/database/types/type.hpp | 2 + test/query/sequences.cpp | 97 +++++++++++++++++++ 4 files changed, 131 insertions(+) diff --git a/include/bitcoin/database/impl/query/sequences.ipp b/include/bitcoin/database/impl/query/sequences.ipp index 6a4e9aa4f..55648b318 100644 --- a/include/bitcoin/database/impl/query/sequences.ipp +++ b/include/bitcoin/database/impl/query/sequences.ipp @@ -160,6 +160,35 @@ bool CLASS::get_ancestry(header_links& ancestry, const header_link& descendant, return true; } +TEMPLATE +bool CLASS::get_thresholds(thresholds& out, + const header_link& top) const NOEXCEPT +{ + header_links ancestry{}; + if (!get_ancestry(ancestry, top, max_size_t)) + return false; + + out.clear(); + auto floor = tx_link::terminal; + auto height = ancestry.size(); + for (const auto& link: ancestry) + { + --height; + const auto coinbase = to_coinbase(link); + if (coinbase.is_terminal()) + return false; + + if (coinbase.value < floor) + { + floor = coinbase.value; + out.emplace_back(height, floor); + } + } + + std::reverse(out.begin(), out.end()); + return true; +} + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 186daee54..981c73dbb 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -844,6 +844,9 @@ class query bool get_ancestry(header_links& ancestry, const header_link& descendant, size_t count) const NOEXCEPT; + /// Ascending height of lowest tx link for block at or above each height. + bool get_thresholds(thresholds& out, const header_link& top) const NOEXCEPT; + /// Address. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/types/type.hpp b/include/bitcoin/database/types/type.hpp index a3886928f..d5d21e630 100644 --- a/include/bitcoin/database/types/type.hpp +++ b/include/bitcoin/database/types/type.hpp @@ -87,6 +87,8 @@ using outpoints = std::vector; using heights = std::vector; using sizes = std::pair; using counts = std::pair; +using threshold = std::pair; +using thresholds = std::vector; } // namespace database } // namespace libbitcoin diff --git a/test/query/sequences.cpp b/test/query/sequences.cpp index be574c0fb..cbc9da455 100644 --- a/test/query/sequences.cpp +++ b/test/query/sequences.cpp @@ -22,4 +22,101 @@ BOOST_FIXTURE_TEST_SUITE(query_sequences_tests, test::directory_setup_fixture) +// get_thresholds +// ---------------------------------------------------------------------------- + +BOOST_AUTO_TEST_CASE(query_sequences__get_thresholds__terminal__false) +{ + database::settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + thresholds out{}; + BOOST_REQUIRE(!query.get_thresholds(out, header_link::terminal)); +} + +BOOST_AUTO_TEST_CASE(query_sequences__get_thresholds__unarchived__false) +{ + database::settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1.header(), database::context{ 0, 1, 0 }, {}, false)); + + thresholds out{}; + BOOST_REQUIRE(!query.get_thresholds(out, 1)); +} + +BOOST_AUTO_TEST_CASE(query_sequences__get_thresholds__genesis__one) +{ + database::settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + + thresholds out{}; + BOOST_REQUIRE(query.get_thresholds(out, 0)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE_EQUAL(out.front().first, 0u); + BOOST_REQUIRE_EQUAL(out.front().second, 0u); +} + +BOOST_AUTO_TEST_CASE(query_sequences__get_thresholds__ordered__every_height) +{ + database::settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, database::context{ 0, 1, 0 }, {}, false, false)); + BOOST_REQUIRE(query.set(test::block2, database::context{ 0, 2, 0 }, {}, false, false)); + BOOST_REQUIRE(query.set(test::block3, database::context{ 0, 3, 0 }, {}, false, false)); + + thresholds out{}; + BOOST_REQUIRE(query.get_thresholds(out, 3)); + BOOST_REQUIRE_EQUAL(out.size(), 4u); + BOOST_REQUIRE_EQUAL(out.at(0).first, 0u); + BOOST_REQUIRE_EQUAL(out.at(1).first, 1u); + BOOST_REQUIRE_EQUAL(out.at(2).first, 2u); + BOOST_REQUIRE_EQUAL(out.at(3).first, 3u); + BOOST_REQUIRE_EQUAL(out.at(0).second, query.to_coinbase(0)); + BOOST_REQUIRE_EQUAL(out.at(1).second, query.to_coinbase(1)); + BOOST_REQUIRE_EQUAL(out.at(2).second, query.to_coinbase(2)); + BOOST_REQUIRE_EQUAL(out.at(3).second, query.to_coinbase(3)); +} + +BOOST_AUTO_TEST_CASE(query_sequences__get_thresholds__disordered__lowest_above) +{ + database::settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1.header(), database::context{ 0, 1, 0 }, {}, false)); + BOOST_REQUIRE(query.set(test::block2.header(), database::context{ 0, 2, 0 }, {}, false)); + BOOST_REQUIRE(query.set(test::block3.header(), database::context{ 0, 3, 0 }, {}, false)); + BOOST_REQUIRE(query.set(test::block3, false, false)); + BOOST_REQUIRE(query.set(test::block1, false, false)); + BOOST_REQUIRE(query.set(test::block2, false, false)); + BOOST_REQUIRE_LT(query.to_coinbase(3), query.to_coinbase(1)); + BOOST_REQUIRE_LT(query.to_coinbase(3), query.to_coinbase(2)); + + thresholds out{}; + BOOST_REQUIRE(query.get_thresholds(out, 3)); + BOOST_REQUIRE_EQUAL(out.size(), 2u); + BOOST_REQUIRE_EQUAL(out.front().first, 0u); + BOOST_REQUIRE_EQUAL(out.front().second, 0u); + BOOST_REQUIRE_EQUAL(out.back().first, 3u); + BOOST_REQUIRE_EQUAL(out.back().second, query.to_coinbase(3)); +} + BOOST_AUTO_TEST_SUITE_END()