diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
index 00232c7f5..f211a158b 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
@@ -141,7 +141,7 @@
-
+
@@ -157,7 +157,7 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/packages.config b/builds/msvc/vs2022/libbitcoin-database-test/packages.config
index e680a5c2f..e50e9f2e1 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/packages.config
+++ b/builds/msvc/vs2022/libbitcoin-database-test/packages.config
@@ -15,6 +15,6 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-database-tools/libbitcoin-database-tools.vcxproj b/builds/msvc/vs2022/libbitcoin-database-tools/libbitcoin-database-tools.vcxproj
index d04ce9424..6b08220f9 100644
--- a/builds/msvc/vs2022/libbitcoin-database-tools/libbitcoin-database-tools.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database-tools/libbitcoin-database-tools.vcxproj
@@ -87,7 +87,7 @@
-
+
@@ -102,7 +102,7 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-database-tools/packages.config b/builds/msvc/vs2022/libbitcoin-database-tools/packages.config
index f5aa23dfb..729728e23 100644
--- a/builds/msvc/vs2022/libbitcoin-database-tools/packages.config
+++ b/builds/msvc/vs2022/libbitcoin-database-tools/packages.config
@@ -15,5 +15,5 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index a04ec3ddd..4d0af41af 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -181,7 +181,7 @@
-
+
@@ -196,7 +196,7 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/packages.config b/builds/msvc/vs2022/libbitcoin-database/packages.config
index f5aa23dfb..729728e23 100644
--- a/builds/msvc/vs2022/libbitcoin-database/packages.config
+++ b/builds/msvc/vs2022/libbitcoin-database/packages.config
@@ -15,5 +15,5 @@
-
+
diff --git a/include/bitcoin/database/impl/query/confirm.ipp b/include/bitcoin/database/impl/query/confirm.ipp
index 7649ead3c..e408781c5 100644
--- a/include/bitcoin/database/impl/query/confirm.ipp
+++ b/include/bitcoin/database/impl/query/confirm.ipp
@@ -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)
@@ -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
@@ -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;
}
@@ -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 fault{ error::success };
const auto is_unspendable = [this, &ctx, &fault](const auto& set) NOEXCEPT
@@ -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
@@ -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();
diff --git a/include/bitcoin/database/impl/query/translate.ipp b/include/bitcoin/database/impl/query/translate.ipp
index 47fbfd6a3..a3b62eaa8 100644
--- a/include/bitcoin/database/impl/query/translate.ipp
+++ b/include/bitcoin/database/impl/query/translate.ipp
@@ -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))
@@ -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
{
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index 33930ec34..1dfbb8388 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -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;
@@ -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.
diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp
index 41ae79283..2e5209f7c 100644
--- a/include/bitcoin/database/tables/archives/txs.hpp
+++ b/include/bitcoin/database/tables/archives/txs.hpp
@@ -20,6 +20,7 @@
#define LIBBITCOIN_DATABASE_TABLES_ARCHIVES_TXS_HPP
#include
+#include
#include
#include
#include
@@ -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();
+ 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();
+ });
+
+ return source;
+ }
+
+ keys tx_fks{};
+ };
+
struct get_tx_quantity
: public schema::txs
{
diff --git a/test/query/confirm.cpp b/test/query/confirm.cpp
index e61889ece..4632cea2b 100644
--- a/test/query/confirm.cpp
+++ b/test/query/confirm.cpp
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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.
@@ -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)
@@ -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)
@@ -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)
@@ -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)
diff --git a/test/query/translate.cpp b/test/query/translate.cpp
index 983ada10d..e62c658e0 100644
--- a/test/query/translate.cpp
+++ b/test/query/translate.cpp
@@ -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);
@@ -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);
@@ -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(""));
@@ -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.