From b253eba47411566ecf63989e9001ce15369301b7 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 13 Apr 2026 13:17:04 -0400 Subject: [PATCH 1/2] Refactor types.hpp/cpp. --- Makefile.am | 23 +- .../libbitcoin-database-test.vcxproj | 4 +- .../libbitcoin-database-test.vcxproj.filters | 29 ++- .../libbitcoin-database.vcxproj | 12 +- .../libbitcoin-database.vcxproj.filters | 82 +++++-- include/bitcoin/database.hpp | 9 +- .../impl/query/address/address_unspent.ipp | 1 - include/bitcoin/database/query.hpp | 2 +- include/bitcoin/database/store.hpp | 2 +- include/bitcoin/database/types/fee_rate.hpp | 39 +++ .../bitcoin/database/types/header_state.hpp | 39 +++ include/bitcoin/database/types/history.hpp | 62 +++++ include/bitcoin/database/types/position.hpp | 38 +++ include/bitcoin/database/types/span.hpp | 41 ++++ .../database/{types.hpp => types/type.hpp} | 95 +------- include/bitcoin/database/types/types.hpp | 30 +++ include/bitcoin/database/types/unspent.hpp | 59 +++++ src/define.cpp | 6 +- src/{types.cpp => types/history.cpp} | 68 +----- src/types/unspent.cpp | 78 ++++++ test/types.cpp | 229 ------------------ test/types/history.cpp | 119 +++++++++ test/types/span.cpp | 49 ++++ test/types/unspent.cpp | 109 +++++++++ 24 files changed, 798 insertions(+), 427 deletions(-) create mode 100644 include/bitcoin/database/types/fee_rate.hpp create mode 100644 include/bitcoin/database/types/header_state.hpp create mode 100644 include/bitcoin/database/types/history.hpp create mode 100644 include/bitcoin/database/types/position.hpp create mode 100644 include/bitcoin/database/types/span.hpp rename include/bitcoin/database/{types.hpp => types/type.hpp} (59%) create mode 100644 include/bitcoin/database/types/types.hpp create mode 100644 include/bitcoin/database/types/unspent.hpp rename src/{types.cpp => types/history.cpp} (59%) create mode 100644 src/types/unspent.cpp delete mode 100644 test/types.cpp create mode 100644 test/types/history.cpp create mode 100644 test/types/span.cpp create mode 100644 test/types/unspent.cpp diff --git a/Makefile.am b/Makefile.am index fd5e7d6e3..5dcaedea9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,6 @@ src_libbitcoin_database_la_SOURCES = \ src/define.cpp \ src/error.cpp \ src/settings.cpp \ - src/types.cpp \ src/file/rotator.cpp \ src/file/utilities.cpp \ src/locks/file_lock.cpp \ @@ -47,7 +46,9 @@ src_libbitcoin_database_la_SOURCES = \ src/memory/map.cpp \ src/memory/utilities.cpp \ src/memory/mman-win32/mman.cpp \ - src/memory/mman-win32/mman.hpp + src/memory/mman-win32/mman.hpp \ + src/types/history.cpp \ + src/types/unspent.cpp # local: test/libbitcoin-database-test #------------------------------------------------------------------------------ @@ -65,7 +66,6 @@ test_libbitcoin_database_test_SOURCES = \ test/store.cpp \ test/test.cpp \ test/test.hpp \ - test/types.cpp \ test/file/rotator.cpp \ test/file/utilities.cpp \ test/locks/file_lock.cpp \ @@ -141,7 +141,10 @@ test_libbitcoin_database_test_SOURCES = \ test/tables/indexes/strong_tx.cpp \ test/tables/optional/address.cpp \ test/tables/optional/filter_bk.cpp \ - test/tables/optional/filter_tx.cpp + test/tables/optional/filter_tx.cpp \ + test/types/history.cpp \ + test/types/span.cpp \ + test/types/unspent.cpp endif WITH_TESTS @@ -171,7 +174,6 @@ include_bitcoin_database_HEADERS = \ include/bitcoin/database/query.hpp \ include/bitcoin/database/settings.hpp \ include/bitcoin/database/store.hpp \ - include/bitcoin/database/types.hpp \ include/bitcoin/database/version.hpp include_bitcoin_database_filedir = ${includedir}/bitcoin/database/file @@ -329,6 +331,17 @@ include_bitcoin_database_tables_optionals_HEADERS = \ include/bitcoin/database/tables/optionals/filter_bk.hpp \ include/bitcoin/database/tables/optionals/filter_tx.hpp +include_bitcoin_database_typesdir = ${includedir}/bitcoin/database/types +include_bitcoin_database_types_HEADERS = \ + include/bitcoin/database/types/fee_rate.hpp \ + include/bitcoin/database/types/header_state.hpp \ + include/bitcoin/database/types/history.hpp \ + include/bitcoin/database/types/position.hpp \ + include/bitcoin/database/types/span.hpp \ + include/bitcoin/database/types/type.hpp \ + include/bitcoin/database/types/types.hpp \ + include/bitcoin/database/types/unspent.hpp + # Custom make targets. #============================================================================== 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 3c94bc21e..45f541e33 100644 --- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj @@ -209,7 +209,9 @@ $(IntDir)test_test.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 510a16a53..4aac69976 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 @@ -29,31 +29,34 @@ {5C4DA53A-8C06-4DA6-0000-000000000006} - {5C4DA53A-8C06-4DA6-0000-000000000008} + {5C4DA53A-8C06-4DA6-0000-000000000009} - {5C4DA53A-8C06-4DA6-0000-000000000009} + {5C4DA53A-8C06-4DA6-0000-00000000000A} - {5C4DA53A-8C06-4DA6-0000-00000000000A} + {5C4DA53A-8C06-4DA6-0000-00000000000B} - {5C4DA53A-8C06-4DA6-0000-00000000000B} + {5C4DA53A-8C06-4DA6-0000-00000000000C} {5C4DA53A-8C06-4DA6-0000-000000000007} - {5C4DA53A-8C06-4DA6-0000-00000000000C} + {5C4DA53A-8C06-4DA6-0000-00000000000D} - {5C4DA53A-8C06-4DA6-0000-00000000000D} + {5C4DA53A-8C06-4DA6-0000-00000000000E} - {5C4DA53A-8C06-4DA6-0000-00000000000E} + {5C4DA53A-8C06-4DA6-0000-00000000000F} - {5C4DA53A-8C06-4DA6-0000-00000000000F} + {5C4DA53A-8C06-4DA6-0000-000000000001} + + + {5C4DA53A-8C06-4DA6-0000-000000000008} @@ -288,8 +291,14 @@ src - - src + + src\types + + + src\types + + + src\types diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj index fd204553d..9e152dc41 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj @@ -138,7 +138,8 @@ $(IntDir)src_memory_utilities.obj - + + @@ -201,7 +202,14 @@ - + + + + + + + + diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters index 7783cf669..f867d21d4 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -8,70 +8,73 @@ - {62D7FBEE-4D52-424A-0000-000000000005} + {62D7FBEE-4D52-424A-0000-000000000006} - {62D7FBEE-4D52-424A-0000-000000000006} + {62D7FBEE-4D52-424A-0000-000000000007} - {62D7FBEE-4D52-424A-0000-000000000007} + {62D7FBEE-4D52-424A-0000-000000000008} - {62D7FBEE-4D52-424A-0000-000000000008} + {62D7FBEE-4D52-424A-0000-000000000009} - {62D7FBEE-4D52-424A-0000-000000000009} + {62D7FBEE-4D52-424A-0000-00000000000A} - {62D7FBEE-4D52-424A-0000-00000000000E} + {62D7FBEE-4D52-424A-0000-000000000001} - {62D7FBEE-4D52-424A-0000-00000000000F} + {62D7FBEE-4D52-424A-0000-000000000002} - {62D7FBEE-4D52-424A-0000-000000000001} + {62D7FBEE-4D52-424A-0000-000000000003} - {62D7FBEE-4D52-424A-0000-000000000002} + {62D7FBEE-4D52-424A-0000-000000000004} - {62D7FBEE-4D52-424A-0000-000000000003} + {62D7FBEE-4D52-424A-0000-000000000005} - {62D7FBEE-4D52-424A-0000-000000000004} + {62D7FBEE-4D52-424A-0000-000000000006} - {62D7FBEE-4D52-424A-0000-000000000005} + {62D7FBEE-4D52-424A-0000-000000000007} - {62D7FBEE-4D52-424A-0000-00000000000A} + {62D7FBEE-4D52-424A-0000-00000000000B} - {62D7FBEE-4D52-424A-0000-00000000000B} + {62D7FBEE-4D52-424A-0000-00000000000C} - {62D7FBEE-4D52-424A-0000-000000000006} + {62D7FBEE-4D52-424A-0000-000000000008} - {62D7FBEE-4D52-424A-0000-00000000000C} + {62D7FBEE-4D52-424A-0000-00000000000D} - {62D7FBEE-4D52-424A-0000-00000000000D} + {62D7FBEE-4D52-424A-0000-00000000000E} - {62D7FBEE-4D52-424A-0000-000000000007} + {62D7FBEE-4D52-424A-0000-000000000009} - {62D7FBEE-4D52-424A-0000-000000000008} + {62D7FBEE-4D52-424A-0000-000000000010} - {62D7FBEE-4D52-424A-0000-000000000009} + {62D7FBEE-4D52-424A-0000-0000000000A1} - {62D7FBEE-4D52-424A-0000-000000000010} + {62D7FBEE-4D52-424A-0000-0000000000B1} + + + {62D7FBEE-4D52-424A-0000-00000000000F} - {62D7FBEE-4D52-424A-0000-0000000000A1} + {62D7FBEE-4D52-424A-0000-0000000000C1} {62D7FBEE-4D52-424A-0000-000000000000} @@ -86,6 +89,9 @@ {62D7FBEE-4D52-424A-0000-000000000003} + {62D7FBEE-4D52-424A-0000-000000000005} + + {62D7FBEE-4D52-424A-0000-000000000004} @@ -123,8 +129,11 @@ src - - src + + src\types + + + src\types @@ -308,8 +317,29 @@ include\bitcoin\database\tables - - include\bitcoin\database + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types include\bitcoin\database diff --git a/include/bitcoin/database.hpp b/include/bitcoin/database.hpp index 2e67ee59b..c7fbe314c 100644 --- a/include/bitcoin/database.hpp +++ b/include/bitcoin/database.hpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -76,5 +75,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #endif diff --git a/include/bitcoin/database/impl/query/address/address_unspent.ipp b/include/bitcoin/database/impl/query/address/address_unspent.ipp index 60e095a56..5571104cc 100644 --- a/include/bitcoin/database/impl/query/address/address_unspent.ipp +++ b/include/bitcoin/database/impl/query/address/address_unspent.ipp @@ -128,7 +128,6 @@ code CLASS::parallel_unspent_transform(const stopper& cancel, bool turbo, return error::success; } - } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 09bd47b83..1eaac3918 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include namespace libbitcoin { namespace database { diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp index d67e1d333..762ec95b9 100644 --- a/include/bitcoin/database/store.hpp +++ b/include/bitcoin/database/store.hpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include namespace libbitcoin { namespace database { diff --git a/include/bitcoin/database/types/fee_rate.hpp b/include/bitcoin/database/types/fee_rate.hpp new file mode 100644 index 000000000..e9b816324 --- /dev/null +++ b/include/bitcoin/database/types/fee_rate.hpp @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_FEE_RATE_HPP +#define LIBBITCOIN_DATABASE_TYPES_FEE_RATE_HPP + +#include + +namespace libbitcoin { +namespace database { + +struct fee_rate +{ + size_t bytes{}; + uint64_t fee{}; +}; + +using fee_rates = std::vector; +using fee_rate_sets = std::vector; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/types/header_state.hpp b/include/bitcoin/database/types/header_state.hpp new file mode 100644 index 000000000..9f75ca151 --- /dev/null +++ b/include/bitcoin/database/types/header_state.hpp @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_HEADER_STATE_HPP +#define LIBBITCOIN_DATABASE_TYPES_HEADER_STATE_HPP + +#include +#include + +namespace libbitcoin { +namespace database { + +struct header_state +{ + header_link link; + code ec; +}; + +using header_states = std::vector; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/types/history.hpp b/include/bitcoin/database/types/history.hpp new file mode 100644 index 000000000..b27f13a1a --- /dev/null +++ b/include/bitcoin/database/types/history.hpp @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_HISTORY_HPP +#define LIBBITCOIN_DATABASE_TYPES_HISTORY_HPP + +#include +#include + +namespace libbitcoin { +namespace database { + +struct BCD_API history +{ + static constexpr size_t rooted_height = zero; + static constexpr size_t unrooted_height = max_size_t; + static constexpr size_t missing_prevout = max_uint64; + static constexpr size_t unconfirmed_position = zero; + + struct less_than + { + bool operator()(const history& a, const history& b) const NOEXCEPT; + }; + + struct equal_to + { + bool operator()(const history& a, const history& b) const NOEXCEPT; + }; + + struct exclude + { + bool operator()(const history& element) const NOEXCEPT; + }; + + static void sort_and_dedup(std::vector& history) NOEXCEPT; + + checkpoint tx{}; + uint64_t fee{}; + size_t position{}; +}; + +using histories = std::vector; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/types/position.hpp b/include/bitcoin/database/types/position.hpp new file mode 100644 index 000000000..cee7615a8 --- /dev/null +++ b/include/bitcoin/database/types/position.hpp @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_POSITION_HPP +#define LIBBITCOIN_DATABASE_TYPES_POSITION_HPP + +#include + +namespace libbitcoin { +namespace database { + +struct position +{ + size_t sibling; + size_t width; +}; + +using positions = std::vector; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/types/span.hpp b/include/bitcoin/database/types/span.hpp new file mode 100644 index 000000000..b5fb639e0 --- /dev/null +++ b/include/bitcoin/database/types/span.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_SPAN_HPP +#define LIBBITCOIN_DATABASE_TYPES_SPAN_HPP + +#include + +namespace libbitcoin { +namespace database { + +struct span +{ + inline size_t size() const NOEXCEPT + { + return system::floored_subtract(end, begin); + } + + size_t begin; + size_t end; +}; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/types.hpp b/include/bitcoin/database/types/type.hpp similarity index 59% rename from include/bitcoin/database/types.hpp rename to include/bitcoin/database/types/type.hpp index d709066dd..b8ff63105 100644 --- a/include/bitcoin/database/types.hpp +++ b/include/bitcoin/database/types/type.hpp @@ -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_TYPES_HPP -#define LIBBITCOIN_DATABASE_TYPES_HPP +#ifndef LIBBITCOIN_DATABASE_TYPES_TYPE_HPP +#define LIBBITCOIN_DATABASE_TYPES_TYPE_HPP #include #include @@ -69,96 +69,21 @@ using data_chunk = system::data_chunk; /// Common system::chain aliases. /// --------------------------------------------------------------------------- +using checkpoint = system::chain::checkpoint; + using inpoint = system::chain::point; -using outpoint = system::chain::outpoint; using inpoints = std::vector; + +using outpoint = system::chain::outpoint; using outpoints = std::vector; -using checkpoint = system::chain::checkpoint; -/// Common carriers. +/// Custom data carriers. /// --------------------------------------------------------------------------- +/// Also: position, header_state, fee_rate, span, unspent, history. -using counts = std::pair; +using heights = std::vector; using sizes = std::pair; -using heights = std_vector; - -struct position { size_t sibling; size_t width; }; -using positions = std::vector; - -struct header_state { header_link link; code ec; }; -using header_states = std::vector; - -struct fee_rate { size_t bytes{}; uint64_t fee{}; }; -using fee_rates = std::vector; -using fee_rate_sets = std::vector; - -struct span -{ - inline size_t size() const NOEXCEPT - { - return system::floored_subtract(end, begin); - } - - size_t begin; - size_t end; -}; - -struct BCD_API unspent -{ - static constexpr size_t excluded_position = max_size_t; - - struct less_than - { - bool operator()(const unspent& a, const unspent& b) const NOEXCEPT; - }; - - struct equal_to - { - bool operator()(const unspent& a, const unspent& b) const NOEXCEPT; - }; - - struct exclude - { - bool operator()(const unspent& element) const NOEXCEPT; - }; - - static void sort_and_dedup(std::vector& unspent) NOEXCEPT; - - outpoint tx{}; - size_t height{}; - size_t position{}; -}; -using unspents = std::vector; - -struct BCD_API history -{ - static constexpr size_t rooted_height = zero; - static constexpr size_t unrooted_height = max_size_t; - static constexpr size_t missing_prevout = max_uint64; - static constexpr size_t unconfirmed_position = zero; - - struct less_than - { - bool operator()(const history& a, const history& b) const NOEXCEPT; - }; - - struct equal_to - { - bool operator()(const history& a, const history& b) const NOEXCEPT; - }; - - struct exclude - { - bool operator()(const history& element) const NOEXCEPT; - }; - - static void sort_and_dedup(std::vector& history) NOEXCEPT; - - checkpoint tx{}; - uint64_t fee{}; - size_t position{}; -}; -using histories = std::vector; +using counts = std::pair; } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/types/types.hpp b/include/bitcoin/database/types/types.hpp new file mode 100644 index 000000000..14d6fa215 --- /dev/null +++ b/include/bitcoin/database/types/types.hpp @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_TYPES_HPP +#define LIBBITCOIN_DATABASE_TYPES_TYPES_HPP + +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/bitcoin/database/types/unspent.hpp b/include/bitcoin/database/types/unspent.hpp new file mode 100644 index 000000000..1f50e3f08 --- /dev/null +++ b/include/bitcoin/database/types/unspent.hpp @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2011-2026 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_TYPES_UNSPENT_HPP +#define LIBBITCOIN_DATABASE_TYPES_UNSPENT_HPP + +#include +#include + +namespace libbitcoin { +namespace database { + +struct BCD_API unspent +{ + static constexpr size_t excluded_position = max_size_t; + + struct less_than + { + bool operator()(const unspent& a, const unspent& b) const NOEXCEPT; + }; + + struct equal_to + { + bool operator()(const unspent& a, const unspent& b) const NOEXCEPT; + }; + + struct exclude + { + bool operator()(const unspent& element) const NOEXCEPT; + }; + + static void sort_and_dedup(std::vector& unspent) NOEXCEPT; + + outpoint tx{}; + size_t height{}; + size_t position{}; +}; + +using unspents = std::vector; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/src/define.cpp b/src/define.cpp index 5b776096a..49a387b3d 100644 --- a/src/define.cpp +++ b/src/define.cpp @@ -28,6 +28,6 @@ // /memory : /file // /primitives : /memory // /tables : /primitives -// types : /tables -// store : types settings /locks -// query : types settings +// /types : /tables +// store : /types settings /locks +// query : /types settings diff --git a/src/types.cpp b/src/types/history.cpp similarity index 59% rename from src/types.cpp rename to src/types/history.cpp index c472bbc92..45f0ccfa3 100644 --- a/src/types.cpp +++ b/src/types/history.cpp @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#include +#include #include #include @@ -24,9 +24,6 @@ namespace libbitcoin { namespace database { -// history -// ---------------------------------------------------------------------------- - // local inline bool hash_less_than(const hash_digest& a, const hash_digest& b) NOEXCEPT { @@ -54,7 +51,8 @@ inline bool hash_less_than(const hash_digest& a, const hash_digest& b) NOEXCEPT return false; } -bool history::less_than::operator()(const history& a, const history& b) const NOEXCEPT +bool history::less_than::operator()(const history& a, + const history& b) const NOEXCEPT { using namespace system; const auto a_height = a.tx.height(); @@ -78,10 +76,10 @@ bool history::less_than::operator()(const history& a, const history& b) const NO return hash_less_than(a.tx.hash(), b.tx.hash()); } -bool history::equal_to::operator()(const history& a, const history& b) const NOEXCEPT +bool history::equal_to::operator()(const history& a, + const history& b) const NOEXCEPT { - history::less_than lesser; - return !lesser(a, b) && !lesser(b, a); + return !less_than{}(a, b) && !less_than{}(b, a); } bool history::exclude::operator()(const history& element) const NOEXCEPT @@ -98,59 +96,5 @@ void history::sort_and_dedup(std::vector& out) NOEXCEPT out.erase(duplicates, out.end()); } -// unspent -// ---------------------------------------------------------------------------- - -bool unspent::less_than::operator()(const unspent& a, - const unspent& b) const NOEXCEPT -{ - const auto a_point = a.tx.point(); - const auto b_point = b.tx.point(); - const bool a_confirmed = !is_zero(a.height); - const bool b_confirmed = !is_zero(b.height); - - // Confirmed before unconfirmed. - if (a_confirmed != b_confirmed) - return a_confirmed; - - if (a_confirmed) - { - // Chain.block height ascending (x < y). - if (a.height != b.height) - return a.height < b.height; - - // Block.tx position ascending. - if (a.position != b.position) - return a.position < b.position; - - // Tx.output index ascending. - return a_point.index() < b_point.index(); - } - - // Unconfirmed have 0 height/position, arbitrary sort (hash:index). - return a_point < b_point; -} - -bool unspent::equal_to::operator()(const unspent& a, - const unspent& b) const NOEXCEPT -{ - unspent::less_than lesser; - return !lesser(a, b) && !lesser(b, a); -} - -bool unspent::exclude::operator()(const unspent& element) const NOEXCEPT -{ - return !element.tx.is_valid(); -} - -void unspent::sort_and_dedup(std::vector& out) NOEXCEPT -{ - auto excluded = std::remove_if(out.begin(), out.end(), unspent::exclude{}); - out.erase(excluded, out.end()); - std::sort(out.begin(), out.end(), unspent::less_than{}); - auto duplicates = std::unique(out.begin(), out.end(), unspent::equal_to{}); - out.erase(duplicates, out.end()); -} - } // namespace database } // namespace libbitcoin diff --git a/src/types/unspent.cpp b/src/types/unspent.cpp new file mode 100644 index 000000000..ad5c05168 --- /dev/null +++ b/src/types/unspent.cpp @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2011-2026 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 + +#include +#include + +namespace libbitcoin { +namespace database { + +bool unspent::less_than::operator()(const unspent& a, + const unspent& b) const NOEXCEPT +{ + const auto a_point = a.tx.point(); + const auto b_point = b.tx.point(); + const bool a_confirmed = !is_zero(a.height); + const bool b_confirmed = !is_zero(b.height); + + // Confirmed before unconfirmed. + if (a_confirmed != b_confirmed) + return a_confirmed; + + if (a_confirmed) + { + // Chain.block height ascending (x < y). + if (a.height != b.height) + return a.height < b.height; + + // Block.tx position ascending. + if (a.position != b.position) + return a.position < b.position; + + // Tx.output index ascending. + return a_point.index() < b_point.index(); + } + + // Unconfirmed have 0 height/position, arbitrary sort (hash:index). + return a_point < b_point; +} + +bool unspent::equal_to::operator()(const unspent& a, + const unspent& b) const NOEXCEPT +{ + return !less_than{}(a, b) && !less_than{}(b, a); +} + +bool unspent::exclude::operator()(const unspent& element) const NOEXCEPT +{ + return !element.tx.is_valid(); +} + +void unspent::sort_and_dedup(std::vector& out) NOEXCEPT +{ + auto excluded = std::remove_if(out.begin(), out.end(), unspent::exclude{}); + out.erase(excluded, out.end()); + std::sort(out.begin(), out.end(), unspent::less_than{}); + auto duplicates = std::unique(out.begin(), out.end(), unspent::equal_to{}); + out.erase(duplicates, out.end()); +} + +} // namespace database +} // namespace libbitcoin diff --git a/test/types.cpp b/test/types.cpp deleted file mode 100644 index 519f5c95e..000000000 --- a/test/types.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/** - * Copyright (c) 2011-2026 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" - -BOOST_AUTO_TEST_SUITE(types_tests) - -using namespace system; - -// span.size - -BOOST_AUTO_TEST_CASE(types__span_size__default__zero) -{ - const span instance{}; - BOOST_REQUIRE_EQUAL(instance.size(), 0u); -} - -BOOST_AUTO_TEST_CASE(types__span_size__empty_range__zero) -{ - const span instance{ 5, 5 }; - BOOST_REQUIRE_EQUAL(instance.size(), 0u); -} - -BOOST_AUTO_TEST_CASE(types__span_size__non_empty_range__expected) -{ - const span instance{ 10, 25 }; - BOOST_REQUIRE_EQUAL(instance.size(), 15u); -} - -BOOST_AUTO_TEST_CASE(types__span_size__negative_range__zero) -{ - const span instance{ 100, 30 }; - BOOST_REQUIRE_EQUAL(instance.size(), 0u); -} - -// unspent.less_than.operator() - -BOOST_AUTO_TEST_CASE(types__unspent_less_than__confirmed_before_unconfirmed__expected) -{ - const unspent a{ {}, 42, 7 }; - const unspent b{ {}, 0, 0 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__unspent_less_than__confirmed_height_ascending__expected) -{ - const unspent a{ {}, 100, 5 }; - const unspent b{ {}, 200, 5 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__unspent_less_than__confirmed_position_ascending__expected) -{ - const unspent a{ {}, 100, 3 }; - const unspent b{ {}, 100, 10 }; - BOOST_REQUIRE(unspent::less_than{}(a, b)); -} - -BOOST_AUTO_TEST_CASE(types__unspent_less_than__confirmed_output_index_ascending__expected) -{ - const outpoint p1{ { {}, 0 }, 0 }; - const outpoint p2{ { {}, 5 }, 0 }; - const unspent a{ p1, 100, 10 }; - const unspent b{ p2, 100, 10 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, b)); -} - -BOOST_AUTO_TEST_CASE(types__unspent_less_than__unconfirmed_outpoint_ascending__expected) -{ - const outpoint p1{ { {}, 3 }, 0 }; - const outpoint p2{ { {}, 8 }, 0 }; - const unspent a{ p1, 0, 0 }; - const unspent b{ p2, 0, 0 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, a)); -} - -// history.less_than.operator() - -BOOST_AUTO_TEST_CASE(types__history_less_than__confirmed_before_unconfirmed__expected) -{ - const history a{ { hash_digest{}, 42 }, 0, 7 }; - const history b{ { hash_digest{}, 0 }, 0, 0 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__history_less_than__confirmed_height_ascending__expected) -{ - const history a{ { hash_digest{}, 100 }, 0, 5 }; - const history b{ { hash_digest{}, 200 }, 0, 5 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__history_less_than__confirmed_position_ascending__expected) -{ - const history a{ { hash_digest{}, 100 }, 0, 3 }; - const history b{ { hash_digest{}, 100 }, 0, 10 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__history_less_than__unconfirmed_hash_high_nibble_difference__expected) -{ - constexpr auto hash1 = base16_hash("0000000000000000000000000000000000000000000000000000000000000000"); - constexpr auto hash2 = base16_hash("0000000000000000000000000000000000000000000000000000000000000010"); - const history a{ { hash1, 0 }, 0, 0 }; - const history b{ { hash2, 0 }, 0, 0 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__history_less_than__unconfirmed_hash_low_nibble_difference__expected) -{ - constexpr auto hash1 = base16_hash("0000000000000000000000000000000000000000000000000000000000000000"); - constexpr auto hash2 = base16_hash("0000000000000000000000000000000000000000000000000000000000000001"); - const history a{ { hash1, 0 }, 0, 0 }; - const history b{ { hash2, 0 }, 0, 0 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); -} - -BOOST_AUTO_TEST_CASE(types__history_less_than__unconfirmed_hash_identical__expected) -{ - constexpr auto hash = base16_hash("0000000000000000000000000000000000000000000000000000000000000000"); - const history value{ { hash, 0 }, 0, 0 }; - BOOST_REQUIRE(!history::less_than{}(value, value)); -} - -// history.sort_and_dedup() - -BOOST_AUTO_TEST_CASE(types__history_sort_and_dedup__unsorted_with_duplicates_mixed__sorted_and_deduped) -{ - constexpr auto h1 = base16_hash("0000000000000000000000000000000000000000000000000000000000000001"); - constexpr auto h2 = base16_hash("0000000000000000000000000000000000000000000000000000000000000002"); - std::vector values - { - { { h2, 0 }, 0, 0 }, // unconfirmed - { { hash_digest{}, 200 }, 0, 5 }, // confirmed - { { h1, 0 }, 0, 0 }, // unconfirmed (duplicate will be removed) - { { h1, 0 }, 0, 0 }, // unconfirmed duplicate - { { hash_digest{}, 100 }, 0, 10 }, // confirmed - { { hash_digest{}, 100 }, 0, 10 } // confirmed duplicate - }; - - history::sort_and_dedup(values); - BOOST_REQUIRE_EQUAL(values.size(), 4u); - BOOST_REQUIRE_EQUAL(values[0].tx.height(), 100u); // confirmed, lowest height - BOOST_REQUIRE_EQUAL(values[1].tx.height(), 200u); // confirmed - BOOST_REQUIRE_EQUAL(values[2].tx.height(), 0u); // unconfirmed (h1) - BOOST_REQUIRE_EQUAL(values[3].tx.height(), 0u); // unconfirmed (h2) -} - -BOOST_AUTO_TEST_CASE(history__sort_and_dedup__exclusions__removes_excluded_items) -{ - std::vector items - { - history{ checkpoint{}, 0, max_size_t }, // excluded (default checkpoint) - history{ checkpoint{}, 0, max_size_t }, // excluded (default checkpoint) - history{ { hash_digest{}, 3 }, 0, 10 }, // valid - history{ { hash_digest{}, 3 }, 0, 5 }, // valid (same height, lower position) - history{ { hash_digest{}, 3 }, 0, 10 } // duplicate - }; - - history::sort_and_dedup(items); - BOOST_REQUIRE_EQUAL(items.size(), 2u); - BOOST_REQUIRE_EQUAL(items[0].position, 5u); - BOOST_REQUIRE_EQUAL(items[1].position, 10u); -} - -// unspent.sort_and_dedup() - -BOOST_AUTO_TEST_CASE(types__unspent_sort_and_dedup__unsorted_with_duplicates_mixed__sorted_and_deduped) -{ - const outpoint lo{ { {}, 0 }, 0 }; - const outpoint hi{ { {}, 5 }, 0 }; - std::vector values - { - { hi, 0, 0 }, // unconfirmed - { lo, 200, 3 }, // confirmed - { lo, 100, 5 }, // confirmed - { lo, 100, 5 }, // confirmed duplicate - { hi, 0, 0 } // unconfirmed duplicate - }; - - unspent::sort_and_dedup(values); - BOOST_REQUIRE_EQUAL(values.size(), 3u); - BOOST_REQUIRE_EQUAL(values[0].height, 100u); // confirmed, lowest height - BOOST_REQUIRE_EQUAL(values[1].height, 200u); // confirmed - BOOST_REQUIRE_EQUAL(values[2].height, 0u); // unconfirmed -} - -BOOST_AUTO_TEST_CASE(unspent__sort_and_dedup__exclusions__removes_excluded_items) -{ - unspents items - { - unspent{ outpoint{}, 10, max_size_t }, // excluded (default outpoint) - unspent{ outpoint{}, 200, max_size_t }, // excluded (default outpoint) - unspent{ { {}, 3 }, 50, 10 }, // valid confirmed - unspent{ { {}, 4 }, 50, 5 }, // valid confirmed (same height, lower position) - unspent{ { {}, 3 }, 50, 10 } // duplicate - }; - - unspent::sort_and_dedup(items); - BOOST_REQUIRE_EQUAL(items.size(), 2u); - BOOST_REQUIRE_EQUAL(items[0].position, 5u); - BOOST_REQUIRE_EQUAL(items[1].position, 10u); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/types/history.cpp b/test/types/history.cpp new file mode 100644 index 000000000..519387f5a --- /dev/null +++ b/test/types/history.cpp @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2011-2026 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" + +BOOST_AUTO_TEST_SUITE(history_tests) + +using namespace system; + +// less_than.operator() + +BOOST_AUTO_TEST_CASE(history__less_than__confirmed_before_unconfirmed__expected) +{ + const history a{ { hash_digest{}, 42 }, 0, 7 }; + const history b{ { hash_digest{}, 0 }, 0, 0 }; + BOOST_REQUIRE( history::less_than{}(a, b)); + BOOST_REQUIRE(!history::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(history__less_than__confirmed_height_ascending__expected) +{ + const history a{ { hash_digest{}, 100 }, 0, 5 }; + const history b{ { hash_digest{}, 200 }, 0, 5 }; + BOOST_REQUIRE( history::less_than{}(a, b)); + BOOST_REQUIRE(!history::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(history__less_than__confirmed_position_ascending__expected) +{ + const history a{ { hash_digest{}, 100 }, 0, 3 }; + const history b{ { hash_digest{}, 100 }, 0, 10 }; + BOOST_REQUIRE( history::less_than{}(a, b)); + BOOST_REQUIRE(!history::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_high_nibble_difference__expected) +{ + constexpr hash_digest hash1{ 0x00 }; + constexpr hash_digest hash2{ 0x10 }; + const history a{ { hash1, 0 }, 0, 0 }; + const history b{ { hash2, 0 }, 0, 0 }; + BOOST_REQUIRE( history::less_than{}(a, b)); + BOOST_REQUIRE(!history::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_low_nibble_difference__expected) +{ + constexpr hash_digest hash1{ 0x00 }; + constexpr hash_digest hash2{ 0x01 }; + const history a{ { hash1, 0 }, 0, 0 }; + const history b{ { hash2, 0 }, 0, 0 }; + BOOST_REQUIRE( history::less_than{}(a, b)); + BOOST_REQUIRE(!history::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_identical__expected) +{ + constexpr hash_digest hash{ 0x00 }; + const history value{ { hash, 0 }, 0, 0 }; + BOOST_REQUIRE(!history::less_than{}(value, value)); +} + +// sort_and_dedup + +BOOST_AUTO_TEST_CASE(history__sort_and_dedup__unsorted_with_duplicates_mixed__sorted_and_deduped) +{ + constexpr hash_digest h1{ 0x01 }; + constexpr hash_digest h2{ 0x02 }; + std::vector values + { + { { h2, 0 }, 0, 0 }, // unconfirmed + { { hash_digest{}, 200 }, 0, 5 }, // confirmed + { { h1, 0 }, 0, 0 }, // unconfirmed (duplicate will be removed) + { { h1, 0 }, 0, 0 }, // unconfirmed duplicate + { { hash_digest{}, 100 }, 0, 10 }, // confirmed + { { hash_digest{}, 100 }, 0, 10 } // confirmed duplicate + }; + + history::sort_and_dedup(values); + BOOST_REQUIRE_EQUAL(values.size(), 4u); + BOOST_REQUIRE_EQUAL(values[0].tx.height(), 100u); // confirmed, lowest height + BOOST_REQUIRE_EQUAL(values[1].tx.height(), 200u); // confirmed + BOOST_REQUIRE_EQUAL(values[2].tx.height(), 0u); // unconfirmed (h1) + BOOST_REQUIRE_EQUAL(values[3].tx.height(), 0u); // unconfirmed (h2) +} + +BOOST_AUTO_TEST_CASE(history__sort_and_dedup__exclusions__removes_excluded_items) +{ + std::vector items + { + history{ checkpoint{}, 0, max_size_t }, // excluded (default checkpoint) + history{ checkpoint{}, 0, max_size_t }, // excluded (default checkpoint) + history{ { hash_digest{}, 3 }, 0, 10 }, // valid + history{ { hash_digest{}, 3 }, 0, 5 }, // valid (same height, lower position) + history{ { hash_digest{}, 3 }, 0, 10 } // duplicate + }; + + history::sort_and_dedup(items); + BOOST_REQUIRE_EQUAL(items.size(), 2u); + BOOST_REQUIRE_EQUAL(items[0].position, 5u); + BOOST_REQUIRE_EQUAL(items[1].position, 10u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/types/span.cpp b/test/types/span.cpp new file mode 100644 index 000000000..baf2f7a1e --- /dev/null +++ b/test/types/span.cpp @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2011-2026 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" + +BOOST_AUTO_TEST_SUITE(span_tests) + +using namespace system; + +BOOST_AUTO_TEST_CASE(span__size__default__zero) +{ + const span instance{}; + BOOST_REQUIRE_EQUAL(instance.size(), 0u); +} + +BOOST_AUTO_TEST_CASE(span__size__empty_range__zero) +{ + const span instance{ 5, 5 }; + BOOST_REQUIRE_EQUAL(instance.size(), 0u); +} + +BOOST_AUTO_TEST_CASE(span__size__non_empty_range__expected) +{ + const span instance{ 10, 25 }; + BOOST_REQUIRE_EQUAL(instance.size(), 15u); +} + +BOOST_AUTO_TEST_CASE(span__size__negative_range__zero) +{ + const span instance{ 100, 30 }; + BOOST_REQUIRE_EQUAL(instance.size(), 0u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/types/unspent.cpp b/test/types/unspent.cpp new file mode 100644 index 000000000..3493beb13 --- /dev/null +++ b/test/types/unspent.cpp @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2011-2026 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" + +BOOST_AUTO_TEST_SUITE(unspent_tests) + +using namespace system; + +// less_than.operator() + +BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_before_unconfirmed__expected) +{ + const unspent a{ {}, 42, 7 }; + const unspent b{ {}, 0, 0 }; + BOOST_REQUIRE( unspent::less_than{}(a, b)); + BOOST_REQUIRE(!unspent::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_height_ascending__expected) +{ + const unspent a{ {}, 100, 5 }; + const unspent b{ {}, 200, 5 }; + BOOST_REQUIRE( unspent::less_than{}(a, b)); + BOOST_REQUIRE(!unspent::less_than{}(b, a)); +} + +BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_position_ascending__expected) +{ + const unspent a{ {}, 100, 3 }; + const unspent b{ {}, 100, 10 }; + BOOST_REQUIRE(unspent::less_than{}(a, b)); +} + +BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_output_index_ascending__expected) +{ + const outpoint p1{ { {}, 0 }, 0 }; + const outpoint p2{ { {}, 5 }, 0 }; + const unspent a{ p1, 100, 10 }; + const unspent b{ p2, 100, 10 }; + BOOST_REQUIRE( unspent::less_than{}(a, b)); + BOOST_REQUIRE(!unspent::less_than{}(b, b)); +} + +BOOST_AUTO_TEST_CASE(unspent__less_than__unconfirmed_outpoint_ascending__expected) +{ + const outpoint p1{ { {}, 3 }, 0 }; + const outpoint p2{ { {}, 8 }, 0 }; + const unspent a{ p1, 0, 0 }; + const unspent b{ p2, 0, 0 }; + BOOST_REQUIRE( unspent::less_than{}(a, b)); + BOOST_REQUIRE(!unspent::less_than{}(b, a)); +} + +// sort_and_dedup + +BOOST_AUTO_TEST_CASE(unspent__sort_and_dedup__unsorted_with_duplicates_mixed__sorted_and_deduped) +{ + const outpoint lo{ { {}, 0 }, 0 }; + const outpoint hi{ { {}, 5 }, 0 }; + std::vector values + { + { hi, 0, 0 }, // unconfirmed + { lo, 200, 3 }, // confirmed + { lo, 100, 5 }, // confirmed + { lo, 100, 5 }, // confirmed duplicate + { hi, 0, 0 } // unconfirmed duplicate + }; + + unspent::sort_and_dedup(values); + BOOST_REQUIRE_EQUAL(values.size(), 3u); + BOOST_REQUIRE_EQUAL(values[0].height, 100u); // confirmed, lowest height + BOOST_REQUIRE_EQUAL(values[1].height, 200u); // confirmed + BOOST_REQUIRE_EQUAL(values[2].height, 0u); // unconfirmed +} + +BOOST_AUTO_TEST_CASE(unspent__sort_and_dedup__exclusions__removes_excluded_items) +{ + unspents items + { + unspent{ outpoint{}, 10, max_size_t }, // excluded (default outpoint) + unspent{ outpoint{}, 200, max_size_t }, // excluded (default outpoint) + unspent{ { {}, 3 }, 50, 10 }, // valid confirmed + unspent{ { {}, 4 }, 50, 5 }, // valid confirmed (same height, lower position) + unspent{ { {}, 3 }, 50, 10 } // duplicate + }; + + unspent::sort_and_dedup(items); + BOOST_REQUIRE_EQUAL(items.size(), 2u); + BOOST_REQUIRE_EQUAL(items[0].position, 5u); + BOOST_REQUIRE_EQUAL(items[1].position, 10u); +} + +BOOST_AUTO_TEST_SUITE_END() From 3e6a82e68435f1864f4210f7867cef2db964a06c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 13 Apr 2026 14:03:56 -0400 Subject: [PATCH 2/2] Change history and unspent to use < and == operators. --- include/bitcoin/database/types/history.hpp | 18 ++-------- include/bitcoin/database/types/unspent.hpp | 18 ++-------- src/types/history.cpp | 25 ++++++++------ src/types/unspent.cpp | 25 ++++++++------ test/types/history.cpp | 39 ++++++++++++++++------ test/types/unspent.cpp | 36 +++++++++++++++----- 6 files changed, 89 insertions(+), 72 deletions(-) diff --git a/include/bitcoin/database/types/history.hpp b/include/bitcoin/database/types/history.hpp index b27f13a1a..3dd7dfe27 100644 --- a/include/bitcoin/database/types/history.hpp +++ b/include/bitcoin/database/types/history.hpp @@ -32,23 +32,11 @@ struct BCD_API history static constexpr size_t missing_prevout = max_uint64; static constexpr size_t unconfirmed_position = zero; - struct less_than - { - bool operator()(const history& a, const history& b) const NOEXCEPT; - }; - - struct equal_to - { - bool operator()(const history& a, const history& b) const NOEXCEPT; - }; - - struct exclude - { - bool operator()(const history& element) const NOEXCEPT; - }; - static void sort_and_dedup(std::vector& history) NOEXCEPT; + bool operator<(const history& other) const NOEXCEPT; + bool operator==(const history& other) const NOEXCEPT; + checkpoint tx{}; uint64_t fee{}; size_t position{}; diff --git a/include/bitcoin/database/types/unspent.hpp b/include/bitcoin/database/types/unspent.hpp index 1f50e3f08..259c1a3e2 100644 --- a/include/bitcoin/database/types/unspent.hpp +++ b/include/bitcoin/database/types/unspent.hpp @@ -29,23 +29,11 @@ struct BCD_API unspent { static constexpr size_t excluded_position = max_size_t; - struct less_than - { - bool operator()(const unspent& a, const unspent& b) const NOEXCEPT; - }; - - struct equal_to - { - bool operator()(const unspent& a, const unspent& b) const NOEXCEPT; - }; - - struct exclude - { - bool operator()(const unspent& element) const NOEXCEPT; - }; - static void sort_and_dedup(std::vector& unspent) NOEXCEPT; + bool operator<(const unspent& other) const NOEXCEPT; + bool operator==(const unspent& other) const NOEXCEPT; + outpoint tx{}; size_t height{}; size_t position{}; diff --git a/src/types/history.cpp b/src/types/history.cpp index 45f0ccfa3..ff283cd91 100644 --- a/src/types/history.cpp +++ b/src/types/history.cpp @@ -51,8 +51,8 @@ inline bool hash_less_than(const hash_digest& a, const hash_digest& b) NOEXCEPT return false; } -bool history::less_than::operator()(const history& a, - const history& b) const NOEXCEPT +// local +inline bool less_than(const history& a, const history& b) NOEXCEPT { using namespace system; const auto a_height = a.tx.height(); @@ -76,24 +76,27 @@ bool history::less_than::operator()(const history& a, return hash_less_than(a.tx.hash(), b.tx.hash()); } -bool history::equal_to::operator()(const history& a, - const history& b) const NOEXCEPT +bool history::operator<(const history& other) const NOEXCEPT { - return !less_than{}(a, b) && !less_than{}(b, a); + return less_than(*this, other); } -bool history::exclude::operator()(const history& element) const NOEXCEPT +bool history::operator==(const history& other) const NOEXCEPT { - return !element.tx.is_valid(); + return !(*this < other) && !(other < *this); } void history::sort_and_dedup(std::vector& out) NOEXCEPT { - auto excluded = std::remove_if(out.begin(), out.end(), history::exclude{}); + const auto excluded = std::remove_if(out.begin(), out.end(), + [](const history& element) NOEXCEPT + { + return !element.tx.is_valid(); + }); + out.erase(excluded, out.end()); - std::sort(out.begin(), out.end(), history::less_than{}); - auto duplicates = std::unique(out.begin(), out.end(), history::equal_to{}); - out.erase(duplicates, out.end()); + std::sort(out.begin(), out.end()); + out.erase(std::unique(out.begin(), out.end()), out.end()); } } // namespace database diff --git a/src/types/unspent.cpp b/src/types/unspent.cpp index ad5c05168..c7ac0824e 100644 --- a/src/types/unspent.cpp +++ b/src/types/unspent.cpp @@ -24,8 +24,8 @@ namespace libbitcoin { namespace database { -bool unspent::less_than::operator()(const unspent& a, - const unspent& b) const NOEXCEPT +// local +bool less_than(const unspent& a, const unspent& b) NOEXCEPT { const auto a_point = a.tx.point(); const auto b_point = b.tx.point(); @@ -54,24 +54,27 @@ bool unspent::less_than::operator()(const unspent& a, return a_point < b_point; } -bool unspent::equal_to::operator()(const unspent& a, - const unspent& b) const NOEXCEPT +bool unspent::operator<(const unspent& other) const NOEXCEPT { - return !less_than{}(a, b) && !less_than{}(b, a); + return less_than(*this, other); } -bool unspent::exclude::operator()(const unspent& element) const NOEXCEPT +bool unspent::operator==(const unspent& other) const NOEXCEPT { - return !element.tx.is_valid(); + return !(*this < other) && !(other < *this); } void unspent::sort_and_dedup(std::vector& out) NOEXCEPT { - auto excluded = std::remove_if(out.begin(), out.end(), unspent::exclude{}); + const auto excluded = std::remove_if(out.begin(), out.end(), + [](const unspent& element) NOEXCEPT + { + return !element.tx.is_valid(); + }); + out.erase(excluded, out.end()); - std::sort(out.begin(), out.end(), unspent::less_than{}); - auto duplicates = std::unique(out.begin(), out.end(), unspent::equal_to{}); - out.erase(duplicates, out.end()); + std::sort(out.begin(), out.end()); + out.erase(std::unique(out.begin(), out.end()), out.end()); } } // namespace database diff --git a/test/types/history.cpp b/test/types/history.cpp index 519387f5a..f3ce74a59 100644 --- a/test/types/history.cpp +++ b/test/types/history.cpp @@ -28,24 +28,24 @@ BOOST_AUTO_TEST_CASE(history__less_than__confirmed_before_unconfirmed__expected) { const history a{ { hash_digest{}, 42 }, 0, 7 }; const history b{ { hash_digest{}, 0 }, 0, 0 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(history__less_than__confirmed_height_ascending__expected) { const history a{ { hash_digest{}, 100 }, 0, 5 }; const history b{ { hash_digest{}, 200 }, 0, 5 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(history__less_than__confirmed_position_ascending__expected) { const history a{ { hash_digest{}, 100 }, 0, 3 }; const history b{ { hash_digest{}, 100 }, 0, 10 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_high_nibble_difference__expected) @@ -54,8 +54,8 @@ BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_high_nibble_difference constexpr hash_digest hash2{ 0x10 }; const history a{ { hash1, 0 }, 0, 0 }; const history b{ { hash2, 0 }, 0, 0 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_low_nibble_difference__expected) @@ -64,15 +64,32 @@ BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_low_nibble_difference_ constexpr hash_digest hash2{ 0x01 }; const history a{ { hash1, 0 }, 0, 0 }; const history b{ { hash2, 0 }, 0, 0 }; - BOOST_REQUIRE( history::less_than{}(a, b)); - BOOST_REQUIRE(!history::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(history__less_than__unconfirmed_hash_identical__expected) { constexpr hash_digest hash{ 0x00 }; const history value{ { hash, 0 }, 0, 0 }; - BOOST_REQUIRE(!history::less_than{}(value, value)); + BOOST_REQUIRE(!(value < value)); +} + +BOOST_AUTO_TEST_CASE(history__equality__distinct__false) +{ + constexpr hash_digest hash1{ 0x00 }; + constexpr hash_digest hash2{ 0x01 }; + const history a{ { hash1, 0 }, 0, 0 }; + const history b{ { hash2, 0 }, 0, 0 }; + BOOST_REQUIRE(!(a == b)); + BOOST_REQUIRE(!(b == a)); +} + +BOOST_AUTO_TEST_CASE(history__equality__same__true) +{ + constexpr hash_digest hash{ 0x01 }; + const history a{ { hash, 1 }, 2, 3 }; + BOOST_REQUIRE(a == a); } // sort_and_dedup diff --git a/test/types/unspent.cpp b/test/types/unspent.cpp index 3493beb13..b405e0a7f 100644 --- a/test/types/unspent.cpp +++ b/test/types/unspent.cpp @@ -28,23 +28,24 @@ BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_before_unconfirmed__expected) { const unspent a{ {}, 42, 7 }; const unspent b{ {}, 0, 0 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_height_ascending__expected) { const unspent a{ {}, 100, 5 }; const unspent b{ {}, 200, 5 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_position_ascending__expected) { const unspent a{ {}, 100, 3 }; const unspent b{ {}, 100, 10 }; - BOOST_REQUIRE(unspent::less_than{}(a, b)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_output_index_ascending__expected) @@ -53,8 +54,8 @@ BOOST_AUTO_TEST_CASE(unspent__less_than__confirmed_output_index_ascending__expec const outpoint p2{ { {}, 5 }, 0 }; const unspent a{ p1, 100, 10 }; const unspent b{ p2, 100, 10 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, b)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); } BOOST_AUTO_TEST_CASE(unspent__less_than__unconfirmed_outpoint_ascending__expected) @@ -63,8 +64,25 @@ BOOST_AUTO_TEST_CASE(unspent__less_than__unconfirmed_outpoint_ascending__expecte const outpoint p2{ { {}, 8 }, 0 }; const unspent a{ p1, 0, 0 }; const unspent b{ p2, 0, 0 }; - BOOST_REQUIRE( unspent::less_than{}(a, b)); - BOOST_REQUIRE(!unspent::less_than{}(b, a)); + BOOST_REQUIRE(a < b); + BOOST_REQUIRE(!(b < a)); +} + +BOOST_AUTO_TEST_CASE(unspent__equality__distinct__false) +{ + const outpoint p1{ { { 42, 0 }, 7 }, 42 }; + const outpoint p2{ { { 24, 1 }, 5 }, 19 }; + const unspent a{ p1, 101, 10 }; + const unspent b{ p2, 100, 11 }; + BOOST_REQUIRE(!(a == b)); + BOOST_REQUIRE(!(b == a)); +} + +BOOST_AUTO_TEST_CASE(unspent__equality__same__true) +{ + const outpoint point{ { { 42, 0 }, 5 }, 42 }; + const unspent a{ point, 100, 10 }; + BOOST_REQUIRE(a == a); } // sort_and_dedup