Skip to content

Commit

Permalink
Minor confirmation optimizations, style.
Browse files Browse the repository at this point in the history
  • Loading branch information
evoskuil committed Nov 18, 2024
1 parent 05efa01 commit fb787da
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 53 deletions.
49 changes: 24 additions & 25 deletions include/bitcoin/database/impl/query/confirm.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -303,20 +303,21 @@ error::error_t CLASS::unspendable_prevout(const point_link& link,
}

TEMPLATE
code CLASS::unspent_duplicates(const tx_link& coinbase,
code CLASS::unspent_duplicates(const header_link& link,
const context& ctx) const NOEXCEPT
{
if (!ctx.is_enabled(system::chain::flags::bip30_rule))
return error::success;

// This will be empty if current block is not set_strong.
const auto coinbases = to_strong_txs(get_tx_key(coinbase));
if (coinbases.empty())
return error::integrity;
const auto coinbases = to_strong_txs(get_tx_key(to_coinbase(link)));

if (is_one(coinbases.size()))
return error::success;

if (coinbases.empty())
return error::integrity;

// bip30: all (but self) must be confirmed spent or dup invalid (cb only).
size_t unspent{};
for (const auto& tx: coinbases)
Expand All @@ -327,6 +328,8 @@ code CLASS::unspent_duplicates(const tx_link& coinbase,
return is_zero(unspent) ? error::integrity : error::success;
}

#if defined(UNDEFINED)

// protected
TEMPLATE
spend_sets CLASS::to_spend_sets(const header_link& link) const NOEXCEPT
Expand Down Expand Up @@ -407,31 +410,27 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
return ec;
}

#if defined(UNDEFINED)
#endif


// protected
TEMPLATE
spend_sets CLASS::to_spend_sets(const header_link& link) const NOEXCEPT
{
const auto txs = to_transactions(link);
// Coinbase tx does not spend.
const auto txs = to_spending_transactions(link);

if (txs.empty())
return {};

// Coinbase optimization.
spend_sets out{ txs.size() };
out.front().tx = txs.front();
if (is_one(out.size()))
return out;

const auto non_coinbase = std::next(txs.begin());
const auto to_set = [this](const auto& tx) NOEXCEPT
{
return to_spend_set(tx);
};

// C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++.
std_transform(bc::par_unseq, std::next(txs.begin()), txs.end(),
std::next(out.begin()), to_set);
std_transform(bc::par_unseq, txs.begin(), txs.end(), out.begin(), to_set);

return out;
}
Expand All @@ -444,16 +443,14 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
if (!get_context(ctx, link))
return error::integrity;

// C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++.
const auto sets = to_spend_sets(link);
if (sets.empty())
return error::integrity;

code ec{};
if ((ec = unspent_duplicates(sets.front().tx, ctx)))
if ((ec = unspent_duplicates(link, ctx)))
return ec;

const auto non_coinbase = std::next(sets.begin());
const auto sets = to_spend_sets(link);
if (sets.empty())
return ec;

std::atomic<error::error_t> fault{ error::success };

const auto is_unspendable = [this, &ctx, &fault](const auto& set) NOEXCEPT
Expand Down Expand Up @@ -484,16 +481,18 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
};

// C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++.
if (std_any_of(bc::par_unseq, non_coinbase, sets.end(), is_unspendable))
if (std_any_of(bc::par_unseq, sets.begin(), sets.end(), is_unspendable))
return { fault.load() };

// C++17 incomplete on GCC/CLang, so presently parallel only on MSVC++.
if (std_any_of(bc::par_unseq, non_coinbase, sets.end(), is_spent))
if (std_any_of(bc::par_unseq, sets.begin(), sets.end(), is_spent))
return { fault.load() };

return ec;
}

#if defined(UNDEFINED)

