From 56517a2793e65bae407388649e990a0815c95884 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 17 Feb 2026 11:49:25 -0500 Subject: [PATCH 1/6] Add public get_merkle_root(). --- include/bitcoin/database/impl/query/services.ipp | 10 ++++++++++ include/bitcoin/database/query.hpp | 1 + test/query/services.cpp | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/bitcoin/database/impl/query/services.ipp b/include/bitcoin/database/impl/query/services.ipp index 657c3ccf..1eb216cb 100644 --- a/include/bitcoin/database/impl/query/services.ipp +++ b/include/bitcoin/database/impl/query/services.ipp @@ -162,6 +162,16 @@ code CLASS::get_merkle_root_and_proof(hash_digest& root, hashes& proof, return {}; } +TEMPLATE +hash_digest CLASS::get_merkle_root(size_t height) const NOEXCEPT +{ + hashes tree{}; + if (const auto ec = get_merkle_tree(tree, height)) + return {}; + + return system::merkle_root(std::move(tree)); +} + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 59d1a4bf..08fe8583 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -402,6 +402,7 @@ class query /// Services. /// ----------------------------------------------------------------------- + hash_digest get_merkle_root(size_t height) const NOEXCEPT; code get_merkle_root_and_proof(hash_digest& root, hashes& proof, size_t target, size_t checkpoint) const NOEXCEPT; diff --git a/test/query/services.cpp b/test/query/services.cpp index 0f85e974..5266a448 100644 --- a/test/query/services.cpp +++ b/test/query/services.cpp @@ -384,6 +384,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__one_full_interval__expecte } // get_merkle_root_and_proof +// get_merkle_proof BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_equals_waypoint__success) { @@ -415,6 +416,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_equals_wa BOOST_REQUIRE_EQUAL(proof.size(), 2u); BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() })); BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() })); + BOOST_REQUIRE_EQUAL(root, query.get_merkle_root(3)); BOOST_REQUIRE_EQUAL(root, expected); } @@ -445,10 +447,11 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_less_than test::block3.hash() }); BOOST_REQUIRE(!query.get_merkle_root_and_proof(root, proof, 1u, 3u)); - BOOST_REQUIRE_EQUAL(root, expected); BOOST_REQUIRE_EQUAL(proof.size(), 2u); BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::genesis.hash() })); BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::block2.hash(), test::block3.hash() })); + BOOST_REQUIRE_EQUAL(root, query.get_merkle_root(3)); + BOOST_REQUIRE_EQUAL(root, expected); } BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_greater_than_waypoint__error_merkle_arguments) @@ -464,6 +467,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_greater_t hashes proof{}; hash_digest root{}; BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 5u, 3u), error::merkle_arguments); + BOOST_REQUIRE_EQUAL(query.get_merkle_root(3), system::null_hash); } BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__waypoint_beyond_top__error_merkle_not_found) @@ -479,6 +483,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__waypoint_beyond_ hashes proof{}; hash_digest root{}; BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 0u, 100u), error::merkle_not_found); + BOOST_REQUIRE_EQUAL(query.get_merkle_root(100), system::null_hash); } BOOST_AUTO_TEST_SUITE_END() From ae4afaa206a10a2e229e32b99bd4e4e43061ec60 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 17 Feb 2026 12:06:36 -0500 Subject: [PATCH 2/6] Rename services.ipp to merkle.ipp. --- Makefile.am | 4 ++-- builds/cmake/CMakeLists.txt | 2 +- .../libbitcoin-database-test.vcxproj | 2 +- .../libbitcoin-database-test.vcxproj.filters | 6 +++--- .../vs2022/libbitcoin-database/libbitcoin-database.vcxproj | 2 +- .../libbitcoin-database/libbitcoin-database.vcxproj.filters | 6 +++--- .../database/impl/query/{services.ipp => merkle.ipp} | 4 ++-- include/bitcoin/database/query.hpp | 2 +- test/query/{services.cpp => merkle.cpp} | 0 9 files changed, 14 insertions(+), 14 deletions(-) rename include/bitcoin/database/impl/query/{services.ipp => merkle.ipp} (98%) rename test/query/{services.cpp => merkle.cpp} (100%) diff --git a/Makefile.am b/Makefile.am index 74c63bc3..46487869 100644 --- a/Makefile.am +++ b/Makefile.am @@ -94,10 +94,10 @@ test_libbitcoin_database_test_SOURCES = \ test/query/extent.cpp \ test/query/height.cpp \ test/query/initialize.cpp \ + test/query/merkle.cpp \ test/query/network.cpp \ test/query/objects.cpp \ test/query/optional.cpp \ - test/query/services.cpp \ test/query/translate.cpp \ test/query/validate.cpp \ test/tables/archives/header.cpp \ @@ -184,11 +184,11 @@ include_bitcoin_database_impl_query_HEADERS = \ include/bitcoin/database/impl/query/extent.ipp \ include/bitcoin/database/impl/query/height.ipp \ include/bitcoin/database/impl/query/initialize.ipp \ + include/bitcoin/database/impl/query/merkle.ipp \ include/bitcoin/database/impl/query/network.ipp \ include/bitcoin/database/impl/query/objects.ipp \ include/bitcoin/database/impl/query/optional.ipp \ include/bitcoin/database/impl/query/query.ipp \ - include/bitcoin/database/impl/query/services.ipp \ include/bitcoin/database/impl/query/translate.ipp \ include/bitcoin/database/impl/query/validate.ipp diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 84c6c588..5b9065bf 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -304,10 +304,10 @@ if (with-tests) "../../test/query/extent.cpp" "../../test/query/height.cpp" "../../test/query/initialize.cpp" + "../../test/query/merkle.cpp" "../../test/query/network.cpp" "../../test/query/objects.cpp" "../../test/query/optional.cpp" - "../../test/query/services.cpp" "../../test/query/translate.cpp" "../../test/query/validate.cpp" "../../test/tables/archives/header.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 2f3d7180..e8224fd2 100644 --- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj @@ -157,10 +157,10 @@ $(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 444136b7..6006bd19 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 @@ -132,6 +132,9 @@ src\query + + src\query + src\query @@ -141,9 +144,6 @@ 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 9f0399b7..87026a63 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj @@ -222,11 +222,11 @@ + - diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters index f4c97428..0de8ccf7 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -355,6 +355,9 @@ include\bitcoin\database\impl\query + + include\bitcoin\database\impl\query + include\bitcoin\database\impl\query @@ -367,9 +370,6 @@ include\bitcoin\database\impl\query - - include\bitcoin\database\impl\query - include\bitcoin\database\impl\query diff --git a/include/bitcoin/database/impl/query/services.ipp b/include/bitcoin/database/impl/query/merkle.ipp similarity index 98% rename from include/bitcoin/database/impl/query/services.ipp rename to include/bitcoin/database/impl/query/merkle.ipp index 1eb216cb..b183b58f 100644 --- a/include/bitcoin/database/impl/query/services.ipp +++ b/include/bitcoin/database/impl/query/merkle.ipp @@ -16,8 +16,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#ifndef LIBBITCOIN_DATABASE_QUERY_SERVICES_IPP -#define LIBBITCOIN_DATABASE_QUERY_SERVICES_IPP +#ifndef LIBBITCOIN_DATABASE_QUERY_MERKLE_IPP +#define LIBBITCOIN_DATABASE_QUERY_MERKLE_IPP #include #include diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 08fe8583..637ff309 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -784,7 +784,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) #include #include #include -#include +#include #include #include diff --git a/test/query/services.cpp b/test/query/merkle.cpp similarity index 100% rename from test/query/services.cpp rename to test/query/merkle.cpp From a5f61eb5be43175f7df46c6c7ef85d463a3b052c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 17 Feb 2026 12:31:55 -0500 Subject: [PATCH 3/6] Stub in fee est sources, remove redundant includes. --- Makefile.am | 2 + builds/cmake/CMakeLists.txt | 1 + .../libbitcoin-database-test.vcxproj | 1 + .../libbitcoin-database-test.vcxproj.filters | 3 ++ .../libbitcoin-database.vcxproj | 1 + .../libbitcoin-database.vcxproj.filters | 3 ++ include/bitcoin/database/file/rotator.hpp | 1 - include/bitcoin/database/file/utilities.hpp | 1 - .../bitcoin/database/impl/memory/accessor.ipp | 1 - .../database/impl/primitives/arrayhead.ipp | 1 - .../database/impl/primitives/hashhead.ipp | 1 - .../database/impl/primitives/hashmap.ipp | 1 - .../database/impl/primitives/iterator.ipp | 1 - .../database/impl/primitives/linkage.ipp | 1 - .../database/impl/primitives/manager.ipp | 1 - .../database/impl/primitives/nomap.ipp | 1 - .../database/impl/query/archive_read.ipp | 1 - .../database/impl/query/archive_write.ipp | 1 - .../bitcoin/database/impl/query/confirm.ipp | 1 - .../bitcoin/database/impl/query/consensus.ipp | 1 - .../bitcoin/database/impl/query/context.ipp | 1 - .../bitcoin/database/impl/query/estimate.ipp | 33 ++++++++++++++ .../bitcoin/database/impl/query/extent.ipp | 1 - .../bitcoin/database/impl/query/height.ipp | 1 - .../database/impl/query/initialize.ipp | 1 - .../bitcoin/database/impl/query/merkle.ipp | 1 - .../bitcoin/database/impl/query/network.ipp | 1 - .../bitcoin/database/impl/query/objects.ipp | 1 - .../bitcoin/database/impl/query/optional.ipp | 1 - include/bitcoin/database/impl/query/query.ipp | 1 - .../bitcoin/database/impl/query/translate.ipp | 1 - .../bitcoin/database/impl/query/validate.ipp | 1 - include/bitcoin/database/locks/file_lock.hpp | 1 - include/bitcoin/database/locks/flush_lock.hpp | 1 - .../database/locks/interprocess_lock.hpp | 1 - include/bitcoin/database/memory/accessor.hpp | 1 - include/bitcoin/database/memory/finalizer.hpp | 1 - .../database/memory/interfaces/memory.hpp | 1 - .../database/memory/interfaces/storage.hpp | 2 - include/bitcoin/database/memory/map.hpp | 1 - include/bitcoin/database/memory/reader.hpp | 1 - include/bitcoin/database/memory/streamers.hpp | 1 - .../bitcoin/database/primitives/arrayhead.hpp | 1 - .../bitcoin/database/primitives/arraymap.hpp | 1 - .../bitcoin/database/primitives/hashhead.hpp | 1 - .../bitcoin/database/primitives/hashmap.hpp | 1 - .../bitcoin/database/primitives/iterator.hpp | 1 - .../bitcoin/database/primitives/linkage.hpp | 1 - .../bitcoin/database/primitives/manager.hpp | 1 - include/bitcoin/database/primitives/nomap.hpp | 1 - include/bitcoin/database/query.hpp | 2 +- include/bitcoin/database/settings.hpp | 1 - .../database/tables/archives/header.hpp | 1 - .../database/tables/archives/input.hpp | 1 - .../bitcoin/database/tables/archives/ins.hpp | 1 - .../database/tables/archives/output.hpp | 1 - .../bitcoin/database/tables/archives/outs.hpp | 1 - .../database/tables/archives/point.hpp | 1 - .../database/tables/archives/transaction.hpp | 1 - .../bitcoin/database/tables/archives/txs.hpp | 1 - .../bitcoin/database/tables/association.hpp | 1 - .../bitcoin/database/tables/associations.hpp | 1 - .../database/tables/caches/duplicate.hpp | 1 - .../database/tables/caches/prevout.hpp | 1 - .../database/tables/caches/validated_bk.hpp | 1 - .../database/tables/caches/validated_tx.hpp | 1 - include/bitcoin/database/tables/context.hpp | 1 - .../database/tables/indexes/height.hpp | 1 - .../database/tables/indexes/strong_tx.hpp | 1 - include/bitcoin/database/tables/names.hpp | 1 - .../database/tables/optionals/address.hpp | 1 - .../database/tables/optionals/filter_bk.hpp | 1 - .../database/tables/optionals/filter_tx.hpp | 1 - include/bitcoin/database/tables/point_set.hpp | 1 - include/bitcoin/database/tables/schema.hpp | 1 - include/bitcoin/database/tables/states.hpp | 1 - src/error.cpp | 2 +- src/file/rotator.cpp | 1 - src/file/utilities.cpp | 1 - src/locks/file_lock.cpp | 1 - src/locks/flush_lock.cpp | 1 - src/locks/interprocess_lock.cpp | 1 - src/memory/map.cpp | 1 - src/memory/utilities.cpp | 1 - src/settings.cpp | 1 - test/query/estimate.cpp | 33 ++++++++++++++ test/query/merkle.cpp | 44 +++++++++---------- test/test.hpp | 1 - tools/initchain/initchain.cpp | 1 - 89 files changed, 101 insertions(+), 103 deletions(-) create mode 100644 include/bitcoin/database/impl/query/estimate.ipp create mode 100644 test/query/estimate.cpp diff --git a/Makefile.am b/Makefile.am index 46487869..87c4a293 100644 --- a/Makefile.am +++ b/Makefile.am @@ -91,6 +91,7 @@ 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/height.cpp \ test/query/initialize.cpp \ @@ -181,6 +182,7 @@ 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/height.ipp \ include/bitcoin/database/impl/query/initialize.ipp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 5b9065bf..73a8c2eb 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -301,6 +301,7 @@ 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/height.cpp" "../../test/query/initialize.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 e8224fd2..144d2090 100644 --- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj @@ -152,6 +152,7 @@ + $(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 6006bd19..c42b1fc4 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,6 +123,9 @@ 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 87026a63..1fcd181f 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj @@ -219,6 +219,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters index 0de8ccf7..8eb2eeef 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -346,6 +346,9 @@ include\bitcoin\database\impl\query + + include\bitcoin\database\impl\query + include\bitcoin\database\impl\query diff --git a/include/bitcoin/database/file/rotator.hpp b/include/bitcoin/database/file/rotator.hpp index 31262b4d..9c4484f4 100644 --- a/include/bitcoin/database/file/rotator.hpp +++ b/include/bitcoin/database/file/rotator.hpp @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/include/bitcoin/database/file/utilities.hpp b/include/bitcoin/database/file/utilities.hpp index 2c823753..9dd4a2a9 100644 --- a/include/bitcoin/database/file/utilities.hpp +++ b/include/bitcoin/database/file/utilities.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_FILE_UTILITIES_HPP #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/memory/accessor.ipp b/include/bitcoin/database/impl/memory/accessor.ipp index a1951123..e257c628 100644 --- a/include/bitcoin/database/impl/memory/accessor.ipp +++ b/include/bitcoin/database/impl/memory/accessor.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_MEMORY_ACCESSOR_IPP ////#include -#include #include // Zero/negative size is allowed (automatically handled by bc streams). diff --git a/include/bitcoin/database/impl/primitives/arrayhead.ipp b/include/bitcoin/database/impl/primitives/arrayhead.ipp index ddfedb97..61157f2c 100644 --- a/include/bitcoin/database/impl/primitives/arrayhead.ipp +++ b/include/bitcoin/database/impl/primitives/arrayhead.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYHEAD_IPP #include -#include #include // TODO: xcode clang++16 does not support C++20 std::atomic_ref. diff --git a/include/bitcoin/database/impl/primitives/hashhead.ipp b/include/bitcoin/database/impl/primitives/hashhead.ipp index 61afb972..7e2d874a 100644 --- a/include/bitcoin/database/impl/primitives/hashhead.ipp +++ b/include/bitcoin/database/impl/primitives/hashhead.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_PRIMITIVES_HASHHEAD_IPP #include -#include #include // Heads are not subject to resize/remap and therefore do not require memory diff --git a/include/bitcoin/database/impl/primitives/hashmap.ipp b/include/bitcoin/database/impl/primitives/hashmap.ipp index 6d3d0b78..f9116eb2 100644 --- a/include/bitcoin/database/impl/primitives/hashmap.ipp +++ b/include/bitcoin/database/impl/primitives/hashmap.ipp @@ -21,7 +21,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/primitives/iterator.ipp b/include/bitcoin/database/impl/primitives/iterator.ipp index 1c012f3e..f1d15564 100644 --- a/include/bitcoin/database/impl/primitives/iterator.ipp +++ b/include/bitcoin/database/impl/primitives/iterator.ipp @@ -22,7 +22,6 @@ #include #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/primitives/linkage.ipp b/include/bitcoin/database/impl/primitives/linkage.ipp index 6dc26adb..00e8c9a4 100644 --- a/include/bitcoin/database/impl/primitives/linkage.ipp +++ b/include/bitcoin/database/impl/primitives/linkage.ipp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_LINKAGE_IPP #define LIBBITCOIN_DATABASE_PRIMITIVES_LINKAGE_IPP -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/primitives/manager.ipp b/include/bitcoin/database/impl/primitives/manager.ipp index f9d748b1..ddc920d6 100644 --- a/include/bitcoin/database/impl/primitives/manager.ipp +++ b/include/bitcoin/database/impl/primitives/manager.ipp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_MANAGER_IPP #define LIBBITCOIN_DATABASE_PRIMITIVES_MANAGER_IPP -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/primitives/nomap.ipp b/include/bitcoin/database/impl/primitives/nomap.ipp index a5d836b8..346a5dd5 100644 --- a/include/bitcoin/database/impl/primitives/nomap.ipp +++ b/include/bitcoin/database/impl/primitives/nomap.ipp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_IPP #define LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_IPP -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/archive_read.ipp b/include/bitcoin/database/impl/query/archive_read.ipp index 9f9cfefb..969a51b0 100644 --- a/include/bitcoin/database/impl/query/archive_read.ipp +++ b/include/bitcoin/database/impl/query/archive_read.ipp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/include/bitcoin/database/impl/query/archive_write.ipp b/include/bitcoin/database/impl/query/archive_write.ipp index 7886372c..a35be377 100644 --- a/include/bitcoin/database/impl/query/archive_write.ipp +++ b/include/bitcoin/database/impl/query/archive_write.ipp @@ -22,7 +22,6 @@ #include #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/confirm.ipp b/include/bitcoin/database/impl/query/confirm.ipp index 5cbd0fc5..4dd7f932 100644 --- a/include/bitcoin/database/impl/query/confirm.ipp +++ b/include/bitcoin/database/impl/query/confirm.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_QUERY_CONFIRM_IPP #include -#include #include #include diff --git a/include/bitcoin/database/impl/query/consensus.ipp b/include/bitcoin/database/impl/query/consensus.ipp index b9929b18..2eb4ec3f 100644 --- a/include/bitcoin/database/impl/query/consensus.ipp +++ b/include/bitcoin/database/impl/query/consensus.ipp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/include/bitcoin/database/impl/query/context.ipp b/include/bitcoin/database/impl/query/context.ipp index c921113d..5756daa4 100644 --- a/include/bitcoin/database/impl/query/context.ipp +++ b/include/bitcoin/database/impl/query/context.ipp @@ -21,7 +21,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/estimate.ipp b/include/bitcoin/database/impl/query/estimate.ipp new file mode 100644 index 00000000..40218bae --- /dev/null +++ b/include/bitcoin/database/impl/query/estimate.ipp @@ -0,0 +1,33 @@ +/** + * 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 . + */ +#ifndef LIBBITCOIN_DATABASE_QUERY_ESTIMATE_IPP +#define LIBBITCOIN_DATABASE_QUERY_ESTIMATE_IPP + +#include + +namespace libbitcoin { +namespace database { + +// fee estimate +// ---------------------------------------------------------------------------- + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/query/extent.ipp b/include/bitcoin/database/impl/query/extent.ipp index 9e543b85..838db316 100644 --- a/include/bitcoin/database/impl/query/extent.ipp +++ b/include/bitcoin/database/impl/query/extent.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_QUERY_EXTENT_IPP #include -#include #include #define DEFINE_SIZES(name) \ diff --git a/include/bitcoin/database/impl/query/height.ipp b/include/bitcoin/database/impl/query/height.ipp index 7e77bf5c..5e6fbf8d 100644 --- a/include/bitcoin/database/impl/query/height.ipp +++ b/include/bitcoin/database/impl/query/height.ipp @@ -21,7 +21,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/initialize.ipp b/include/bitcoin/database/impl/query/initialize.ipp index 762183b9..e6f23db1 100644 --- a/include/bitcoin/database/impl/query/initialize.ipp +++ b/include/bitcoin/database/impl/query/initialize.ipp @@ -21,7 +21,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/merkle.ipp b/include/bitcoin/database/impl/query/merkle.ipp index b183b58f..da7e295c 100644 --- a/include/bitcoin/database/impl/query/merkle.ipp +++ b/include/bitcoin/database/impl/query/merkle.ipp @@ -22,7 +22,6 @@ #include #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/network.ipp b/include/bitcoin/database/impl/query/network.ipp index e3c5ccca..cf347b02 100644 --- a/include/bitcoin/database/impl/query/network.ipp +++ b/include/bitcoin/database/impl/query/network.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_QUERY_NETWORK_IPP #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/objects.ipp b/include/bitcoin/database/impl/query/objects.ipp index 72b7e0de..d057dc82 100644 --- a/include/bitcoin/database/impl/query/objects.ipp +++ b/include/bitcoin/database/impl/query/objects.ipp @@ -21,7 +21,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/optional.ipp b/include/bitcoin/database/impl/query/optional.ipp index 1092baef..dd2b3b01 100644 --- a/include/bitcoin/database/impl/query/optional.ipp +++ b/include/bitcoin/database/impl/query/optional.ipp @@ -23,7 +23,6 @@ #include #include #include -#include #include // TODO: address table could use point keys to compress the multimap. diff --git a/include/bitcoin/database/impl/query/query.ipp b/include/bitcoin/database/impl/query/query.ipp index 87dae87d..3d35b6ad 100644 --- a/include/bitcoin/database/impl/query/query.ipp +++ b/include/bitcoin/database/impl/query/query.ipp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_QUERY_QUERY_IPP #define LIBBITCOIN_DATABASE_QUERY_QUERY_IPP -#include #include // Error handling: diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp index d59fbec1..bcffdb39 100644 --- a/include/bitcoin/database/impl/query/translate.ipp +++ b/include/bitcoin/database/impl/query/translate.ipp @@ -24,7 +24,6 @@ #include #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/impl/query/validate.ipp b/include/bitcoin/database/impl/query/validate.ipp index 4b78ff86..dcd4aa9e 100644 --- a/include/bitcoin/database/impl/query/validate.ipp +++ b/include/bitcoin/database/impl/query/validate.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_QUERY_VALIDATE_IPP #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/locks/file_lock.hpp b/include/bitcoin/database/locks/file_lock.hpp index 0dde1274..8ee2f1e5 100644 --- a/include/bitcoin/database/locks/file_lock.hpp +++ b/include/bitcoin/database/locks/file_lock.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_LOCKS_FILE_LOCK_HPP #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/locks/flush_lock.hpp b/include/bitcoin/database/locks/flush_lock.hpp index b38f675a..ba2e05b2 100644 --- a/include/bitcoin/database/locks/flush_lock.hpp +++ b/include/bitcoin/database/locks/flush_lock.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_LOCKS_FLUSH_LOCK_HPP #include -#include #include #include diff --git a/include/bitcoin/database/locks/interprocess_lock.hpp b/include/bitcoin/database/locks/interprocess_lock.hpp index 7b0ac880..6fdbcda3 100644 --- a/include/bitcoin/database/locks/interprocess_lock.hpp +++ b/include/bitcoin/database/locks/interprocess_lock.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_LOCKS_INTERPROCESS_LOCK_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/memory/accessor.hpp b/include/bitcoin/database/memory/accessor.hpp index f7984c4d..2b0e7461 100644 --- a/include/bitcoin/database/memory/accessor.hpp +++ b/include/bitcoin/database/memory/accessor.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_MEMORY_ACCESSOR_HPP #include -#include #include #include diff --git a/include/bitcoin/database/memory/finalizer.hpp b/include/bitcoin/database/memory/finalizer.hpp index 613c97e8..42e26106 100644 --- a/include/bitcoin/database/memory/finalizer.hpp +++ b/include/bitcoin/database/memory/finalizer.hpp @@ -21,7 +21,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/memory/interfaces/memory.hpp b/include/bitcoin/database/memory/interfaces/memory.hpp index 26ad67e7..db371cee 100644 --- a/include/bitcoin/database/memory/interfaces/memory.hpp +++ b/include/bitcoin/database/memory/interfaces/memory.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_MEMORY_INTERFACES_MEMORY_HPP #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/memory/interfaces/storage.hpp b/include/bitcoin/database/memory/interfaces/storage.hpp index 09edee74..6b1d9367 100644 --- a/include/bitcoin/database/memory/interfaces/storage.hpp +++ b/include/bitcoin/database/memory/interfaces/storage.hpp @@ -20,8 +20,6 @@ #define LIBBITCOIN_DATABASE_MEMORY_INTERFACES_STORAGE_HPP #include -#include -#include #include #include diff --git a/include/bitcoin/database/memory/map.hpp b/include/bitcoin/database/memory/map.hpp index 41d63d75..ab3e410e 100644 --- a/include/bitcoin/database/memory/map.hpp +++ b/include/bitcoin/database/memory/map.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/include/bitcoin/database/memory/reader.hpp b/include/bitcoin/database/memory/reader.hpp index 6a62d183..2424528c 100644 --- a/include/bitcoin/database/memory/reader.hpp +++ b/include/bitcoin/database/memory/reader.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_MEMORY_READER_HPP #define LIBBITCOIN_DATABASE_MEMORY_READER_HPP -#include #include #include diff --git a/include/bitcoin/database/memory/streamers.hpp b/include/bitcoin/database/memory/streamers.hpp index 068a557a..b485daa2 100644 --- a/include/bitcoin/database/memory/streamers.hpp +++ b/include/bitcoin/database/memory/streamers.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_MEMORY_STREAMERS_HPP #define LIBBITCOIN_DATABASE_MEMORY_STREAMERS_HPP -#include #include #include diff --git a/include/bitcoin/database/primitives/arrayhead.hpp b/include/bitcoin/database/primitives/arrayhead.hpp index f14e075c..df5846bb 100644 --- a/include/bitcoin/database/primitives/arrayhead.hpp +++ b/include/bitcoin/database/primitives/arrayhead.hpp @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/include/bitcoin/database/primitives/arraymap.hpp b/include/bitcoin/database/primitives/arraymap.hpp index 32d97704..ebb08c07 100644 --- a/include/bitcoin/database/primitives/arraymap.hpp +++ b/include/bitcoin/database/primitives/arraymap.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYMAP_HPP #define LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYMAP_HPP -#include #include #include #include diff --git a/include/bitcoin/database/primitives/hashhead.hpp b/include/bitcoin/database/primitives/hashhead.hpp index b0ae7dd5..35fb0a0f 100644 --- a/include/bitcoin/database/primitives/hashhead.hpp +++ b/include/bitcoin/database/primitives/hashhead.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/include/bitcoin/database/primitives/hashmap.hpp b/include/bitcoin/database/primitives/hashmap.hpp index 5a38c114..7ac90a0a 100644 --- a/include/bitcoin/database/primitives/hashmap.hpp +++ b/include/bitcoin/database/primitives/hashmap.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAP_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/primitives/iterator.hpp b/include/bitcoin/database/primitives/iterator.hpp index ad97310c..4ec5f2fe 100644 --- a/include/bitcoin/database/primitives/iterator.hpp +++ b/include/bitcoin/database/primitives/iterator.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_ITERATOR_HPP #define LIBBITCOIN_DATABASE_PRIMITIVES_ITERATOR_HPP -#include #include #include #include diff --git a/include/bitcoin/database/primitives/linkage.hpp b/include/bitcoin/database/primitives/linkage.hpp index 3d8ca352..7c67dd1f 100644 --- a/include/bitcoin/database/primitives/linkage.hpp +++ b/include/bitcoin/database/primitives/linkage.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_LINKAGE_HPP #define LIBBITCOIN_DATABASE_PRIMITIVES_LINKAGE_HPP -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/primitives/manager.hpp b/include/bitcoin/database/primitives/manager.hpp index a5ff380b..1c00bb67 100644 --- a/include/bitcoin/database/primitives/manager.hpp +++ b/include/bitcoin/database/primitives/manager.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_MANAGER_HPP #define LIBBITCOIN_DATABASE_PRIMITIVES_MANAGER_HPP -#include #include #include #include diff --git a/include/bitcoin/database/primitives/nomap.hpp b/include/bitcoin/database/primitives/nomap.hpp index 749282c3..a02a2169 100644 --- a/include/bitcoin/database/primitives/nomap.hpp +++ b/include/bitcoin/database/primitives/nomap.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_HPP #define LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_HPP -#include #include #include #include diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 637ff309..8c719674 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -785,6 +784,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) #include #include #include +#include #include #include diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp index c4c5d523..6bb3f142 100644 --- a/include/bitcoin/database/settings.hpp +++ b/include/bitcoin/database/settings.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_SETTINGS_HPP #include -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/tables/archives/header.hpp b/include/bitcoin/database/tables/archives/header.hpp index e43fa428..cc9a03e5 100644 --- a/include/bitcoin/database/tables/archives/header.hpp +++ b/include/bitcoin/database/tables/archives/header.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_ARCHIVES_HEADER_HPP #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_HEADER_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/input.hpp b/include/bitcoin/database/tables/archives/input.hpp index 8432a7d1..8784c2b2 100644 --- a/include/bitcoin/database/tables/archives/input.hpp +++ b/include/bitcoin/database/tables/archives/input.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_INPUT_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/ins.hpp b/include/bitcoin/database/tables/archives/ins.hpp index 55673a10..cb72bc6f 100644 --- a/include/bitcoin/database/tables/archives/ins.hpp +++ b/include/bitcoin/database/tables/archives/ins.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_ARCHIVES_INS_HPP #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_INS_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/output.hpp b/include/bitcoin/database/tables/archives/output.hpp index 43cba2ef..f87caf6c 100644 --- a/include/bitcoin/database/tables/archives/output.hpp +++ b/include/bitcoin/database/tables/archives/output.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_OUTPUT_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/outs.hpp b/include/bitcoin/database/tables/archives/outs.hpp index 0d5060de..0c295c23 100644 --- a/include/bitcoin/database/tables/archives/outs.hpp +++ b/include/bitcoin/database/tables/archives/outs.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_OUTS_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/point.hpp b/include/bitcoin/database/tables/archives/point.hpp index 18d154a4..edfd21ff 100644 --- a/include/bitcoin/database/tables/archives/point.hpp +++ b/include/bitcoin/database/tables/archives/point.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_POINT_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/transaction.hpp b/include/bitcoin/database/tables/archives/transaction.hpp index 0663dd27..e7b6759d 100644 --- a/include/bitcoin/database/tables/archives/transaction.hpp +++ b/include/bitcoin/database/tables/archives/transaction.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_ARCHIVES_TRANSACTION_HPP #define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_TRANSACTION_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp index b93943df..88217b6b 100644 --- a/include/bitcoin/database/tables/archives/txs.hpp +++ b/include/bitcoin/database/tables/archives/txs.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/include/bitcoin/database/tables/association.hpp b/include/bitcoin/database/tables/association.hpp index 3383df42..397820eb 100644 --- a/include/bitcoin/database/tables/association.hpp +++ b/include/bitcoin/database/tables/association.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_ASSOCIATION_HPP #define LIBBITCOIN_DATABASE_TABLES_ASSOCIATION_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/associations.hpp b/include/bitcoin/database/tables/associations.hpp index c2b6cdce..fdf3efa3 100644 --- a/include/bitcoin/database/tables/associations.hpp +++ b/include/bitcoin/database/tables/associations.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_ASSOCIATIONS_HPP #define LIBBITCOIN_DATABASE_TABLES_ASSOCIATIONS_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/caches/duplicate.hpp b/include/bitcoin/database/tables/caches/duplicate.hpp index 8c4ccec2..dc98bbbb 100644 --- a/include/bitcoin/database/tables/caches/duplicate.hpp +++ b/include/bitcoin/database/tables/caches/duplicate.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_CACHES_DOUBLES_HPP #define LIBBITCOIN_DATABASE_TABLES_CACHES_DOUBLES_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/caches/prevout.hpp b/include/bitcoin/database/tables/caches/prevout.hpp index f593e426..b0307220 100644 --- a/include/bitcoin/database/tables/caches/prevout.hpp +++ b/include/bitcoin/database/tables/caches/prevout.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_TABLES_CACHES_PREVOUT_HPP #include -#include #include #include #include diff --git a/include/bitcoin/database/tables/caches/validated_bk.hpp b/include/bitcoin/database/tables/caches/validated_bk.hpp index 284446f3..a5f7d7d7 100644 --- a/include/bitcoin/database/tables/caches/validated_bk.hpp +++ b/include/bitcoin/database/tables/caches/validated_bk.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_CACHES_VALIDATED_BK_HPP #define LIBBITCOIN_DATABASE_TABLES_CACHES_VALIDATED_BK_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/caches/validated_tx.hpp b/include/bitcoin/database/tables/caches/validated_tx.hpp index 89c06ca1..172d3174 100644 --- a/include/bitcoin/database/tables/caches/validated_tx.hpp +++ b/include/bitcoin/database/tables/caches/validated_tx.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_CACHES_VALIDATED_TX_HPP #define LIBBITCOIN_DATABASE_TABLES_CACHES_VALIDATED_TX_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/context.hpp b/include/bitcoin/database/tables/context.hpp index d12dd21d..c21036bf 100644 --- a/include/bitcoin/database/tables/context.hpp +++ b/include/bitcoin/database/tables/context.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_CONTEXT_HPP #define LIBBITCOIN_DATABASE_CONTEXT_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/indexes/height.hpp b/include/bitcoin/database/tables/indexes/height.hpp index 6f65b9c0..343ced0d 100644 --- a/include/bitcoin/database/tables/indexes/height.hpp +++ b/include/bitcoin/database/tables/indexes/height.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_INDEXES_HEIGHT_HPP #define LIBBITCOIN_DATABASE_TABLES_INDEXES_HEIGHT_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/indexes/strong_tx.hpp b/include/bitcoin/database/tables/indexes/strong_tx.hpp index c143f405..ac67f3ca 100644 --- a/include/bitcoin/database/tables/indexes/strong_tx.hpp +++ b/include/bitcoin/database/tables/indexes/strong_tx.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_INDEXES_STRONG_TX_HPP #define LIBBITCOIN_DATABASE_TABLES_INDEXES_STRONG_TX_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/names.hpp b/include/bitcoin/database/tables/names.hpp index a12ed75d..6cff748d 100644 --- a/include/bitcoin/database/tables/names.hpp +++ b/include/bitcoin/database/tables/names.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_NAMES_HPP #define LIBBITCOIN_DATABASE_TABLES_NAMES_HPP -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/tables/optionals/address.hpp b/include/bitcoin/database/tables/optionals/address.hpp index 68c2738d..138dbbf2 100644 --- a/include/bitcoin/database/tables/optionals/address.hpp +++ b/include/bitcoin/database/tables/optionals/address.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_OPTIONALS_ADDRESS_HPP #define LIBBITCOIN_DATABASE_TABLES_OPTIONALS_ADDRESS_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/optionals/filter_bk.hpp b/include/bitcoin/database/tables/optionals/filter_bk.hpp index 42021f4c..4128cf04 100644 --- a/include/bitcoin/database/tables/optionals/filter_bk.hpp +++ b/include/bitcoin/database/tables/optionals/filter_bk.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_OPTIONALS_FILTER_BK_HPP #define LIBBITCOIN_DATABASE_TABLES_OPTIONALS_FILTER_BK_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/optionals/filter_tx.hpp b/include/bitcoin/database/tables/optionals/filter_tx.hpp index cc548677..4fce3d9e 100644 --- a/include/bitcoin/database/tables/optionals/filter_tx.hpp +++ b/include/bitcoin/database/tables/optionals/filter_tx.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_OPTIONALS_FILTER_TX_HPP #define LIBBITCOIN_DATABASE_TABLES_OPTIONALS_FILTER_TX_HPP -#include #include #include #include diff --git a/include/bitcoin/database/tables/point_set.hpp b/include/bitcoin/database/tables/point_set.hpp index d3deeb70..f0dc1e9a 100644 --- a/include/bitcoin/database/tables/point_set.hpp +++ b/include/bitcoin/database/tables/point_set.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_POINT_SET_HPP #define LIBBITCOIN_DATABASE_TABLES_POINT_SET_HPP -#include #include #include diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 49ed4960..92170ef7 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_SCHEMA_HPP #define LIBBITCOIN_DATABASE_TABLES_SCHEMA_HPP -#include #include namespace libbitcoin { diff --git a/include/bitcoin/database/tables/states.hpp b/include/bitcoin/database/tables/states.hpp index 7a675a25..b5056cdd 100644 --- a/include/bitcoin/database/tables/states.hpp +++ b/include/bitcoin/database/tables/states.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_DATABASE_TABLES_STATES_HPP #define LIBBITCOIN_DATABASE_TABLES_STATES_HPP -#include #include namespace libbitcoin { diff --git a/src/error.cpp b/src/error.cpp index d1cf1f45..3b2bc0ba 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include namespace libbitcoin { namespace database { diff --git a/src/file/rotator.cpp b/src/file/rotator.cpp index c451ed30..d72a0388 100644 --- a/src/file/rotator.cpp +++ b/src/file/rotator.cpp @@ -20,7 +20,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/src/file/utilities.cpp b/src/file/utilities.cpp index f9595ebe..f4985e72 100644 --- a/src/file/utilities.cpp +++ b/src/file/utilities.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/src/locks/file_lock.cpp b/src/locks/file_lock.cpp index 5c56c0fb..d56c3dd8 100644 --- a/src/locks/file_lock.cpp +++ b/src/locks/file_lock.cpp @@ -19,7 +19,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/src/locks/flush_lock.cpp b/src/locks/flush_lock.cpp index d195c0f3..0e73c84e 100644 --- a/src/locks/flush_lock.cpp +++ b/src/locks/flush_lock.cpp @@ -19,7 +19,6 @@ #include #include -#include #include namespace libbitcoin { diff --git a/src/locks/interprocess_lock.cpp b/src/locks/interprocess_lock.cpp index c8b7b36b..9f5ef358 100644 --- a/src/locks/interprocess_lock.cpp +++ b/src/locks/interprocess_lock.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/src/memory/map.cpp b/src/memory/map.cpp index 27e98251..382c5333 100644 --- a/src/memory/map.cpp +++ b/src/memory/map.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/src/memory/utilities.cpp b/src/memory/utilities.cpp index c1b9f2ed..e6182394 100644 --- a/src/memory/utilities.cpp +++ b/src/memory/utilities.cpp @@ -23,7 +23,6 @@ #else #include #endif -#include #include namespace libbitcoin { diff --git a/src/settings.cpp b/src/settings.cpp index ec2ef5a7..a901411c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,7 +18,6 @@ */ #include -#include #include namespace libbitcoin { diff --git a/test/query/estimate.cpp b/test/query/estimate.cpp new file mode 100644 index 00000000..74e5365f --- /dev/null +++ b/test/query/estimate.cpp @@ -0,0 +1,33 @@ +/** + * 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/merkle.cpp b/test/query/merkle.cpp index 5266a448..ab4ce41f 100644 --- a/test/query/merkle.cpp +++ b/test/query/merkle.cpp @@ -20,7 +20,7 @@ #include "../mocks/blocks.hpp" #include "../mocks/chunk_store.hpp" -BOOST_FIXTURE_TEST_SUITE(query_services_tests, test::directory_setup_fixture) +BOOST_FIXTURE_TEST_SUITE(query_merkle_tests, test::directory_setup_fixture) // nop event handler. const auto events_handler = [](auto, auto) {}; @@ -51,7 +51,7 @@ class merkle_accessor // interval_span -BOOST_AUTO_TEST_CASE(query_services__interval_span__uninitialized__max_size_t) +BOOST_AUTO_TEST_CASE(query_merkle__interval_span__uninitialized__max_size_t) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(query_services__interval_span__uninitialized__max_size_t) BOOST_REQUIRE_EQUAL(query.interval_span(), max_size_t); } -BOOST_AUTO_TEST_CASE(query_services__interval_span__11__2048) +BOOST_AUTO_TEST_CASE(query_merkle__interval_span__11__2048) { settings settings{}; settings.interval_depth = 11; @@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(query_services__interval_span__11__2048) BOOST_REQUIRE_EQUAL(query.interval_span(), 2048u); } -BOOST_AUTO_TEST_CASE(query_services__interval_span__0__1) +BOOST_AUTO_TEST_CASE(query_merkle__interval_span__0__1) { settings settings{}; settings.interval_depth = 0; @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(query_services__interval_span__0__1) // create_interval -BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_0__block_hash) +BOOST_AUTO_TEST_CASE(query_merkle__create_interval__depth_0__block_hash) { settings settings{}; settings.interval_depth = 0; @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_0__block_hash) BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), test::block3.hash()); } -BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_1__expected) +BOOST_AUTO_TEST_CASE(query_merkle__create_interval__depth_1__expected) { settings settings{}; settings.interval_depth = 1; @@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_1__expected) BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), root02); } -BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_2__expected) +BOOST_AUTO_TEST_CASE(query_merkle__create_interval__depth_2__expected) { settings settings{}; settings.interval_depth = 2; @@ -168,7 +168,7 @@ BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_2__expected) // get_confirmed_interval -BOOST_AUTO_TEST_CASE(query_services__get_confirmed_interval__not_multiple__no_value) +BOOST_AUTO_TEST_CASE(query_merkle__get_confirmed_interval__not_multiple__no_value) { settings settings{}; settings.interval_depth = 3; @@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_confirmed_interval__not_multiple__no_va } // Interval is set by create_interval(), integral to set(block). -BOOST_AUTO_TEST_CASE(query_services__get_confirmed_interval__multiple__expected_value) +BOOST_AUTO_TEST_CASE(query_merkle__get_confirmed_interval__multiple__expected_value) { settings settings{}; settings.interval_depth = 2; @@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_confirmed_interval__multiple__expected_ // merge_merkle -BOOST_AUTO_TEST_CASE(query_services__merge_merkle__empty_from__empty_to) +BOOST_AUTO_TEST_CASE(query_merkle__merge_merkle__empty_from__empty_to) { hashes to{}; merkle_accessor::merge_merkle(to, {}, 0); @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(query_services__merge_merkle__empty_from__empty_to) BOOST_REQUIRE(to.empty()); } -BOOST_AUTO_TEST_CASE(query_services__push_merkle__two_leaves_target_zero__merges_one_sibling) +BOOST_AUTO_TEST_CASE(query_merkle__push_merkle__two_leaves_target_zero__merges_one_sibling) { hashes to{}; hashes from @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(query_services__push_merkle__two_leaves_target_zero__merges BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::block1.hash() })); } -BOOST_AUTO_TEST_CASE(query_services__merge_merkle__three_leaves_target_two__handles_odd_length) +BOOST_AUTO_TEST_CASE(query_merkle__merge_merkle__three_leaves_target_two__handles_odd_length) { hashes to{}; hashes from @@ -250,7 +250,7 @@ BOOST_AUTO_TEST_CASE(query_services__merge_merkle__three_leaves_target_two__hand BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::genesis.hash(), test::block1.hash() })); } -BOOST_AUTO_TEST_CASE(query_services__merge_merkle__four_leaves_target_three__merges_two_siblings) +BOOST_AUTO_TEST_CASE(query_merkle__merge_merkle__four_leaves_target_three__merges_two_siblings) { hashes to{}; hashes from @@ -269,7 +269,7 @@ BOOST_AUTO_TEST_CASE(query_services__merge_merkle__four_leaves_target_three__mer // get_merkle_proof -BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__no_confirmed_blocks__error_merkle_proof) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_proof__no_confirmed_blocks__error_merkle_proof) { settings settings{}; settings.interval_depth = 2; @@ -284,7 +284,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__no_confirmed_blocks__erro BOOST_REQUIRE(proof.empty()); } -BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__target_in_first_interval__expected) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_proof__target_in_first_interval__expected) { settings settings{}; settings.interval_depth = 2; @@ -307,7 +307,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__target_in_first_interval_ BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() })); } -BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__multiple_intervals__expected) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_proof__multiple_intervals__expected) { settings settings{}; settings.interval_depth = 1; @@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__multiple_intervals__expec // get_merkle_tree -BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__waypoint_zero__genesis) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_tree__waypoint_zero__genesis) { settings settings{}; settings.interval_depth = 2; @@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__waypoint_zero__genesis) BOOST_REQUIRE_EQUAL(tree[0], expected); } -BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__one_full_interval__expected_root) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_tree__one_full_interval__expected_root) { settings settings{}; settings.interval_depth = 2; @@ -386,7 +386,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__one_full_interval__expecte // get_merkle_root_and_proof // get_merkle_proof -BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_equals_waypoint__success) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_equals_waypoint__success) { settings settings{}; settings.interval_depth = 2; @@ -420,7 +420,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_equals_wa BOOST_REQUIRE_EQUAL(root, expected); } -BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_less_than_waypoint__success) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_less_than_waypoint__success) { settings settings{}; settings.interval_depth = 2; @@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_less_than BOOST_REQUIRE_EQUAL(root, expected); } -BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_greater_than_waypoint__error_merkle_arguments) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_greater_than_waypoint__error_merkle_arguments) { settings settings{}; settings.interval_depth = 2; @@ -470,7 +470,7 @@ BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_greater_t BOOST_REQUIRE_EQUAL(query.get_merkle_root(3), system::null_hash); } -BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__waypoint_beyond_top__error_merkle_not_found) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__waypoint_beyond_top__error_merkle_not_found) { settings settings{}; settings.interval_depth = 2; diff --git a/test/test.hpp b/test/test.hpp index aca5b40d..7e8c1012 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -23,7 +23,6 @@ #include #include -#include #include #define TEST_NAME \ diff --git a/tools/initchain/initchain.cpp b/tools/initchain/initchain.cpp index 2fa27727..46513ac0 100644 --- a/tools/initchain/initchain.cpp +++ b/tools/initchain/initchain.cpp @@ -16,7 +16,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#include #include int main(int, char**) From f982770f8d216a5c2988ad472b14d6fa3571d1d5 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 20 Feb 2026 15:13:29 -0500 Subject: [PATCH 4/6] First cut on fee estimation. --- include/bitcoin/database/error.hpp | 11 +- .../bitcoin/database/impl/query/estimate.ipp | 337 +++++++++++++++++- .../bitcoin/database/impl/query/merkle.ipp | 4 +- include/bitcoin/database/query.hpp | 40 ++- include/bitcoin/database/types.hpp | 77 +++- src/error.cpp | 11 +- test/error.cpp | 59 ++- test/query/merkle.cpp | 8 +- 8 files changed, 511 insertions(+), 36 deletions(-) diff --git a/include/bitcoin/database/error.hpp b/include/bitcoin/database/error.hpp index 2272f607..2dd91ff4 100644 --- a/include/bitcoin/database/error.hpp +++ b/include/bitcoin/database/error.hpp @@ -145,12 +145,15 @@ enum error_t : uint8_t txs_confirm, txs_txs_put, - /// optional + /// services + not_found, + empty_block, + query_canceled, + invalid_argument, + missing_prevouts, merkle_proof, merkle_interval, - merkle_hashes, - merkle_arguments, - merkle_not_found + merkle_hashes }; // No current need for error_code equivalence mapping. diff --git a/include/bitcoin/database/impl/query/estimate.ipp b/include/bitcoin/database/impl/query/estimate.ipp index 40218bae..7459d08f 100644 --- a/include/bitcoin/database/impl/query/estimate.ipp +++ b/include/bitcoin/database/impl/query/estimate.ipp @@ -19,14 +19,349 @@ #ifndef LIBBITCOIN_DATABASE_QUERY_ESTIMATE_IPP #define LIBBITCOIN_DATABASE_QUERY_ESTIMATE_IPP +#include +#include +#include +#include +#include +#include +#include #include namespace libbitcoin { namespace database { - + // fee estimate // ---------------------------------------------------------------------------- +// protected +TEMPLATE +code CLASS::get_block_fees(fee_states& out, + const header_link& link) const NOEXCEPT +{ + out.clear(); + const auto block = get_block(link, false); + if (!block) + return error::not_found; + + block->populate(); + if (!populate_without_metadata(*block)) + return error::missing_prevouts; + + const auto& txs = *block->transactions_ptr(); + if (txs.empty()) + return error::empty_block; + + out.reserve(txs.size()); + for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) + out.emplace_back((*tx)->virtual_size(), (*tx)->fee()); + + return error::success; +} + +// public +TEMPLATE +code CLASS::get_block_fees(std::atomic_bool& cancel, fee_state_sets& out, + size_t top, size_t count) const NOEXCEPT +{ + out.clear(); + if (is_zero(count)) + return {}; + + if (top > get_top_confirmed()) + return error::not_found; + + const auto start = top - sub1(count); + if (system::is_subtract_overflow(top, sub1(count))) + return error::invalid_argument; + + out.resize(count); + std::vector offsets(count); + std::iota(offsets.begin(), offsets.end(), zero); + std::atomic failure{ error::success }; + constexpr auto relaxed = std::memory_order_relaxed; + + std::for_each(poolstl::execution::par, offsets.begin(), offsets.end(), + [&](const size_t& offset) NOEXCEPT + { + if (failure.load(relaxed) != error::success) + return; + + if (cancel.load(relaxed)) + { + failure.store(error::query_canceled, relaxed); + return; + } + + const auto link = to_confirmed(start + offset); + if (const auto ec = get_block_fees(out.at(offset), link)) + { + failure.store(ec.value(), relaxed); + return; + } + }); + + code ec{ failure.load(relaxed) }; + if (ec) out.clear(); + return ec; +} + +// TODO: move to server as its own class, fee::accumulator as member. +// ---------------------------------------------------------------------------- +// static + +TEMPLATE +uint64_t CLASS::estimate_fee(const fee::accumulator& fees, size_t target, + fee::mode mode) NOEXCEPT +{ + auto estimate = max_uint64; + constexpr auto large = fee::horizon::large; + if (target >= large) + return estimate; + + // Valid results are effectively limited to at least 1 sat/vb. + // threshold_fee is thread safe but values are affected during update. + using namespace fee::confidence; + switch (mode) + { + case fee::mode::basic: + { + estimate = compute_fee(fees, target, high); + break; + } + case fee::mode::markov: + { + estimate = compute_fee(fees, target, high, true); + break; + } + case fee::mode::economical: + { + const auto target1 = to_half(target); + const auto target2 = std::min(one, target); + const auto target3 = std::min(large, two * target); + const auto fee1 = compute_fee(fees, target1, low); + const auto fee2 = compute_fee(fees, target2, mid); + const auto fee3 = compute_fee(fees, target3, high); + estimate = std::max({ fee1, fee2, fee3 }); + break; + } + case fee::mode::conservative: + { + const auto target1 = to_half(target); + const auto target2 = std::min(one, target); + const auto target3 = std::min(large, two * target); + const auto fee1 = compute_fee(fees, target1, low); + const auto fee2 = compute_fee(fees, target2, mid); + const auto fee3 = compute_fee(fees, target3, high); + estimate = std::max({ fee1, fee2, fee3 }); + break; + } + } + + // max_uint64 is failure sentinel (and unachievable/invalid as a fee). + return estimate; +} + +TEMPLATE +bool CLASS::create_fees(fee::accumulator& out, + const fee_state_sets& blocks) NOEXCEPT +{ + const auto count = blocks.size(); + if (is_zero(count)) + return true; + + const auto top = out.top_height; + auto height = top - sub1(count); + if (system::is_subtract_overflow(top, sub1(count))) + return error::invalid_argument; + + // TODO: could be parallel. + for (const auto& block: blocks) + for (const auto& tx: block) + if (!update_fees(out, tx, height++, true)) + return false; + + return true; +} + +TEMPLATE +bool CLASS::pop_fees(fee::accumulator& fees, const fee_states& states) NOEXCEPT +{ + // Blocks must be pushed in order (but independent of chain index). + const auto result = update_fees(fees, states, fees.top_height--, false); + decay_fees(fees, false); + return result; +} + +TEMPLATE +bool CLASS::push_fees(fee::accumulator& fees, const fee_states& states) NOEXCEPT +{ + // Blocks must be pushed in order (but independent of chain index). + decay_fees(fees, true); + return update_fees(fees, states, ++fees.top_height, true); +} + +// protected +// ---------------------------------------------------------------------------- +// static + +TEMPLATE +static double CLASS::to_scale_term(size_t age) NOEXCEPT +{ + const auto scale = std::pow(decay_rate(), age); +} + +TEMPLATE +static double CLASS::to_scale_factor(bool push) NOEXCEPT +{ + const auto scale = std::pow(decay_rate(), push ? +1.0 : -1.0); +} + +TEMPLATE +void CLASS::decay_fees(fee::accumulator& fees, bool push) NOEXCEPT +{ + // Not thread safe (use sequentially by block). + const auto factor = to_scale_factor(push); + decay_fees(fees.large, factor); + decay_fees(fees.medium, factor); + decay_fees(fees.small, factor); +} + +TEMPLATE +void CLASS::decay_fees(auto& buckets, double factor) NOEXCEPT +{ + // Not thread safe (apply sequentially by block). + const auto apply_decay = [factor](auto& count) NOEXCEPT + { + const auto relaxed = std::memory_order_relaxed; + const auto value = count.load(relaxed); + count.store(system::to_floored_integer(value * factor), relaxed); + }; + + for (auto& bucket: buckets) + { + apply_decay(bucket.total); + for (auto& count: bucket.confirmed) + apply_decay(count); + } +} + +TEMPLATE +uint64_t CLASS::compute_fee(const fee::accumulator& fees, size_t target, + double confidence, bool use_markov) NOEXCEPT +{ + const auto threshold = [&](double part, double total) NOEXCEPT + { + return part / total; + }; + + // Geometric distribution approximation, not a full Markov process. + const auto markov = [&](double part, double total) NOEXCEPT + { + return system::power(part / total, target); + }; + + const auto call = [&](const auto& buckets) NOEXCEPT + { + using namespace system; + constexpr auto magic_number = 2u; + constexpr auto relaxed = std::memory_order_relaxed; + const auto at_least_four = magic_number * add1(target); + const auto& contribution = use_markov ? markov : threshold; + + double total{}, part{}; + auto index = buckets.size(); + auto found = index; + for (const auto& bucket: std::views::reverse(buckets)) + { + --index; + total += to_floating(bucket.total.load(relaxed)); + part += to_floating(bucket.confirmed[target].load(relaxed)); + if (total < at_least_four) + continue; + + if (contribution(part, total) > (1.0 - confidence)) + break; + + found = index; + } + + if (found == buckets.size()) + return max_uint64; + + const auto minimum = fee::size::min * std::pow(fee::size::step, found); + return to_ceilinged_integer(minimum); + }; + + if (target < fee::horizon::small) return call(fees.small); + if (target < fee::horizon::medium) return call(fees.medium); + if (target < fee::horizon::large) return call(fees.large); + return max_uint64; +} + +TEMPLATE +bool CLASS::update_fees(fee::accumulator& fees, const fee_states& states, + size_t height, bool push) NOEXCEPT +{ + using namespace system; + if (( push && (height == max_size_t)) || + (!push && is_zero(height)) || + (height > fees.top_height)) + return false; + + // std::log (replace static with constexpr in c++26). + static const auto growth = std::log(fee::size::step); + std::array counts{}; + + for (const auto& state: states) + { + if (is_zero(state.bytes)) + return false; + + if (is_zero(state.fee)) + continue; + + const auto rate = to_floating(state.fee) / state.bytes; + if (rate < fee::size::min) + continue; + + // Clamp overflow to last bin. + const auto bin = std::log(rate / fee::size::min) / growth; + ++counts[std::min(to_floored_integer(bin), sub1(fee::size::count))]; + } + + const auto age = fees.top_height - height; + const auto scale = to_scale_term(age); + const auto update = [age, scale](const auto& buckets) NOEXCEPT + { + // The array count of the buckets element type. + const auto depth = buckets.front().confirmed.size(); + + size_t bin{}; + for (const auto count: counts) + { + if (is_zero(count)) + { + ++bin; + continue; + } + + auto& bucket = buckets[bin++]; + const auto scaled = to_floored_integer(count * scale); + const auto signed_term = push ? scaled : twos_complement(scaled); + constexpr auto relaxed = std::memory_order_relaxed; + bucket.total.fetch_add(signed_term, relaxed); + for (auto target = age; target < depth; ++target) + bucket.confirmed[target].fetch_add(signed_term, relaxed); + } + }; + + update(fees.large); + update(fees.medium); + update(fees.small); + return true; +} + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/query/merkle.ipp b/include/bitcoin/database/impl/query/merkle.ipp index da7e295c..1327841f 100644 --- a/include/bitcoin/database/impl/query/merkle.ipp +++ b/include/bitcoin/database/impl/query/merkle.ipp @@ -144,10 +144,10 @@ code CLASS::get_merkle_root_and_proof(hash_digest& root, hashes& proof, size_t target, size_t waypoint) const NOEXCEPT { if (target > waypoint) - return error::merkle_arguments; + return error::invalid_argument; if (waypoint > get_top_confirmed()) - return error::merkle_not_found; + return error::not_found; hashes tree{}; if (const auto ec = get_merkle_tree(tree, waypoint)) diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 8c719674..1b8fd6e9 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -401,6 +401,20 @@ class query /// Services. /// ----------------------------------------------------------------------- + /// Fee estimation in satoshis / transaction virtual size. + /// Pass zero to target next block for confirmation, range:0..1007. + static uint64_t estimate_fee(const fee::accumulator& fees, + size_t target, fee::mode mode=fee::mode::basic) NOEXCEPT; + + /// Create and maintain a fee estimation accumulator. + static bool create_fees(fee::accumulator& out, + const fee_state_sets& blocks) NOEXCEPT; + static bool pop_fees(fee::accumulator& fees, + const fee_states& block) NOEXCEPT; + static bool push_fees(fee::accumulator& fees, + const fee_states& block) NOEXCEPT; + + /// Merkle computations over the index of confirmed headers. hash_digest get_merkle_root(size_t height) const NOEXCEPT; code get_merkle_root_and_proof(hash_digest& root, hashes& proof, size_t target, size_t checkpoint) const NOEXCEPT; @@ -723,7 +737,31 @@ class query outpoints& out, const hash_digest& key, uint64_t minimum) const NOEXCEPT; - /// services:merkle + /// estimate + /// ----------------------------------------------------------------------- + + // std::pow (replace static const with constexpr in c++23). + static inline double decay_rate() NOEXCEPT + { + return std::pow(0.5, 1.0 / fee::size::count); + } + + /// Update fee accumulation based on block organization. + static double to_scale_term(size_t age) NOEXCEPT; + static double to_scale_factor(bool push) NOEXCEPT; + static void decay_fees(auto& buckets, double factor) NOEXCEPT; + static void decay_fees(fee::accumulator& fees, bool push) NOEXCEPT; + static uint64_t compute_fee(const fee::accumulator& fees, size_t target, + double measure, bool use_markov=false) NOEXCEPT; + static bool update_fees(fee::accumulator& fees, const fee_states& states, + size_t height, bool push) NOEXCEPT; + + code get_block_fees(fee_states& out, + const header_link& link) const NOEXCEPT; + code get_block_fees(std::atomic_bool& cancel, fee_state_sets& out, + size_t top, size_t count) const NOEXCEPT; + + /// merkle /// ----------------------------------------------------------------------- static void merge_merkle(hashes& branch, hashes&& hashes, diff --git a/include/bitcoin/database/types.hpp b/include/bitcoin/database/types.hpp index b6141ebe..ec09eb81 100644 --- a/include/bitcoin/database/types.hpp +++ b/include/bitcoin/database/types.hpp @@ -47,15 +47,84 @@ using point_links = std::vector; using two_counts = std::pair; using point_key = table::point::key; -struct header_state{ header_link link; code ec; }; -using header_states = std::vector; - -// TODO: define a system::chain::inpoint with added state. using inpoint = system::chain::point; using inpoints = std::set; using outpoint = system::chain::outpoint; using outpoints = std::set; +struct header_state { header_link link; code ec; }; +using header_states = std::vector; + +struct fee_state { size_t bytes{}; uint64_t fee{}; }; +using fee_states = std::vector; +using fee_state_sets = std::vector; + +namespace fee +{ + using atomic_size = std::atomic; + + /// Estimation modes. + enum class mode + { + basic, + markov, + economical, + conservative + }; + + /// Estimation confidences. + namespace confidence + { + static constexpr double low = 0.60; + static constexpr double mid = 0.85; + static constexpr double high = 0.95; + } + + /// Bucket count sizing parameters. + namespace horizon + { + static constexpr size_t small = 12; + static constexpr size_t medium = 48; + static constexpr size_t large = 1008; + } + + /// Bucket count sizing parameters. + namespace size + { + ////static constexpr double min = 1.0; + static constexpr double min = 0.1; + static constexpr double max = 100'000.0; + static constexpr double step = 1.05; + + /// This is derived from min/max/step above. + ////static constexpr size_t count = 236; + static constexpr size_t count = 283; + } + + /// Accumulator (persistent, decay-weighted counters). + struct accumulator + { + template + struct bucket + { + /// Total scaled txs in bucket. + atomic_size total{}; + + /// confirmed[n]: scaled txs confirmed in > n blocks. + std::array confirmed; + }; + + /// Current block height of accumulated state. + size_t top_height{}; + + /// Accumulated scaled fee in decayed buckets by horizon. + /// Array count is the half life of the decay it implies. + std::array, size::count> small{}; + std::array, size::count> medium{}; + std::array, size::count> large{}; + }; +} // namespace fee + } // namespace database } // namespace libbitcoin diff --git a/src/error.cpp b/src/error.cpp index 3b2bc0ba..8e14571e 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -138,12 +138,15 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { txs_confirm, "txs_confirm" }, { txs_txs_put, "txs_txs_put" }, - // optional + // services + { not_found, "not_found" }, + { empty_block, "empty_block" }, + { query_canceled, "query_canceled" }, + { invalid_argument, "invalid_argument" }, + { missing_prevouts, "missing_prevouts" }, { merkle_proof, "merkle_proof" }, { merkle_interval, "merkle_interval" }, - { merkle_hashes, "merkle_hashes" }, - { merkle_arguments, "merkle_arguments" }, - { merkle_not_found, "merkle_not_found" } + { merkle_hashes, "merkle_hashes" } }; DEFINE_ERROR_T_CATEGORY(error, "database", "database code") diff --git a/test/error.cpp b/test/error.cpp index 231b2553..be391077 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -713,51 +713,78 @@ BOOST_AUTO_TEST_CASE(error_t__code__txs_txs_put__true_expected_message) BOOST_REQUIRE_EQUAL(ec.message(), "txs_txs_put"); } -// optional +// services -BOOST_AUTO_TEST_CASE(error_t__code__merkle_proof__true_expected_message) +BOOST_AUTO_TEST_CASE(error_t__code__not_found__true_expected_message) { - constexpr auto value = error::merkle_proof; + constexpr auto value = error::not_found; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "merkle_proof"); + BOOST_REQUIRE_EQUAL(ec.message(), "not_found"); } -BOOST_AUTO_TEST_CASE(error_t__code__merkle_interval__true_expected_message) +BOOST_AUTO_TEST_CASE(error_t__code__empty_block__true_expected_message) { - constexpr auto value = error::merkle_interval; + constexpr auto value = error::empty_block; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "merkle_interval"); + BOOST_REQUIRE_EQUAL(ec.message(), "empty_block"); } -BOOST_AUTO_TEST_CASE(error_t__code__merkle_hashes__true_expected_message) +BOOST_AUTO_TEST_CASE(error_t__code__query_canceled__true_expected_message) { - constexpr auto value = error::merkle_hashes; + constexpr auto value = error::query_canceled; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "merkle_hashes"); + BOOST_REQUIRE_EQUAL(ec.message(), "query_canceled"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__invalid_argument__true_expected_message) +{ + constexpr auto value = error::invalid_argument; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "invalid_argument"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__missing_prevouts__true_expected_message) +{ + constexpr auto value = error::missing_prevouts; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "missing_prevouts"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__merkle_proof__true_expected_message) +{ + constexpr auto value = error::merkle_proof; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "merkle_proof"); } -BOOST_AUTO_TEST_CASE(error_t__code__merkle_arguments__true_expected_message) +BOOST_AUTO_TEST_CASE(error_t__code__merkle_interval__true_expected_message) { - constexpr auto value = error::merkle_arguments; + constexpr auto value = error::merkle_interval; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "merkle_arguments"); + BOOST_REQUIRE_EQUAL(ec.message(), "merkle_interval"); } -BOOST_AUTO_TEST_CASE(error_t__code__merkle_not_found__true_expected_message) +BOOST_AUTO_TEST_CASE(error_t__code__merkle_hashes__true_expected_message) { - constexpr auto value = error::merkle_not_found; + constexpr auto value = error::merkle_hashes; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "merkle_not_found"); + BOOST_REQUIRE_EQUAL(ec.message(), "merkle_hashes"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/merkle.cpp b/test/query/merkle.cpp index ab4ce41f..648cc463 100644 --- a/test/query/merkle.cpp +++ b/test/query/merkle.cpp @@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_less_than_w BOOST_REQUIRE_EQUAL(root, expected); } -BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_greater_than_waypoint__error_merkle_arguments) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_greater_than_waypoint__error_invalid_argument) { settings settings{}; settings.interval_depth = 2; @@ -466,11 +466,11 @@ BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__target_greater_tha hashes proof{}; hash_digest root{}; - BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 5u, 3u), error::merkle_arguments); + BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 5u, 3u), error::invalid_argument); BOOST_REQUIRE_EQUAL(query.get_merkle_root(3), system::null_hash); } -BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__waypoint_beyond_top__error_merkle_not_found) +BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__waypoint_beyond_top__error_not_found) { settings settings{}; settings.interval_depth = 2; @@ -482,7 +482,7 @@ BOOST_AUTO_TEST_CASE(query_merkle__get_merkle_root_and_proof__waypoint_beyond_to hashes proof{}; hash_digest root{}; - BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 0u, 100u), error::merkle_not_found); + BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 0u, 100u), error::not_found); BOOST_REQUIRE_EQUAL(query.get_merkle_root(100), system::null_hash); } From c7099d2ef0f45f67fb62a3562d9c8f97414ac363 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 20 Feb 2026 18:14:52 -0500 Subject: [PATCH 5/6] Comments. --- include/bitcoin/database/impl/query/height.ipp | 2 -- include/bitcoin/database/impl/query/network.ipp | 1 - 2 files changed, 3 deletions(-) diff --git a/include/bitcoin/database/impl/query/height.ipp b/include/bitcoin/database/impl/query/height.ipp index 5e6fbf8d..fa2544ac 100644 --- a/include/bitcoin/database/impl/query/height.ipp +++ b/include/bitcoin/database/impl/query/height.ipp @@ -381,8 +381,6 @@ bool CLASS::pop_confirmed() NOEXCEPT if (!set_strong(link, txs.number, txs.coinbase_fk, false)) return false; - // Truncate cannot fail for disk full. - // This truncate assumes no concurrent writes to the table. /////////////////////////////////////////////////////////////////////////// std::unique_lock interlock{ confirmed_reorganization_mutex_ }; return store_.confirmed.truncate(top); diff --git a/include/bitcoin/database/impl/query/network.ipp b/include/bitcoin/database/impl/query/network.ipp index cf347b02..f1084e44 100644 --- a/include/bitcoin/database/impl/query/network.ipp +++ b/include/bitcoin/database/impl/query/network.ipp @@ -133,7 +133,6 @@ bool CLASS::get_ancestry(header_links& ancestry, const header_link& descendant, auto link = descendant; // Ancestry navigation ensures continuity without locks. - // If count is zero then not even descendant is pushed. // link terminal if previous was genesis (avoided by count <= height). for (auto& ancestor: ancestry) link = to_parent((ancestor = link)); From 079150f6afa06ad1386ddfede7d8e8d5508e0ffd Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 20 Feb 2026 18:15:12 -0500 Subject: [PATCH 6/6] Move fee estimator to server and retain only queries. --- .../bitcoin/database/impl/query/estimate.ipp | 295 ++---------------- include/bitcoin/database/query.hpp | 40 +-- include/bitcoin/database/types.hpp | 73 +---- 3 files changed, 26 insertions(+), 382 deletions(-) diff --git a/include/bitcoin/database/impl/query/estimate.ipp b/include/bitcoin/database/impl/query/estimate.ipp index 7459d08f..da976ee7 100644 --- a/include/bitcoin/database/impl/query/estimate.ipp +++ b/include/bitcoin/database/impl/query/estimate.ipp @@ -20,11 +20,10 @@ #define LIBBITCOIN_DATABASE_QUERY_ESTIMATE_IPP #include -#include +#include #include #include #include -#include #include #include @@ -36,330 +35,74 @@ namespace database { // protected TEMPLATE -code CLASS::get_block_fees(fee_states& out, +bool CLASS::get_block_fees(fee_rates& out, const header_link& link) const NOEXCEPT { out.clear(); const auto block = get_block(link, false); if (!block) - return error::not_found; + return false; block->populate(); if (!populate_without_metadata(*block)) - return error::missing_prevouts; + return false; const auto& txs = *block->transactions_ptr(); if (txs.empty()) - return error::empty_block; + return false; out.reserve(txs.size()); for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) out.emplace_back((*tx)->virtual_size(), (*tx)->fee()); - return error::success; + return true; } // public TEMPLATE -code CLASS::get_block_fees(std::atomic_bool& cancel, fee_state_sets& out, +bool CLASS::get_block_fees(std::atomic_bool& cancel, fee_rate_sets& out, size_t top, size_t count) const NOEXCEPT { out.clear(); if (is_zero(count)) - return {}; + return true; if (top > get_top_confirmed()) - return error::not_found; + return false; const auto start = top - sub1(count); if (system::is_subtract_overflow(top, sub1(count))) - return error::invalid_argument; + return false; out.resize(count); std::vector offsets(count); std::iota(offsets.begin(), offsets.end(), zero); - std::atomic failure{ error::success }; - constexpr auto relaxed = std::memory_order_relaxed; + std::atomic_bool failure{}; + constexpr auto relaxed = std::memory_order_relaxed; std::for_each(poolstl::execution::par, offsets.begin(), offsets.end(), [&](const size_t& offset) NOEXCEPT { - if (failure.load(relaxed) != error::success) + if (failure.load(relaxed)) return; if (cancel.load(relaxed)) { - failure.store(error::query_canceled, relaxed); + failure.store(true, relaxed); return; } const auto link = to_confirmed(start + offset); - if (const auto ec = get_block_fees(out.at(offset), link)) + if (!get_block_fees(out.at(offset), link)) { - failure.store(ec.value(), relaxed); + failure.store(false, relaxed); return; } }); - code ec{ failure.load(relaxed) }; - if (ec) out.clear(); - return ec; -} - -// TODO: move to server as its own class, fee::accumulator as member. -// ---------------------------------------------------------------------------- -// static - -TEMPLATE -uint64_t CLASS::estimate_fee(const fee::accumulator& fees, size_t target, - fee::mode mode) NOEXCEPT -{ - auto estimate = max_uint64; - constexpr auto large = fee::horizon::large; - if (target >= large) - return estimate; - - // Valid results are effectively limited to at least 1 sat/vb. - // threshold_fee is thread safe but values are affected during update. - using namespace fee::confidence; - switch (mode) - { - case fee::mode::basic: - { - estimate = compute_fee(fees, target, high); - break; - } - case fee::mode::markov: - { - estimate = compute_fee(fees, target, high, true); - break; - } - case fee::mode::economical: - { - const auto target1 = to_half(target); - const auto target2 = std::min(one, target); - const auto target3 = std::min(large, two * target); - const auto fee1 = compute_fee(fees, target1, low); - const auto fee2 = compute_fee(fees, target2, mid); - const auto fee3 = compute_fee(fees, target3, high); - estimate = std::max({ fee1, fee2, fee3 }); - break; - } - case fee::mode::conservative: - { - const auto target1 = to_half(target); - const auto target2 = std::min(one, target); - const auto target3 = std::min(large, two * target); - const auto fee1 = compute_fee(fees, target1, low); - const auto fee2 = compute_fee(fees, target2, mid); - const auto fee3 = compute_fee(fees, target3, high); - estimate = std::max({ fee1, fee2, fee3 }); - break; - } - } - - // max_uint64 is failure sentinel (and unachievable/invalid as a fee). - return estimate; -} - -TEMPLATE -bool CLASS::create_fees(fee::accumulator& out, - const fee_state_sets& blocks) NOEXCEPT -{ - const auto count = blocks.size(); - if (is_zero(count)) - return true; - - const auto top = out.top_height; - auto height = top - sub1(count); - if (system::is_subtract_overflow(top, sub1(count))) - return error::invalid_argument; - - // TODO: could be parallel. - for (const auto& block: blocks) - for (const auto& tx: block) - if (!update_fees(out, tx, height++, true)) - return false; - - return true; -} - -TEMPLATE -bool CLASS::pop_fees(fee::accumulator& fees, const fee_states& states) NOEXCEPT -{ - // Blocks must be pushed in order (but independent of chain index). - const auto result = update_fees(fees, states, fees.top_height--, false); - decay_fees(fees, false); - return result; -} - -TEMPLATE -bool CLASS::push_fees(fee::accumulator& fees, const fee_states& states) NOEXCEPT -{ - // Blocks must be pushed in order (but independent of chain index). - decay_fees(fees, true); - return update_fees(fees, states, ++fees.top_height, true); -} - -// protected -// ---------------------------------------------------------------------------- -// static - -TEMPLATE -static double CLASS::to_scale_term(size_t age) NOEXCEPT -{ - const auto scale = std::pow(decay_rate(), age); -} - -TEMPLATE -static double CLASS::to_scale_factor(bool push) NOEXCEPT -{ - const auto scale = std::pow(decay_rate(), push ? +1.0 : -1.0); -} - -TEMPLATE -void CLASS::decay_fees(fee::accumulator& fees, bool push) NOEXCEPT -{ - // Not thread safe (use sequentially by block). - const auto factor = to_scale_factor(push); - decay_fees(fees.large, factor); - decay_fees(fees.medium, factor); - decay_fees(fees.small, factor); -} - -TEMPLATE -void CLASS::decay_fees(auto& buckets, double factor) NOEXCEPT -{ - // Not thread safe (apply sequentially by block). - const auto apply_decay = [factor](auto& count) NOEXCEPT - { - const auto relaxed = std::memory_order_relaxed; - const auto value = count.load(relaxed); - count.store(system::to_floored_integer(value * factor), relaxed); - }; - - for (auto& bucket: buckets) - { - apply_decay(bucket.total); - for (auto& count: bucket.confirmed) - apply_decay(count); - } -} - -TEMPLATE -uint64_t CLASS::compute_fee(const fee::accumulator& fees, size_t target, - double confidence, bool use_markov) NOEXCEPT -{ - const auto threshold = [&](double part, double total) NOEXCEPT - { - return part / total; - }; - - // Geometric distribution approximation, not a full Markov process. - const auto markov = [&](double part, double total) NOEXCEPT - { - return system::power(part / total, target); - }; - - const auto call = [&](const auto& buckets) NOEXCEPT - { - using namespace system; - constexpr auto magic_number = 2u; - constexpr auto relaxed = std::memory_order_relaxed; - const auto at_least_four = magic_number * add1(target); - const auto& contribution = use_markov ? markov : threshold; - - double total{}, part{}; - auto index = buckets.size(); - auto found = index; - for (const auto& bucket: std::views::reverse(buckets)) - { - --index; - total += to_floating(bucket.total.load(relaxed)); - part += to_floating(bucket.confirmed[target].load(relaxed)); - if (total < at_least_four) - continue; - - if (contribution(part, total) > (1.0 - confidence)) - break; - - found = index; - } - - if (found == buckets.size()) - return max_uint64; - - const auto minimum = fee::size::min * std::pow(fee::size::step, found); - return to_ceilinged_integer(minimum); - }; - - if (target < fee::horizon::small) return call(fees.small); - if (target < fee::horizon::medium) return call(fees.medium); - if (target < fee::horizon::large) return call(fees.large); - return max_uint64; -} - -TEMPLATE -bool CLASS::update_fees(fee::accumulator& fees, const fee_states& states, - size_t height, bool push) NOEXCEPT -{ - using namespace system; - if (( push && (height == max_size_t)) || - (!push && is_zero(height)) || - (height > fees.top_height)) - return false; - - // std::log (replace static with constexpr in c++26). - static const auto growth = std::log(fee::size::step); - std::array counts{}; - - for (const auto& state: states) - { - if (is_zero(state.bytes)) - return false; - - if (is_zero(state.fee)) - continue; - - const auto rate = to_floating(state.fee) / state.bytes; - if (rate < fee::size::min) - continue; - - // Clamp overflow to last bin. - const auto bin = std::log(rate / fee::size::min) / growth; - ++counts[std::min(to_floored_integer(bin), sub1(fee::size::count))]; - } - - const auto age = fees.top_height - height; - const auto scale = to_scale_term(age); - const auto update = [age, scale](const auto& buckets) NOEXCEPT - { - // The array count of the buckets element type. - const auto depth = buckets.front().confirmed.size(); - - size_t bin{}; - for (const auto count: counts) - { - if (is_zero(count)) - { - ++bin; - continue; - } - - auto& bucket = buckets[bin++]; - const auto scaled = to_floored_integer(count * scale); - const auto signed_term = push ? scaled : twos_complement(scaled); - constexpr auto relaxed = std::memory_order_relaxed; - bucket.total.fetch_add(signed_term, relaxed); - for (auto target = age; target < depth; ++target) - bucket.confirmed[target].fetch_add(signed_term, relaxed); - } - }; - - update(fees.large); - update(fees.medium); - update(fees.small); - return true; + const auto failed = failure.load(relaxed); + if (failed) out.clear(); + return failed; } } // namespace database diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 1b8fd6e9..217b4772 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -401,18 +401,10 @@ class query /// Services. /// ----------------------------------------------------------------------- - /// Fee estimation in satoshis / transaction virtual size. - /// Pass zero to target next block for confirmation, range:0..1007. - static uint64_t estimate_fee(const fee::accumulator& fees, - size_t target, fee::mode mode=fee::mode::basic) NOEXCEPT; - - /// Create and maintain a fee estimation accumulator. - static bool create_fees(fee::accumulator& out, - const fee_state_sets& blocks) NOEXCEPT; - static bool pop_fees(fee::accumulator& fees, - const fee_states& block) NOEXCEPT; - static bool push_fees(fee::accumulator& fees, - const fee_states& block) NOEXCEPT; + /// Gether fee rate tuples by block or set of blocks. + bool get_block_fees(fee_rates& out, const header_link& link) const NOEXCEPT; + bool get_block_fees(std::atomic_bool& cancel, fee_rate_sets& out, + size_t top, size_t count) const NOEXCEPT; /// Merkle computations over the index of confirmed headers. hash_digest get_merkle_root(size_t height) const NOEXCEPT; @@ -737,30 +729,6 @@ class query outpoints& out, const hash_digest& key, uint64_t minimum) const NOEXCEPT; - /// estimate - /// ----------------------------------------------------------------------- - - // std::pow (replace static const with constexpr in c++23). - static inline double decay_rate() NOEXCEPT - { - return std::pow(0.5, 1.0 / fee::size::count); - } - - /// Update fee accumulation based on block organization. - static double to_scale_term(size_t age) NOEXCEPT; - static double to_scale_factor(bool push) NOEXCEPT; - static void decay_fees(auto& buckets, double factor) NOEXCEPT; - static void decay_fees(fee::accumulator& fees, bool push) NOEXCEPT; - static uint64_t compute_fee(const fee::accumulator& fees, size_t target, - double measure, bool use_markov=false) NOEXCEPT; - static bool update_fees(fee::accumulator& fees, const fee_states& states, - size_t height, bool push) NOEXCEPT; - - code get_block_fees(fee_states& out, - const header_link& link) const NOEXCEPT; - code get_block_fees(std::atomic_bool& cancel, fee_state_sets& out, - size_t top, size_t count) const NOEXCEPT; - /// merkle /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/types.hpp b/include/bitcoin/database/types.hpp index ec09eb81..263556ba 100644 --- a/include/bitcoin/database/types.hpp +++ b/include/bitcoin/database/types.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_TYPES_HPP #include -#include #include #include @@ -55,75 +54,9 @@ using outpoints = std::set; struct header_state { header_link link; code ec; }; using header_states = std::vector; -struct fee_state { size_t bytes{}; uint64_t fee{}; }; -using fee_states = std::vector; -using fee_state_sets = std::vector; - -namespace fee -{ - using atomic_size = std::atomic; - - /// Estimation modes. - enum class mode - { - basic, - markov, - economical, - conservative - }; - - /// Estimation confidences. - namespace confidence - { - static constexpr double low = 0.60; - static constexpr double mid = 0.85; - static constexpr double high = 0.95; - } - - /// Bucket count sizing parameters. - namespace horizon - { - static constexpr size_t small = 12; - static constexpr size_t medium = 48; - static constexpr size_t large = 1008; - } - - /// Bucket count sizing parameters. - namespace size - { - ////static constexpr double min = 1.0; - static constexpr double min = 0.1; - static constexpr double max = 100'000.0; - static constexpr double step = 1.05; - - /// This is derived from min/max/step above. - ////static constexpr size_t count = 236; - static constexpr size_t count = 283; - } - - /// Accumulator (persistent, decay-weighted counters). - struct accumulator - { - template - struct bucket - { - /// Total scaled txs in bucket. - atomic_size total{}; - - /// confirmed[n]: scaled txs confirmed in > n blocks. - std::array confirmed; - }; - - /// Current block height of accumulated state. - size_t top_height{}; - - /// Accumulated scaled fee in decayed buckets by horizon. - /// Array count is the half life of the decay it implies. - std::array, size::count> small{}; - std::array, size::count> medium{}; - std::array, size::count> large{}; - }; -} // namespace fee +struct fee_rate { size_t bytes{}; uint64_t fee{}; }; +using fee_rates = std::vector; +using fee_rate_sets = std::vector; } // namespace database } // namespace libbitcoin