diff --git a/include/bitcoin/database/impl/query/objects.ipp b/include/bitcoin/database/impl/query/objects.ipp index 8fc3923e..f15a1f13 100644 --- a/include/bitcoin/database/impl/query/objects.ipp +++ b/include/bitcoin/database/impl/query/objects.ipp @@ -305,25 +305,32 @@ typename CLASS::output::cptr CLASS::get_output( } TEMPLATE -typename CLASS::point CLASS::get_spent(const output_link& link) const NOEXCEPT +typename CLASS::outpoint CLASS::get_spent( + const output_link& link) const NOEXCEPT { - if (const auto tx = to_output_tx(link); !tx.is_terminal()) - if (const auto index = to_output_index(tx, link); - index != point::null_index) - return { get_tx_key(tx), index }; + table::output::get_parent_value out{}; + if (!store_.output.get(link, out)) + return {}; + + const auto index = to_output_index(out.parent_fk, link); + if (index == point::null_index) + return {}; - return {}; + return { { get_tx_key(out.parent_fk), index }, out.value }; } TEMPLATE typename CLASS::point CLASS::get_spender(const point_link& link) const NOEXCEPT { - if (const auto tx = to_spending_tx(link); !tx.is_terminal()) - if (const auto index = to_input_index(tx, link); - index != point::null_index) - return { get_tx_key(tx), index }; + const auto tx = to_spending_tx(link); + if (tx.is_terminal()) + return {}; + + const auto index = to_input_index(tx, link); + if (index == point::null_index) + return {}; - return {}; + return { get_tx_key(tx), index }; } TEMPLATE diff --git a/include/bitcoin/database/impl/query/optional.ipp b/include/bitcoin/database/impl/query/optional.ipp index 75dfef0e..4502cc4b 100644 --- a/include/bitcoin/database/impl/query/optional.ipp +++ b/include/bitcoin/database/impl/query/optional.ipp @@ -19,6 +19,7 @@ #ifndef LIBBITCOIN_DATABASE_QUERY_OPTIONAL_IPP #define LIBBITCOIN_DATABASE_QUERY_OPTIONAL_IPP +#include #include #include #include @@ -32,19 +33,19 @@ namespace database { // ---------------------------------------------------------------------------- // TODO: use point keys (for multimap compression). -// TODO: test more. TEMPLATE -bool CLASS::to_address_outputs(output_links& out, - const hash_digest& key) const NOEXCEPT +bool CLASS::to_address_outputs(const std::atomic_bool& cancel, + output_links& out, const hash_digest& key) const NOEXCEPT { out.clear(); + + // Pushing into the vector is more efficient than precomputation of size. for (auto it = store_.address.it(key); it; ++it) { table::address::record address{}; - if (!store_.address.get(it, address)) + if (cancel || !store_.address.get(it, address)) { out.clear(); - out.shrink_to_fit(); return false; } @@ -56,11 +57,11 @@ bool CLASS::to_address_outputs(output_links& out, // TODO: test more. TEMPLATE -bool CLASS::to_confirmed_unspent_outputs(output_links& out, - const hash_digest& key) const NOEXCEPT +bool CLASS::to_confirmed_unspent_outputs(const std::atomic_bool& cancel, + output_links& out, const hash_digest& key) const NOEXCEPT { output_links output_fks{}; - if (!to_address_outputs(output_fks, key)) + if (!to_address_outputs(cancel, output_fks, key)) return false; out.clear(); @@ -75,11 +76,11 @@ bool CLASS::to_confirmed_unspent_outputs(output_links& out, // TODO: test more. TEMPLATE -bool CLASS::to_minimum_unspent_outputs(output_links& out, - const hash_digest& key, uint64_t minimum) const NOEXCEPT +bool CLASS::to_minimum_unspent_outputs(const std::atomic_bool& cancel, + output_links& out, const hash_digest& key, uint64_t minimum) const NOEXCEPT { output_links unspent_fks{}; - if (!to_confirmed_unspent_outputs(unspent_fks, key)) + if (!to_confirmed_unspent_outputs(cancel, unspent_fks, key)) return false; out.clear(); @@ -104,12 +105,12 @@ bool CLASS::to_minimum_unspent_outputs(output_links& out, // TODO: test more. TEMPLATE -bool CLASS::get_confirmed_balance(uint64_t& out, - const hash_digest& key) const NOEXCEPT +bool CLASS::get_confirmed_balance(const std::atomic_bool& cancel, + uint64_t& out, const hash_digest& key) const NOEXCEPT { out = zero; output_links unspent_fks{}; - if (!to_confirmed_unspent_outputs(unspent_fks, key)) + if (!to_confirmed_unspent_outputs(cancel, unspent_fks, key)) return false; for (auto unspent_fk: unspent_fks) diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 397fd57f..48b13436 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -19,6 +19,7 @@ #ifndef LIBBITCOIN_DATABASE_QUERY_HPP #define LIBBITCOIN_DATABASE_QUERY_HPP +#include #include #include #include @@ -65,6 +66,7 @@ class query using point = system::chain::point; using input = system::chain::input; using output = system::chain::output; + using outpoint = system::chain::outpoint; using header = system::chain::header; using script = system::chain::script; using witness = system::chain::witness; @@ -394,8 +396,8 @@ class query input::cptr get_input(const tx_link& link, uint32_t index, bool witness) const NOEXCEPT; - point get_spent(const output_link& link) const NOEXCEPT; point get_spender(const point_link& link) const NOEXCEPT; + outpoint get_spent(const output_link& link) const NOEXCEPT; script::cptr get_output_script(const output_link& link) const NOEXCEPT; output::cptr get_output(const output_link& link) const NOEXCEPT; output::cptr get_output(const tx_link& link, uint32_t index) const NOEXCEPT; @@ -570,15 +572,14 @@ class query /// Optional Tables. /// ----------------------------------------------------------------------- - /// Address, set internal to tx (natural-keyed). - bool to_address_outputs(output_links& out, - const hash_digest& key) const NOEXCEPT; - bool to_confirmed_unspent_outputs(output_links& out, - const hash_digest& key) const NOEXCEPT; - bool to_minimum_unspent_outputs(output_links& out, const hash_digest& key, - uint64_t value) const NOEXCEPT; - bool get_confirmed_balance(uint64_t& out, - const hash_digest& key) const NOEXCEPT; + bool to_address_outputs(const std::atomic_bool& cancel, + output_links& out, const hash_digest& key) const NOEXCEPT; + bool to_confirmed_unspent_outputs(const std::atomic_bool& cancel, + output_links& out, const hash_digest& key) const NOEXCEPT; + bool to_minimum_unspent_outputs(const std::atomic_bool& cancel, + output_links& out, const hash_digest& key, uint64_t value) const NOEXCEPT; + bool get_confirmed_balance(const std::atomic_bool& cancel, + uint64_t& out, const hash_digest& key) const NOEXCEPT; bool is_filtered_body(const header_link& link) const NOEXCEPT; bool get_filter_body(filter& out, const header_link& link) const NOEXCEPT; diff --git a/include/bitcoin/database/tables/archives/output.hpp b/include/bitcoin/database/tables/archives/output.hpp index 445ff530..3fa8642e 100644 --- a/include/bitcoin/database/tables/archives/output.hpp +++ b/include/bitcoin/database/tables/archives/output.hpp @@ -133,6 +133,26 @@ struct output system::chain::script::cptr script{}; }; + struct get_parent_value + : public schema::output + { + link count() const NOEXCEPT + { + BC_ASSERT(false); + return {}; + } + + inline bool from_data(reader& source) NOEXCEPT + { + parent_fk = source.read_little_endian(); + value = source.read_variable(); + return source; + } + + tx::integer parent_fk{}; + uint64_t value{}; + }; + struct get_parent : public schema::output { diff --git a/test/query/optional.cpp b/test/query/optional.cpp index 13ff316e..ea2d8230 100644 --- a/test/query/optional.cpp +++ b/test/query/optional.cpp @@ -27,7 +27,7 @@ const auto events_handler = [](auto, auto) {}; const auto genesis_address = test::genesis.transactions_ptr()->front()->outputs_ptr()->front()->script().hash(); -BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_balance__genesis__expected) +BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -36,12 +36,14 @@ BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_balance__genesis__expected) BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - uint64_t out{}; - BOOST_REQUIRE(query.get_confirmed_balance(out, genesis_address)); - BOOST_REQUIRE_EQUAL(out, 5000000000u); + output_links out{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(query.to_address_outputs(cancel, out, genesis_address)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE_EQUAL(out.front(), query.to_output(0, 0)); } -BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__genesis__expected) +BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__cancel__canceled_false) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -51,9 +53,9 @@ BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__genesis__expected) BOOST_REQUIRE(query.initialize(test::genesis)); output_links out{}; - BOOST_REQUIRE(query.to_address_outputs(out, genesis_address)); - BOOST_REQUIRE_EQUAL(out.size(), 1u); - BOOST_REQUIRE_EQUAL(out.front(), query.to_output(0, 0)); + const std::atomic_bool cancel{ true }; + BOOST_REQUIRE(!query.to_address_outputs(cancel, out, genesis_address)); + BOOST_REQUIRE(out.empty()); } BOOST_AUTO_TEST_CASE(query_optional__to_confirmed_unspent_outputs__genesis__expected) @@ -66,24 +68,26 @@ BOOST_AUTO_TEST_CASE(query_optional__to_confirmed_unspent_outputs__genesis__expe BOOST_REQUIRE(query.initialize(test::genesis)); output_links out{}; - BOOST_REQUIRE(query.to_confirmed_unspent_outputs(out, genesis_address)); + const std::atomic_bool cancel{}; + BOOST_REQUIRE(query.to_confirmed_unspent_outputs(cancel, out, genesis_address)); BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_REQUIRE_EQUAL(out.front(), 0); } -////BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__above__excluded) -////{ -//// settings settings{}; -//// settings.path = TEST_DIRECTORY; -//// test::chunk_store store{ settings }; -//// test::query_accessor query{ store }; -//// BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); -//// BOOST_REQUIRE(query.initialize(test::genesis)); -//// -//// output_links out{}; -//// BOOST_REQUIRE(query.to_minimum_unspent_outputs(out, genesis_address, 5000000001)); -//// BOOST_REQUIRE(out.empty()); -////} +BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__above__excluded) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); + BOOST_REQUIRE(query.initialize(test::genesis)); + + output_links out{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 5000000001)); + BOOST_REQUIRE(out.empty()); +} BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__at__included) { @@ -95,7 +99,8 @@ BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__at__included) BOOST_REQUIRE(query.initialize(test::genesis)); output_links out{}; - BOOST_REQUIRE(query.to_minimum_unspent_outputs(out, genesis_address, 5000000000)); + const std::atomic_bool cancel{}; + BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 5000000000)); BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_REQUIRE_EQUAL(out.front(), 0); } @@ -110,14 +115,30 @@ BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__below__included BOOST_REQUIRE(query.initialize(test::genesis)); output_links out{}; - BOOST_REQUIRE(query.to_minimum_unspent_outputs(out, genesis_address, 0)); + const std::atomic_bool cancel{}; + BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 0)); BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_REQUIRE_EQUAL(out.front(), 0); - BOOST_REQUIRE(query.to_minimum_unspent_outputs(out, genesis_address, 4999999999)); + BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 4999999999)); BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_REQUIRE_EQUAL(out.front(), 0); } +BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_balance__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); + BOOST_REQUIRE(query.initialize(test::genesis)); + + uint64_t out{}; + const std::atomic_bool cancel{}; + BOOST_REQUIRE(query.get_confirmed_balance(cancel, out, genesis_address)); + BOOST_REQUIRE_EQUAL(out, 5000000000u); +} + ////BOOST_AUTO_TEST_CASE(query_optional__set_filter__get_filter_and_head__expected) ////{ //// const auto& filter_head0 = system::null_hash;