diff --git a/include/bitcoin/database/impl/query/address/address_balance.ipp b/include/bitcoin/database/impl/query/address/address_balance.ipp index 9a655ca0..c2ab6377 100644 --- a/include/bitcoin/database/impl/query/address/address_balance.ipp +++ b/include/bitcoin/database/impl/query/address/address_balance.ipp @@ -33,10 +33,15 @@ namespace database { // unused TEMPLATE -code CLASS::get_unconfirmed_balance(stopper& , uint64_t& , - const hash_digest& , bool ) const NOEXCEPT +code CLASS::get_unconfirmed_balance(stopper& cancel, uint64_t& out, + const hash_digest& key, bool turbo) const NOEXCEPT { - return {}; + // While duplicates are easily filtered out, conflict resolution is murky. + // An output may have multiple directly or indirectly conflicting spends, + // and other spends and receives may not be visible. An unconfirmed balance + // is therefore inherehtly ambiguous. Given the lack of tx pooling, + // presently we just return combined = confirmed (net zero unconfirmed). + return get_confirmed_balance(cancel, out, key, turbo); } // server/native @@ -63,11 +68,13 @@ code CLASS::get_confirmed_balance(stopper& cancel, uint64_t& out, // server/electrum TEMPLATE -code CLASS::get_balance(stopper& , uint64_t& , - uint64_t& , const hash_digest& , - bool ) const NOEXCEPT +code CLASS::get_balance(stopper& cancel, uint64_t& confirmed, + uint64_t& combined, const hash_digest& key, bool turbo) const NOEXCEPT { - return {}; + // See notes on get_unconfirmed_balance(). + const auto ec = get_confirmed_balance(cancel, confirmed, key, turbo); + combined = confirmed; + return ec; } } // namespace database diff --git a/include/bitcoin/database/impl/query/address/address_history.ipp b/include/bitcoin/database/impl/query/address/address_history.ipp index b854ff57..8c5b04b1 100644 --- a/include/bitcoin/database/impl/query/address/address_history.ipp +++ b/include/bitcoin/database/impl/query/address/address_history.ipp @@ -35,26 +35,30 @@ namespace database { // tied-height transactions sorted by base16 txid (not converted). TEMPLATE -code CLASS::get_unconfirmed_address(stopper& , histories& , +code CLASS::get_unconfirmed_history(stopper& , histories& , const hash_digest& , bool ) const NOEXCEPT { return {}; } TEMPLATE -code CLASS::get_confirmed_address(stopper& , histories& , +code CLASS::get_confirmed_history(stopper& , histories& , const hash_digest& , bool ) const NOEXCEPT { return {}; } TEMPLATE -code CLASS::get_address(stopper& , histories& , +code CLASS::get_history(stopper& , histories& , const hash_digest& , bool ) const NOEXCEPT { return {}; } +// turbos +// ---------------------------------------------------------------------------- +// protected + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/query/address/address_outpoints.ipp b/include/bitcoin/database/impl/query/address/address_outpoints.ipp index 46639ee8..bc6fcd4d 100644 --- a/include/bitcoin/database/impl/query/address/address_outpoints.ipp +++ b/include/bitcoin/database/impl/query/address/address_outpoints.ipp @@ -33,88 +33,44 @@ namespace database { // server/native TEMPLATE -code CLASS::get_address_outputs(stopper& cancel, outpoints& out, - const hash_digest& key, bool turbo) const NOEXCEPT -{ - if (turbo && store_.turbo()) - return get_address_outputs_turbo(cancel, out, key); - - out.clear(); - for (auto it = store_.address.it(key); it; ++it) - { - if (cancel) - return error::canceled; - - table::address::record address{}; - if (!store_.address.get(it, address)) - return error::integrity; - - out.insert(get_spent(address.output_fk)); - } - - return error::success; -} - -// protected -TEMPLATE -code CLASS::get_confirmed_unspent_outputs_turbo(stopper& cancel, - outpoints& out, const hash_digest& key) const NOEXCEPT +code CLASS::get_confirmed_unspent_outputs(stopper& cancel, + outpoints& out, const hash_digest& key, bool turbo) const NOEXCEPT { out.clear(); output_links links{}; if (const code ec = to_address_outputs(cancel, links, key)) return ec; - return parallel_address_transform(cancel, out, links, + return parallel_address_transform(cancel, turbo, out, links, [this](const auto& link, auto& cancel, auto& fail) NOEXCEPT { + // !is_confirmed_unspent must be filtered out. if (cancel || fail || !is_confirmed_unspent(link)) return outpoint{}; - auto outpoint = get_spent(link); - fail = (outpoint.point().index() == point::null_index); + auto outpoint = get_outpoint(link); + fail = outpoint.point().is_null(); return outpoint; }); -} - -// server/native -TEMPLATE -code CLASS::get_confirmed_unspent_outputs(stopper& cancel, - outpoints& out, const hash_digest& key, bool turbo) const NOEXCEPT -{ - if (turbo && store_.turbo()) - return get_confirmed_unspent_outputs_turbo(cancel, out, key); - - out.clear(); - for (auto it = store_.address.it(key); it; ++it) - { - 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.insert(get_spent(address.output_fk)); - } return error::success; } -// protected +// unused TEMPLATE -code CLASS::get_minimum_unspent_outputs_turbo(stopper& cancel, - outpoints& out, const hash_digest& key, uint64_t minimum) const NOEXCEPT +code CLASS::get_minimum_unspent_outputs(stopper& cancel, + outpoints& out, const hash_digest& key, uint64_t minimum, + bool turbo) const NOEXCEPT { out.clear(); output_links links{}; if (const code ec = to_address_outputs(cancel, links, key)) return ec; - return parallel_address_transform(cancel, out, links, + return parallel_address_transform(cancel, turbo, out, links, [this, minimum](const auto& link, auto& cancel, auto& fail) NOEXCEPT { + // !is_confirmed_unspent must be filtered out. if (cancel || fail || !is_confirmed_unspent(link)) return outpoint{}; @@ -125,100 +81,78 @@ code CLASS::get_minimum_unspent_outputs_turbo(stopper& cancel, return outpoint{}; } + // Must be filtered out. if (value < minimum) return outpoint{}; - auto outpoint = get_spent(link); - fail = (outpoint.point().index() == point::null_index); + auto outpoint = get_outpoint(link); + fail = outpoint.point().is_null(); return outpoint; }); + + return error::success; } -// unused +// server/native TEMPLATE -code CLASS::get_minimum_unspent_outputs(stopper& cancel, - outpoints& out, const hash_digest& key, uint64_t minimum, - bool turbo) const NOEXCEPT +code CLASS::get_address_outputs(stopper& cancel, outpoints& out, + const hash_digest& key, bool turbo) const NOEXCEPT { - if (turbo && store_.turbo()) - return get_minimum_unspent_outputs_turbo(cancel, out, key, minimum); - out.clear(); - for (auto it = store_.address.it(key); it; ++it) - { - if (cancel) - return error::canceled; - - table::address::record address{}; - if (!store_.address.get(it, address)) - return error::integrity; + output_links links{}; + if (const code ec = to_address_outputs(cancel, links, key)) + return ec; - if (is_confirmed_unspent(address.output_fk)) + return parallel_address_transform(cancel, turbo, out, links, + [this](const auto& link, auto& cancel, auto& fail) NOEXCEPT { - uint64_t value{}; - if (!get_value(value, address.output_fk)) - return error::integrity; - - if (value >= minimum) - out.insert(get_spent(address.output_fk)); - } - } + if (cancel || fail) return outpoint{}; + auto outpoint = get_outpoint(link); + fail = outpoint.point().is_null(); + return outpoint; + }); return error::success; } // utilities // ---------------------------------------------------------------------------- - // private/static + TEMPLATE template -inline code CLASS::parallel_address_transform(stopper& cancel, +inline code CLASS::parallel_address_transform(stopper& cancel, bool turbo, outpoints& out, const output_links& links, Functor&& functor) NOEXCEPT { - constexpr auto parallel = poolstl::execution::par; - + out.clear(); stopper fail{}; std::vector outpoints(links.size()); - std::transform(parallel, links.begin(), links.end(), outpoints.begin(), + const auto policy = poolstl::execution::par_if(turbo); + std::transform(policy, links.begin(), links.end(), outpoints.begin(), [&functor, &cancel, &fail](const auto& link) NOEXCEPT { return functor(link, cancel, fail); }); - out.clear(); - if (fail) return error::integrity; - if (cancel) return error::canceled; + if (fail) + return error::integrity; + + if (cancel) + return error::canceled; + for (auto& outpoint: outpoints) { - if (cancel) return error::canceled; - if (outpoint.point().index() != point::null_index) + if (cancel) + return error::canceled; + + // Filter out non-failures. + if (!outpoint.point().is_null()) out.insert(std::move(outpoint)); } return error::success; } -// protected -TEMPLATE -code CLASS::get_address_outputs_turbo(stopper& cancel, outpoints& out, - const hash_digest& key) const NOEXCEPT -{ - out.clear(); - output_links links{}; - if (const code ec = to_address_outputs(cancel, links, key)) - return ec; - - return parallel_address_transform(cancel, out, links, - [this](const auto& link, auto& cancel, auto& fail) NOEXCEPT - { - if (cancel || fail) return outpoint{}; - auto outpoint = get_spent(link); - fail = (outpoint.point().index() == point::null_index); - return outpoint; - }); -} - } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/query/address/address_unspent.ipp b/include/bitcoin/database/impl/query/address/address_unspent.ipp index 336f0d17..2d01cac6 100644 --- a/include/bitcoin/database/impl/query/address/address_unspent.ipp +++ b/include/bitcoin/database/impl/query/address/address_unspent.ipp @@ -33,14 +33,14 @@ namespace database { // Unconfirmed unspent are included at end of list in consistent order. TEMPLATE -code CLASS::get_unconfirmed_unspent(stopper& , histories& , +code CLASS::get_unconfirmed_unspent(stopper& , unspents& , const hash_digest& , bool ) const NOEXCEPT { return {}; } TEMPLATE -code CLASS::get_confirmed_unspent(stopper& , histories& , +code CLASS::get_confirmed_unspent(stopper& , unspents& , const hash_digest& , bool ) const NOEXCEPT { return {}; @@ -53,6 +53,10 @@ code CLASS::get_unspent(stopper& , unspents& , return {}; } +// turbos +// ---------------------------------------------------------------------------- +// protected + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/query/archive/chain_reader.ipp b/include/bitcoin/database/impl/query/archive/chain_reader.ipp index b2156208..6845d643 100644 --- a/include/bitcoin/database/impl/query/archive/chain_reader.ipp +++ b/include/bitcoin/database/impl/query/archive/chain_reader.ipp @@ -336,7 +336,7 @@ typename CLASS::inputs_ptr CLASS::get_spenders( // ---------------------------------------------------------------------------- TEMPLATE -outpoint CLASS::get_spent(const output_link& link) const NOEXCEPT +outpoint CLASS::get_outpoint(const output_link& link) const NOEXCEPT { table::output::get_parent_value out{}; if (!store_.output.get(link, out)) diff --git a/include/bitcoin/database/impl/query/confirmed.ipp b/include/bitcoin/database/impl/query/confirmed.ipp index 4c5f2be7..732c1e8e 100644 --- a/include/bitcoin/database/impl/query/confirmed.ipp +++ b/include/bitcoin/database/impl/query/confirmed.ipp @@ -57,7 +57,7 @@ point_link CLASS::find_confirmed_spender(const point& prevout) const NOEXCEPT TEMPLATE bool CLASS::is_confirmed_unspent(const output_link& link) const NOEXCEPT { - return is_confirmed_output(link) && !is_spent_output(link); + return is_confirmed_output(link) && !is_confirmed_spent_output(link); } TEMPLATE @@ -111,7 +111,7 @@ bool CLASS::is_confirmed_output(const output_link& link) const NOEXCEPT } TEMPLATE -bool CLASS::is_spent_output(const output_link& link) const NOEXCEPT +bool CLASS::is_confirmed_spent_output(const output_link& link) const NOEXCEPT { // The spender is strong *and* its block is confirmed (by height). const auto ins = to_spenders(link); diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 107f663f..e22917ff 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -414,7 +414,7 @@ class query bool witness) const NOEXCEPT; /// Inpoint and outpoint result sets. - outpoint get_spent(const output_link& link) const NOEXCEPT; + outpoint get_outpoint(const output_link& link) const NOEXCEPT; inpoint get_spender(const point_link& link) const NOEXCEPT; inpoints get_spenders(const point& point) const NOEXCEPT; @@ -550,7 +550,7 @@ class query bool is_confirmed_tx(const tx_link& link) const NOEXCEPT; bool is_confirmed_input(const point_link& link) const NOEXCEPT; bool is_confirmed_output(const output_link& link) const NOEXCEPT; - bool is_spent_output(const output_link& link) const NOEXCEPT; + bool is_confirmed_spent_output(const output_link& link) const NOEXCEPT; /// Height index not used by these. bool is_strong_tx(const tx_link& link) const NOEXCEPT; @@ -611,26 +611,28 @@ class query /// Address. /// ----------------------------------------------------------------------- - /// Native queries (deduped, arbitrary sort). - code get_minimum_unspent_outputs(stopper& cancel, outpoints& out, - const hash_digest& key, uint64_t value, bool turbo=false) const NOEXCEPT; + /// Native queries (outpoints, deduped, arbitrary sort). + ////code get_unconfirmed_unspent_outputs(stopper& cancel, outpoints& out, + //// const hash_digest& key, bool turbo=false) const NOEXCEPT; code get_confirmed_unspent_outputs(stopper& cancel, outpoints& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; + code get_minimum_unspent_outputs(stopper& cancel, outpoints& out, + const hash_digest& key, uint64_t value, bool turbo=false) const NOEXCEPT; code get_address_outputs(stopper& cancel, outpoints& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; - /// Electrum queries (deduped, electrum sort). - code get_unconfirmed_address(stopper& cancel, histories& out, + /// Electrum queries (histories, deduped, electrum sort). + code get_unconfirmed_history(stopper& cancel, histories& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; - code get_confirmed_address(stopper& cancel, histories& out, + code get_confirmed_history(stopper& cancel, histories& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; - code get_address(stopper& cancel, histories& out, + code get_history(stopper& cancel, histories& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; - /// Electrum queries (deduped, electrum sort). - code get_unconfirmed_unspent(stopper& cancel, histories& out, + /// Electrum queries (unspents, deduped, electrum sort). + code get_unconfirmed_unspent(stopper& cancel, unspents& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; - code get_confirmed_unspent(stopper& cancel, histories& out, + code get_confirmed_unspent(stopper& cancel, unspents& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; code get_unspent(stopper& cancel, unspents& out, const hash_digest& key, bool turbo=false) const NOEXCEPT; @@ -713,6 +715,7 @@ class query /// Confirm. /// ----------------------------------------------------------------------- bool is_confirmed_unspent(const output_link& link) const NOEXCEPT; + bool is_unconfirmed_unspent(const output_link& link) const NOEXCEPT; /// Consensus. /// ----------------------------------------------------------------------- @@ -782,15 +785,6 @@ class query bool populate_with_metadata_(const transaction& tx, bool chain) const NOEXCEPT; - /// address - /// ----------------------------------------------------------------------- - code get_address_outputs_turbo(stopper& cancel, outpoints& out, - const hash_digest& key) const NOEXCEPT; - code get_confirmed_unspent_outputs_turbo(stopper& cancel, outpoints& out, - const hash_digest& key) const NOEXCEPT; - code get_minimum_unspent_outputs_turbo(stopper& cancel, outpoints& out, - const hash_digest& key, uint64_t minimum) const NOEXCEPT; - /// merkle /// ----------------------------------------------------------------------- struct position { size_t sibling; size_t width; }; @@ -827,7 +821,7 @@ class query static inline bool push_bool(std_vector& stack, const Bool& element) NOEXCEPT; template - static inline code parallel_address_transform(stopper& cancel, + static inline code parallel_address_transform(stopper& cancel, bool turbo, outpoints& out, const output_links& links, Functor&& functor) NOEXCEPT; static inline point::cptr make_point(hash_digest&& hash, uint32_t index) NOEXCEPT; diff --git a/include/bitcoin/database/types.hpp b/include/bitcoin/database/types.hpp index 8bcc7c62..8dcf4540 100644 --- a/include/bitcoin/database/types.hpp +++ b/include/bitcoin/database/types.hpp @@ -90,7 +90,11 @@ using fee_rate_sets = std::vector; struct span { - inline size_t size() const NOEXCEPT { return end - begin; } + inline size_t size() const NOEXCEPT + { + return system::floored_subtract(end, begin); + } + size_t begin; size_t end; }; diff --git a/src/types.cpp b/src/types.cpp index d3b98f80..ed4c9d59 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -33,8 +33,8 @@ inline bool hash_less_than(const hash_digest& a, const hash_digest& b) NOEXCEPT // return encode_hash(a) < encode_hash(a) for (auto byte{ hash_size }; !is_zero(byte); --byte) { - const auto byte_a = a[sub1(byte)]; - const auto byte_b = b[sub1(byte)]; + const auto byte_a = a.at(sub1(byte)); + const auto byte_b = b.at(sub1(byte)); const auto hi_a = shift_right(byte_a, hi); const auto hi_b = shift_right(byte_b, hi); diff --git a/test/mocks/blocks.cpp b/test/mocks/blocks.cpp index 4659cd23..d2ca7eaa 100644 --- a/test/mocks/blocks.cpp +++ b/test/mocks/blocks.cpp @@ -25,6 +25,8 @@ using namespace system; constexpr hash_digest two_hash = from_uintx(uint256_t(two)); constexpr database::context context{ 0x01020304, 0x11121314, 0x21222324 }; +constexpr hash_digest genesis_address = base16_hash("740485f380ff6379d11ef6fe7d7cdd68aea7f8bd0d953d9fdf3531fb7d531833"); + constexpr hash_digest block0_hash = base16_hash("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); constexpr hash_digest block1_hash = base16_hash("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"); constexpr hash_digest block2_hash = base16_hash("000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"); @@ -81,13 +83,6 @@ const block block7{ block7_data, true }; const block block8{ block8_data, true }; const block block9{ block9_data, true }; -system::hash_digest genesis_address() NOEXCEPT -{ - static const auto address = genesis.transactions_ptr()->front()-> - outputs_ptr()->front()->script().hash(); - return address; -} - bool setup_three_block_store(query_t& query) NOEXCEPT { return query.initialize(genesis) && diff --git a/test/mocks/blocks.hpp b/test/mocks/blocks.hpp index f802e78f..219c3066 100644 --- a/test/mocks/blocks.hpp +++ b/test/mocks/blocks.hpp @@ -30,6 +30,8 @@ using query_t = database::query>; const auto events_handler = [](auto, auto) {}; extern const database::context context; +extern const system::hash_digest genesis_address; + extern const system::hash_digest block0_hash; extern const system::hash_digest block1_hash; extern const system::hash_digest block2_hash; @@ -101,7 +103,6 @@ extern const system::chain::block block_spend_internal_2b; extern const system::chain::block block_missing_prevout_2b; extern const system::chain::block block_valid_spend_internal_2b; -system::hash_digest genesis_address() NOEXCEPT; bool setup_three_block_store(query_t& query) NOEXCEPT; bool setup_three_block_witness_store(query_t& query) NOEXCEPT; diff --git a/test/query/address/address_balance.cpp b/test/query/address/address_balance.cpp index 71325747..dd8a2fa6 100644 --- a/test/query/address/address_balance.cpp +++ b/test/query/address/address_balance.cpp @@ -22,11 +22,11 @@ BOOST_FIXTURE_TEST_SUITE(query_address_tests, test::directory_setup_fixture) -// TODO: get_unconfirmed_balance +// get_unconfirmed_balance // get_confirmed_balance -// TODO: get_balance +// get_balance -BOOST_AUTO_TEST_CASE(query_address__get_confirmed_balance__turbo_genesis__expected) +BOOST_AUTO_TEST_CASE(query_address__get_balance__turbo_genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -35,13 +35,22 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_balance__turbo_genesis__expect BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - uint64_t out{}; + uint64_t combined{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_confirmed_balance(cancel, out, test::genesis_address(), true)); - BOOST_REQUIRE_EQUAL(out, 5000000000u); + BOOST_REQUIRE(!query.get_unconfirmed_balance(cancel, combined, test::genesis_address, true)); + BOOST_REQUIRE_EQUAL(combined, 5000000000u); + + uint64_t confirmed{}; + BOOST_REQUIRE(!query.get_confirmed_balance(cancel, confirmed, test::genesis_address, true)); + BOOST_REQUIRE_EQUAL(confirmed, 5000000000u); + + confirmed = combined = 42; + BOOST_REQUIRE(!query.get_balance(cancel, confirmed, combined, test::genesis_address, true)); + BOOST_REQUIRE_EQUAL(confirmed, 5000000000u); + BOOST_REQUIRE_EQUAL(combined, 5000000000u); } -BOOST_AUTO_TEST_CASE(query_address__get_confirmed_balance__genesis__expected) +BOOST_AUTO_TEST_CASE(query_address__get_balance__genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -50,10 +59,19 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_balance__genesis__expected) BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - uint64_t out{}; + uint64_t combined{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_confirmed_balance(cancel, out, test::genesis_address())); - BOOST_REQUIRE_EQUAL(out, 5000000000u); + BOOST_REQUIRE(!query.get_unconfirmed_balance(cancel, combined, test::genesis_address)); + BOOST_REQUIRE_EQUAL(combined, 5000000000u); + + uint64_t confirmed{}; + BOOST_REQUIRE(!query.get_confirmed_balance(cancel, confirmed, test::genesis_address)); + BOOST_REQUIRE_EQUAL(confirmed, 5000000000u); + + confirmed = combined = 42; + BOOST_REQUIRE(!query.get_balance(cancel, confirmed, combined, test::genesis_address)); + BOOST_REQUIRE_EQUAL(confirmed, 5000000000u); + BOOST_REQUIRE_EQUAL(combined, 5000000000u); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/address/address_history.cpp b/test/query/address/address_history.cpp index 66af766d..ecd5c228 100644 --- a/test/query/address/address_history.cpp +++ b/test/query/address/address_history.cpp @@ -22,8 +22,54 @@ BOOST_FIXTURE_TEST_SUITE(query_address_tests, test::directory_setup_fixture) -// get_unconfirmed_address -// get_confirmed_address -// get_address +// get_unconfirmed_history +// get_confirmed_history +// get_history + +BOOST_AUTO_TEST_CASE(query_address__get_history__turbo_genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); + BOOST_REQUIRE(query.initialize(test::genesis)); + + histories out{}; + std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.get_unconfirmed_history(cancel, out, test::genesis_address, true)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_confirmed_history(cancel, out, test::genesis_address, true)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_history(cancel, out, test::genesis_address, true)); + BOOST_REQUIRE(out.empty()); +} + +BOOST_AUTO_TEST_CASE(query_address__get_history__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); + BOOST_REQUIRE(query.initialize(test::genesis)); + + histories out{}; + std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.get_unconfirmed_history(cancel, out, test::genesis_address)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_confirmed_history(cancel, out, test::genesis_address)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_history(cancel, out, test::genesis_address)); + BOOST_REQUIRE(out.empty()); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/address/address_outpoints.cpp b/test/query/address/address_outpoints.cpp index 8dedabf5..517b8fa7 100644 --- a/test/query/address/address_outpoints.cpp +++ b/test/query/address/address_outpoints.cpp @@ -22,9 +22,9 @@ BOOST_FIXTURE_TEST_SUITE(query_address_tests, test::directory_setup_fixture) -// get_address_outputs +// get_confirmed_unspent_outputs -BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__turbo_genesis__expected) +BOOST_AUTO_TEST_CASE(query_address__get_confirmed_unspent_outputs__turbo_genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -35,12 +35,12 @@ BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__turbo_genesis__expected outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_address_outputs(cancel, out, test::genesis_address(), true)); + BOOST_REQUIRE(!query.get_confirmed_unspent_outputs(cancel, out, test::genesis_address, true)); BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), query.to_output(0, 0)); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); } -BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__genesis__expected) +BOOST_AUTO_TEST_CASE(query_address__get_confirmed_unspent_outputs__genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -51,12 +51,14 @@ BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__genesis__expected) outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_address_outputs(cancel, out, test::genesis_address())); + BOOST_REQUIRE(!query.get_confirmed_unspent_outputs(cancel, out, test::genesis_address)); BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), query.to_output(0, 0)); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); } -BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__cancel__canceled_false) +// get_minimum_unspent_outputs + +BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__turbo_above__excluded) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -66,14 +68,12 @@ BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__cancel__canceled_false) BOOST_REQUIRE(query.initialize(test::genesis)); outpoints out{}; - std::atomic_bool cancel{ true }; - BOOST_REQUIRE_EQUAL(query.get_address_outputs(cancel, out, test::genesis_address()), error::canceled); + std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address, 5000000001, true)); BOOST_REQUIRE(out.empty()); } -// get_confirmed_unspent_outputs - -BOOST_AUTO_TEST_CASE(query_address__get_confirmed_unspent_outputs__turbo_genesis__expected) +BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__above__excluded) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -84,12 +84,11 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_unspent_outputs__turbo_genesis outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_confirmed_unspent_outputs(cancel, out, test::genesis_address(), true)); - BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), 0); + BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address, 5000000001)); + BOOST_REQUIRE(out.empty()); } -BOOST_AUTO_TEST_CASE(query_address__get_confirmed_unspent_outputs__genesis__expected) +BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__at__included) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -100,14 +99,12 @@ BOOST_AUTO_TEST_CASE(query_address__get_confirmed_unspent_outputs__genesis__expe outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_confirmed_unspent_outputs(cancel, out, test::genesis_address())); + BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address, 5000000000)); BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), 0); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); } -// get_minimum_unspent_outputs - -BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__turbo_above__excluded) +BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__below__included) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -118,11 +115,18 @@ BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__turbo_above__ex outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address(), 5000000001, true)); - BOOST_REQUIRE(out.empty()); + BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address, 0)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); + + BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address, 4999999999)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); } -BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__above__excluded) +// get_address_outputs + +BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__turbo_genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -133,11 +137,12 @@ BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__above__excluded outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address(), 5000000001)); - BOOST_REQUIRE(out.empty()); + BOOST_REQUIRE(!query.get_address_outputs(cancel, out, test::genesis_address, true)); + BOOST_REQUIRE_EQUAL(out.size(), 1u); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); } -BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__at__included) +BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__genesis__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -148,12 +153,12 @@ BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__at__included) outpoints out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address(), 5000000000)); + BOOST_REQUIRE(!query.get_address_outputs(cancel, out, test::genesis_address)); BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), 0); + BOOST_REQUIRE(*out.begin() == query.get_outpoint(query.to_output(0, 0))); } -BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__below__included) +BOOST_AUTO_TEST_CASE(query_address__get_address_outputs__cancel__canceled_false) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -163,13 +168,9 @@ BOOST_AUTO_TEST_CASE(query_address__get_minimum_unspent_outputs__below__included BOOST_REQUIRE(query.initialize(test::genesis)); outpoints out{}; - std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address(), 0)); - BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), 0); - BOOST_REQUIRE(!query.get_minimum_unspent_outputs(cancel, out, test::genesis_address(), 4999999999)); - BOOST_REQUIRE_EQUAL(out.size(), 1u); - ////BOOST_REQUIRE_EQUAL(out.front(), 0); + std::atomic_bool cancel{ true }; + BOOST_REQUIRE_EQUAL(query.get_address_outputs(cancel, out, test::genesis_address), error::canceled); + BOOST_REQUIRE(out.empty()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/address/address_unspent.cpp b/test/query/address/address_unspent.cpp index 0482c55d..069527bf 100644 --- a/test/query/address/address_unspent.cpp +++ b/test/query/address/address_unspent.cpp @@ -26,4 +26,52 @@ BOOST_FIXTURE_TEST_SUITE(query_address_tests, test::directory_setup_fixture) // get_confirmed_unspent // get_unspent +const auto genesis_address = test::genesis_address; + +BOOST_AUTO_TEST_CASE(query_address__get_unspent__turbo_genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); + BOOST_REQUIRE(query.initialize(test::genesis)); + + unspents out{}; + std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.get_unconfirmed_unspent(cancel, out, genesis_address, true)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_confirmed_unspent(cancel, out, genesis_address, true)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_unspent(cancel, out, genesis_address, true)); + BOOST_REQUIRE(out.empty()); +} + +BOOST_AUTO_TEST_CASE(query_address__get_unspent__genesis__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); + BOOST_REQUIRE(query.initialize(test::genesis)); + + unspents out{}; + std::atomic_bool cancel{}; + BOOST_REQUIRE(!query.get_unconfirmed_unspent(cancel, out, genesis_address)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_confirmed_unspent(cancel, out, genesis_address)); + BOOST_REQUIRE(out.empty()); + + out.clear(); + BOOST_REQUIRE(!query.get_unspent(cancel, out, genesis_address)); + BOOST_REQUIRE(out.empty()); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/query/confirmed.cpp b/test/query/confirmed.cpp index 35d59376..5832163e 100644 --- a/test/query/confirmed.cpp +++ b/test/query/confirmed.cpp @@ -240,8 +240,8 @@ BOOST_AUTO_TEST_CASE(query_confirmed__is_spent_output__genesis__false) test::query_accessor query{ store }; BOOST_REQUIRE_EQUAL(store.create(test::events_handler), error::success); BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(!query.is_spent_output(query.to_output(0, 0))); - BOOST_REQUIRE(!query.is_spent_output(query.to_output(1, 1))); + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(0, 0))); + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(1, 1))); } BOOST_AUTO_TEST_CASE(query_confirmed__is_spent_output__strong_confirmed__true) @@ -255,21 +255,21 @@ BOOST_AUTO_TEST_CASE(query_confirmed__is_spent_output__strong_confirmed__true) BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false)); BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false, false)); BOOST_REQUIRE(query.set(test::block3a, context{ 0, 3, 0 }, false, false)); - BOOST_REQUIRE(!query.is_spent_output(query.to_output(0, 0))); // genesis - BOOST_REQUIRE(!query.is_spent_output(query.to_output(1, 0))); // block1a - BOOST_REQUIRE(!query.is_spent_output(query.to_output(1, 1))); // block1a + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(0, 0))); // genesis + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(1, 0))); // block1a + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(1, 1))); // block1a BOOST_REQUIRE(query.set_strong(1)); BOOST_REQUIRE(query.set_strong(2)); BOOST_REQUIRE(query.set_strong(3)); - BOOST_REQUIRE(!query.is_spent_output(query.to_output(0, 0))); // genesis - BOOST_REQUIRE(!query.is_spent_output(query.to_output(1, 0))); // block1a - BOOST_REQUIRE(!query.is_spent_output(query.to_output(1, 1))); // block1a + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(0, 0))); // genesis + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(1, 0))); // block1a + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(1, 1))); // block1a BOOST_REQUIRE(query.push_confirmed(1, false)); BOOST_REQUIRE(query.push_confirmed(2, false)); BOOST_REQUIRE(query.push_confirmed(3, false)); - BOOST_REQUIRE(!query.is_spent_output(query.to_output(0, 0))); // genesis - BOOST_REQUIRE(query.is_spent_output(query.to_output(1, 0))); // block1a - BOOST_REQUIRE(query.is_spent_output(query.to_output(1, 1))); // block1a + BOOST_REQUIRE(!query.is_confirmed_spent_output(query.to_output(0, 0))); // genesis + BOOST_REQUIRE(query.is_confirmed_spent_output(query.to_output(1, 0))); // block1a + BOOST_REQUIRE(query.is_confirmed_spent_output(query.to_output(1, 1))); // block1a } // The coinbase tx is strong. diff --git a/test/query/navigate/navigate_reverse.cpp b/test/query/navigate/navigate_reverse.cpp index 217ac290..253c429c 100644 --- a/test/query/navigate/navigate_reverse.cpp +++ b/test/query/navigate/navigate_reverse.cpp @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(query_address__to_address_outputs__genesis__expected) output_links out{}; std::atomic_bool cancel{}; - BOOST_REQUIRE(!query.to_address_outputs(cancel, out, test::genesis_address())); + BOOST_REQUIRE(!query.to_address_outputs(cancel, out, test::genesis_address)); BOOST_REQUIRE_EQUAL(out.size(), 1u); BOOST_REQUIRE_EQUAL(out.front(), query.to_output(0, 0)); } diff --git a/test/types.cpp b/test/types.cpp index 6850ecc3..383db94d 100644 --- a/test/types.cpp +++ b/test/types.cpp @@ -20,9 +20,130 @@ BOOST_AUTO_TEST_SUITE(types_tests) -BOOST_AUTO_TEST_CASE(types_test) +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{ checkpoint{ {}, 42 }, 0, 7 }; + const history b{ checkpoint{ {}, 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{ checkpoint{ {}, 100 }, 0, 5 }; + const history b{ checkpoint{ {}, 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{ checkpoint{ {}, 100 }, 0, 3 }; + const history b{ checkpoint{ {}, 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) { - BOOST_REQUIRE(true); + constexpr auto hash = base16_hash("0000000000000000000000000000000000000000000000000000000000000000"); + const history value{ { hash, 0 }, 0, 0 }; + BOOST_REQUIRE(!history::less_than{}(value, value)); } BOOST_AUTO_TEST_SUITE_END()