diff --git a/include/bitcoin/database/impl/query/archive.ipp b/include/bitcoin/database/impl/query/archive.ipp index b8da31e1..62469cc6 100644 --- a/include/bitcoin/database/impl/query/archive.ipp +++ b/include/bitcoin/database/impl/query/archive.ipp @@ -430,7 +430,7 @@ typename CLASS::inputs_ptr CLASS::get_inputs( { // TODO: eliminate shared memory pointer reallocations. using namespace system; - const auto fks = to_tx_spends(link); + const auto fks = to_spends(link); if (fks.empty()) return {}; @@ -450,7 +450,7 @@ typename CLASS::outputs_ptr CLASS::get_outputs( { // TODO: eliminate shared memory pointer reallocations. using namespace system; - const auto fks = to_tx_outputs(link); + const auto fks = to_outputs(link); if (fks.empty()) return {}; diff --git a/include/bitcoin/database/impl/query/extent.ipp b/include/bitcoin/database/impl/query/extent.ipp index 754b1f87..60894d65 100644 --- a/include/bitcoin/database/impl/query/extent.ipp +++ b/include/bitcoin/database/impl/query/extent.ipp @@ -19,6 +19,7 @@ #ifndef LIBBITCOIN_DATABASE_QUERY_EXTENT_IPP #define LIBBITCOIN_DATABASE_QUERY_EXTENT_IPP +#include #include #include @@ -207,6 +208,26 @@ two_counts CLASS::put_counts(const tx_link& link) const NOEXCEPT return { tx.ins_count, tx.outs_count }; } +TEMPLATE +size_t CLASS::input_count(const tx_links& txs) const NOEXCEPT +{ + const auto fn = [this](auto tx) NOEXCEPT { return input_count(tx); }; + return std_reduce(bc::par_unseq, txs.begin(), txs.end(), zero, fn); +} + +TEMPLATE +size_t CLASS::output_count(const tx_links& txs) const NOEXCEPT +{ + const auto fn = [this](auto tx) NOEXCEPT { return output_count(tx); }; + return std_reduce(bc::par_unseq, txs.begin(), txs.end(), zero, fn); +} + +TEMPLATE +two_counts CLASS::put_counts(const tx_links& txs) const NOEXCEPT +{ + return { input_count(txs), output_count(txs) }; +} + TEMPLATE bool CLASS::address_enabled() const NOEXCEPT { diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp index a3b62eaa..f032413e 100644 --- a/include/bitcoin/database/impl/query/translate.ipp +++ b/include/bitcoin/database/impl/query/translate.ipp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -326,7 +327,7 @@ uint32_t CLASS::to_spend_index(const tx_link& parent_fk, const spend_link& spend_fk) const NOEXCEPT { uint32_t index{}; - for (const auto& in_fk: to_tx_spends(parent_fk)) + for (const auto& in_fk: to_spends(parent_fk)) { if (in_fk == spend_fk) return index; ++index; @@ -341,7 +342,7 @@ uint32_t CLASS::to_output_index(const tx_link& parent_fk, const output_link& output_fk) const NOEXCEPT { uint32_t index{}; - for (const auto& out_fk: to_tx_outputs(parent_fk)) + for (const auto& out_fk: to_outputs(parent_fk)) { if (out_fk == output_fk) return index; ++index; @@ -356,7 +357,7 @@ spend_link CLASS::to_spender(const tx_link& link, const foreign_point& point) const NOEXCEPT { table::spend::get_key spend{}; - for (const auto& spend_fk: to_tx_spends(link)) + for (const auto& spend_fk: to_spends(link)) if (store_.spend.get(spend_fk, spend) && (spend.key == point)) return spend_fk; @@ -410,7 +411,7 @@ spend_links CLASS::to_spenders(const foreign_point& point) const NOEXCEPT return fault; auto found{ false }; - for (const auto& spend_fk: to_tx_spends(spender.parent_fk)) + for (const auto& spend_fk: to_spends(spender.parent_fk)) { table::spend::get_key spend{}; if (!store_.spend.get(it, spend_fk, spend)) @@ -435,7 +436,22 @@ spend_links CLASS::to_spenders(const foreign_point& point) const NOEXCEPT // ---------------------------------------------------------------------------- TEMPLATE -output_links CLASS::to_tx_outputs(const tx_link& link) const NOEXCEPT +spend_links CLASS::to_spends(const tx_link& link) const NOEXCEPT +{ + table::transaction::get_puts tx{}; + if (!store_.tx.get(link, tx)) + return {}; + + table::puts::get_spends puts{}; + puts.spend_fks.resize(tx.ins_count); + if (!store_.puts.get(tx.puts_fk, puts)) + return {}; + + return std::move(puts.spend_fks); +} + +TEMPLATE +output_links CLASS::to_outputs(const tx_link& link) const NOEXCEPT { table::transaction::get_puts tx{}; if (!store_.tx.get(link, tx)) @@ -450,18 +466,18 @@ output_links CLASS::to_tx_outputs(const tx_link& link) const NOEXCEPT } TEMPLATE -spend_links CLASS::to_tx_spends(const tx_link& link) const NOEXCEPT +output_links CLASS::to_prevouts(const tx_link& link) const NOEXCEPT { - table::transaction::get_puts tx{}; - if (!store_.tx.get(link, tx)) + const auto spends = to_spends(link); + if (spends.empty()) return {}; - table::puts::get_spends puts{}; - puts.spend_fks.resize(tx.ins_count); - if (!store_.puts.get(tx.puts_fk, puts)) - return {}; + output_links prevouts{}; + prevouts.reserve(spends.size()); + for (const auto& spend: spends) + prevouts.push_back(to_prevout(spend)); - return std::move(puts.spend_fks); + return prevouts; } // protected @@ -502,29 +518,71 @@ spend_set CLASS::to_spend_set(const tx_link& link) const NOEXCEPT return set; } -// block to txs/puts (forward navigation) +// txs to puts (forward navigation) // ---------------------------------------------------------------------------- TEMPLATE -tx_links CLASS::to_transactions(const header_link& link) const NOEXCEPT +spend_links CLASS::to_spends(const tx_links& txs) const NOEXCEPT { - table::txs::get_txs txs{}; - if (!store_.txs.find(link, txs)) - return {}; + spend_links spends{}; + for (const auto& tx: txs) + { + const auto tx_spends = to_spends(tx); + spends.insert(spends.end(), tx_spends.begin(), tx_spends.end()); + } - return std::move(txs.tx_fks); + return spends; } TEMPLATE -tx_links CLASS::to_spending_transactions(const header_link& link) const NOEXCEPT +output_links CLASS::to_outputs(const tx_links& txs) const NOEXCEPT { - table::txs::get_spending_txs txs{}; - if (!store_.txs.find(link, txs)) - return {}; + output_links outputs{}; + for (const auto& tx: txs) + { + const auto tx_outputs = to_outputs(tx); + outputs.insert(outputs.end(), tx_outputs.begin(), tx_outputs.end()); + } - return std::move(txs.tx_fks); + return outputs; +} + +TEMPLATE +output_links CLASS::to_prevouts(const tx_links& txs) const NOEXCEPT +{ + const auto ins = to_spends(txs); + output_links outs(ins.size()); + const auto fn = [this](auto spend) NOEXCEPT{ return to_prevout(spend); }; + + // C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++. + std_transform(bc::par_unseq, ins.begin(), ins.end(), outs.begin(), fn); + return outs; +} + +// block to puts (forward navigation) +// ---------------------------------------------------------------------------- + +TEMPLATE +spend_links CLASS::to_block_spends(const header_link& link) const NOEXCEPT +{ + return to_spends(to_spending_transactions(link)); } +TEMPLATE +output_links CLASS::to_block_outputs(const header_link& link) const NOEXCEPT +{ + return to_outputs(to_transactions(link)); +} + +TEMPLATE +output_links CLASS::to_block_prevouts(const header_link& link) const NOEXCEPT +{ + return to_prevouts(to_spending_transactions(link)); +} + +// block to txs (forward navigation) +// ---------------------------------------------------------------------------- + TEMPLATE tx_link CLASS::to_coinbase(const header_link& link) const NOEXCEPT { @@ -536,33 +594,23 @@ tx_link CLASS::to_coinbase(const header_link& link) const NOEXCEPT } TEMPLATE -spend_links CLASS::to_block_spends(const header_link& link) const NOEXCEPT +tx_links CLASS::to_transactions(const header_link& link) const NOEXCEPT { - spend_links spends{}; - const auto txs = to_transactions(link); - - for (const auto& tx: txs) - { - const auto tx_spends = to_tx_spends(tx); - spends.insert(spends.end(), tx_spends.begin(), tx_spends.end()); - } + table::txs::get_txs txs{}; + if (!store_.txs.find(link, txs)) + return {}; - return spends; + return std::move(txs.tx_fks); } TEMPLATE -output_links CLASS::to_block_outputs(const header_link& link) const NOEXCEPT +tx_links CLASS::to_spending_transactions(const header_link& link) const NOEXCEPT { - output_links outputs{}; - const auto txs = to_transactions(link); - - for (const auto& tx: txs) - { - const auto tx_outputs = to_tx_outputs(tx); - outputs.insert(outputs.end(), tx_outputs.begin(), tx_outputs.end()); - } + table::txs::get_spending_txs txs{}; + if (!store_.txs.find(link, txs)) + return {}; - return outputs; + return std::move(txs.tx_fks); } // hashmap enumeration diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 1dfbb838..8a516d34 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -217,6 +217,9 @@ class query size_t input_count(const tx_link& link) const NOEXCEPT; size_t output_count(const tx_link& link) const NOEXCEPT; two_counts put_counts(const tx_link& link) const NOEXCEPT; + size_t input_count(const tx_links& txs) const NOEXCEPT; + size_t output_count(const tx_links& txs) const NOEXCEPT; + two_counts put_counts(const tx_links& txs) const NOEXCEPT; /// Optional table state. bool address_enabled() const NOEXCEPT; @@ -270,7 +273,7 @@ class query uint32_t output_index) const NOEXCEPT; output_link to_prevout(const spend_link& link) const NOEXCEPT; - /// block/tx to block/s (reverse navigation) + /// block/tx to block (reverse navigation) header_link to_parent(const header_link& link) const NOEXCEPT; header_link to_block(const tx_link& key) const NOEXCEPT; @@ -282,15 +285,24 @@ class query uint32_t output_index) const NOEXCEPT; /// tx to puts (forward navigation) - output_links to_tx_outputs(const tx_link& link) const NOEXCEPT; - spend_links to_tx_spends(const tx_link& link) const NOEXCEPT; + spend_links to_spends(const tx_link& link) const NOEXCEPT; + output_links to_outputs(const tx_link& link) const NOEXCEPT; + output_links to_prevouts(const tx_link& link) const NOEXCEPT; - /// block to txs/puts (forward navigation) + /// txs to puts (forward navigation) + spend_links to_spends(const tx_links& txs) const NOEXCEPT; + output_links to_outputs(const tx_links& txs) const NOEXCEPT; + output_links to_prevouts(const tx_links& txs) const NOEXCEPT; + + /// block to puts (forward navigation) + spend_links to_block_spends(const header_link& link) const NOEXCEPT; + output_links to_block_outputs(const header_link& link) const NOEXCEPT; + output_links to_block_prevouts(const header_link& link) const NOEXCEPT; + + /// block to txs (forward navigation) tx_link to_coinbase(const header_link& link) const NOEXCEPT; tx_links to_transactions(const header_link& link) const NOEXCEPT; tx_links to_spending_transactions(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; diff --git a/test/query/translate.cpp b/test/query/translate.cpp index e62c658e..dd238e20 100644 --- a/test/query/translate.cpp +++ b/test/query/translate.cpp @@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(query_translate__to_tx__txs__expected) BOOST_REQUIRE_EQUAL(query.to_tx(test::block3.transactions_ptr()->front()->hash(true)), tx_link::terminal); } -// to_spend_tx/to_spend/to_tx_spends/to_spend_key/to_spend_sets +// to_spend_tx/to_spend/to_spends/to_spend_key/to_spend_sets class accessor : public test::query_accessor @@ -276,11 +276,11 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected) BOOST_REQUIRE_EQUAL(query.to_spend_key(query.to_spend(4, 2)), base16_array("010000002b0000")); const spend_links expected_links4{ 4, 5, 6 }; - BOOST_REQUIRE_EQUAL(query.to_tx_spends(0), spend_links{ 0 }); - BOOST_REQUIRE_EQUAL(query.to_tx_spends(1), spend_links{ 1 }); - BOOST_REQUIRE_EQUAL(query.to_tx_spends(2), spend_links{ 2 }); - BOOST_REQUIRE_EQUAL(query.to_tx_spends(3), spend_links{ 3 }); - BOOST_REQUIRE_EQUAL(query.to_tx_spends(4), expected_links4); + BOOST_REQUIRE_EQUAL(query.to_spends(0), spend_links{ 0 }); + BOOST_REQUIRE_EQUAL(query.to_spends(1), spend_links{ 1 }); + BOOST_REQUIRE_EQUAL(query.to_spends(2), spend_links{ 2 }); + BOOST_REQUIRE_EQUAL(query.to_spends(3), spend_links{ 3 }); + BOOST_REQUIRE_EQUAL(query.to_spends(4), expected_links4); auto spends = query.to_spend_set_(0); BOOST_REQUIRE_EQUAL(spends.tx, 0u); @@ -338,7 +338,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_EQUAL(query.to_spend_sets_(5).size(), 0u); - BOOST_REQUIRE(query.to_tx_spends(5).empty()); + BOOST_REQUIRE(query.to_spends(5).empty()); // Verify expectations. const auto spend_head = base16_chunk @@ -500,7 +500,7 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_sets__populated__expected) BOOST_REQUIRE_EQUAL(spends[0].spends[0].sequence, 0xb2u); } -// to_output_tx/to_output/to_tx_outputs/to_block_outputs +// to_output_tx/to_output/to_outputs/to_prevouts/to_block_outputs BOOST_AUTO_TEST_CASE(query_translate__to_output_tx__to_output__expected) { @@ -530,24 +530,39 @@ BOOST_AUTO_TEST_CASE(query_translate__to_output_tx__to_output__expected) BOOST_REQUIRE_EQUAL(query.to_output(4, 0), 4u * 0x51u); BOOST_REQUIRE_EQUAL(query.to_output(4, 1), 4u * 0x51u + 7u); - const output_links expected_links4{ 4 * 0x51, 4 * 0x51 + 7 }; - BOOST_REQUIRE_EQUAL(query.to_tx_outputs(0), output_links{ 0 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_tx_outputs(1), output_links{ 1 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_tx_outputs(2), output_links{ 2 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_tx_outputs(3), output_links{ 3 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_tx_outputs(4), expected_links4); + const output_links expected_outputs4{ 4 * 0x51, 4 * 0x51 + 7 }; + BOOST_REQUIRE_EQUAL(query.to_outputs(0), output_links{ 0 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(1), output_links{ 1 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(2), output_links{ 2 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(3), output_links{ 3 * 0x51 }); + BOOST_REQUIRE_EQUAL(query.to_outputs(4), expected_outputs4); - // TODO: All blocks have one transaction. + // All blocks have one transaction. BOOST_REQUIRE_EQUAL(query.to_block_outputs(0), output_links{ 0 * 0x51 }); BOOST_REQUIRE_EQUAL(query.to_block_outputs(1), output_links{ 1 * 0x51 }); BOOST_REQUIRE_EQUAL(query.to_block_outputs(2), output_links{ 2 * 0x51 }); BOOST_REQUIRE_EQUAL(query.to_block_outputs(3), output_links{ 3 * 0x51 }); - BOOST_REQUIRE_EQUAL(query.to_block_outputs(4), expected_links4); + BOOST_REQUIRE_EQUAL(query.to_block_outputs(4), expected_outputs4); + + // No prevouts that exist. + const output_links expected_prevouts4{ output_link::terminal, output_link::terminal, output_link::terminal }; + BOOST_REQUIRE_EQUAL(query.to_prevouts(0), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(1), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(2), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(3), output_links{ output_link::terminal }); + BOOST_REQUIRE_EQUAL(query.to_prevouts(4), expected_prevouts4); + + // All blocks have one transaction. + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(3), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(4), output_links{}); // Past end. BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 14), tx_link::terminal); BOOST_REQUIRE_EQUAL(query.to_output(5, 0), output_link::terminal); - BOOST_REQUIRE(query.to_tx_outputs(5).empty()); + BOOST_REQUIRE(query.to_outputs(5).empty()); BOOST_REQUIRE(query.to_block_outputs(5).empty()); // Verify expectations. @@ -564,7 +579,7 @@ BOOST_AUTO_TEST_CASE(query_translate__to_output_tx__to_output__expected) BOOST_REQUIRE_EQUAL(store.output_body(), output_body); } -// to_prevout_tx/to_prevout +// to_prevout_tx/to_prevout/to_prevouts BOOST_AUTO_TEST_CASE(query_translate__to_prevout_tx__to_prevout__expected) { @@ -597,6 +612,20 @@ BOOST_AUTO_TEST_CASE(query_translate__to_prevout_tx__to_prevout__expected) BOOST_REQUIRE_EQUAL(query.to_prevout(6), output_link::terminal); BOOST_REQUIRE_EQUAL(query.to_prevout(7), output_link::terminal); + const output_links expected_prevouts1{ output_link::terminal }; + const output_links expected_prevouts2{ output_link::terminal, output_link::terminal }; + const output_links expected_prevouts3{ output_link::terminal, output_link::terminal, output_link::terminal }; + const output_links expected_prevouts{ 0x51u, 0x51u + 7u }; + BOOST_REQUIRE_EQUAL(query.to_prevouts(0), expected_prevouts1); + BOOST_REQUIRE_EQUAL(query.to_prevouts(1), expected_prevouts3); + BOOST_REQUIRE_EQUAL(query.to_prevouts(2), expected_prevouts); + BOOST_REQUIRE_EQUAL(query.to_prevouts(3), expected_prevouts2); + + // First tx is coinbase, or tx has undefined prevouts. + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{}); + BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), expected_prevouts2); + // Past end. BOOST_REQUIRE_EQUAL(query.to_prevout_tx(8), tx_link::terminal); BOOST_REQUIRE_EQUAL(query.to_prevout(8), output_link::terminal);