diff --git a/include/bitcoin/database/impl/primitives/hashmap.ipp b/include/bitcoin/database/impl/primitives/hashmap.ipp index ef5faafa..c1e943a7 100644 --- a/include/bitcoin/database/impl/primitives/hashmap.ipp +++ b/include/bitcoin/database/impl/primitives/hashmap.ipp @@ -154,8 +154,6 @@ Link CLASS::first(const Key& key) const NOEXCEPT TEMPLATE typename CLASS::iterator CLASS::it(const Key& key) const NOEXCEPT { - // (14.09% + 5.36%) - // Expensive construction, avoid unless iteration is necessary. return { manager_.get(), head_.top(key), key }; } @@ -215,7 +213,6 @@ bool CLASS::set(const Link& link, const Element& element) NOEXCEPT flipper sink{ stream }; sink.skip_bytes(index_size); - // (1.65%) if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(Size);) } return element.to_data(sink); } @@ -267,7 +264,6 @@ bool CLASS::put_link(Link& link, const Key& key, sink.skip_bytes(Link::size); sink.write_bytes(key); - // (1.63%) if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(Size * count);) } if (!element.to_data(sink)) return false; @@ -352,6 +348,7 @@ bool CLASS::read(const memory_ptr& ptr, const Link& link, iostream stream{ offset, size - position }; reader source{ stream }; source.skip_bytes(index_size); + if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(Size);) } return element.from_data(source); } diff --git a/include/bitcoin/database/impl/query/confirm.ipp b/include/bitcoin/database/impl/query/confirm.ipp index 9e22cb3d..3846a29f 100644 --- a/include/bitcoin/database/impl/query/confirm.ipp +++ b/include/bitcoin/database/impl/query/confirm.ipp @@ -136,7 +136,7 @@ bool CLASS::is_spent(const spend_link& link) const NOEXCEPT if (spend.is_null()) return false; - return spent_prevout(spend.prevout(), spend.parent_fk); + return !!spent_prevout(spend.prevout(), spend.parent_fk); } // unused @@ -157,12 +157,12 @@ bool CLASS::is_mature(const spend_link& link, size_t height) const NOEXCEPT if (spend.is_null()) return true; - return mature_prevout(spend.point_fk, height) == error::success; + return !mature_prevout(spend.point_fk, height); } // protected (only for is_mature/unused) TEMPLATE -error::error_t CLASS::mature_prevout(const point_link& link, +code CLASS::mature_prevout(const point_link& link, size_t height) const NOEXCEPT { // Get hash from point, search for prevout tx and get its link. @@ -201,12 +201,12 @@ bool CLASS::is_locked(const spend_link& link, uint32_t sequence, if (spend.is_null()) return true; - return locked_prevout(spend.point_fk, sequence, ctx) == error::success; + return !locked_prevout(spend.point_fk, sequence, ctx); } // protected (only for is_locked/unused) TEMPLATE -error::error_t CLASS::locked_prevout(const point_link& link, uint32_t sequence, +code CLASS::locked_prevout(const point_link& link, uint32_t sequence, const context& ctx) const NOEXCEPT { if (!ctx.is_enabled(system::chain::flags::bip68_rule)) @@ -235,19 +235,16 @@ error::error_t CLASS::locked_prevout(const point_link& link, uint32_t sequence, // protected TEMPLATE -inline error::error_t CLASS::spent_prevout(tx_link link, - index index) const NOEXCEPT +code CLASS::spent_prevout(tx_link link, index index) const NOEXCEPT { - return spent_prevout(table::spend::compose(link, index), - tx_link::terminal); + return spent_prevout(table::spend::compose(link, index), tx_link::terminal); } // protected TEMPLATE -inline error::error_t CLASS::spent_prevout(const foreign_point& point, +code CLASS::spent_prevout(const foreign_point& point, const tx_link& self) const NOEXCEPT { - // (2.94%) auto it = store_.spend.it(point); if (!it) return error::success; @@ -255,36 +252,27 @@ inline error::error_t CLASS::spent_prevout(const foreign_point& point, table::spend::get_parent spend{}; do { - // (0.38%) if (!store_.spend.get(it, spend)) return error::integrity; - // Free (trivial). // Skip current spend, which is the only one if not double spent. if (spend.parent_fk == self) continue; - // Free (zero iteration without double spend). // If strong spender exists then prevout is confirmed double spent. if (!to_block(spend.parent_fk).is_terminal()) return error::confirmed_double_spend; } - // Expensive (31.19%). - // Iteration exists because we allow double spending, and by design cannot - // preclude it because we download and index concurrently before confirm. while (it.advance()); return error::success; } // protected TEMPLATE -inline error::error_t CLASS::unspendable_prevout(const point_link& link, +code CLASS::unspendable_prevout(const point_link& link, uint32_t sequence, uint32_t version, const context& ctx) const NOEXCEPT { - // Modest (1.24%), and with 4.77 conflict ratio. const auto key = get_point_key(link); - - // Expensize (8.6%). const auto strong = to_strong(key); if (strong.block.is_terminal()) @@ -307,7 +295,6 @@ inline error::error_t CLASS::unspendable_prevout(const point_link& link, return error::success; } - TEMPLATE code CLASS::unspent_duplicates(const tx_link& link, const context& ctx) const NOEXCEPT @@ -338,22 +325,13 @@ code CLASS::tx_confirmable(const tx_link& link, const context& ctx) const NOEXCEPT { code ec{}; - uint32_t version{}; - table::spend::get_prevout_sequence spend{}; - - // (4.71%) tx.get, puts.get, reduce collision. - for (const auto& spend_fk: to_tx_spends(version, link)) + const auto set = to_spend_set(link); + for (const auto& spend: set.spends) { - // (3.65%) spend.get, reduce collision. - if (!store_.spend.get(spend_fk, spend)) - return error::integrity; - - // (33.42%) if ((ec = unspendable_prevout(spend.point_fk, spend.sequence, - version, ctx))) + set.version, ctx))) return ec; - // (34.74%) if ((ec = spent_prevout(spend.prevout(), link))) return ec; } @@ -361,31 +339,138 @@ code CLASS::tx_confirmable(const tx_link& link, return error::success; } +// split(3) 219 secs for 400k-410k +// split(2) 255 secs for 400k-410k (not shown) +// split(0) 456 secs for 400k-410k (not shown) TEMPLATE code CLASS::block_confirmable(const header_link& link) const NOEXCEPT { + // C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++. + constexpr auto par = bc::par_unseq; + context ctx{}; if (!get_context(ctx, link)) return error::integrity; - // (0.07%) + code ec{}; + if ((ec = unspent_duplicates(to_coinbase(link), ctx))) + return ec; + + const auto is_unspendable = [&ctx, this] (const auto& set) NOEXCEPT + { + for (const auto& spend: set.spends) + if (unspendable_prevout(spend.point_fk, spend.sequence, + set.version, ctx)) + return true; + + return false; + }; + + const auto is_spent = [this](const auto& set) NOEXCEPT + { + for (const auto& spend: set.spends) + if (spent_prevout(spend.prevout(), set.tx)) + return true; + + return false; + }; + + const auto sets = to_non_coinbase_spends(link); + + if (std_any_of(par, sets.begin(), sets.end(), is_unspendable)) + return error::integrity; + + if (std_any_of(par, sets.begin(), sets.end(), is_spent)) + return error::integrity; + + return error::success; +} + +#if defined(UNDEFINED) + +// split(0) 403 secs for 400k-410k +TEMPLATE +code CLASS::block_confirmable(const header_link& link) const NOEXCEPT +{ + context ctx{}; + if (!get_context(ctx, link)) + return error::integrity; + + code ec{}; const auto txs = to_transactions(link); if (txs.empty()) - return error::success; + return ec; - // (0.11%) because !bip30. - code ec{}; if ((ec = unspent_duplicates(txs.front(), ctx))) return ec; - // (0.33%) for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) if ((ec = tx_confirmable(*tx, ctx))) return ec; - return error::success; + return ec; } +// split(1) 446 secs for 400k-410k +TEMPLATE +code CLASS::block_confirmable(const header_link& link) const NOEXCEPT +{ + context ctx{}; + if (!get_context(ctx, link)) + return error::integrity; + + code ec{}; + if ((ec = unspent_duplicates(to_coinbase(link), ctx))) + return ec; + + const auto sets = to_non_coinbase_spends(link); + for (const auto& set: sets) + { + for (const auto& spend: set.spends) + { + if ((ec = unspendable_prevout(spend.point_fk, spend.sequence, + set.version, ctx))) + return ec; + + if ((ec = spent_prevout(spend.prevout(), set.tx))) + return ec; + } + } + + return ec; +} + +// split(3) 416 secs for 400k-410k +TEMPLATE +code CLASS::block_confirmable(const header_link& link) const NOEXCEPT +{ + context ctx{}; + if (!get_context(ctx, link)) + return error::integrity; + + code ec{}; + if ((ec = unspent_duplicates(to_coinbase(link), ctx))) + return ec; + + const auto sets = to_non_coinbase_spends(link); + for (const auto& set: sets) + for (const auto& spend: set.spends) + if ((ec = unspendable_prevout(spend.point_fk, spend.sequence, + set.version, ctx))) + return ec; + + if (ec) return ec; + + for (const auto& set: sets) + for (const auto& spend: set.spends) + if ((ec = spent_prevout(spend.prevout(), set.tx))) + return ec; + + return ec; +} + +#endif // DISABLED + // protected TEMPLATE bool CLASS::set_strong(const header_link& link, const tx_links& txs, @@ -414,7 +499,6 @@ bool CLASS::is_strong(const header_link& link) const NOEXCEPT TEMPLATE bool CLASS::set_strong(const header_link& link) NOEXCEPT { - // (0.22%) after milestone. const auto txs = to_transactions(link); if (txs.empty()) return false; @@ -422,7 +506,6 @@ bool CLASS::set_strong(const header_link& link) NOEXCEPT // ======================================================================== const auto scope = store_.get_transactor(); - // (4.04%) after milestone. // Clean allocation failure (e.g. disk full), see set_strong() comments. return set_strong(link, txs, true); // ======================================================================== diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp index 870bae98..fe2f707c 100644 --- a/include/bitcoin/database/impl/query/translate.ipp +++ b/include/bitcoin/database/impl/query/translate.ipp @@ -201,7 +201,6 @@ header_link CLASS::to_parent(const header_link& link) const NOEXCEPT TEMPLATE header_link CLASS::to_block(const tx_link& link) const NOEXCEPT { - // (10.36%) table::strong_tx::record strong{}; if (!store_.strong_tx.find(link, strong)) return {}; @@ -220,7 +219,6 @@ header_link CLASS::to_block(const tx_link& link) const NOEXCEPT TEMPLATE inline strong_pair CLASS::to_strong(const hash_digest& tx_hash) const NOEXCEPT { - // (14.21%) from block_confirmable, reduce collision. auto it = store_.tx.it(tx_hash); strong_pair strong{ {}, it.self() }; if (!it) @@ -229,13 +227,10 @@ inline strong_pair CLASS::to_strong(const hash_digest& tx_hash) const NOEXCEPT do { strong.tx = it.self(); - - // (10.99%) from block_confirmable. strong.block = to_block(strong.tx); if (!strong.block.is_terminal()) return strong; } - // (0.28%) while (it.advance()); return strong; } @@ -257,9 +252,15 @@ inline strong_pairs CLASS::to_strongs(const hash_digest& tx_hash) const NOEXCEPT strong_pairs strongs{}; do { - // clang emplace_back bug (no matching constructor), using push_back. for (const auto& link: to_blocks(it.self())) + { +// work around clang emplace_back bug (no matching constructor). +#if defined(HAVE_CLANG) strongs.push_back({ link, it.self() }); +#else + strongs.emplace_back(link, it.self()); +#endif + } } while (it.advance()); return strongs; @@ -444,25 +445,31 @@ spend_links CLASS::to_tx_spends(const tx_link& link) const NOEXCEPT // protected TEMPLATE -spend_links CLASS::to_tx_spends(uint32_t& version, - const tx_link& link) const NOEXCEPT +spend_set CLASS::to_spend_set(const tx_link& link) const NOEXCEPT { - // (4.71%) from block_confirmable. - - // (2.53%) table::transaction::get_version_puts tx{}; if (!store_.tx.get(link, tx)) return {}; - version = tx.version; table::puts::get_spends puts{}; - - // (2.1%) puts.spend_fks.resize(tx.ins_count); if (!store_.puts.get(tx.puts_fk, puts)) return {}; - return std::move(puts.spend_fks); + spend_set set{ link, tx.version, {} }; + set.spends.reserve(tx.ins_count); + + table::spend::get_prevout_sequence get{}; + for (const auto& spend_fk: puts.spend_fks) + { + if (!store_.spend.get(spend_fk, get)) + return {}; + + // Translate query to public struct. + set.spends.emplace_back(get.point_fk, get.point_index, get.sequence); + } + + return set; } // block to txs/puts (forward navigation) @@ -471,7 +478,7 @@ spend_links CLASS::to_tx_spends(uint32_t& version, TEMPLATE tx_links CLASS::to_transactions(const header_link& link) const NOEXCEPT { - table::txs::slab txs{}; + table::txs::get_txs txs{}; if (!store_.txs.find(link, txs)) return {}; @@ -488,25 +495,47 @@ tx_link CLASS::to_coinbase(const header_link& link) const NOEXCEPT return txs.coinbase_fk; } +// protected TEMPLATE -spend_links CLASS::to_non_coinbase_spends( +spend_sets CLASS::to_non_coinbase_spends( const header_link& link) const NOEXCEPT { + // C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++. + constexpr auto par = bc::par_unseq; + const auto txs = to_transactions(link); if (txs.size() <= one) return {}; - // Dynamic spends allocation is an unnecessary block_confirmable cost. - spend_links spends{}; - for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) + spend_sets out{ sub1(txs.size()) }; + const auto to_set = [this](const auto& tx) NOEXCEPT { - const auto tx_spends = to_tx_spends(*tx); - spends.insert(spends.end(), tx_spends.begin(), tx_spends.end()); - } + return to_spend_set(tx); + }; - return spends; + std_transform(par, std::next(txs.begin()), txs.end(), out.begin(), to_set); + return out; } +#if defined(UNDEFINED) +// protected +TEMPLATE +spend_sets CLASS::to_non_coinbase_spends( + const header_link& link) const NOEXCEPT +{ + const auto txs = to_transactions(link); + if (txs.size() <= one) + return {}; + + spend_sets sets{}; + sets.reserve(sub1(txs.size())); + for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) + sets.push_back(to_spend_set(*tx)); + + return sets; +} +#endif + TEMPLATE spend_links CLASS::to_block_spends(const header_link& link) const NOEXCEPT { @@ -515,7 +544,7 @@ spend_links CLASS::to_block_spends(const header_link& link) const NOEXCEPT for (const auto& tx: txs) { - const auto tx_spends = to_tx_spends(tx); + const auto tx_spends = to_tx_spenders(tx); spends.insert(spends.end(), tx_spends.begin(), tx_spends.end()); } diff --git a/include/bitcoin/database/impl/query/validate.ipp b/include/bitcoin/database/impl/query/validate.ipp index 0f589854..e1aac3b3 100644 --- a/include/bitcoin/database/impl/query/validate.ipp +++ b/include/bitcoin/database/impl/query/validate.ipp @@ -125,7 +125,6 @@ bool CLASS::get_bits(uint32_t& bits, const header_link& link) const NOEXCEPT TEMPLATE bool CLASS::get_context(context& ctx, const header_link& link) const NOEXCEPT { - // (4.04%) table::header::record_context header{}; if (!store_.header.get(link, header)) return false; diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 4c4f1762..19acaf5f 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -45,15 +45,40 @@ using filter_link = table::neutrino::link; using header_links = std_vector; using tx_links = std_vector; -using spend_links = std_vector; using input_links = std_vector; using output_links = std_vector; +using spend_links = std_vector; using foreign_point = table::spend::search_key; using two_counts = std::pair; -struct strong_pair { header_link block; tx_link tx; }; +struct strong_pair { header_link block{}; tx_link tx{}; }; using strong_pairs = std_vector; +struct spend_set +{ + struct spend + { + inline table::spend::search_key prevout() const NOEXCEPT + { + return table::spend::compose(point_fk, point_index); + } + + inline bool is_null() const NOEXCEPT + { + return point_fk == table::spend::pt::terminal; + } + + table::spend::pt::integer point_fk{}; + table::spend::ix::integer point_index{}; + uint32_t sequence{}; + }; + + tx_link tx{}; + uint32_t version{}; + std_vector spends{}; +}; +using spend_sets = std_vector; + // Writers (non-const) are only: push_, pop_, set_ and initialize. template class query @@ -264,9 +289,8 @@ class query /// block to txs/puts (forward navigation) tx_link to_coinbase(const header_link& link) const NOEXCEPT; tx_links to_transactions(const header_link& link) const NOEXCEPT; - spend_links to_non_coinbase_spends(const header_link& link) const NOEXCEPT; - spend_links to_block_spends(const header_link& link) const NOEXCEPT; output_links to_block_outputs(const header_link& link) const NOEXCEPT; + spend_links to_block_spends(const header_link& link) const NOEXCEPT; /// hashmap enumeration header_link top_header(size_t bucket) const NOEXCEPT; @@ -494,19 +518,18 @@ class query const filter& body) NOEXCEPT; protected: - bool set_strong(const header_link& link, const tx_links& txs, - bool positive) NOEXCEPT; - /// Translate. /// ----------------------------------------------------------------------- + + spend_set to_spend_set(const tx_link& link) const NOEXCEPT; + spend_sets to_non_coinbase_spends(const header_link& link) const NOEXCEPT; + uint32_t to_spend_index(const tx_link& parent_fk, const spend_link& input_fk) const NOEXCEPT; uint32_t to_output_index(const tx_link& parent_fk, const output_link& output_fk) const NOEXCEPT; spend_link to_spender(const tx_link& link, const foreign_point& point) const NOEXCEPT; - spend_links to_tx_spends(uint32_t& version, - const tx_link& link) const NOEXCEPT; // Critical path inline header_links to_blocks(const tx_link& link) const NOEXCEPT; @@ -526,19 +549,20 @@ class query height_link get_height(const hash_digest& key) const NOEXCEPT; height_link get_height(const header_link& link) const NOEXCEPT; bool is_confirmed_unspent(const output_link& link) const NOEXCEPT; - error::error_t mature_prevout(const point_link& link, + code mature_prevout(const point_link& link, size_t height) const NOEXCEPT; - error::error_t locked_prevout(const point_link& link, uint32_t sequence, + code locked_prevout(const point_link& link, uint32_t sequence, const context& ctx) const NOEXCEPT; // Critical path - inline error::error_t spent_prevout(tx_link link, - index index) const NOEXCEPT; - inline error::error_t spent_prevout(const foreign_point& point, + code spent_prevout(tx_link link, index index) const NOEXCEPT; + code spent_prevout(const foreign_point& point, const tx_link& self) const NOEXCEPT; - inline error::error_t unspendable_prevout(const point_link& link, + code unspendable_prevout(const point_link& link, uint32_t sequence, uint32_t version, const context& ctx) const NOEXCEPT; + bool set_strong(const header_link& link, const tx_links& txs, + bool positive) NOEXCEPT; /// context /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp index c4b4da63..ff4ccaba 100644 --- a/include/bitcoin/database/tables/archives/txs.hpp +++ b/include/bitcoin/database/tables/archives/txs.hpp @@ -150,6 +150,25 @@ struct txs bool associated{}; }; + + struct get_txs + : public schema::txs + { + inline bool from_data(reader& source) NOEXCEPT + { + tx_fks.resize(source.read_little_endian()); + source.skip_bytes(bytes::size); + std::for_each(tx_fks.begin(), tx_fks.end(), [&](auto& fk) NOEXCEPT + { + fk = source.read_little_endian(); + }); + + BC_ASSERT(source.get_read_position() == count()); + return source; + } + + keys tx_fks{}; + }; }; } // namespace table diff --git a/test/query/confirm.cpp b/test/query/confirm.cpp index 93d4b8b8..00461dae 100644 --- a/test/query/confirm.cpp +++ b/test/query/confirm.cpp @@ -484,9 +484,9 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__null_points__success) // block1/2/3 at links 1/2/3 confirming at heights 1/2/3. // blocks have only coinbase txs, all txs should be set strong before calling // confirmable, but these are bip30 default configuration. - BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success); - BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); - BOOST_REQUIRE_EQUAL(query.block_confirmable(3), error::success); + BOOST_REQUIRE(!query.block_confirmable(1)); + BOOST_REQUIRE(!query.block_confirmable(2)); + BOOST_REQUIRE(!query.block_confirmable(3)); } BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__missing_prevouts__integrity) @@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__missing_prevouts__integri // block1a is missing all three input prevouts. BOOST_REQUIRE(query.set_strong(1)); - BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success); + BOOST_REQUIRE(!query.block_confirmable(1)); } BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_gensis__coinbase_maturity) @@ -518,7 +518,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_gensis__coinbase_ma BOOST_REQUIRE(query.set_strong(1)); // 1 + 100 = 101 (maturity, except genesis) - BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success); + BOOST_REQUIRE(!query.block_confirmable(1)); } BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__immature_prevouts__coinbase_maturity) @@ -533,12 +533,12 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__immature_prevouts__coinba // block1b has only a coinbase tx. BOOST_REQUIRE(query.set(test::block1b, context{ bip68, 1, 0 }, false, false)); BOOST_REQUIRE(query.set_strong(1)); - BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success); + BOOST_REQUIRE(!query.block_confirmable(1)); // block2b prematurely spends block1b's coinbase outputs. BOOST_REQUIRE(query.set(test::block2b, context{ 0, 100, 0 }, false, false)); BOOST_REQUIRE(query.set_strong(2)); - BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); + BOOST_REQUIRE(!query.block_confirmable(2)); } BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__mature_prevouts__success) @@ -553,12 +553,12 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__mature_prevouts__success) // block1b has only a coinbase tx. BOOST_REQUIRE(query.set(test::block1b, context{ bip68, 1, 0 }, false, false)); BOOST_REQUIRE(query.set_strong(1)); - BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success); + BOOST_REQUIRE(!query.block_confirmable(1)); // block2b spends block1b's coinbase outputs. BOOST_REQUIRE(query.set(test::block2b, context{ 0, 101, 0 }, false, false)); BOOST_REQUIRE(query.set_strong(2)); - BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); + BOOST_REQUIRE(!query.block_confirmable(2)); } BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__success) @@ -579,7 +579,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__succe BOOST_REQUIRE(query.set_strong(2)); // Maturity applies only to coinbase prevouts. - BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); + BOOST_REQUIRE(!query.block_confirmable(2)); } // These pas but test vectors need to be updated to create clear test conditions. @@ -605,7 +605,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__succe //// BOOST_REQUIRE(query.set_strong(2)); //// //// // Not confirmable because lack of maturity. -//// BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); +//// BOOST_REQUIRE(!query.block_confirmable(2)); ////} //// ////BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_coinbase_and_internal_mature__success) @@ -632,7 +632,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__succe //// // It spends only its first own output (coinbase) and that can never be mature. //// // But spend target is not stored as coinbase because it's not a null point. //// BOOST_REQUIRE(query.set_strong(2)); -//// BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); +//// BOOST_REQUIRE(!query.block_confirmable(2)); ////} //// ////BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__confirmed_double_spend__confirmed_double_spend) @@ -657,7 +657,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__succe //// BOOST_REQUIRE(query.set_strong(3)); //// //// // Not confirmable because of intervening block2a implies double spend. -//// BOOST_REQUIRE_EQUAL(query.block_confirmable(3), error::success); +//// BOOST_REQUIRE(!query.block_confirmable(3)); ////} //// ////BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__unconfirmed_double_spend__success) @@ -681,7 +681,7 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__succe //// BOOST_REQUIRE(query.set_strong(2)); //// //// // Confirmable because of intervening tx5 is unconfirmed double spend. -//// BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success); +//// BOOST_REQUIRE(!query.block_confirmable(2)); ////} BOOST_AUTO_TEST_CASE(query_confirm__set_strong__unassociated__false) diff --git a/test/query/translate.cpp b/test/query/translate.cpp index b2e9f7df..ec49656c 100644 --- a/test/query/translate.cpp +++ b/test/query/translate.cpp @@ -218,20 +218,23 @@ BOOST_AUTO_TEST_CASE(query_translate__to_tx__txs__expected) // to_spend_tx/to_spend/to_tx_spends/to_spend_key/to_non_coinbase_spends -BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected) +class accessor + : public test::query_accessor { - class accessor - : public test::query_accessor +public: + using test::query_accessor::query_accessor; + spend_set to_spend_set_(const tx_link& link) const NOEXCEPT { - public: - using test::query_accessor::query_accessor; - spend_links to_tx_spends_(uint32_t& version, - const tx_link& link) const NOEXCEPT - { - return test::query_accessor::to_tx_spends(version, link); - } - }; + return test::query_accessor::to_spend_set(link); + } + spend_sets to_non_coinbase_spends_(const header_link& link) const NOEXCEPT + { + return test::query_accessor::to_non_coinbase_spends(link); + } +}; +BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected) +{ settings settings{}; settings.path = TEST_DIRECTORY; settings.spend_buckets = 5; @@ -279,24 +282,54 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected) BOOST_REQUIRE_EQUAL(query.to_tx_spends(3), spend_links{ 3 }); BOOST_REQUIRE_EQUAL(query.to_tx_spends(4), expected_links4); - uint32_t version{}; - BOOST_REQUIRE_EQUAL(query.to_tx_spends_(version, 0), spend_links{ 0 }); - BOOST_REQUIRE_EQUAL(version, test::genesis.transactions_ptr()->front()->version()); - BOOST_REQUIRE_EQUAL(query.to_tx_spends_(version, 1), spend_links{ 1 }); - BOOST_REQUIRE_EQUAL(version, test::block1.transactions_ptr()->front()->version()); - BOOST_REQUIRE_EQUAL(query.to_tx_spends_(version, 2), spend_links{ 2 }); - BOOST_REQUIRE_EQUAL(version, test::block2.transactions_ptr()->front()->version()); - BOOST_REQUIRE_EQUAL(query.to_tx_spends_(version, 3), spend_links{ 3 }); - BOOST_REQUIRE_EQUAL(version, test::block3.transactions_ptr()->front()->version()); - BOOST_REQUIRE_EQUAL(query.to_tx_spends_(version, 4), expected_links4); - BOOST_REQUIRE_EQUAL(version, test::block1a.transactions_ptr()->front()->version()); + auto spends = query.to_spend_set_(0); + BOOST_REQUIRE_EQUAL(spends.tx, 0u); + BOOST_REQUIRE_EQUAL(spends.spends.size(), 1u); + BOOST_REQUIRE(spends.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(spends.version, test::genesis.transactions_ptr()->front()->version()); + + spends = query.to_spend_set_(1); + BOOST_REQUIRE_EQUAL(spends.tx, 1u); + BOOST_REQUIRE_EQUAL(spends.spends.size(), 1u); + BOOST_REQUIRE(spends.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(spends.version, test::block1.transactions_ptr()->front()->version()); + + spends = query.to_spend_set_(2); + BOOST_REQUIRE_EQUAL(spends.tx, 2u); + BOOST_REQUIRE_EQUAL(spends.spends.size(), 1u); + BOOST_REQUIRE(spends.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(spends.version, test::block2.transactions_ptr()->front()->version()); + + spends = query.to_spend_set_(3); + BOOST_REQUIRE_EQUAL(spends.tx, 3u); + BOOST_REQUIRE_EQUAL(spends.spends.size(), 1u); + BOOST_REQUIRE(spends.spends.front().is_null()); + BOOST_REQUIRE_EQUAL(spends.version, test::block3.transactions_ptr()->front()->version()); + + // block1a has no first coinbase. + spends = query.to_spend_set_(4); + BOOST_REQUIRE_EQUAL(spends.tx, 4u); + BOOST_REQUIRE_EQUAL(spends.spends.size(), 3u); + BOOST_REQUIRE(!spends.spends[0].is_null()); + BOOST_REQUIRE(!spends.spends[1].is_null()); + BOOST_REQUIRE(!spends.spends[2].is_null()); + BOOST_REQUIRE_EQUAL(spends.spends[0].sequence, 42u); + BOOST_REQUIRE_EQUAL(spends.spends[1].sequence, 24u); + BOOST_REQUIRE_EQUAL(spends.spends[2].sequence, 25u); + BOOST_REQUIRE_EQUAL(spends.spends[0].point_index, 24u); + BOOST_REQUIRE_EQUAL(spends.spends[1].point_index, 42u); + BOOST_REQUIRE_EQUAL(spends.spends[2].point_index, 43u); + BOOST_REQUIRE_EQUAL(spends.spends[0].point_index, (*test::block1a.transactions_ptr()->front()->inputs_ptr())[0]->point().index()); + BOOST_REQUIRE_EQUAL(spends.spends[1].point_index, (*test::block1a.transactions_ptr()->front()->inputs_ptr())[1]->point().index()); + BOOST_REQUIRE_EQUAL(spends.spends[2].point_index, (*test::block1a.transactions_ptr()->front()->inputs_ptr())[2]->point().index()); + BOOST_REQUIRE_EQUAL(spends.version, test::block1a.transactions_ptr()->front()->version()); // TODO: All blocks have one transaction. - BOOST_REQUIRE_EQUAL(query.to_non_coinbase_spends(0), spend_links{}); - BOOST_REQUIRE_EQUAL(query.to_non_coinbase_spends(1), spend_links{}); - BOOST_REQUIRE_EQUAL(query.to_non_coinbase_spends(2), spend_links{}); - BOOST_REQUIRE_EQUAL(query.to_non_coinbase_spends(3), spend_links{}); - BOOST_REQUIRE_EQUAL(query.to_non_coinbase_spends(4), spend_links{}); + BOOST_REQUIRE(query.to_non_coinbase_spends_(0).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(1).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(2).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(3).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(4).empty()); // Past end. BOOST_REQUIRE_EQUAL(query.to_spend_tx(7), tx_link::terminal); @@ -304,7 +337,7 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected) BOOST_REQUIRE_EQUAL(query.to_spend_key(spend_link::terminal), foreign_point{}); BOOST_REQUIRE_EQUAL(query.to_spend_key(query.to_spend(5, 0)), foreign_point{}); BOOST_REQUIRE(query.to_tx_spends(5).empty()); - BOOST_REQUIRE(query.to_non_coinbase_spends(5).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(5).empty()); // Verify expectations. const auto spend_head = base16_chunk @@ -392,14 +425,14 @@ BOOST_AUTO_TEST_CASE(query_translate__to_non_coinbase_spends__populated__expecte settings settings{}; settings.path = TEST_DIRECTORY; test::chunk_store store{ settings }; - test::query_accessor query{ store }; + accessor query{ store }; BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success); // coinbase only (null and first). BOOST_REQUIRE(query.initialize(test::genesis)); - BOOST_REQUIRE(query.to_non_coinbase_spends(0).empty()); - BOOST_REQUIRE(query.to_non_coinbase_spends(1).empty()); - BOOST_REQUIRE(query.to_non_coinbase_spends(2).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(0).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(1).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(2).empty()); BOOST_REQUIRE_EQUAL(store.point_body(), system::base16_chunk("")); BOOST_REQUIRE_EQUAL(store.spend_body(), @@ -411,9 +444,9 @@ BOOST_AUTO_TEST_CASE(query_translate__to_non_coinbase_spends__populated__expecte // coinbase only (null and first). BOOST_REQUIRE(query.set(test::block1b, context{ 0, 1, 0 }, false, false)); - BOOST_REQUIRE(query.to_non_coinbase_spends(0).empty()); - BOOST_REQUIRE(query.to_non_coinbase_spends(1).empty()); - BOOST_REQUIRE(query.to_non_coinbase_spends(2).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(0).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(1).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(2).empty()); BOOST_REQUIRE_EQUAL(store.point_body(), system::base16_chunk("")); BOOST_REQUIRE_EQUAL(store.spend_body(), @@ -429,8 +462,8 @@ BOOST_AUTO_TEST_CASE(query_translate__to_non_coinbase_spends__populated__expecte // 2 inputs (block1b and tx2b). BOOST_REQUIRE(query.set(test::block_spend_internal_2b, context{ 0, 101, 0 }, false, false)); - BOOST_REQUIRE(query.to_non_coinbase_spends(0).empty()); - BOOST_REQUIRE(query.to_non_coinbase_spends(1).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(0).empty()); + BOOST_REQUIRE(query.to_non_coinbase_spends_(1).empty()); // Two points because non-null, but only one is non-first (also coinbase criteria). // block_spend_internal_2b first tx (tx2b) is first but with non-null input. @@ -456,7 +489,10 @@ BOOST_AUTO_TEST_CASE(query_translate__to_non_coinbase_spends__populated__expecte "03000000""b2""0179")); // to_non_coinbase_spends keys on first-tx-ness, so only one input despite two points (second point). - BOOST_REQUIRE_EQUAL(query.to_non_coinbase_spends(2), spend_links{ 3 }); + const auto spends = query.to_non_coinbase_spends_(2); + BOOST_REQUIRE_EQUAL(spends.size(), 1u); + ////BOOST_REQUIRE_EQUAL(spends.front().spend_fks.size(), 1u); + ////BOOST_REQUIRE_EQUAL(spends.front().spend_fks, spend_links{ 3 }); } // to_output_tx/to_output/to_tx_outputs/to_block_outputs