diff --git a/include/bitcoin/database/error.hpp b/include/bitcoin/database/error.hpp index acac774d8..c8f52f92a 100644 --- a/include/bitcoin/database/error.hpp +++ b/include/bitcoin/database/error.hpp @@ -35,7 +35,10 @@ enum error_t : uint8_t { /// general success, + canceled, unknown_state, + + /// integrity (internal fault) integrity, integrity1, integrity2, diff --git a/include/bitcoin/database/impl/query/optional.ipp b/include/bitcoin/database/impl/query/optional.ipp index 975fbcdc5..44e12cf9e 100644 --- a/include/bitcoin/database/impl/query/optional.ipp +++ b/include/bitcoin/database/impl/query/optional.ipp @@ -26,103 +26,109 @@ #include #include + // TODO: address table could use point keys to compress the multimap. + namespace libbitcoin { namespace database { // Address (natural-keyed). // ---------------------------------------------------------------------------- -// TODO: could use point keys (for multimap compression). +// Pushing into vectors is more efficient than precomputation of size. TEMPLATE -bool CLASS::to_address_outputs(const std::atomic_bool& cancel, - output_links& out, const hash_digest& key) const NOEXCEPT +code CLASS::to_address_outputs(const std::atomic_bool& cancel, + outpoints& 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) { + if (cancel) + return error::canceled; + table::address::record address{}; - if (cancel || !store_.address.get(it, address)) - { - out.clear(); - return false; - } + if (!store_.address.get(it, address)) + return error::integrity; - out.push_back(address.output_fk); + out.insert(get_spent(address.output_fk)); } - return true; + return error::success; } TEMPLATE -bool CLASS::to_confirmed_unspent_outputs(const std::atomic_bool& cancel, - output_links& out, const hash_digest& key) const NOEXCEPT +code CLASS::to_confirmed_unspent_outputs(const std::atomic_bool& cancel, + outpoints& out, const hash_digest& key) const NOEXCEPT { - output_links output_fks{}; - if (!to_address_outputs(cancel, output_fks, key)) - return false; - out.clear(); - out.reserve(output_fks.size()); - for (auto output_fk: output_fks) - if (is_confirmed_unspent(output_fk)) - out.push_back(output_fk); + for (auto it = store_.address.it(key); it; ++it) + { + if (cancel) + return error::canceled; - out.shrink_to_fit(); - return true; + table::address::record address{}; + if (!store_.address.get(it, address)) + return error::integrity; + + if (is_confirmed_unspent(address.output_fk)) + out.insert(get_spent(address.output_fk)); + } + + return error::success; } -// TODO: test more. TEMPLATE -bool CLASS::to_minimum_unspent_outputs(const std::atomic_bool& cancel, - output_links& out, const hash_digest& key, uint64_t minimum) const NOEXCEPT +code CLASS::to_minimum_unspent_outputs(const std::atomic_bool& cancel, + outpoints& out, const hash_digest& key, uint64_t minimum) const NOEXCEPT { - output_links unspent_fks{}; - if (!to_confirmed_unspent_outputs(cancel, unspent_fks, key)) - return false; - out.clear(); - out.reserve(unspent_fks.size()); - for (auto unspent_fk: unspent_fks) + for (auto it = store_.address.it(key); it; ++it) { - uint64_t value{}; - if (!get_value(value, unspent_fk)) + if (cancel) + return error::canceled; + + table::address::record address{}; + if (!store_.address.get(it, address)) + return error::integrity; + + if (is_confirmed_unspent(address.output_fk)) { - out.clear(); - out.shrink_to_fit(); - return false; - } + uint64_t value{}; + if (!get_value(value, address.output_fk)) + return error::integrity; - if (value >= minimum) - out.push_back(unspent_fk); + if (value >= minimum) + out.insert(get_spent(address.output_fk)); + } } - out.shrink_to_fit(); - return true; + return error::success; } -// TODO: test more. TEMPLATE -bool CLASS::get_confirmed_balance(const std::atomic_bool& cancel, +code 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(cancel, unspent_fks, key)) - return false; - - for (auto unspent_fk: unspent_fks) + for (auto it = store_.address.it(key); it; ++it) { - uint64_t value{}; - if (!get_value(value, unspent_fk)) - return false; + if (cancel) + return error::canceled; + + table::address::record address{}; + if (!store_.address.get(it, address)) + return error::integrity; - // max if overflowed. - out = system::ceilinged_add(value, out); + if (is_confirmed_unspent(address.output_fk)) + { + uint64_t value{}; + if (!get_value(value, address.output_fk)) + return error::integrity; + + out = system::ceilinged_add(value, out); + } } - return true; + return error::success; } ////TEMPLATE diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 3be2d9df6..6eec21c4f 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -549,13 +549,13 @@ class query /// Optional Tables. /// ----------------------------------------------------------------------- - 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, + code to_address_outputs(const std::atomic_bool& cancel, + outpoints& out, const hash_digest& key) const NOEXCEPT; + code to_confirmed_unspent_outputs(const std::atomic_bool& cancel, + outpoints& out, const hash_digest& key) const NOEXCEPT; + code to_minimum_unspent_outputs(const std::atomic_bool& cancel, + outpoints& out, const hash_digest& key, uint64_t value) const NOEXCEPT; + code 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; diff --git a/src/error.cpp b/src/error.cpp index 307a2bab5..a7877eb09 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -28,7 +28,10 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { // general { success, "success" }, + { canceled, "canceled" }, { unknown_state, "unknown state" }, + + // integrity (internal fault) { integrity, "store corrupted" }, { integrity1, "store corrupted1" }, { integrity2, "store corrupted2" }, diff --git a/test/error.cpp b/test/error.cpp index cce073c9b..824c0de1d 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -32,6 +32,15 @@ BOOST_AUTO_TEST_CASE(error_t__code__success__false_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "success"); } +BOOST_AUTO_TEST_CASE(error_t__code__canceled__true_exected_message) +{ + constexpr auto value = error::canceled; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "canceled"); +} + BOOST_AUTO_TEST_CASE(error_t__code__unknown_state__true_exected_message) { constexpr auto value = error::unknown_state; diff --git a/test/query/optional.cpp b/test/query/optional.cpp index ea2d82304..052f82ecf 100644 --- a/test/query/optional.cpp +++ b/test/query/optional.cpp @@ -36,11 +36,11 @@ BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__genesis__expected) BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - output_links out{}; + outpoints out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(query.to_address_outputs(cancel, out, genesis_address)); + 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_REQUIRE_EQUAL(out.front(), query.to_output(0, 0)); } BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__cancel__canceled_false) @@ -52,9 +52,9 @@ BOOST_AUTO_TEST_CASE(query_optional__to_address_outputs__cancel__canceled_false) BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - output_links out{}; + outpoints out{}; const std::atomic_bool cancel{ true }; - BOOST_REQUIRE(!query.to_address_outputs(cancel, out, genesis_address)); + BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, out, genesis_address), error::canceled); BOOST_REQUIRE(out.empty()); } @@ -67,11 +67,11 @@ BOOST_AUTO_TEST_CASE(query_optional__to_confirmed_unspent_outputs__genesis__expe BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - output_links out{}; + outpoints out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(query.to_confirmed_unspent_outputs(cancel, out, genesis_address)); + BOOST_REQUIRE(!query.to_confirmed_unspent_outputs(cancel, out, genesis_address)); BOOST_REQUIRE_EQUAL(out.size(), 1u); - BOOST_REQUIRE_EQUAL(out.front(), 0); + ////BOOST_REQUIRE_EQUAL(out.front(), 0); } BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__above__excluded) @@ -83,9 +83,9 @@ BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__above__excluded BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - output_links out{}; + outpoints out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 5000000001)); + BOOST_REQUIRE(!query.to_minimum_unspent_outputs(cancel, out, genesis_address, 5000000001)); BOOST_REQUIRE(out.empty()); } @@ -98,11 +98,11 @@ BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__at__included) BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - output_links out{}; + outpoints out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 5000000000)); + BOOST_REQUIRE(!query.to_minimum_unspent_outputs(cancel, out, genesis_address, 5000000000)); BOOST_REQUIRE_EQUAL(out.size(), 1u); - BOOST_REQUIRE_EQUAL(out.front(), 0); + ////BOOST_REQUIRE_EQUAL(out.front(), 0); } BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__below__included) @@ -114,14 +114,14 @@ BOOST_AUTO_TEST_CASE(query_optional__to_minimum_unspent_outputs__below__included BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - output_links out{}; + outpoints out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(query.to_minimum_unspent_outputs(cancel, out, genesis_address, 0)); + 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(cancel, out, genesis_address, 4999999999)); + ////BOOST_REQUIRE_EQUAL(out.front(), 0); + 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_REQUIRE_EQUAL(out.front(), 0); } BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_balance__genesis__expected) @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_balance__genesis__expected) uint64_t out{}; const std::atomic_bool cancel{}; - BOOST_REQUIRE(query.get_confirmed_balance(cancel, out, genesis_address)); + BOOST_REQUIRE(!query.get_confirmed_balance(cancel, out, genesis_address)); BOOST_REQUIRE_EQUAL(out, 5000000000u); }