diff --git a/include/bitcoin/database/impl/query/archive.ipp b/include/bitcoin/database/impl/query/archive.ipp index 611705eb..8f09bd27 100644 --- a/include/bitcoin/database/impl/query/archive.ipp +++ b/include/bitcoin/database/impl/query/archive.ipp @@ -125,14 +125,14 @@ TEMPLATE inline bool CLASS::is_malleable64(const header_link& link) const NOEXCEPT { table::txs::get_malleable txs{}; - return store_.txs.get(to_txs_link(link), txs) && txs.malleable; + return store_.txs.find(link, txs) && txs.malleable; } TEMPLATE inline bool CLASS::is_associated(const header_link& link) const NOEXCEPT { table::txs::get_associated txs{}; - return store_.txs.get(to_txs_link(link), txs) && txs.associated; + return store_.txs.find(link, txs) && txs.associated; } TEMPLATE @@ -283,7 +283,7 @@ bool CLASS::populate_with_metadata(const block& block) const NOEXCEPT TEMPLATE hashes CLASS::get_tx_keys(const header_link& link) const NOEXCEPT { - const auto tx_fks = to_txs(link); + const auto tx_fks = to_transactions(link); if (tx_fks.empty()) return {}; @@ -314,9 +314,21 @@ inline hash_digest CLASS::get_tx_key(const tx_link& link) const NOEXCEPT return store_.tx.get_key(link); } +TEMPLATE +bool CLASS::get_height(size_t& out, const hash_digest& key) const NOEXCEPT +{ + const auto height = get_height(key); + if (height >= height_link::terminal) + return false; + + out = system::possible_narrow_cast(height.value); + return true; +} + TEMPLATE bool CLASS::get_height(size_t& out, const header_link& link) const NOEXCEPT { + // Use get_height(..., key) in place of get(to_header(key)). const auto height = get_height(link); if (height >= height_link::terminal) return false; @@ -343,7 +355,7 @@ bool CLASS::get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT // False return below implies an integrity error (tx should be indexed). table::txs::get_position txs{ {}, link }; - if (!store_.txs.get(to_txs_link(block_fk), txs)) + if (!store_.txs.find(block_fk, txs)) return false; out = txs.position; @@ -430,7 +442,7 @@ typename CLASS::transactions_ptr CLASS::get_transactions( const header_link& link) const NOEXCEPT { using namespace system; - const auto txs = to_txs(link); + const auto txs = to_transactions(link); if (txs.empty()) return {}; @@ -482,7 +494,7 @@ TEMPLATE size_t CLASS::get_block_size(const header_link& link) const NOEXCEPT { table::txs::get_block_size txs{}; - return store_.txs.get(to_txs_link(link), txs) ? txs.wire : zero; + return store_.txs.find(link, txs) ? txs.wire : zero; } TEMPLATE @@ -997,7 +1009,7 @@ code CLASS::set_code(txs_link& out_fk, const transactions& txs, ////// This is only fully effective if there is a single database thread. ////// Guard must be lifted for an existing top malleable association so ////// that a non-malleable association may be accomplished. - ////out_fk = to_txs_link(key); + ////out_fk = to_txs(key); ////if (!out_fk.is_terminal() && !is_malleable(key)) //// return error::success; diff --git a/include/bitcoin/database/impl/query/confirm.ipp b/include/bitcoin/database/impl/query/confirm.ipp index d4e9b8c6..ec52d13f 100644 --- a/include/bitcoin/database/impl/query/confirm.ipp +++ b/include/bitcoin/database/impl/query/confirm.ipp @@ -31,6 +31,17 @@ namespace database { // ---------------------------------------------------------------------------- // Not for use in validatation (2 additional gets) or confirmation (height). +// protected +TEMPLATE +height_link CLASS::get_height(const hash_digest& key) const NOEXCEPT +{ + table::header::get_height header{}; + if (!store_.header.find(key, header)) + return {}; + + return header.height; +} + // protected TEMPLATE height_link CLASS::get_height(const header_link& link) const NOEXCEPT @@ -236,7 +247,7 @@ TEMPLATE inline error::error_t CLASS::spent_prevout(const foreign_point& point, const tx_link& self) const NOEXCEPT { - // Modest (1.3%) after head optimization. + // (2.94%) auto it = store_.spend.it(point); if (!it) return error::success; @@ -244,6 +255,7 @@ 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; @@ -257,7 +269,7 @@ inline error::error_t CLASS::spent_prevout(const foreign_point& point, if (!to_block(spend.parent_fk).is_terminal()) return error::confirmed_double_spend; } - // Expensive (41%). + // 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()); @@ -328,32 +340,34 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT if (!get_context(ctx, link)) return error::integrity; - const auto txs = to_txs(link); + // (0.07%) + const auto txs = to_transactions(link); if (txs.empty()) return error::success; - // Cheap (0.2%) because !bip30. + // (0.11%) because !bip30. code ec{}; if ((ec = unspent_duplicates(txs.front(), ctx))) return ec; + // (0.33%) uint32_t version{}; table::spend::get_prevout_sequence spend{}; for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx) { - // Modest (1.4% tx.get, 1% puts.get - to_tx_spends), reduce collision. + // (4.71%) tx.get, puts.get, reduce collision. for (const auto& spend_fk: to_tx_spends(version, *tx)) { - // Modest, (1.4% spend.get), reduce collision. + // (3.65%) spend.get, reduce collision. if (!store_.spend.get(spend_fk, spend)) return error::integrity; - // Expensive (11%). + // (33.42%) if ((ec = unspendable_prevout(spend.point_fk, spend.sequence, version, ctx))) return ec; - // Expensive (44%). + // (34.74%) if ((ec = spent_prevout(spend.prevout(), *tx))) return ec; } @@ -390,7 +404,7 @@ bool CLASS::is_strong(const header_link& link) const NOEXCEPT TEMPLATE bool CLASS::set_strong(const header_link& link) NOEXCEPT { - const auto txs = to_txs(link); + const auto txs = to_transactions(link); if (txs.empty()) return false; @@ -405,7 +419,7 @@ bool CLASS::set_strong(const header_link& link) NOEXCEPT TEMPLATE bool CLASS::set_unstrong(const header_link& link) NOEXCEPT { - const auto txs = to_txs(link); + const auto txs = to_transactions(link); if (txs.empty()) return false; diff --git a/include/bitcoin/database/impl/query/optional.ipp b/include/bitcoin/database/impl/query/optional.ipp index 65fdbfde..842a8596 100644 --- a/include/bitcoin/database/impl/query/optional.ipp +++ b/include/bitcoin/database/impl/query/optional.ipp @@ -179,7 +179,7 @@ TEMPLATE bool CLASS::get_filter(filter& out, const header_link& link) const NOEXCEPT { table::neutrino::get_filter neutrino{}; - if (!store_.neutrino.get(store_.neutrino.first(link), neutrino)) + if (!store_.neutrino.find(link, neutrino)) return false; out = std::move(neutrino.filter); @@ -191,7 +191,7 @@ bool CLASS::get_filter_head(hash_digest& out, const header_link& link) const NOEXCEPT { table::neutrino::get_head neutrino{}; - if (!store_.neutrino.get(store_.neutrino.first(link), neutrino)) + if (!store_.neutrino.find(link, neutrino)) return false; out = std::move(neutrino.filter_head); @@ -229,7 +229,7 @@ bool CLASS::set_filter(const header_link& link, const hash_digest& filter_head, //// const tx_link& link) const NOEXCEPT ////{ //// table::buffer::slab_ptr buffer{}; -//// if (!store_.buffer.get(store_.buffer.first(link), buffer)) +//// if (!store_.buffer.find(link, buffer)) //// return {}; //// //// return buffer.tx; diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp index cfb2c5dd..a1e71565 100644 --- a/include/bitcoin/database/impl/query/translate.ipp +++ b/include/bitcoin/database/impl/query/translate.ipp @@ -65,30 +65,35 @@ inline header_link CLASS::to_confirmed(size_t height) const NOEXCEPT TEMPLATE inline header_link CLASS::to_header(const hash_digest& key) const NOEXCEPT { + // Use header.find(key) in place of get(to_header(key)). return store_.header.first(key); } TEMPLATE inline point_link CLASS::to_point(const hash_digest& key) const NOEXCEPT { + // Use point.find(key) in place of get(to_point(key)). return store_.point.first(key); } TEMPLATE inline tx_link CLASS::to_tx(const hash_digest& key) const NOEXCEPT { + // Use tx.find(key) in place of get(to_tx(key)). return store_.tx.first(key); } TEMPLATE -inline txs_link CLASS::to_txs_link(const header_link& key) const NOEXCEPT +inline txs_link CLASS::to_txs(const header_link& key) const NOEXCEPT { + // Use txs.find(key) in place of get(to_txs(key)). return store_.txs.first(key); } TEMPLATE inline filter_link CLASS::to_filter(const header_link& key) const NOEXCEPT { + // Use neutrino.find(key) in place of get(to_filter(key)). return store_.neutrino.first(key); } @@ -196,13 +201,15 @@ header_link CLASS::to_parent(const header_link& link) const NOEXCEPT TEMPLATE header_link CLASS::to_block(const tx_link& link) const NOEXCEPT { - // Expensive (3.8%) from block_confirmable->to_strong. + // (10.99%) from block_confirmable->unspendable_prevout->to_strong->to_block. - // No to_strong_tx_link() because no type for strong_tx_link. - const auto to_strong_tx_link = store_.strong_tx.first(link); + // (8.25%) + ////const auto to_strong_tx_link = store_.strong_tx.first(link); + // (2.68%) table::strong_tx::record strong{}; - if (!store_.strong_tx.get(to_strong_tx_link, strong)) + ////if (!store_.strong_tx.get(to_strong_tx_link, strong)) + if (!store_.strong_tx.find(link, strong)) return {}; // Terminal implies not strong (false). @@ -219,7 +226,7 @@ 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 { - // Expensive (4.6%) from block_confirmable, reduce collision. + // (14.21%) from block_confirmable, reduce collision. auto it = store_.tx.it(tx_hash); strong_pair strong{ {}, it.self() }; if (!it) @@ -229,11 +236,12 @@ inline strong_pair CLASS::to_strong(const hash_digest& tx_hash) const NOEXCEPT { strong.tx = it.self(); - // Expensive (3.8%) from block_confirmable. + // (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; } @@ -444,12 +452,17 @@ TEMPLATE spend_links CLASS::to_tx_spends(uint32_t& version, 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 {}; @@ -461,10 +474,11 @@ spend_links CLASS::to_tx_spends(uint32_t& version, // ---------------------------------------------------------------------------- TEMPLATE -tx_links CLASS::to_txs(const header_link& link) const NOEXCEPT +tx_links CLASS::to_transactions(const header_link& link) const NOEXCEPT { table::txs::slab txs{}; - if (!store_.txs.get(to_txs_link(link), txs)) + ////if (!store_.txs.get(to_txs(link), txs)) + if (!store_.txs.find(link, txs)) return {}; return std::move(txs.tx_fks); @@ -474,7 +488,8 @@ TEMPLATE tx_link CLASS::to_coinbase(const header_link& link) const NOEXCEPT { table::txs::get_coinbase txs{}; - if (!store_.txs.get(to_txs_link(link), txs)) + ////if (!store_.txs.get(to_txs(link), txs)) + if (!store_.txs.find(link, txs)) return {}; return txs.coinbase_fk; @@ -484,7 +499,7 @@ TEMPLATE spend_links CLASS::to_non_coinbase_spends( const header_link& link) const NOEXCEPT { - const auto txs = to_txs(link); + const auto txs = to_transactions(link); if (txs.size() <= one) return {}; @@ -503,7 +518,7 @@ TEMPLATE spend_links CLASS::to_block_spends(const header_link& link) const NOEXCEPT { spend_links spends{}; - const auto txs = to_txs(link); + const auto txs = to_transactions(link); for (const auto& tx: txs) { @@ -518,7 +533,7 @@ TEMPLATE output_links CLASS::to_block_outputs(const header_link& link) const NOEXCEPT { output_links outputs{}; - const auto txs = to_txs(link); + const auto txs = to_transactions(link); for (const auto& tx: txs) { diff --git a/include/bitcoin/database/impl/query/validate.ipp b/include/bitcoin/database/impl/query/validate.ipp index fd923d25..dc3bf07d 100644 --- a/include/bitcoin/database/impl/query/validate.ipp +++ b/include/bitcoin/database/impl/query/validate.ipp @@ -170,13 +170,9 @@ bool CLASS::get_bypass(bool& bypass, const header_link& link) const NOEXCEPT TEMPLATE code CLASS::get_header_state(const header_link& link) const NOEXCEPT { - const auto fk = store_.validated_bk.first(link); - if (fk.is_terminal()) - return error::unvalidated; - table::validated_bk::slab_get_code valid{}; - if (!store_.validated_bk.get(fk, valid)) - return error::integrity; + if (!store_.validated_bk.find(link, valid)) + return error::unvalidated; return to_block_code(valid.code); } @@ -184,13 +180,9 @@ code CLASS::get_header_state(const header_link& link) const NOEXCEPT TEMPLATE code CLASS::get_block_state(const header_link& link) const NOEXCEPT { - const auto fk = store_.validated_bk.first(link); - if (fk.is_terminal()) - return is_associated(link) ? error::unvalidated : error::unassociated; - table::validated_bk::slab_get_code valid{}; - if (!store_.validated_bk.get(fk, valid)) - return error::integrity; + if (!store_.validated_bk.find(link, valid)) + return is_associated(link) ? error::unvalidated : error::unassociated; // Fees only valid if block_confirmable. return to_block_code(valid.code); @@ -200,13 +192,9 @@ TEMPLATE code CLASS::get_block_state(uint64_t& fees, const header_link& link) const NOEXCEPT { - const auto fk = store_.validated_bk.first(link); - if (fk.is_terminal()) - return is_associated(link) ? error::unvalidated : error::unassociated; - table::validated_bk::slab valid{}; - if (!store_.validated_bk.get(fk, valid)) - return error::integrity; + if (!store_.validated_bk.find(link, valid)) + return is_associated(link) ? error::unvalidated : error::unassociated; // Fees only valid if block_confirmable. fees = valid.fees; @@ -375,7 +363,7 @@ bool CLASS::set_txs_connected(const header_link& link) NOEXCEPT if (!get_context(ctx, link)) return false; - const auto txs = to_txs(link); + const auto txs = to_transactions(link); if (txs.empty()) return false; diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 0688c462..e86cdd5c 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -230,7 +230,7 @@ class query inline header_link to_header(const hash_digest& key) const NOEXCEPT; inline point_link to_point(const hash_digest& key) const NOEXCEPT; inline tx_link to_tx(const hash_digest& key) const NOEXCEPT; - inline txs_link to_txs_link(const header_link& key) const NOEXCEPT; + inline txs_link to_txs(const header_link& key) const NOEXCEPT; inline filter_link to_filter(const header_link& key) const NOEXCEPT; /// put to tx (reverse navigation) @@ -260,8 +260,8 @@ class query spend_links to_tx_spends(const tx_link& link) const NOEXCEPT; /// block to txs/puts (forward navigation) - tx_links to_txs(const header_link& link) const NOEXCEPT; 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; @@ -317,6 +317,7 @@ class query bool get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT; /// False implies fault. + bool get_height(size_t& out, const hash_digest& key) const NOEXCEPT; bool get_height(size_t& out, const header_link& link) const NOEXCEPT; bool get_value(uint64_t& out, const output_link& link) const NOEXCEPT; bool get_unassociated(association& out, const header_link& link) const NOEXCEPT; @@ -491,6 +492,7 @@ class query /// Confirm. /// ----------------------------------------------------------------------- + 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, diff --git a/test/query/archive.cpp b/test/query/archive.cpp index 60c8c2a2..d3d1988d 100644 --- a/test/query/archive.cpp +++ b/test/query/archive.cpp @@ -1217,7 +1217,7 @@ BOOST_AUTO_TEST_CASE(query_archive__get_tx_key__always__expected) BOOST_REQUIRE_EQUAL(query.get_tx_key(3), system::null_hash); } -BOOST_AUTO_TEST_CASE(query_archive__get_height__always__expected) +BOOST_AUTO_TEST_CASE(query_archive__get_height1__always__expected) { settings settings{}; settings.path = TEST_DIRECTORY; @@ -1247,6 +1247,36 @@ BOOST_AUTO_TEST_CASE(query_archive__get_height__always__expected) BOOST_REQUIRE(!query.get_height(out, 6)); } +BOOST_AUTO_TEST_CASE(query_archive__get_height2__always__expected) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false)); + BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false)); + BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false)); + BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false)); + BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false)); + + size_t out{}; + BOOST_REQUIRE(query.get_height(out, test::genesis.hash())); + BOOST_REQUIRE_EQUAL(out, 0u); + BOOST_REQUIRE(query.get_height(out, test::block1.hash())); + BOOST_REQUIRE_EQUAL(out, 1u); + BOOST_REQUIRE(query.get_height(out, test::block2.hash())); + BOOST_REQUIRE_EQUAL(out, 2u); + BOOST_REQUIRE(query.get_height(out, test::block3.hash())); + BOOST_REQUIRE_EQUAL(out, 3u); + BOOST_REQUIRE(query.get_height(out, test::block1a.hash())); + BOOST_REQUIRE_EQUAL(out, 1u); + BOOST_REQUIRE(query.get_height(out, test::block2a.hash())); + BOOST_REQUIRE_EQUAL(out, 2u); + BOOST_REQUIRE(!query.get_height(out, system::one_hash)); +} + BOOST_AUTO_TEST_CASE(query_archive__get_tx_height__not_strong__false) { settings settings{}; diff --git a/test/query/translate.cpp b/test/query/translate.cpp index 5fed519e..e263ced3 100644 --- a/test/query/translate.cpp +++ b/test/query/translate.cpp @@ -776,10 +776,10 @@ BOOST_AUTO_TEST_CASE(query_translate__to_txs__always__expected) BOOST_REQUIRE(query.set(test::block2a, test::context, false)); const tx_links expected_links2{ 2, 3 }; - BOOST_REQUIRE_EQUAL(query.to_txs(0), tx_links{ 0 }); - BOOST_REQUIRE_EQUAL(query.to_txs(1), tx_links{ 1 }); - BOOST_REQUIRE_EQUAL(query.to_txs(2), expected_links2); - BOOST_REQUIRE(query.to_txs(3).empty()); + BOOST_REQUIRE_EQUAL(query.to_transactions(0), tx_links{ 0 }); + BOOST_REQUIRE_EQUAL(query.to_transactions(1), tx_links{ 1 }); + BOOST_REQUIRE_EQUAL(query.to_transactions(2), expected_links2); + BOOST_REQUIRE(query.to_transactions(3).empty()); } // to_spenders