// split(1) 446 secs for 400k-410k
TEMPLATE
code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
Expand Down Expand Up @@ -620,7 +619,7 @@ TEMPLATE
bool CLASS::initialize(const block& genesis) NOEXCEPT
{
BC_ASSERT(!is_initialized());
BC_ASSERT(genesis.transactions_ptr()->size() == one);
BC_ASSERT(is_one(genesis.transactions_ptr()->size()));

// ========================================================================
const auto scope = store_.get_transactor();
Expand Down
13 changes: 11 additions & 2 deletions include/bitcoin/database/impl/query/translate.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -479,13 +479,12 @@ spend_set CLASS::to_spend_set(const tx_link& link) const NOEXCEPT

spend_set set{ link, tx.version, {} };
set.spends.reserve(tx.ins_count);

// This is not concurrent because to_spend_sets is (by tx).
table::spend::get_prevout_sequence get{};

// This reduced a no-bypass 840k sync/confirmable/confirm run by 8.3%.
const auto ptr = store_.spend.get_memory();

// This is not concurrent because to_spend_sets is (by tx).
for (const auto& spend_fk: puts.spend_fks)
{
if (!store_.spend.get(ptr, spend_fk, get))
Expand Down Expand Up @@ -516,6 +515,16 @@ tx_links CLASS::to_transactions(const header_link& link) const NOEXCEPT
return std::move(txs.tx_fks);
}

TEMPLATE
tx_links CLASS::to_spending_transactions(const header_link& link) const NOEXCEPT
{
table::txs::get_spending_txs txs{};
if (!store_.txs.find(link, txs))
return {};

return std::move(txs.tx_fks);
}

TEMPLATE
tx_link CLASS::to_coinbase(const header_link& link) const NOEXCEPT
{
Expand Down
5 changes: 3 additions & 2 deletions include/bitcoin/database/query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ 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;
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;

Expand Down Expand Up @@ -497,8 +498,8 @@ class query
bool set_strong(const header_link& link) NOEXCEPT;
bool set_unstrong(const header_link& link) NOEXCEPT;
code block_confirmable(const header_link& link) const NOEXCEPT;
code tx_confirmable(const tx_link& link, const context& ctx) const NOEXCEPT;
code unspent_duplicates(const tx_link& coinbase,
////code tx_confirmable(const tx_link& link, const context& ctx) const NOEXCEPT;
code unspent_duplicates(const header_link& coinbase,
const context& ctx) const NOEXCEPT;

/// Height indexation.
Expand Down
23 changes: 23 additions & 0 deletions include/bitcoin/database/tables/archives/txs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_TXS_HPP

#include <algorithm>
#include <iterator>
#include <bitcoin/system.hpp>
#include <bitcoin/database/define.hpp>
#include <bitcoin/database/memory/memory.hpp>
Expand Down Expand Up @@ -169,6 +170,28 @@ struct txs
keys tx_fks{};
};

struct get_spending_txs
: public schema::txs
{
inline bool from_data(reader& source) NOEXCEPT
{
const auto count = source.read_little_endian<tx::integer, schema::count_>();
if (count <= one)
return source;

tx_fks.resize(sub1(count));
source.skip_bytes(bytes::size + tx::size);
std::for_each(tx_fks.begin(), tx_fks.end(), [&](auto& fk) NOEXCEPT
{
fk = source.read_little_endian<tx::integer, tx::size>();
});

return source;
}

keys tx_fks{};
};

struct get_tx_quantity
: public schema::txs
{
Expand Down
34 changes: 20 additions & 14 deletions test/query/confirm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,12 +484,13 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__null_points__success)
BOOST_REQUIRE(query.set(test::block2, context{ bip68 }, false, false));
BOOST_REQUIRE(query.set(test::block3, context{ bip68 }, false, false));

// ALL COINBASE TXS
// 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(!query.block_confirmable(1));
BOOST_REQUIRE(!query.block_confirmable(2));
BOOST_REQUIRE(!query.block_confirmable(3));
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_AUTO_TEST_CASE(query_confirm__block_confirmable__missing_prevouts__integrity)
Expand All @@ -502,9 +503,10 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__missing_prevouts__integri
BOOST_REQUIRE(query.initialize(test::genesis));
BOOST_REQUIRE(query.set(test::block1a, context{ bip68, 1, 0 }, false, false));

// ONLY COINBASE TXS
// block1a is missing all three input prevouts.
BOOST_REQUIRE(query.set_strong(1));
BOOST_REQUIRE(!query.block_confirmable(1));
BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success);
}

BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_gensis__coinbase_maturity)
Expand All @@ -520,8 +522,9 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_gensis__coinbase_ma
BOOST_REQUIRE(query.set(test::block_spend_genesis, context{ 0, 101, 0 }, false, false));
BOOST_REQUIRE(query.set_strong(1));

// COINBASE TX
// 1 + 100 = 101 (maturity, except genesis)
BOOST_REQUIRE(!query.block_confirmable(1));
BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success);
}

BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__immature_prevouts__coinbase_maturity)
Expand All @@ -536,12 +539,13 @@ 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(!query.block_confirmable(1));
BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success);

// COINBASE TX
// 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(!query.block_confirmable(2));
BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success);
}

BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__mature_prevouts__success)
Expand All @@ -556,12 +560,13 @@ 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(!query.block_confirmable(1));
BOOST_REQUIRE_EQUAL(query.block_confirmable(1), error::success);

// COINBASE TX
// 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(!query.block_confirmable(2));
BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success);
}

BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__success)
Expand All @@ -581,8 +586,9 @@ BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_non_coinbase__succe
BOOST_REQUIRE(query.set(test::block_spend_1a, context{ 0, 2, 0 }, false, false));
BOOST_REQUIRE(query.set_strong(2));

// COINBASE TX
// Maturity applies only to coinbase prevouts.
BOOST_REQUIRE(!query.block_confirmable(2));
BOOST_REQUIRE_EQUAL(query.block_confirmable(2), error::success);
}

// These pas but test vectors need to be updated to create clear test conditions.
Expand All @@ -608,7 +614,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(!query.block_confirmable(2));
//// BOOST_REQUIRE_EQUAL(!query.block_confirmable(2), error::success);
////}
////
////BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__spend_coinbase_and_internal_mature__success)
Expand All @@ -635,7 +641,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(!query.block_confirmable(2));
//// BOOST_REQUIRE_EQUAL(!query.block_confirmable(2), error::success);
////}
////
////BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__confirmed_double_spend__confirmed_double_spend)
Expand All @@ -660,7 +666,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(!query.block_confirmable(3));
//// BOOST_REQUIRE_EQUAL(!query.block_confirmable(3), error::success);
////}
////
////BOOST_AUTO_TEST_CASE(query_confirm__block_confirmable__unconfirmed_double_spend__success)
Expand All @@ -684,7 +690,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(!query.block_confirmable(2));
//// BOOST_REQUIRE_EQUAL(!query.block_confirmable(2));
////}

BOOST_AUTO_TEST_CASE(query_confirm__set_strong__unassociated__false)
Expand Down
22 changes: 12 additions & 10 deletions test/query/translate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,13 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_tx__to_spend__expected)
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());

// COINBASE TXS!
// TODO: All blocks have one transaction.
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(2).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(3).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(4).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(2).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(3).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(4).size(), 0u);

// Past end.
BOOST_REQUIRE_EQUAL(query.to_spend_tx(7), tx_link::terminal);
Expand Down Expand Up @@ -430,7 +431,7 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_sets__populated__expected)

// coinbase only (null and first).
BOOST_REQUIRE(query.initialize(test::genesis));
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(2).size(), 0u);

Expand All @@ -444,8 +445,8 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_sets__populated__expected)

// coinbase only (null and first).
BOOST_REQUIRE(query.set(test::block1b, context{ 0, 1, 0 }, false, false));
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(2).size(), 0u);

BOOST_REQUIRE_EQUAL(store.point_body(), system::base16_chunk(""));
Expand All @@ -460,10 +461,11 @@ BOOST_AUTO_TEST_CASE(query_translate__to_spend_sets__populated__expected)
"01000000""b1""0179"
"01000000""b1""0179"));

// COINBASE TX
// 2 inputs (block1b and tx2b).
BOOST_REQUIRE(query.set(test::block_spend_internal_2b, context{ 0, 101, 0 }, false, false));
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 1u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(0).size(), 0u);
BOOST_REQUIRE_EQUAL(query.to_spend_sets_(1).size(), 0u);

// 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.
Expand Down

0 comments on commit fb787da

Please sign in to comment.