diff --git a/include/bitcoin/database/impl/query/consensus.ipp b/include/bitcoin/database/impl/query/consensus.ipp index 2e2153581..870ed65ed 100644 --- a/include/bitcoin/database/impl/query/consensus.ipp +++ b/include/bitcoin/database/impl/query/consensus.ipp @@ -176,6 +176,13 @@ code CLASS::unspent_duplicates(const header_link& link, // unspendable // ---------------------------------------------------------------------------- +TEMPLATE +bool CLASS::is_strong(const tx_link& tx) const NOEXCEPT +{ + // Try all txs with same hash as self (any instance will suffice). + return !to_strong(get_tx_key(tx)).is_terminal(); +} + // protected TEMPLATE error::error_t CLASS::unspendable(uint32_t sequence, bool coinbase, @@ -184,14 +191,10 @@ error::error_t CLASS::unspendable(uint32_t sequence, bool coinbase, // Ensure prevout tx is in a strong block, first try self link. auto strong = to_block(tx); - // Extremely rare, normally implies a duplicate tx. - if (strong.is_terminal()) - { - // Try all txs with same hash as self (any instance will suffice). - strong = to_strong(get_tx_key(tx)); - if (strong.is_terminal()) - return error::unconfirmed_spend; - } + // Unassociated to block is rare, generally implies a duplicate tx. + // Not strong (in any block) implies the spend is not confirmed. + if (strong.is_terminal() && !is_strong(tx)) + return error::unconfirmed_spend; const auto relative = ctx.is_enabled(system::chain::flags::bip68_rule) && transaction::is_relative_locktime_applied(coinbase, version, sequence); diff --git a/include/bitcoin/database/impl/query/objects.ipp b/include/bitcoin/database/impl/query/objects.ipp index c1c41144b..2aeaca175 100644 --- a/include/bitcoin/database/impl/query/objects.ipp +++ b/include/bitcoin/database/impl/query/objects.ipp @@ -304,6 +304,17 @@ typename CLASS::output::cptr CLASS::get_output( return out.output; } +TEMPLATE +typename CLASS::point CLASS::get_spender(const point_link& link) const NOEXCEPT +{ + if (const auto tx = to_spending_tx(link); !tx.is_terminal()) + if (const auto index = to_input_index(tx, link); + index != point::null_index) + return { get_tx_key(tx), index }; + + return {}; +} + TEMPLATE typename CLASS::inputs_ptr CLASS::get_spenders( const output_link& link, bool witness) const NOEXCEPT diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp index 2c27383e4..b150b4c55 100644 --- a/include/bitcoin/database/impl/query/translate.ipp +++ b/include/bitcoin/database/impl/query/translate.ipp @@ -187,7 +187,7 @@ header_link CLASS::to_strong(const hash_digest& tx_hash) const NOEXCEPT txs.push_back(*it); // Find the first strong tx of the set and return its block. - for (auto tx: txs) + for (const auto& tx: txs) { const auto block = to_block(tx); if (!block.is_terminal()) @@ -222,9 +222,13 @@ header_link CLASS::to_confirmed_block( } TEMPLATE -point_link CLASS::to_confirmed_spender(const point&) const NOEXCEPT +point_link CLASS::to_confirmed_spender(const point& prevout) const NOEXCEPT { - // TODO: implement. + // see is_spent_output(). + for (const auto& in: to_spenders(prevout)) + if (is_confirmed_input(in)) + return in; + return {}; } @@ -261,20 +265,6 @@ uint32_t CLASS::to_output_index(const tx_link& parent_fk, return point::null_index; } -// Assumes singular which doesn't make sense. -// protected/to_spenders -////TEMPLATE -////spend_link CLASS::to_spender(const tx_link& link, -//// const spend_key& point) const NOEXCEPT -////{ -//// table::spend::get_key spend{}; -//// for (const auto& spend_fk: to_spends(link)) -//// if (store_.spend.get(spend_fk, spend) && (spend.key == point)) -//// return spend_fk; -//// -//// return {}; -////} - TEMPLATE point_links CLASS::to_spenders(const output_link& link) const NOEXCEPT { @@ -286,12 +276,6 @@ point_links CLASS::to_spenders(const output_link& link) const NOEXCEPT return to_spenders(out.parent_fk, to_output_index(out.parent_fk, link)); } -TEMPLATE -point_links CLASS::to_spenders(const point& point) const NOEXCEPT -{ - return to_spenders(point.hash(), point.index()); -} - TEMPLATE point_links CLASS::to_spenders(const tx_link& output_tx, uint32_t output_index) const NOEXCEPT @@ -302,13 +286,19 @@ point_links CLASS::to_spenders(const tx_link& output_tx, TEMPLATE point_links CLASS::to_spenders(const hash_digest& point_hash, uint32_t output_index) const NOEXCEPT +{ + return to_spenders({ point_hash, output_index }); +} + +TEMPLATE +point_links CLASS::to_spenders(const point& point) const NOEXCEPT { // Avoid returning spend links for coinbase inputs (not spenders). - if (output_index == point::null_index) + if (point.index() == point::null_index) return {}; point_links points{}; - for (auto it = store_.point.it({ point_hash, output_index }); it; ++it) + for (auto it = store_.point.it(point); it; ++it) points.push_back(*it); return points; diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 632988a7f..96bfd1bd1 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -397,6 +397,7 @@ class query script::cptr get_output_script(const output_link& link) const NOEXCEPT; output::cptr get_output(const output_link& link) const NOEXCEPT; output::cptr get_output(const tx_link& link, uint32_t index) const NOEXCEPT; + point get_spender(const point_link& link) const NOEXCEPT; inputs_ptr get_spenders(const output_link& link, bool witness) const NOEXCEPT; @@ -524,6 +525,7 @@ class query code block_confirmable(const header_link& link) const NOEXCEPT; bool is_prevouts_cached(const header_link& link) const NOEXCEPT; + bool is_strong(const tx_link& link) const NOEXCEPT; bool set_strong(const header_link& link) NOEXCEPT; bool set_unstrong(const header_link& link) NOEXCEPT; bool set_prevouts(const header_link& link, const block& block) NOEXCEPT;