diff --git a/Makefile.am b/Makefile.am
index 87c4a2935..2bd2516dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -91,8 +91,8 @@ test_libbitcoin_database_test_SOURCES = \
test/query/confirm.cpp \
test/query/consensus.cpp \
test/query/context.cpp \
- test/query/estimate.cpp \
test/query/extent.cpp \
+ test/query/fees.cpp \
test/query/height.cpp \
test/query/initialize.cpp \
test/query/merkle.cpp \
@@ -182,8 +182,8 @@ include_bitcoin_database_impl_query_HEADERS = \
include/bitcoin/database/impl/query/confirm.ipp \
include/bitcoin/database/impl/query/consensus.ipp \
include/bitcoin/database/impl/query/context.ipp \
- include/bitcoin/database/impl/query/estimate.ipp \
include/bitcoin/database/impl/query/extent.ipp \
+ include/bitcoin/database/impl/query/fees.ipp \
include/bitcoin/database/impl/query/height.ipp \
include/bitcoin/database/impl/query/initialize.ipp \
include/bitcoin/database/impl/query/merkle.ipp \
diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt
index 73a8c2ebb..7ae2c9799 100644
--- a/builds/cmake/CMakeLists.txt
+++ b/builds/cmake/CMakeLists.txt
@@ -301,8 +301,8 @@ if (with-tests)
"../../test/query/confirm.cpp"
"../../test/query/consensus.cpp"
"../../test/query/context.cpp"
- "../../test/query/estimate.cpp"
"../../test/query/extent.cpp"
+ "../../test/query/fees.cpp"
"../../test/query/height.cpp"
"../../test/query/initialize.cpp"
"../../test/query/merkle.cpp"
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
index 144d20903..8eed6eb7f 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
@@ -152,8 +152,8 @@
-
+
$(IntDir)test_query_height.obj
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
index c42b1fc49..53f110246 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
@@ -123,10 +123,10 @@
src\query
-
+
src\query
-
+
src\query
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index 1fcd181f2..2b3d00b02 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -219,8 +219,8 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
index 8eb2eeef8..cfbdf8ea8 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -346,10 +346,10 @@
include\bitcoin\database\impl\query
-
+
include\bitcoin\database\impl\query
-
+
include\bitcoin\database\impl\query
diff --git a/include/bitcoin/database/impl/query/estimate.ipp b/include/bitcoin/database/impl/query/fees.ipp
similarity index 78%
rename from include/bitcoin/database/impl/query/estimate.ipp
rename to include/bitcoin/database/impl/query/fees.ipp
index aabea730b..b6edb132b 100644
--- a/include/bitcoin/database/impl/query/estimate.ipp
+++ b/include/bitcoin/database/impl/query/fees.ipp
@@ -29,12 +29,35 @@
namespace libbitcoin {
namespace database {
-
+
+// TODO: optimize.
+
+TEMPLATE
+uint64_t CLASS::get_tx_fee(const tx_link& link) const NOEXCEPT
+{
+ const auto tx = get_transaction(link, false);
+ if (!tx)
+ return max_uint64;
+
+ if (tx->is_coinbase())
+ return zero;
+
+ return populate_without_metadata(*tx) ? tx->fee() : max_uint64;
+}
+
+TEMPLATE
+uint64_t CLASS::get_block_fee(const header_link& link) const NOEXCEPT
+{
+ const auto block = get_block(link, false);
+ return block && populate_without_metadata(*block) ? block->fees() :
+ max_uint64;
+}
+
TEMPLATE
bool CLASS::get_tx_fees(fee_rate& out, const tx_link& link) const NOEXCEPT
{
const auto tx = get_transaction(link, false);
- if (!tx || !populate_without_metadata(*tx))
+ if (!tx || tx->is_coinbase() || !populate_without_metadata(*tx))
return false;
out.bytes = tx->virtual_size();
@@ -68,17 +91,17 @@ bool CLASS::get_block_fees(fee_rates& out,
TEMPLATE
bool CLASS::get_branch_fees(std::atomic_bool& cancel, fee_rate_sets& out,
- size_t top, size_t count) const NOEXCEPT
+ size_t start, size_t count) const NOEXCEPT
{
out.clear();
if (is_zero(count))
return true;
- if (top > get_top_confirmed())
+ if (system::is_add_overflow(start, sub1(count)))
return false;
- const auto start = top - sub1(count);
- if (system::is_subtract_overflow(top, sub1(count)))
+ const auto last = start + sub1(count);
+ if (last > get_top_confirmed())
return false;
out.resize(count);
diff --git a/include/bitcoin/database/impl/query/validate.ipp b/include/bitcoin/database/impl/query/validate.ipp
index dfa1be95d..65e26af68 100644
--- a/include/bitcoin/database/impl/query/validate.ipp
+++ b/include/bitcoin/database/impl/query/validate.ipp
@@ -140,26 +140,6 @@ code CLASS::get_block_state(const header_link& link) const NOEXCEPT
return to_block_code(valid.code);
}
-TEMPLATE
-uint64_t CLASS::get_block_fee(const header_link& link) const NOEXCEPT
-{
- // TODO: optimize.
- const auto block = get_block(link, false);
- return block && populate_without_metadata(*block) ? block->fees() :
- max_uint64;
-}
-
-TEMPLATE
-uint64_t CLASS::get_tx_fee(const tx_link& link) const NOEXCEPT
-{
- // TODO: optimize.
- const auto tx = get_transaction(link, false);
- if (is_coinbase(link))
- return {};
-
- return tx && populate_without_metadata(*tx) ? tx->fee() : max_uint64;
-}
-
TEMPLATE
inline bool CLASS::is_validated(const header_link& link) const NOEXCEPT
{
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index 865ffee4d..525a96bbc 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -398,17 +398,21 @@ class query
bool populate_without_metadata(const block& block) const NOEXCEPT;
bool populate_without_metadata(const transaction& tx) const NOEXCEPT;
- /// Services.
+ /// Fees.
/// -----------------------------------------------------------------------
+ /// Total fee value by tx or block.
uint64_t get_tx_fee(const tx_link& link) const NOEXCEPT;
uint64_t get_block_fee(const header_link& link) const NOEXCEPT;
- /// Gether fee rate tuples by tx, block or branch.
+ /// Fee rate tuples by tx, block or branch.
bool get_tx_fees(fee_rate& out, const tx_link& link) const NOEXCEPT;
bool get_block_fees(fee_rates& out, const header_link& link) const NOEXCEPT;
bool get_branch_fees(std::atomic_bool& cancel, fee_rate_sets& out,
- size_t top, size_t count) const NOEXCEPT;
+ size_t start, size_t count) const NOEXCEPT;
+
+ /// Merkle.
+ /// -----------------------------------------------------------------------
/// Merkle computations over the index of confirmed headers.
hash_digest get_merkle_root(size_t height) const NOEXCEPT;
@@ -786,13 +790,13 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
diff --git a/test/mocks/blocks.hpp b/test/mocks/blocks.hpp
index 442276db1..96b881bb6 100644
--- a/test/mocks/blocks.hpp
+++ b/test/mocks/blocks.hpp
@@ -522,6 +522,115 @@ const block block_spend_internal_2b
}
}
};
+const block block_missing_prevout_2b
+{
+ header
+ {
+ 0x31323334, // version
+ block1b.hash(), // previous_block_hash
+ system::one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx2b,
+ transaction
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ // missing prevout index.
+ point{ tx2b.hash(false), 0x01 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb0, // fee will be 0x01
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb2
+ }
+ }
+};
+const block block_valid_spend_internal_2b
+{
+ header
+ {
+ 0x31323334, // version
+ block1b.hash(), // previous_block_hash
+ system::one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx2b,
+ transaction
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ point{ tx2b.hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb0, // fee will be 0x01
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb2
+ },
+ transaction
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ },
+ input
+ {
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x01 },
+ script{ { { opcode::checkmultisig } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb2, // fee will be 0xb1 + 0xb1 - 0xb2 = 0xb0
+ script{ { { opcode::pick }, { opcode::roll }, { opcode::pick } } }
+ }
+ },
+ 0xb2
+ }
+ }
+};
} // namespace test
diff --git a/test/query/estimate.cpp b/test/query/estimate.cpp
deleted file mode 100644
index 74e5365fa..000000000
--- a/test/query/estimate.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS)
- *
- * This file is part of libbitcoin.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-#include "../test.hpp"
-#include "../mocks/blocks.hpp"
-#include "../mocks/chunk_store.hpp"
-
-BOOST_FIXTURE_TEST_SUITE(query_estimate_tests, test::directory_setup_fixture)
-
-// nop event handler.
-////const auto events_handler = [](auto, auto) {};
-
-BOOST_AUTO_TEST_CASE(query_estimate_test)
-{
- BOOST_REQUIRE(true);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/query/fees.cpp b/test/query/fees.cpp
new file mode 100644
index 000000000..bf8aae3de
--- /dev/null
+++ b/test/query/fees.cpp
@@ -0,0 +1,400 @@
+/**
+ * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include "../test.hpp"
+#include "../mocks/blocks.hpp"
+#include "../mocks/chunk_store.hpp"
+
+BOOST_FIXTURE_TEST_SUITE(query_fees_tests, test::directory_setup_fixture)
+
+// nop event handler.
+const auto events_handler = [](auto, auto) {};
+
+// get_tx_fee
+// get_tx_fees
+
+BOOST_AUTO_TEST_CASE(query_fees__get_tx_fee__invalid__max_uint64)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_tx_fee(42), max_uint64);
+
+ fee_rate rate{};
+ BOOST_CHECK(!query.get_tx_fees(rate, 42));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_tx_fee__missing_prevouts__max_uint64)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_tx_fee(0), 0u);
+ BOOST_CHECK(query.set(test::block1a, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2a, test::context, false, false));
+ BOOST_CHECK_EQUAL(query.get_tx_fee(3), max_uint64);
+
+ fee_rate rate{};
+ BOOST_CHECK(!query.get_tx_fees(rate, 3));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_tx_fee__genesis__zero)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_tx_fee(0), 0u);
+
+ fee_rate rate{};
+ BOOST_CHECK(!query.get_tx_fees(rate, 0u));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_tx_fee__valid_non_coinbase__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1b, test::context, false, false));
+ BOOST_CHECK(query.set(test::tx2b));
+ BOOST_CHECK_EQUAL(query.get_tx_fee(2), 0u);
+
+ fee_rate rate{};
+ BOOST_CHECK(query.get_tx_fees(rate, 2));
+ BOOST_CHECK_EQUAL(rate.bytes, test::tx2b.virtual_size());
+ BOOST_CHECK_EQUAL(rate.fee, test::tx2b.fee());
+}
+
+// get_block_fee
+// get_block_fees
+
+BOOST_AUTO_TEST_CASE(query_fees__get_block_fee__invalid__max_uint64)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_block_fee(24), max_uint64);
+
+ fee_rates rates{};
+ BOOST_CHECK(!query.get_block_fees(rates, 24));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_block_fee__block_missing_prevout__max_uint64)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1b, test::context, false, false));
+ BOOST_CHECK(query.set(test::block_missing_prevout_2b, test::context, false, false));
+ BOOST_CHECK_EQUAL(query.get_block_fee(2), max_uint64);
+
+ fee_rates rates{};
+ BOOST_CHECK(!query.get_block_fees(rates, 2));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_block_fee__coinbases__zero)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+ BOOST_CHECK_EQUAL(query.get_block_fee(2), zero);
+
+ fee_rates rates{};
+ BOOST_CHECK(query.get_block_fees(rates, 2));
+ BOOST_CHECK(rates.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_block_fee__valid__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1b, test::context, false, false));
+ BOOST_CHECK(query.set(test::block_valid_spend_internal_2b, test::context, false, false));
+ BOOST_CHECK_EQUAL(query.get_block_fee(2), 0xb1u);
+
+ // 3 txs - 1 coinbase = 2 rates.
+ fee_rates rates{};
+ BOOST_CHECK(query.get_block_fees(rates, 2));
+ BOOST_CHECK_EQUAL(rates.size(), 2u);
+ BOOST_CHECK_EQUAL(rates.at(0).bytes, 63u);
+ BOOST_CHECK_EQUAL(rates.at(0).fee, 0x01u);
+ BOOST_CHECK_EQUAL(rates.at(1).bytes, 107u);
+ BOOST_CHECK_EQUAL(rates.at(1).fee, 0xb0u);
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_block_fee__genesis__zero)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_block_fee(0), 0u);
+
+ fee_rates rates{};
+ BOOST_CHECK(query.get_block_fees(rates, 0));
+ BOOST_CHECK(rates.empty());
+}
+
+// get_branch_fees
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__zero__true_empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 0, 0));
+ BOOST_CHECK(rates_sets.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__genesis__true_one_empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 0, 1));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 1u);
+ BOOST_CHECK(rates_sets.front().empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__unconfirmed_blocks__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 0, 3));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__confirmed_overflow__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+ BOOST_CHECK(query.push_confirmed(1, true));
+ BOOST_CHECK(query.push_confirmed(2, true));
+
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 0, 4));
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 1, 3));
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 2, 2));
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 3, 1));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__zero_over_top__true)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ // A start of 1 is over the chain top, but requested count is zero (ok).
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 1, 0));
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__confirmed_empty_blocks__all_empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+ BOOST_CHECK(query.push_confirmed(1, true));
+ BOOST_CHECK(query.push_confirmed(2, true));
+
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 0, 3));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 3u);
+ BOOST_CHECK(rates_sets.at(0).empty());
+ BOOST_CHECK(rates_sets.at(1).empty());
+ BOOST_CHECK(rates_sets.at(2).empty());
+
+ rates_sets.clear();
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 1, 2));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 2u);
+ BOOST_CHECK(rates_sets.at(0).empty());
+ BOOST_CHECK(rates_sets.at(1).empty());
+
+ rates_sets.clear();
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 2, 1));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 1u);
+ BOOST_CHECK(rates_sets.at(0).empty());
+
+ rates_sets.clear();
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 3, 0));
+ BOOST_CHECK(rates_sets.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__confirmed_non_empty_blocks__all_expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1b, test::context, false, false));
+ BOOST_CHECK(query.set(test::block_valid_spend_internal_2b, test::context, false, false));
+ BOOST_CHECK(query.push_confirmed(1, true));
+ BOOST_CHECK(query.push_confirmed(2, true));
+
+ std::atomic_bool cancel{};
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 0, 3));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 3u);
+ BOOST_CHECK(rates_sets.at(0).empty());
+ BOOST_CHECK(rates_sets.at(1).empty());
+ BOOST_CHECK_EQUAL(rates_sets.at(2).size(), 2u);
+ BOOST_CHECK_EQUAL(rates_sets.at(2).at(0).bytes, 63u);
+ BOOST_CHECK_EQUAL(rates_sets.at(2).at(0).fee, 0x01u);
+ BOOST_CHECK_EQUAL(rates_sets.at(2).at(1).bytes, 107u);
+ BOOST_CHECK_EQUAL(rates_sets.at(2).at(1).fee, 0xb0u);
+
+ rates_sets.clear();
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 1, 2));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 2u);
+ BOOST_CHECK(rates_sets.at(0).empty());
+ BOOST_CHECK_EQUAL(rates_sets.at(1).size(), 2u);
+ BOOST_CHECK_EQUAL(rates_sets.at(1).at(0).bytes, 63u);
+ BOOST_CHECK_EQUAL(rates_sets.at(1).at(0).fee, 0x01u);
+ BOOST_CHECK_EQUAL(rates_sets.at(1).at(1).bytes, 107u);
+ BOOST_CHECK_EQUAL(rates_sets.at(1).at(1).fee, 0xb0u);
+
+ rates_sets.clear();
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 2, 1));
+ BOOST_CHECK_EQUAL(rates_sets.size(), 1u);
+ BOOST_CHECK_EQUAL(rates_sets.at(0).size(), 2u);
+ BOOST_CHECK_EQUAL(rates_sets.at(0).at(0).bytes, 63u);
+ BOOST_CHECK_EQUAL(rates_sets.at(0).at(0).fee, 0x01u);
+ BOOST_CHECK_EQUAL(rates_sets.at(0).at(1).bytes, 107u);
+ BOOST_CHECK_EQUAL(rates_sets.at(0).at(1).fee, 0xb0u);
+
+ rates_sets.clear();
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 3, 0));
+ BOOST_CHECK(rates_sets.empty());
+}
+
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__cancel_zero__true_empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ std::atomic_bool cancel{ true };
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(query.get_branch_fees(cancel, rates_sets, 0, 0));
+ BOOST_CHECK(rates_sets.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__cancel_genesis__false_empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ std::atomic_bool cancel{ true };
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 0, 1));
+ BOOST_CHECK(rates_sets.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_fees__get_branch_fees__cancel_three_blocks__false_empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+
+ std::atomic_bool cancel{ true };
+ fee_rate_sets rates_sets{};
+ BOOST_CHECK(!query.get_branch_fees(cancel, rates_sets, 0, 3));
+ BOOST_CHECK(rates_sets.empty());
+}
+
+BOOST_AUTO_TEST_SUITE_END()