Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 52 additions & 8 deletions include/bitcoin/database/impl/query/pool.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ bool CLASS::is_confirmable_spent(const output_link& link) const NOEXCEPT
return false;
}

TEMPLATE
ins_link CLASS::find_confirmable_spender(const point& prevout) const NOEXCEPT
{
for (const auto& in: to_spenders(prevout))
{
const auto tx = to_input_tx(in);
if (is_pooled(tx) && is_confirmable_tx(tx))
return in;
}

return {};
}

TEMPLATE
code CLASS::get_pooled(pooled_tx& out, const tx_link& link,
const context& ctx) const NOEXCEPT
Expand Down Expand Up @@ -185,6 +198,18 @@ tx_links CLASS::to_confirmable_children(const tx_link& link) const NOEXCEPT
return out;
}

TEMPLATE
tx_links CLASS::to_confirmable_ancestors(const tx_link& link) const NOEXCEPT
{
return to_confirmable_relatives(link, true);
}

TEMPLATE
tx_links CLASS::to_confirmable_descendants(const tx_link& link) const NOEXCEPT
{
return to_confirmable_relatives(link, false);
}

TEMPLATE
bool CLASS::get_ancestor_totals(fee_totals& out,
const tx_link& link) const NOEXCEPT
Expand Down Expand Up @@ -376,31 +401,50 @@ bool CLASS::get_confirmable_totals(fee_totals& out, const tx_link& link,
bool ancestors) const NOEXCEPT
{
using namespace system;
auto links = to_confirmable_relatives(link, ancestors);
links.push_back(link.value);

out = {};
std::vector<tx_link::integer> pending{ link.value };
std::unordered_set<tx_link::integer> visited{ link.value };
while (!pending.empty())
for (const auto fk: links)
{
const tx_link next{ pending.back() };
pending.pop_back();

fee_rate rate{};
if (!get_tx_fees(rate, next))
if (!get_tx_fees(rate, fk))
return false;

++out.count;
out.bytes = ceilinged_add(out.bytes, rate.bytes);
out.fee = ceilinged_add(out.fee, rate.fee);
}

return true;
}

TEMPLATE
tx_links CLASS::to_confirmable_relatives(const tx_link& link,
bool ancestors) const NOEXCEPT
{
tx_links out{};
std::vector<tx_link::integer> pending{ link.value };
std::unordered_set<tx_link::integer> visited{ link.value };
while (!pending.empty())
{
const tx_link next{ pending.back() };
pending.pop_back();

const auto links = ancestors ? to_confirmable_parents(next) :
to_confirmable_children(next);

for (const auto fk: links)
{
if (visited.insert(fk).second)
{
out.push_back(fk);
pending.push_back(fk);
}
}
}

return true;
return out;
}

// writers
Expand Down
11 changes: 11 additions & 0 deletions include/bitcoin/database/query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ class query
/// Spent by a pooled and confirmable tx.
bool is_confirmable_spent(const output_link& link) const NOEXCEPT;

/// Spender of prevout in a pooled and confirmable tx, terminal if none.
ins_link find_confirmable_spender(const point& prevout) const NOEXCEPT;

/// Confirmable tx links, from cursor toward end, at most limit.
code get_confirmable_txs(pool_link& cursor, tx_links& out,
const pool_link& end, size_t limit) const NOEXCEPT;
Expand All @@ -737,6 +740,10 @@ class query
tx_links to_confirmable_parents(const tx_link& link) const NOEXCEPT;
tx_links to_confirmable_children(const tx_link& link) const NOEXCEPT;

/// Confirmable ancestors, or descendants, of the tx (excluding the tx).
tx_links to_confirmable_ancestors(const tx_link& link) const NOEXCEPT;
tx_links to_confirmable_descendants(const tx_link& link) const NOEXCEPT;

/// Totals of the tx with its confirmable ancestors, or descendants.
bool get_ancestor_totals(fee_totals& out,
const tx_link& link) const NOEXCEPT;
Expand Down Expand Up @@ -1119,6 +1126,10 @@ class query
bool is_confirmable_tx(confirmables& memo,
const tx_link& link) const NOEXCEPT;

/// Confirmable ancestors or descendants of the tx (excluding the tx).
tx_links to_confirmable_relatives(const tx_link& link,
bool ancestors) const NOEXCEPT;

/// Totals of the tx with its confirmable ancestors or descendants.
bool get_confirmable_totals(fee_totals& out, const tx_link& link,
bool ancestors) const NOEXCEPT;
Expand Down
102 changes: 102 additions & 0 deletions test/query/pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,48 @@ BOOST_AUTO_TEST_CASE(query_pool__is_confirmable_spent__conflicted_spender__false
BOOST_REQUIRE(!query.is_confirmable_spent(query.to_output(parent.hash(false), 0)));
}

// find_confirmable_spender

