From f1f905ddb7a8c0a7f1970247aa3bcbed7fcb8741 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 23 Apr 2026 11:47:58 -0400 Subject: [PATCH 1/3] Optimize history by not getting height twice. --- .../impl/query/address/address_history.ipp | 27 ++++++++-------- .../impl/query/address/address_unspent.ipp | 19 ++++++------ .../bitcoin/database/impl/query/confirmed.ipp | 2 +- .../impl/query/consensus/consensus_forks.ipp | 18 +++++++++++ include/bitcoin/database/query.hpp | 1 + test/query/confirmed.cpp | 31 +++++++++++++++++++ 6 files changed, 75 insertions(+), 23 deletions(-) diff --git a/include/bitcoin/database/impl/query/address/address_history.ipp b/include/bitcoin/database/impl/query/address/address_history.ipp index 75065cbb3..853df2e8a 100644 --- a/include/bitcoin/database/impl/query/address/address_history.ipp +++ b/include/bitcoin/database/impl/query/address/address_history.ipp @@ -152,13 +152,8 @@ history CLASS::get_tx_history(hash_digest&& key, auto position = history::unconfirmed_position; const auto block = find_strong(link); - if (is_confirmed_block(block)) - { - if (!get_height(height, block) || - !get_tx_position(position, link, block)) - return {}; - } - else + const auto at = get_confirmed_height(block); + if (at.is_terminal()) { if (!get_tx_fee(fee, link)) fee = history::missing_prevout; @@ -166,6 +161,12 @@ history CLASS::get_tx_history(hash_digest&& key, if (is_confirmed_all_prevouts(link)) height = history::rooted_height; } + else + { + height = at.value; + if (!get_tx_position(position, link, block)) + return {}; + } return { { std::move(key), height }, fee, position }; } @@ -186,18 +187,18 @@ history CLASS::get_tx_confirmed_history(hash_digest&& key, return { .position = zero }; const auto block = find_strong(link); + const auto at = get_confirmed_height(block); // Returns invalid (filtered) but also !confirmed(). - if (!is_confirmed_block(block)) + if (at.is_terminal()) return { .position = history::unconfirmed_position }; - size_t height{}, position{}; - if (!get_height(height, block) || - !get_tx_position(position, link, block)) + size_t position{}; + if (!get_tx_position(position, link, block)) return { .position = zero }; // Electrum uses fees only on unconfirmed (expensive). - return { { std::move(key), height }, history::missing_prevout, position }; + return { { std::move(key), at.value }, history::missing_prevout, position }; } TEMPLATE @@ -216,7 +217,7 @@ history CLASS::get_tx_unconfirmed_history(hash_digest&& key, return { .position = history::unconfirmed_position }; // Returns invalid (filtered) but also confirmed(). - if (is_confirmed_block(find_strong(link))) + if (!get_confirmed_height(find_strong(link)).is_terminal()) return { .position = zero }; uint64_t fee{}; diff --git a/include/bitcoin/database/impl/query/address/address_unspent.ipp b/include/bitcoin/database/impl/query/address/address_unspent.ipp index ae553af95..7e97d93c5 100644 --- a/include/bitcoin/database/impl/query/address/address_unspent.ipp +++ b/include/bitcoin/database/impl/query/address/address_unspent.ipp @@ -107,22 +107,22 @@ code CLASS::get_confirmed_unspent(const stopper& cancel, unspents& out, // chain::outpoint invalid in default construction (filter). const auto& tx = out.parent_fk; const auto block = find_strong(tx); - if (!is_confirmed_block(block)) + const auto at = get_confirmed_height(block); + if (at.is_terminal()) return unspent{}; - size_t height{}, position{}; + size_t position{}; auto hash = get_tx_key(tx); const auto index = to_output_index(tx, link); if ((index == point::null_index) || (hash == system::null_hash) || - !get_height(height, block) || !get_tx_position(position, tx, block)) { fail = true; return unspent{}; } - return unspent{ { { std::move(hash), index }, out.value }, height, - position }; + return unspent{ { { std::move(hash), index }, out.value }, + at.value, position }; }); } @@ -163,11 +163,12 @@ code CLASS::get_unspent(const stopper& cancel, unspents& out, auto height = unspent::unused_height; auto position = unspent::unconfirmed_position; - if (const auto block = find_strong(tx); - is_confirmed_block(block)) + const auto block = find_strong(tx); + const auto at = get_confirmed_height(block); + if (!at.is_terminal()) { - if (!get_height(height, block) || - !get_tx_position(position, tx, block)) + height = at.value; + if (!get_tx_position(position, tx, block)) { fail = true; return unspent{}; diff --git a/include/bitcoin/database/impl/query/confirmed.ipp b/include/bitcoin/database/impl/query/confirmed.ipp index 5199f115d..bcf870c66 100644 --- a/include/bitcoin/database/impl/query/confirmed.ipp +++ b/include/bitcoin/database/impl/query/confirmed.ipp @@ -24,7 +24,7 @@ namespace libbitcoin { namespace database { - + // find_confirmed // ---------------------------------------------------------------------------- // These ensure both strong and candidate/confirmed indexation. diff --git a/include/bitcoin/database/impl/query/consensus/consensus_forks.ipp b/include/bitcoin/database/impl/query/consensus/consensus_forks.ipp index bd9f72a27..c70a8b410 100644 --- a/include/bitcoin/database/impl/query/consensus/consensus_forks.ipp +++ b/include/bitcoin/database/impl/query/consensus/consensus_forks.ipp @@ -25,6 +25,24 @@ namespace libbitcoin { namespace database { +// get_confirmed +// ---------------------------------------------------------------------------- + +TEMPLATE +height_link CLASS::get_confirmed_height(const header_link& link) const NOEXCEPT +{ + if (const auto height = get_height(link); !height.is_terminal()) + { + // The block is confirmed (by height). + table::height::record confirmed{}; + if (store_.confirmed.get(height, confirmed) && + (confirmed.header_fk == link)) + return height; + } + + return {}; +} + // protected fork readers. // ---------------------------------------------------------------------------- // Protected against index pop (low contention) to ensure branch consistency. diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 14fee6889..9e10d773d 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -598,6 +598,7 @@ class query header_links get_confirmed_headers(size_t first, size_t limit) const NOEXCEPT; + height_link get_confirmed_height(const header_link& link) const NOEXCEPT; header_links get_confirmed_fork(const header_link& fork) const NOEXCEPT; header_links get_candidate_fork(size_t& fork_point) const NOEXCEPT; header_states get_validated_fork(size_t& fork_point, diff --git a/test/query/confirmed.cpp b/test/query/confirmed.cpp index 2f8393641..96a54282c 100644 --- a/test/query/confirmed.cpp +++ b/test/query/confirmed.cpp @@ -56,6 +56,37 @@ BOOST_AUTO_TEST_CASE(query_confirmed__is_candidate_block__push_pop_candidate__ex BOOST_REQUIRE(!query.is_candidate_header(database::header_link::terminal)); } +BOOST_AUTO_TEST_CASE(query_confirmed__get_confirmed_height__push_pop_confirmed__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false)); + BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false)); + BOOST_REQUIRE(!query.get_confirmed_height(0).is_terminal()); + BOOST_REQUIRE(query.get_confirmed_height(1).is_terminal()); + BOOST_REQUIRE(query.get_confirmed_height(2).is_terminal()); + + BOOST_REQUIRE(query.push_confirmed(1, false)); + BOOST_REQUIRE(!query.get_confirmed_height(1).is_terminal()); + + BOOST_REQUIRE(query.push_confirmed(2, false)); + BOOST_REQUIRE(!query.get_confirmed_height(2).is_terminal()); + + BOOST_REQUIRE(query.pop_confirmed()); + BOOST_REQUIRE(!query.get_confirmed_height(0).is_terminal()); + BOOST_REQUIRE(!query.get_confirmed_height(1).is_terminal()); + BOOST_REQUIRE(query.get_confirmed_height(2).is_terminal()); + + BOOST_REQUIRE(!!query.pop_confirmed()); + BOOST_REQUIRE(!query.get_confirmed_height(0).is_terminal()); + BOOST_REQUIRE(query.get_confirmed_height(1).is_terminal()); + BOOST_REQUIRE(query.get_confirmed_height(2).is_terminal()); +} + BOOST_AUTO_TEST_CASE(query_confirmed__is_confirmed_block__push_pop_confirmed__expected) { settings settings{}; From 36c73400a07faf46988a20f35aab292f302bc85d Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 23 Apr 2026 21:27:00 -0400 Subject: [PATCH 2/3] Replace address history cursor queries with min confirmed height. --- .../impl/query/address/address_history.ipp | 124 +++++++------ .../impl/query/address/address_outpoints.ipp | 24 --- .../impl/query/address/address_unspent.ipp | 169 +++++++++--------- .../impl/query/navigate/navigate_reverse.ipp | 2 +- include/bitcoin/database/query.hpp | 47 +++-- include/bitcoin/database/types/history.hpp | 5 +- include/bitcoin/database/types/unspent.hpp | 5 +- src/types/history.cpp | 6 + src/types/unspent.cpp | 6 + test/query/address/address_history.cpp | 122 ++++++++++--- test/query/address/address_outpoints.cpp | 22 --- test/query/navigate/navigate_reverse.cpp | 27 +-- test/types/history.cpp | 29 ++- test/types/unspent.cpp | 29 ++- 14 files changed, 356 insertions(+), 261 deletions(-) diff --git a/include/bitcoin/database/impl/query/address/address_history.ipp b/include/bitcoin/database/impl/query/address/address_history.ipp index 853df2e8a..98b32b131 100644 --- a/include/bitcoin/database/impl/query/address/address_history.ipp +++ b/include/bitcoin/database/impl/query/address/address_history.ipp @@ -38,109 +38,95 @@ namespace database { // server/electrum TEMPLATE code CLASS::get_unconfirmed_history(const stopper& cancel, histories& out, - const hash_digest& key, bool turbo) const NOEXCEPT -{ - address_link cursor{}; - return get_unconfirmed_history(cancel, cursor, out, key, max_size_t, turbo); -} - -// server/electrum -TEMPLATE -code CLASS::get_unconfirmed_history(const stopper& cancel, - address_link& cursor, histories& out, const hash_digest& key, - size_t limit, bool turbo) const NOEXCEPT + const hash_digest& key, size_t limit, bool turbo) const NOEXCEPT { tx_links txs{}; - if (const auto ec = get_address_txs(cancel, cursor, txs, key, limit)) + if (const auto ec = get_address_txs(cancel, txs, key, limit)) return ec; + // There is no cursor for unconfirmed, since it's height-based. out.clear(); out.resize(txs.size()); return parallel_history_transform(cancel, turbo, out, txs, - [this](const tx_link& link, auto& cancel, auto& fail) NOEXCEPT + [this](const auto& link, auto& cancel, auto& fail) NOEXCEPT { if (cancel || fail) return history{}; const auto out = get_tx_unconfirmed_history(link); - if (!out.confirmed() && !out.valid()) fail = true; + if (out.fault()) fail = true; return out; }); } // ununsed TEMPLATE -code CLASS::get_confirmed_history(const stopper& cancel, histories& out, - const hash_digest& key, bool turbo) const NOEXCEPT -{ - address_link cursor{}; - return get_confirmed_history(cancel, cursor, out, key, max_size_t, turbo); -} - -// ununsed -TEMPLATE -code CLASS::get_confirmed_history(const stopper& cancel, - address_link& cursor, histories& out, const hash_digest& key, - size_t limit, bool turbo) const NOEXCEPT +code CLASS::get_confirmed_history(const stopper& cancel, height_link& cursor, + histories& out, const hash_digest& key, size_t limit, + bool turbo) const NOEXCEPT { tx_links txs{}; - if (const auto ec = get_address_txs(cancel, cursor, txs, key, limit)) + if (const auto ec = get_address_txs(cancel, txs, key, limit)) return ec; + // Cursor is still advanced in the case of (integrity) failure. + // End is required because of the possiblity of intervening organization. + const auto start = cursor.is_terminal() ? zero : cursor.value; + const auto end = get_top_confirmed(); + cursor = system::possible_narrow_cast(add1(end)); + out.clear(); out.resize(txs.size()); return parallel_history_transform(cancel, turbo, out, txs, - [this](const tx_link& link, auto& cancel, auto& fail) NOEXCEPT + [this, start, end](const auto& link, auto& cancel, auto& fail) NOEXCEPT { if (cancel || fail) return history{}; - const auto out = get_tx_confirmed_history(link); - if (out.confirmed() && !out.valid()) fail = true; + const auto out = get_tx_confirmed_history(link, start, end); + if (out.fault()) fail = true; return out; }); } -// ununsed -TEMPLATE -code CLASS::get_history(const stopper& cancel, histories& out, - const hash_digest& key, bool turbo) const NOEXCEPT -{ - address_link cursor{}; - return get_history(cancel, cursor, out, key, max_size_t, turbo); -} - // server/electrum TEMPLATE -code CLASS::get_history(const stopper& cancel, address_link& cursor, +code CLASS::get_history(const stopper& cancel, height_link& cursor, histories& out, const hash_digest& key, size_t limit, bool turbo) const NOEXCEPT { tx_links txs{}; - if (const auto ec = get_address_txs(cancel, cursor, txs, key, limit)) + if (const auto ec = get_address_txs(cancel, txs, key, limit)) return ec; + // Cursor is still advanced in the case of (integrity) failure. + // End is required because of the possiblity of intervening organization. + const auto start = cursor.is_terminal() ? zero : cursor.value; + const auto end = get_top_confirmed(); + cursor = system::possible_narrow_cast(add1(end)); + out.clear(); out.resize(txs.size()); return parallel_history_transform(cancel, turbo, out, txs, - [this](const tx_link& link, auto& cancel, auto& fail) NOEXCEPT + [this, start, end](const auto& link, auto& cancel, auto& fail) NOEXCEPT { if (cancel || fail) return history{}; - const auto out = get_tx_history(link); - if (!out.valid()) fail = true; + const auto out = get_tx_history(link, start, end); + if (out.fault()) fail = true; return out; }); } -// History queries. +// get_tx_history // ---------------------------------------------------------------------------- TEMPLATE -history CLASS::get_tx_history(const tx_link& link) const NOEXCEPT +history CLASS::get_tx_history(const tx_link& link, size_t start, + size_t end) const NOEXCEPT { - return get_tx_history(get_tx_key(link), link); + return get_tx_history(get_tx_key(link), link, start, end); } // protected TEMPLATE -history CLASS::get_tx_history(hash_digest&& key, - const tx_link& link) const NOEXCEPT +history CLASS::get_tx_history(hash_digest&& key, const tx_link& link, + size_t start, size_t end) const NOEXCEPT { // history is invalid in default construction. if (link.is_terminal()) @@ -161,6 +147,11 @@ history CLASS::get_tx_history(hash_digest&& key, if (is_confirmed_all_prevouts(link)) height = history::rooted_height; } + else if (system::is_limited(at.value, start, end)) + { + // Invalid with unconfirmed_position signals no fault. + return { .position = history::unconfirmed_position }; + } else { height = at.value; @@ -172,30 +163,31 @@ history CLASS::get_tx_history(hash_digest&& key, } TEMPLATE -history CLASS::get_tx_confirmed_history(const tx_link& link) const NOEXCEPT +history CLASS::get_tx_confirmed_history(const tx_link& link, size_t start, + size_t end) const NOEXCEPT { - return get_tx_confirmed_history(get_tx_key(link), link); + return get_tx_confirmed_history(get_tx_key(link), link, start, end); } // protected TEMPLATE -history CLASS::get_tx_confirmed_history(hash_digest&& key, - const tx_link& link) const NOEXCEPT +history CLASS::get_tx_confirmed_history(hash_digest&& key, const tx_link& link, + size_t start, size_t end) const NOEXCEPT { - // history is invalid in default construction. + // history is invalid in default construction with position. if (link.is_terminal()) - return { .position = zero }; + return {}; const auto block = find_strong(link); const auto at = get_confirmed_height(block); - // Returns invalid (filtered) but also !confirmed(). - if (at.is_terminal()) + // Invalid with unconfirmed_position signals no fault. + if (at.is_terminal() || system::is_limited(at.value, start, end)) return { .position = history::unconfirmed_position }; size_t position{}; if (!get_tx_position(position, link, block)) - return { .position = zero }; + return {}; // Electrum uses fees only on unconfirmed (expensive). return { { std::move(key), at.value }, history::missing_prevout, position }; @@ -212,13 +204,13 @@ TEMPLATE history CLASS::get_tx_unconfirmed_history(hash_digest&& key, const tx_link& link) const NOEXCEPT { - // history is invalid in default construction. + // history is invalid in default construction with position. if (link.is_terminal()) - return { .position = history::unconfirmed_position }; + return {}; - // Returns invalid (filtered) but also confirmed(). + // Invalid with unconfirmed_position signals no fault. if (!get_confirmed_height(find_strong(link)).is_terminal()) - return { .position = zero }; + return { .position = history::unconfirmed_position }; uint64_t fee{}; if (!get_tx_fee(fee, link)) @@ -230,6 +222,9 @@ history CLASS::get_tx_unconfirmed_history(hash_digest&& key, return { { std::move(key), height }, fee, history::unconfirmed_position }; } +// get_spenders_history +// ---------------------------------------------------------------------------- + // server/electrum TEMPLATE histories CLASS::get_spenders_history( @@ -255,10 +250,11 @@ histories CLASS::get_spenders_history(const hash_digest& key, // ---------------------------------------------------------------------------- TEMPLATE -code CLASS::get_address_txs(const stopper& cancel, address_link& cursor, - tx_links& out, const hash_digest& key, size_t limit) const NOEXCEPT +code CLASS::get_address_txs(const stopper& cancel, tx_links& out, + const hash_digest& key, size_t limit) const NOEXCEPT { output_links links{}; + address_link cursor{}; if (const auto ec = to_address_outputs(cancel, cursor, links, key, limit)) return ec; diff --git a/include/bitcoin/database/impl/query/address/address_outpoints.ipp b/include/bitcoin/database/impl/query/address/address_outpoints.ipp index eb402cf70..bd7040b3c 100644 --- a/include/bitcoin/database/impl/query/address/address_outpoints.ipp +++ b/include/bitcoin/database/impl/query/address/address_outpoints.ipp @@ -117,30 +117,6 @@ code CLASS::get_address_outpoints(const stopper& cancel, outpoints& out, }); } -// TODO: server/native -TEMPLATE -code CLASS::get_address_outpoints(const stopper& cancel, address_link& cursor, - outpoints& out, const hash_digest& key, bool turbo) const NOEXCEPT -{ - out.clear(); - output_links links{}; - if (const code ec = to_address_outputs(cancel, cursor, links, key)) - return ec; - - return parallel_outpoint_transform(cancel, turbo, out, links, - [this](const output_link& link, auto& cancel, auto& fail) NOEXCEPT - { - if (cancel || fail) - return outpoint{}; - - const auto point = get_outpoint(link); - if (!point.point().is_valid()) - fail = true; - - return point; - }); -} - // utilities // ---------------------------------------------------------------------------- // private/static diff --git a/include/bitcoin/database/impl/query/address/address_unspent.ipp b/include/bitcoin/database/impl/query/address/address_unspent.ipp index 7e97d93c5..35fdc3df5 100644 --- a/include/bitcoin/database/impl/query/address/address_unspent.ipp +++ b/include/bitcoin/database/impl/query/address/address_unspent.ipp @@ -53,28 +53,9 @@ code CLASS::get_unconfirmed_unspent(const stopper& cancel, unspents& out, if (cancel || fail || is_spent(link)) return unspent{}; - table::output::get_parent_value out{}; - if (!store_.output.get(link, out)) - { - fail = true; - return unspent{}; - } - - // chain::outpoint invalid in default construction (filter). - const auto& tx = out.parent_fk; - if (is_confirmed_block(find_strong(tx))) - return unspent{}; - - auto hash = get_tx_key(tx); - const auto index = to_output_index(tx, link); - if ((index == point::null_index) || (hash == system::null_hash)) - { - fail = true; - return unspent{}; - } - - return unspent{ { { std::move(hash), index }, out.value }, - unspent::unused_height, unspent::unconfirmed_position }; + const auto out = get_tx_unconfirmed_unspent(link); + if (out.fault()) fail = true; + return out; }); } @@ -97,32 +78,9 @@ code CLASS::get_confirmed_unspent(const stopper& cancel, unspents& out, if (cancel || fail || is_confirmed_spent(link)) return unspent{}; - table::output::get_parent_value out{}; - if (!store_.output.get(link, out)) - { - fail = true; - return unspent{}; - } - - // chain::outpoint invalid in default construction (filter). - const auto& tx = out.parent_fk; - const auto block = find_strong(tx); - const auto at = get_confirmed_height(block); - if (at.is_terminal()) - return unspent{}; - - size_t position{}; - auto hash = get_tx_key(tx); - const auto index = to_output_index(tx, link); - if ((index == point::null_index) || (hash == system::null_hash) || - !get_tx_position(position, tx, block)) - { - fail = true; - return unspent{}; - } - - return unspent{ { { std::move(hash), index }, out.value }, - at.value, position }; + const auto out = get_tx_confirmed_unspent(link); + if (out.fault()) fail = true; + return out; }); } @@ -145,41 +103,92 @@ code CLASS::get_unspent(const stopper& cancel, unspents& out, if (cancel || fail || is_spent(link)) return unspent{}; - table::output::get_parent_value out{}; - if (!store_.output.get(link, out)) - { - fail = true; - return unspent{}; - } - - const auto& tx = out.parent_fk; - auto hash = get_tx_key(tx); - const auto index = to_output_index(tx, link); - if ((index == point::null_index) || (hash == system::null_hash)) - { - fail = true; - return unspent{}; - } - - auto height = unspent::unused_height; - auto position = unspent::unconfirmed_position; - const auto block = find_strong(tx); - const auto at = get_confirmed_height(block); - if (!at.is_terminal()) - { - height = at.value; - if (!get_tx_position(position, tx, block)) - { - fail = true; - return unspent{}; - } - } - - return unspent{ { { std::move(hash), index }, out.value }, height, - position }; + auto out = get_tx_unspent(link); + if (out.fault()) fail = true; + return out; }); } +// Unspent queries. +// ---------------------------------------------------------------------------- + +TEMPLATE +unspent CLASS::get_tx_unspent(const output_link& link) const NOEXCEPT +{ + table::output::get_parent_value out{}; + if (!store_.output.get(link, out)) + return {}; + + const auto tx = out.parent_fk; + auto hash = get_tx_key(tx); + const auto index = to_output_index(tx, link); + if ((index == point::null_index) || (hash == system::null_hash)) + return {}; + + auto height = unspent::unused_height; + auto position = unspent::unconfirmed_position; + + const auto block = find_strong(tx); + const auto at = get_confirmed_height(block); + if (!at.is_terminal()) + { + height = at.value; + if (!get_tx_position(position, tx, block)) + return {}; + } + + return { { { std::move(hash), index }, out.value }, height, position }; +} + +TEMPLATE +unspent CLASS::get_tx_confirmed_unspent(const output_link& link) const NOEXCEPT +{ + // unspent is invalid in default construction with position. + table::output::get_parent_value out{}; + if (!store_.output.get(link, out)) + return {}; + + const auto tx = out.parent_fk; + const auto block = find_strong(tx); + const auto at = get_confirmed_height(block); + + // Invalid with unconfirmed_position signals no fault. + if (at.is_terminal()) + return { .position = unspent::unconfirmed_position }; + + size_t position{}; + auto hash = get_tx_key(tx); + const auto index = to_output_index(tx, link); + if ((index == point::null_index) || (hash == system::null_hash) || + !get_tx_position(position, tx, block)) + return {}; + + return { { { std::move(hash), index }, out.value }, at.value, position }; +} + +TEMPLATE +unspent CLASS::get_tx_unconfirmed_unspent(const output_link& link) const NOEXCEPT +{ + // unspent is invalid in default construction with position. + table::output::get_parent_value out{}; + if (!store_.output.get(link, out)) + return {}; + + const auto tx = out.parent_fk; + + // Invalid with unconfirmed_position signals no fault. + if (!get_confirmed_height(find_strong(tx)).is_terminal()) + return { .position = unspent::unconfirmed_position }; + + auto hash = get_tx_key(tx); + const auto index = to_output_index(tx, link); + if ((index == point::null_index) || (hash == system::null_hash)) + return {}; + + return { { { std::move(hash), index }, out.value }, unspent::unused_height, + unspent::unconfirmed_position }; +} + // utilities // ---------------------------------------------------------------------------- // private/static diff --git a/include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp b/include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp index 46e006611..ba3e570ec 100644 --- a/include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp +++ b/include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp @@ -100,7 +100,7 @@ code CLASS::to_address_outputs(const stopper& cancel, output_links& out, const hash_digest& key) const NOEXCEPT { address_link cursor{}; - return to_address_outputs(cancel, cursor, out, key); + return to_address_outputs(cancel, cursor, out, key, max_size_t); } TEMPLATE diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 9e10d773d..a340c5d3c 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -317,13 +317,15 @@ class query const output_links& outputs) const NOEXCEPT; code to_touched_txs(const stopper& cancel, tx_links& out, const output_links& outputs) const NOEXCEPT; + code to_address_outputs(output_links& out, const hash_digest& key) const NOEXCEPT; code to_address_outputs(const stopper& cancel, output_links& out, const hash_digest& key) const NOEXCEPT; + code to_address_outputs(const stopper& cancel, address_link& cursor, output_links& out, const hash_digest& key, - size_t limit=max_size_t) const NOEXCEPT; + size_t limit) const NOEXCEPT; /// Archive reads. /// ----------------------------------------------------------------------- @@ -632,25 +634,15 @@ class query const hash_digest& key, uint64_t value, bool turbo=false) const NOEXCEPT; code get_address_outpoints(const stopper& cancel, outpoints& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; - code get_address_outpoints(const stopper& cancel, address_link& cursor, - outpoints& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; /// Electrum queries (histories, deduped, electrum sort). code get_unconfirmed_history(const stopper& cancel, histories& out, - const hash_digest& key, bool turbo=false) const NOEXCEPT; - code get_confirmed_history(const stopper& cancel, histories& out, - const hash_digest& key, bool turbo=false) const NOEXCEPT; - code get_history(const stopper& cancel, histories& out, - const hash_digest& key, bool turbo=false) const NOEXCEPT; - - /// Electrum queries (with limit and cursor options). - code get_unconfirmed_history(const stopper& cancel, address_link& cursor, - histories& out, const hash_digest& key, size_t limit=max_size_t, + const hash_digest& key, size_t limit=max_size_t, bool turbo=false) const NOEXCEPT; - code get_confirmed_history(const stopper& cancel, address_link& cursor, - histories& out, const hash_digest& key,size_t limit=max_size_t, + code get_confirmed_history(const stopper& cancel, height_link& cursor, + histories& out, const hash_digest& key, size_t limit=max_size_t, bool turbo=false) const NOEXCEPT; - code get_history(const stopper& cancel, address_link& cursor, + code get_history(const stopper& cancel, height_link& cursor, histories& out, const hash_digest& key, size_t limit=max_size_t, bool turbo=false) const NOEXCEPT; @@ -672,13 +664,21 @@ class query bool turbo=false) const NOEXCEPT; /// History queries. - history get_tx_history(const tx_link& link) const NOEXCEPT; - history get_tx_confirmed_history(const tx_link& link) const NOEXCEPT; + history get_tx_history(const tx_link& link, size_t start=zero, + size_t end=max_size_t) const NOEXCEPT; + history get_tx_confirmed_history(const tx_link& link, size_t start=zero, + size_t end=max_size_t) const NOEXCEPT; history get_tx_unconfirmed_history(const tx_link& link) const NOEXCEPT; + histories get_spenders_history(const point& prevout) const NOEXCEPT; histories get_spenders_history(const hash_digest& key, uint32_t index) const NOEXCEPT; + /// Unspent queries. + unspent get_tx_unspent(const output_link& link) const NOEXCEPT; + unspent get_tx_confirmed_unspent(const output_link& link) const NOEXCEPT; + unspent get_tx_unconfirmed_unspent(const output_link& link) const NOEXCEPT; + /// Filters. /// ----------------------------------------------------------------------- @@ -842,15 +842,14 @@ class query /// History. /// ----------------------------------------------------------------------- - history get_tx_history(hash_digest&& key, - const tx_link& link) const NOEXCEPT; - history get_tx_confirmed_history(hash_digest&& key, - const tx_link& link) const NOEXCEPT; + history get_tx_history(hash_digest&& key, const tx_link& link, + size_t start, size_t end) const NOEXCEPT; + history get_tx_confirmed_history(hash_digest&& key, const tx_link& link, + size_t start, size_t end) const NOEXCEPT; history get_tx_unconfirmed_history(hash_digest&& key, const tx_link& link) const NOEXCEPT; - - code get_address_txs(const stopper& cancel, address_link& cursor, - tx_links& out, const hash_digest& key, size_t limit) const NOEXCEPT; + code get_address_txs(const stopper& cancel, tx_links& out, + const hash_digest& key, size_t limit) const NOEXCEPT; private: // This value should never be read, but may be useful in debugging. diff --git a/include/bitcoin/database/types/history.hpp b/include/bitcoin/database/types/history.hpp index b34ba023f..7c72f70a5 100644 --- a/include/bitcoin/database/types/history.hpp +++ b/include/bitcoin/database/types/history.hpp @@ -36,9 +36,12 @@ struct BCD_API history /// Filter out invalid history elements (txs) and sort. static void filter_sort_and_dedup(std::vector& history) NOEXCEPT; - /// The tx is valid (not defaulted). + /// The tx is valid. bool valid() const NOEXCEPT; + /// A fault occured (and the tx is not valid). + bool fault() const NOEXCEPT; + /// The unconfirmed tx is rooted in chain (all prevouts confirmed). bool rooted() const NOEXCEPT; diff --git a/include/bitcoin/database/types/unspent.hpp b/include/bitcoin/database/types/unspent.hpp index 0e35707f5..042d9b09c 100644 --- a/include/bitcoin/database/types/unspent.hpp +++ b/include/bitcoin/database/types/unspent.hpp @@ -33,9 +33,12 @@ struct BCD_API unspent /// Filter out invalid unspent output elements and sort. static void filter_sort_and_dedup(std::vector& unspent) NOEXCEPT; - /// The outpoint is valid (not defaulted). + /// The unspent is valid. bool valid() const NOEXCEPT; + /// A fault occured (and the unspent is not valid). + bool fault() const NOEXCEPT; + /// The tx is confirmed in a block. bool confirmed() const NOEXCEPT; diff --git a/src/types/history.cpp b/src/types/history.cpp index 2999faaaa..6d757ac8c 100644 --- a/src/types/history.cpp +++ b/src/types/history.cpp @@ -80,6 +80,12 @@ bool history::valid() const NOEXCEPT return tx.is_valid(); } +bool history::fault() const NOEXCEPT +{ + // Invalid with default position implies fault (return {}). + return !valid() && is_zero(position); +} + // Only applies when !confirmed, and only other option is unrooted_height. bool history::rooted() const NOEXCEPT { diff --git a/src/types/unspent.cpp b/src/types/unspent.cpp index c5793c2f4..e72e44e4f 100644 --- a/src/types/unspent.cpp +++ b/src/types/unspent.cpp @@ -60,6 +60,12 @@ bool unspent::valid() const NOEXCEPT return out.is_valid(); } +bool unspent::fault() const NOEXCEPT +{ + // Invalid with default position implies fault (return {}). + return !valid() && is_zero(position); +} + bool unspent::confirmed() const NOEXCEPT { return position != unconfirmed_position; diff --git a/test/query/address/address_history.cpp b/test/query/address/address_history.cpp index 37e3eab45..65f6d548c 100644 --- a/test/query/address/address_history.cpp +++ b/test/query/address/address_history.cpp @@ -40,14 +40,18 @@ BOOST_AUTO_TEST_CASE(query_address__get_history__genesis__expected) BOOST_CHECK_EQUAL(out.size(), 0u); out.clear(); - BOOST_CHECK(!query.get_confirmed_history(cancel, out, hash)); + height_link cursor{}; + BOOST_CHECK(!query.get_confirmed_history(cancel, cursor, out, hash)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_CHECK_EQUAL(out.at(0).fee, history::missing_prevout); BOOST_CHECK_EQUAL(out.at(0).position, 0u); BOOST_CHECK_EQUAL(out.at(0).tx.height(), 0u); BOOST_CHECK_EQUAL(out.at(0).tx.hash(), test::genesis.transactions_ptr()->at(0)->hash(false)); out.clear(); - BOOST_CHECK(!query.get_history(cancel, out, hash)); + cursor = {}; + BOOST_CHECK(!query.get_history(cancel, cursor, out, hash)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_CHECK_EQUAL(out.at(0).fee, history::missing_prevout); BOOST_CHECK_EQUAL(out.at(0).position, 0u); BOOST_CHECK_EQUAL(out.at(0).tx.height(), 0u); @@ -64,9 +68,10 @@ BOOST_AUTO_TEST_CASE(query_address__get_history__turbo_block1a_address0__expecte BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); histories out{}; + height_link cursor{}; const std::atomic_bool cancel{}; - BOOST_CHECK(!query.get_history(cancel, out, test::block1a_address0, true)); - BOOST_CHECK_EQUAL(out.size(), 8u); + BOOST_CHECK(!query.get_history(cancel, cursor, out, test::block1a_address0, max_size_t, true)); + BOOST_REQUIRE_EQUAL(out.size(), 8u); // Identities (not part of sort). BOOST_CHECK_EQUAL(out.at(0).tx.hash(), test::block1a.transactions_ptr()->at(0)->hash(false)); // tx1/tx1 (same) @@ -127,7 +132,7 @@ BOOST_AUTO_TEST_CASE(query_address__get_history__limited__depth_limited_empty) BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); histories out{}; - address_link cursor{}; + height_link cursor{}; const std::atomic_bool cancel{}; const auto& hash = test::block1a_address0; @@ -135,17 +140,55 @@ BOOST_AUTO_TEST_CASE(query_address__get_history__limited__depth_limited_empty) BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 0, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = address_link::terminal; + cursor = {}; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 8, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = address_link::terminal; + cursor = {}; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 9, true), error::success); BOOST_CHECK_EQUAL(out.size(), 8u); - cursor = address_link::terminal; + cursor = {}; + BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 100, true), error::success); + BOOST_CHECK_EQUAL(out.size(), 8u); +} + +BOOST_AUTO_TEST_CASE(query_address__get_history__start__excludes_prior) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); + + histories out{}; + height_link cursor{ 42 }; + const std::atomic_bool cancel{}; + const auto& hash = test::block1a_address0; + + // Limited returned when limit precludes reaching the address cursor (or terminal). + BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 0, true), error::depth_limited); + BOOST_CHECK(out.empty()); + + cursor = { 42 }; + BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 8, true), error::depth_limited); + BOOST_CHECK(out.empty()); + + // Only unconfirmed. + cursor = { 42 }; + BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 9, true), error::success); + BOOST_CHECK_EQUAL(out.size(), 4u); + + // Only excludes genesis. + cursor = { 1 }; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 100, true), error::success); BOOST_CHECK_EQUAL(out.size(), 8u); + + // Excludes block1a which has one confirmed touching tx. + cursor = { 2 }; + BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 100, true), error::success); + BOOST_CHECK_EQUAL(out.size(), 7u); } // ---------------------------------------------------------------------------- @@ -188,8 +231,8 @@ BOOST_AUTO_TEST_CASE(query_address__get_unconfirmed_history__turbo_block1a_addre histories out{}; const std::atomic_bool cancel{}; - BOOST_CHECK(!query.get_unconfirmed_history(cancel, out, test::block1a_address0, true)); - BOOST_CHECK_EQUAL(out.size(), 4u); + BOOST_CHECK(!query.get_unconfirmed_history(cancel, out, test::block1a_address0, max_size_t, true)); + BOOST_REQUIRE_EQUAL(out.size(), 4u); // Identities (not part of sort). BOOST_CHECK_EQUAL(out.at(0).tx.hash(), test::tx5.hash(false)); // tx5 @@ -233,24 +276,20 @@ BOOST_AUTO_TEST_CASE(query_address__get_unconfirmed_history__limited__depth_limi BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); histories out{}; - address_link cursor{}; const std::atomic_bool cancel{}; const auto& hash = test::block1a_address0; // Limited returned when limit precludes reaching the address cursor (or terminal). - BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, cursor, out, hash, 0, true), error::depth_limited); + BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, out, hash, 0, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = address_link::terminal; - BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, cursor, out, hash, 8, true), error::depth_limited); + BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, out, hash, 8, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = address_link::terminal; - BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, cursor, out, hash, 9, true), error::success); + BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, out, hash, 9, true), error::success); BOOST_CHECK_EQUAL(out.size(), 4u); - cursor = address_link::terminal; - BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, cursor, out, hash, 100, true), error::success); + BOOST_CHECK_EQUAL(query.get_unconfirmed_history(cancel, out, hash, 100, true), error::success); BOOST_CHECK_EQUAL(out.size(), 4u); } @@ -266,9 +305,10 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_history__turbo_block1a_address BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); histories out{}; + height_link cursor{}; const std::atomic_bool cancel{}; - BOOST_CHECK(!query.get_confirmed_history(cancel, out, test::block1a_address0, true)); - BOOST_CHECK_EQUAL(out.size(), 4u); + BOOST_CHECK(!query.get_confirmed_history(cancel, cursor, out, test::block1a_address0, max_size_t, true)); + BOOST_REQUIRE_EQUAL(out.size(), 4u); // Identities (not part of sort). BOOST_CHECK_EQUAL(out.at(0).tx.hash(), test::block1a.transactions_ptr()->at(0)->hash(false)); // tx1/tx1 (same) @@ -305,7 +345,7 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_history__limited__depth_limite BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); histories out{}; - address_link cursor{}; + height_link cursor{}; const std::atomic_bool cancel{}; const auto& hash = test::block1a_address0; @@ -313,19 +353,53 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_history__limited__depth_limite BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 0, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = address_link::terminal; + cursor = {}; BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 8, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = address_link::terminal; + cursor = {}; BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 9, true), error::success); BOOST_CHECK_EQUAL(out.size(), 4u); - cursor = address_link::terminal; + cursor = {}; BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 100, true), error::success); BOOST_CHECK_EQUAL(out.size(), 4u); } +BOOST_AUTO_TEST_CASE(query_address__get_confirmed_history__start__excludes_prior) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_CHECK(!store.create(test::events_handler)); + BOOST_CHECK(test::setup_three_block_confirmed_address_store(query)); + + histories out{}; + height_link cursor{ 42 }; + const std::atomic_bool cancel{}; + const auto& hash = test::block1a_address0; + + // Limited returned when limit precludes reaching address cursor (or terminal). + BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 0, true), error::depth_limited); + BOOST_CHECK(out.empty()); + + // Only unconfirmed. + cursor = { 42 }; + BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 8, true), error::depth_limited); + BOOST_CHECK(out.empty()); + + // Only excludes genesis. + cursor = { 1 }; + BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 9, true), error::success); + BOOST_CHECK_EQUAL(out.size(), 4u); + + // Excludes block1a which has one confirmed touching tx. + cursor = { 2 }; + BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 100, true), error::success); + BOOST_CHECK_EQUAL(out.size(), 3u); +} + // get_tx_history BOOST_AUTO_TEST_CASE(query_address__get_tx_history__genesis__expected) diff --git a/test/query/address/address_outpoints.cpp b/test/query/address/address_outpoints.cpp index 8b46e7960..3fb1eb925 100644 --- a/test/query/address/address_outpoints.cpp +++ b/test/query/address/address_outpoints.cpp @@ -173,26 +173,4 @@ BOOST_AUTO_TEST_CASE(query_address__get_address_outpoints1__cancel__query_cancel BOOST_REQUIRE(out.empty()); } -// get_address_outpoints2 - -BOOST_AUTO_TEST_CASE(query_address__get_address_outpoints2__progressive_cursor__expected) -{ - settings settings{}; - settings.path = TEST_DIRECTORY; - test::chunk_store store{ settings }; - test::query_accessor query{ store }; - BOOST_REQUIRE(!store.create(test::events_handler)); - BOOST_REQUIRE(query.initialize(test::genesis)); - - outpoints out{}; - address_link cursor{}; - const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_address_outpoints(cancel, cursor, out, test::genesis_address0)); - BOOST_REQUIRE_EQUAL(out.size(), 1u); - BOOST_REQUIRE_EQUAL(cursor, 0u); - BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); - - // TODO: add same tx again. -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/navigate/navigate_reverse.cpp b/test/query/navigate/navigate_reverse.cpp index e3634fa27..d1b15f85a 100644 --- a/test/query/navigate/navigate_reverse.cpp +++ b/test/query/navigate/navigate_reverse.cpp @@ -22,6 +22,9 @@ BOOST_FIXTURE_TEST_SUITE(query_navigate_tests, test::directory_setup_fixture) +const auto& address0 = test::block1a_address0; +const auto& address1 = test::block1a_address1; + // to_parent BOOST_AUTO_TEST_CASE(query_navigate__to_parent__always__expected) @@ -56,7 +59,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs1__always__expected) BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); output_links links{}; - BOOST_REQUIRE(!query.to_address_outputs(links, test::block1a_address1)); + BOOST_REQUIRE(!query.to_address_outputs(links, address1)); tx_links out{}; BOOST_REQUIRE(!query.to_touched_txs(out, links)); @@ -84,7 +87,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs2__always__expected) output_links links{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, links, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, links, address0)); tx_links out{}; BOOST_REQUIRE(!query.to_touched_txs(cancel, out, links)); @@ -119,7 +122,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs1__always__expected) BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); output_links out{}; - BOOST_REQUIRE(!query.to_address_outputs(out, test::block1a_address1)); + BOOST_REQUIRE(!query.to_address_outputs(out, address1)); // There is 1 instance of the `script{ { { opcode::roll } } }` output. BOOST_REQUIRE_EQUAL(out.size(), 1u); @@ -139,7 +142,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs2__always__expected) output_links out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, out, address0)); // There are 6 instances of the `script{ { { opcode::pick } } }` output. BOOST_REQUIRE_EQUAL(out.size(), 6u); @@ -165,7 +168,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__terminal__not_reduced) output_links out{}; address_link end{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, end, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, end, out, address0, max_size_t)); // There are 6 instances of the `script{ { { opcode::pick } } }` output. BOOST_REQUIRE_EQUAL(out.size(), 6u); @@ -189,7 +192,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__limit__depth_limited) output_links out{}; address_link end{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, end, out, test::block1a_address0, 4), error::depth_limited); + BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, end, out, address0, 4), error::depth_limited); // The limit is applied before deduplication. // There are 6 instances of the `script{ { { opcode::pick } } }` output, limited to 4. @@ -214,7 +217,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_mismatch__populat output_links out{}; address_link cursor{ 4242 }; const std::atomic_bool cancel{}; - const auto ec = query.to_address_outputs(cancel, cursor, out, test::block1a_address0); + const auto ec = query.to_address_outputs(cancel, cursor, out, address0, max_size_t); BOOST_REQUIRE_EQUAL(ec, error::invalid_cursor); // The end was not found but the full list is returned. @@ -239,7 +242,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_match__expected) output_links out{}; address_link cursor{ 3 }; const std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); BOOST_REQUIRE_EQUAL(cursor.value, 7u); // The stop was found so partial list is returned. @@ -264,7 +267,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected) // Add three unconfirmed blocks and two txs, with 7 outputs, 6 matching address. BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query)); - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); BOOST_REQUIRE_EQUAL(cursor.value, 7u); BOOST_REQUIRE_EQUAL(out.size(), 6u); BOOST_REQUIRE_EQUAL(out.at(0), 123u); @@ -277,7 +280,7 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected) // Add two unconfirmed blocks with 3 outputs, all matching address. BOOST_REQUIRE(query.set(test::block1b, database::context{ 0, 1, 0 }, false, false)); BOOST_REQUIRE(query.set(test::block2b, database::context{ 0, 2, 0 }, false, false)); - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); BOOST_REQUIRE_EQUAL(cursor.value, 10u); BOOST_REQUIRE_EQUAL(out.size(), 3u); BOOST_REQUIRE_EQUAL(out.at(0), 144u); @@ -286,13 +289,13 @@ BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected) // Add one tx with one output, matching address. BOOST_REQUIRE(query.set(test::tx2b)); - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); BOOST_REQUIRE_EQUAL(cursor.value, 11u); BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_REQUIRE_EQUAL(out.at(0), 151u); // No changes to this address since cursor. - BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, test::block1a_address0)); + BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t)); BOOST_REQUIRE_EQUAL(cursor.value, 11u); BOOST_REQUIRE(out.empty()); } diff --git a/test/types/history.cpp b/test/types/history.cpp index 3fab741b6..2de850bfe 100644 --- a/test/types/history.cpp +++ b/test/types/history.cpp @@ -104,11 +104,32 @@ BOOST_AUTO_TEST_CASE(history__equality__same__true) BOOST_AUTO_TEST_CASE(history__valid__always__expected) { - const auto valid = history{ { hash_digest{}, 42 }, 2, 3 }.valid(); - BOOST_REQUIRE(valid); + const auto valid1 = history{ { hash_digest{}, 42 }, 2, 0 }.valid(); + BOOST_REQUIRE(valid1); - const auto invalid = !history{ checkpoint{}, 2, 3 }.valid(); - BOOST_REQUIRE(invalid); + const auto valid2 = history{ { hash_digest{}, 42 }, 2, history::unconfirmed_position }.valid(); + BOOST_REQUIRE(valid2); + + const auto invalid1 = !history{ checkpoint{}, 2, 3 }.valid(); + BOOST_REQUIRE(invalid1); + + const auto invalid2 = !history{ checkpoint{}, 2, history::unconfirmed_position }.valid(); + BOOST_REQUIRE(invalid2); +} + +BOOST_AUTO_TEST_CASE(history__fault__always__expected) +{ + const auto fault1 = history{}.fault(); + BOOST_REQUIRE(fault1); + + const auto fault2 = history{ checkpoint{}, 2, 0 }.fault(); + BOOST_REQUIRE(fault2); + + const auto nonfault1 = !history{ checkpoint{}, 2, 3 }.fault(); + BOOST_REQUIRE(nonfault1); + + const auto nonfault2 = !history{ checkpoint{}, 2, history::unconfirmed_position }.fault(); + BOOST_REQUIRE(nonfault2); } BOOST_AUTO_TEST_CASE(history__rooted__always__expected) diff --git a/test/types/unspent.cpp b/test/types/unspent.cpp index 6751b8d2f..b5a5991b4 100644 --- a/test/types/unspent.cpp +++ b/test/types/unspent.cpp @@ -87,11 +87,32 @@ BOOST_AUTO_TEST_CASE(unspent__equality__same__true) BOOST_AUTO_TEST_CASE(unspent__valid__always__expected) { - const auto valid = unspent{ { { hash_digest{}, 0 }, 42 }, 2, 3 }.valid(); - BOOST_REQUIRE(valid); + const auto valid1 = unspent{ { { hash_digest{}, 0 }, 42 }, 2, 0 }.valid(); + BOOST_REQUIRE(valid1); - const auto invalid = !unspent{ {}, 2, 3 }.valid(); - BOOST_REQUIRE(invalid); + const auto valid2 = unspent{ { { hash_digest{}, 0 }, 42 }, 2, unspent::unconfirmed_position }.valid(); + BOOST_REQUIRE(valid2); + + const auto invalid1 = !unspent{ {}, 2, 3 }.valid(); + BOOST_REQUIRE(invalid1); + + const auto invalid2 = !unspent{ {}, 2, unspent::unconfirmed_position }.valid(); + BOOST_REQUIRE(invalid2); +} + +BOOST_AUTO_TEST_CASE(unspent__fault__always__expected) +{ + const auto fault1 = unspent{}.fault(); + BOOST_REQUIRE(fault1); + + const auto fault2 = unspent{ {}, 2, 0 }.fault(); + BOOST_REQUIRE(fault2); + + const auto nonfault1 = !unspent{ {}, 2, 3 }.fault(); + BOOST_REQUIRE(nonfault1); + + const auto nonfault2 = !unspent{ {}, 2, unspent::unconfirmed_position }.fault(); + BOOST_REQUIRE(nonfault2); } BOOST_AUTO_TEST_CASE(unspent__confirmed__always__expected) From 9892c157468951bfb6952038d1b51c3ce8a5df9d Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 23 Apr 2026 22:40:19 -0400 Subject: [PATCH 3/3] Delint. --- .../impl/query/address/address_history.ipp | 6 +++--- test/query/address/address_history.cpp | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/bitcoin/database/impl/query/address/address_history.ipp b/include/bitcoin/database/impl/query/address/address_history.ipp index 98b32b131..25393cacb 100644 --- a/include/bitcoin/database/impl/query/address/address_history.ipp +++ b/include/bitcoin/database/impl/query/address/address_history.ipp @@ -51,7 +51,7 @@ code CLASS::get_unconfirmed_history(const stopper& cancel, histories& out, [this](const auto& link, auto& cancel, auto& fail) NOEXCEPT { if (cancel || fail) return history{}; - const auto out = get_tx_unconfirmed_history(link); + const auto out = this->get_tx_unconfirmed_history(link); if (out.fault()) fail = true; return out; }); @@ -79,7 +79,7 @@ code CLASS::get_confirmed_history(const stopper& cancel, height_link& cursor, [this, start, end](const auto& link, auto& cancel, auto& fail) NOEXCEPT { if (cancel || fail) return history{}; - const auto out = get_tx_confirmed_history(link, start, end); + const auto out = this->get_tx_confirmed_history(link, start, end); if (out.fault()) fail = true; return out; }); @@ -107,7 +107,7 @@ code CLASS::get_history(const stopper& cancel, height_link& cursor, [this, start, end](const auto& link, auto& cancel, auto& fail) NOEXCEPT { if (cancel || fail) return history{}; - const auto out = get_tx_history(link, start, end); + const auto out = this->get_tx_history(link, start, end); if (out.fault()) fail = true; return out; }); diff --git a/test/query/address/address_history.cpp b/test/query/address/address_history.cpp index 65f6d548c..a93190f80 100644 --- a/test/query/address/address_history.cpp +++ b/test/query/address/address_history.cpp @@ -171,22 +171,22 @@ BOOST_AUTO_TEST_CASE(query_address__get_history__start__excludes_prior) BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 0, true), error::depth_limited); BOOST_CHECK(out.empty()); - cursor = { 42 }; + cursor = 42; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 8, true), error::depth_limited); BOOST_CHECK(out.empty()); // Only unconfirmed. - cursor = { 42 }; + cursor = 42; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 9, true), error::success); BOOST_CHECK_EQUAL(out.size(), 4u); // Only excludes genesis. - cursor = { 1 }; + cursor = 1; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 100, true), error::success); BOOST_CHECK_EQUAL(out.size(), 8u); // Excludes block1a which has one confirmed touching tx. - cursor = { 2 }; + cursor = 2; BOOST_CHECK_EQUAL(query.get_history(cancel, cursor, out, hash, 100, true), error::success); BOOST_CHECK_EQUAL(out.size(), 7u); } @@ -385,17 +385,17 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_history__start__excludes_prior BOOST_CHECK(out.empty()); // Only unconfirmed. - cursor = { 42 }; + cursor = 42; BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 8, true), error::depth_limited); BOOST_CHECK(out.empty()); // Only excludes genesis. - cursor = { 1 }; + cursor = 1; BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 9, true), error::success); BOOST_CHECK_EQUAL(out.size(), 4u); // Excludes block1a which has one confirmed touching tx. - cursor = { 2 }; + cursor = 2; BOOST_CHECK_EQUAL(query.get_confirmed_history(cancel, cursor, out, hash, 100, true), error::success); BOOST_CHECK_EQUAL(out.size(), 3u); }