From 05efa019bf8f644e6eb2038f80cba7f5796c764c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Sat, 16 Nov 2024 23:15:07 -0500 Subject: [PATCH 1/2] Update to latest NuGet secp256k1 package. --- .../libbitcoin-database-test/libbitcoin-database-test.vcxproj | 4 ++-- builds/msvc/vs2022/libbitcoin-database-test/packages.config | 2 +- .../libbitcoin-database-tools.vcxproj | 4 ++-- builds/msvc/vs2022/libbitcoin-database-tools/packages.config | 2 +- .../vs2022/libbitcoin-database/libbitcoin-database.vcxproj | 4 ++-- builds/msvc/vs2022/libbitcoin-database/packages.config | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) 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 00232c7f..f211a158 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 e680a5c2..e50e9f2e 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 d04ce942..6b08220f 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 f5aa23df..729728e2 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 a04ec3dd..4d0af41a 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 f5aa23df..729728e2 100644 --- a/builds/msvc/vs2022/libbitcoin-database/packages.config +++ b/builds/msvc/vs2022/libbitcoin-database/packages.config @@ -15,5 +15,5 @@ - + From fb787daae0ab1cae97fcb5ff72d1ac812fa495fd Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 Nov 2024 18:44:28 -0500 Subject: [PATCH 2/2] Minor confirmation optimizations, style. --- .../bitcoin/database/impl/query/confirm.ipp | 49 +++++++++---------- .../bitcoin/database/impl/query/translate.ipp | 13 ++++- include/bitcoin/database/query.hpp | 5 +- .../bitcoin/database/tables/archives/txs.hpp | 23 +++++++++ test/query/confirm.cpp | 34 +++++++------ test/query/translate.cpp | 22 +++++---- 6 files changed, 93 insertions(+), 53 deletions(-) diff --git a/include/bitcoin/database/impl/query/confirm.ipp b/include/bitcoin/database/impl/query/confirm.ipp index 7649ead3..e408781c 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 47fbfd6a..a3b62eaa 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 33930ec3..1dfbb838 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 41ae7928..2e5209f7 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 e61889ec..4632cea2 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 983ada10..e62c658e 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.