BOOST_AUTO_TEST_CASE(query_pool__find_confirmable_spender__pooled_spender__expected)
{
settings settings{};
settings.path = TEST_DIRECTORY;
test::chunk_store store{ settings };
test::query_accessor query{ store };
BOOST_REQUIRE(!store.create(test::events_handler));
BOOST_REQUIRE(query.initialize(test::genesis));
store.set_pooling();

const auto parent = to_spend(genesis_coinbase(), 2u);
const auto child = to_spend(parent.hash(false), 1u);
const point prevout{ parent.hash(false), 0 };
BOOST_REQUIRE(archive_pool(query, parent));
BOOST_REQUIRE(query.find_confirmable_spender(prevout).is_terminal());

BOOST_REQUIRE(archive_pool(query, child));
const auto spender = query.find_confirmable_spender(prevout);
BOOST_REQUIRE(query.to_input_tx(spender) == query.to_tx(child.hash(false)));
}

BOOST_AUTO_TEST_CASE(query_pool__find_confirmable_spender__conflicted_spender__terminal)
{
settings settings{};
settings.path = TEST_DIRECTORY;
test::chunk_store store{ settings };
test::query_accessor query{ store };
BOOST_REQUIRE(!store.create(test::events_handler));
BOOST_REQUIRE(query.initialize(test::genesis));
store.set_pooling();

const auto parent = to_spend(genesis_coinbase(), 2u);
const auto child = to_spend(parent.hash(false), 1u);
const point prevout{ parent.hash(false), 0 };
BOOST_REQUIRE(archive_pool(query, parent));
BOOST_REQUIRE(archive_pool(query, child));
BOOST_REQUIRE(!archive_confirm(query, to_spend(genesis_coinbase(), 3u)).is_terminal());
BOOST_REQUIRE(query.find_confirmable_spender(prevout).is_terminal());
}

// get_pooled_time

BOOST_AUTO_TEST_CASE(query_pool__get_pooled_time__pooled__expected)
Expand Down Expand Up @@ -739,6 +781,66 @@ BOOST_AUTO_TEST_CASE(query_pool__to_confirmable_children__conflicted_child__empt
BOOST_REQUIRE(query.to_confirmable_children(parent_link).empty());
}

// to_confirmable_ancestors

BOOST_AUTO_TEST_CASE(query_pool__to_confirmable_ancestors__pooled_chain__ancestors)
{
settings settings{};
settings.path = TEST_DIRECTORY;
test::chunk_store store{ settings };
test::query_accessor query{ store };
BOOST_REQUIRE(!store.create(test::events_handler));
BOOST_REQUIRE(query.initialize(test::genesis));
store.set_pooling();

const auto parent = to_spend(genesis_coinbase(), 2u);
const auto child = to_spend(parent.hash(false), 1u);
const auto grandchild = to_spend(child.hash(false), 1u);
BOOST_REQUIRE(archive_pool(query, parent));
BOOST_REQUIRE(archive_pool(query, child));
BOOST_REQUIRE(archive_pool(query, grandchild));

const auto parent_link = query.to_tx(parent.hash(false));
const auto child_link = query.to_tx(child.hash(false));
const auto grandchild_link = query.to_tx(grandchild.hash(false));
BOOST_REQUIRE(query.to_confirmable_ancestors(parent_link).empty());

const auto ancestors = query.to_confirmable_ancestors(grandchild_link);
BOOST_REQUIRE_EQUAL(ancestors.size(), two);
BOOST_REQUIRE(system::contains(ancestors, parent_link.value));
BOOST_REQUIRE(system::contains(ancestors, child_link.value));
}

// to_confirmable_descendants

BOOST_AUTO_TEST_CASE(query_pool__to_confirmable_descendants__pooled_chain__descendants)
{
settings settings{};
settings.path = TEST_DIRECTORY;
test::chunk_store store{ settings };
test::query_accessor query{ store };
BOOST_REQUIRE(!store.create(test::events_handler));
BOOST_REQUIRE(query.initialize(test::genesis));
store.set_pooling();

const auto parent = to_spend(genesis_coinbase(), 2u);
const auto child = to_spend(parent.hash(false), 1u);
const auto grandchild = to_spend(child.hash(false), 1u);
BOOST_REQUIRE(archive_pool(query, parent));
BOOST_REQUIRE(archive_pool(query, child));
BOOST_REQUIRE(archive_pool(query, grandchild));

const auto parent_link = query.to_tx(parent.hash(false));
const auto child_link = query.to_tx(child.hash(false));
const auto grandchild_link = query.to_tx(grandchild.hash(false));
BOOST_REQUIRE(query.to_confirmable_descendants(grandchild_link).empty());

const auto descendants = query.to_confirmable_descendants(parent_link);
BOOST_REQUIRE_EQUAL(descendants.size(), two);
BOOST_REQUIRE(system::contains(descendants, child_link.value));
BOOST_REQUIRE(system::contains(descendants, grandchild_link.value));
}

// get_ancestor_totals

BOOST_AUTO_TEST_CASE(query_pool__get_ancestor_totals__pooled_parent__both)
Expand Down
Loading