From 11ee5afdcd07bc70baa9559e18b04198b1e237c6 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 24 Jun 2026 17:02:10 -0400 Subject: [PATCH 1/3] Split up batching queries, stub in silent queries. --- Makefile.am | 7 +- .../libbitcoin-database.vcxproj | 4 +- .../libbitcoin-database.vcxproj.filters | 53 ++++++----- .../libbitcoin-database.vcxproj | 4 +- .../libbitcoin-database.vcxproj.filters | 53 ++++++----- .../query/{signatures.ipp => batch/ecdsa.ipp} | 64 +------------ .../database/impl/query/batch/schnorr.ipp | 95 +++++++++++++++++++ .../database/impl/query/batch/silent.ipp | 61 ++++++++++++ include/bitcoin/database/query.hpp | 22 ++++- .../bitcoin/database/tables/caches/silent.hpp | 6 +- include/bitcoin/database/types/type.hpp | 3 + 11 files changed, 258 insertions(+), 114 deletions(-) rename include/bitcoin/database/impl/query/{signatures.ipp => batch/ecdsa.ipp} (60%) create mode 100644 include/bitcoin/database/impl/query/batch/schnorr.ipp create mode 100644 include/bitcoin/database/impl/query/batch/silent.ipp diff --git a/Makefile.am b/Makefile.am index 0104e2e9c..fa603f7c6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -218,7 +218,6 @@ include_bitcoin_database_impl_query_HEADERS = \ include/bitcoin/database/impl/query/properties_tx.ipp \ include/bitcoin/database/impl/query/query.ipp \ include/bitcoin/database/impl/query/sequences.ipp \ - include/bitcoin/database/impl/query/signatures.ipp \ include/bitcoin/database/impl/query/sizes.ipp include_bitcoin_database_impl_query_addressdir = ${includedir}/bitcoin/database/impl/query/address @@ -235,6 +234,12 @@ include_bitcoin_database_impl_query_archive_HEADERS = \ include/bitcoin/database/impl/query/archive/wire_reader.ipp \ include/bitcoin/database/impl/query/archive/wire_writer.ipp +include_bitcoin_database_impl_query_batchdir = ${includedir}/bitcoin/database/impl/query/batch +include_bitcoin_database_impl_query_batch_HEADERS = \ + include/bitcoin/database/impl/query/batch/ecdsa.ipp \ + include/bitcoin/database/impl/query/batch/schnorr.ipp \ + include/bitcoin/database/impl/query/batch/silent.ipp + include_bitcoin_database_impl_query_consensusdir = ${includedir}/bitcoin/database/impl/query/consensus include_bitcoin_database_impl_query_consensus_HEADERS = \ include/bitcoin/database/impl/query/consensus/consensus_block.ipp \ diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj index c0061fcaa..d317d58b9 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj @@ -237,6 +237,9 @@ + + + @@ -264,7 +267,6 @@ - diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters index 91698b98c..bf1441636 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -37,63 +37,66 @@ {62D7FBEE-4D52-424A-0000-000000000009} - + {62D7FBEE-4D52-424A-0000-00000000000A} - + {62D7FBEE-4D52-424A-0000-00000000000B} - + {62D7FBEE-4D52-424A-0000-00000000000C} - + {62D7FBEE-4D52-424A-0000-00000000000D} - + {62D7FBEE-4D52-424A-0000-00000000000E} - + {62D7FBEE-4D52-424A-0000-00000000000F} - + {62D7FBEE-4D52-424A-0000-000000000001} - + {62D7FBEE-4D52-424A-0000-000000000002} - + {62D7FBEE-4D52-424A-0000-000000000003} - + {62D7FBEE-4D52-424A-0000-000000000004} - + {62D7FBEE-4D52-424A-0000-000000000005} - + {62D7FBEE-4D52-424A-0000-000000000006} - + {62D7FBEE-4D52-424A-0000-000000000007} - + {62D7FBEE-4D52-424A-0000-000000000008} - + {62D7FBEE-4D52-424A-0000-000000000009} - + {62D7FBEE-4D52-424A-0000-000000000010} - + {62D7FBEE-4D52-424A-0000-0000000000A1} - + {62D7FBEE-4D52-424A-0000-0000000000B1} - + {62D7FBEE-4D52-424A-0000-0000000000C1} + + {62D7FBEE-4D52-424A-0000-0000000000D1} + @@ -424,6 +427,15 @@ include\bitcoin\database\impl\query\archive + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + include\bitcoin\database\impl\query @@ -505,9 +517,6 @@ include\bitcoin\database\impl\query - - include\bitcoin\database\impl\query - include\bitcoin\database\impl\query diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj index 97d06d361..61f99c7a8 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj @@ -237,6 +237,9 @@ + + + @@ -264,7 +267,6 @@ - diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters index 91698b98c..bf1441636 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -37,63 +37,66 @@ {62D7FBEE-4D52-424A-0000-000000000009} - + {62D7FBEE-4D52-424A-0000-00000000000A} - + {62D7FBEE-4D52-424A-0000-00000000000B} - + {62D7FBEE-4D52-424A-0000-00000000000C} - + {62D7FBEE-4D52-424A-0000-00000000000D} - + {62D7FBEE-4D52-424A-0000-00000000000E} - + {62D7FBEE-4D52-424A-0000-00000000000F} - + {62D7FBEE-4D52-424A-0000-000000000001} - + {62D7FBEE-4D52-424A-0000-000000000002} - + {62D7FBEE-4D52-424A-0000-000000000003} - + {62D7FBEE-4D52-424A-0000-000000000004} - + {62D7FBEE-4D52-424A-0000-000000000005} - + {62D7FBEE-4D52-424A-0000-000000000006} - + {62D7FBEE-4D52-424A-0000-000000000007} - + {62D7FBEE-4D52-424A-0000-000000000008} - + {62D7FBEE-4D52-424A-0000-000000000009} - + {62D7FBEE-4D52-424A-0000-000000000010} - + {62D7FBEE-4D52-424A-0000-0000000000A1} - + {62D7FBEE-4D52-424A-0000-0000000000B1} - + {62D7FBEE-4D52-424A-0000-0000000000C1} + + {62D7FBEE-4D52-424A-0000-0000000000D1} + @@ -424,6 +427,15 @@ include\bitcoin\database\impl\query\archive + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + include\bitcoin\database\impl\query @@ -505,9 +517,6 @@ include\bitcoin\database\impl\query - - include\bitcoin\database\impl\query - include\bitcoin\database\impl\query diff --git a/include/bitcoin/database/impl/query/signatures.ipp b/include/bitcoin/database/impl/query/batch/ecdsa.ipp similarity index 60% rename from include/bitcoin/database/impl/query/signatures.ipp rename to include/bitcoin/database/impl/query/batch/ecdsa.ipp index 23d4a1df5..8494639a4 100644 --- a/include/bitcoin/database/impl/query/signatures.ipp +++ b/include/bitcoin/database/impl/query/batch/ecdsa.ipp @@ -16,8 +16,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#ifndef LIBBITCOIN_DATABASE_QUERY_SIGNATURES_IPP -#define LIBBITCOIN_DATABASE_QUERY_SIGNATURES_IPP +#ifndef LIBBITCOIN_DATABASE_QUERY_BATCH_ECDSA_IPP +#define LIBBITCOIN_DATABASE_QUERY_BATCH_ECDSA_IPP #include #include @@ -38,19 +38,6 @@ bool CLASS::verify_ecdsa_signatures(const stopper& cancel, return !cancel; } -TEMPLATE -bool CLASS::verify_schnorr_signatures(const stopper& cancel, - header_links& links) NOEXCEPT -{ - // False return only implies canceled. - using batch = system::schnorr::batch; - const auto count = store_.schnorr.count().value; - const auto ptr = store_.schnorr.get_memory(); - const auto rows = system::pointer_cast(ptr->data()); - links = batch::verify(cancel, { rows, count }); - return !cancel; -} - // setters // ---------------------------------------------------------------------------- @@ -63,15 +50,6 @@ bool CLASS::purge_ecdsa_signatures() NOEXCEPT // ======================================================================== } -TEMPLATE -bool CLASS::purge_schnorr_signatures() NOEXCEPT -{ - // ======================================================================== - const auto scope = store_.get_transactor(); - return store_.schnorr.truncate(0); - // ======================================================================== -} - TEMPLATE bool CLASS::set_signature(const hash_digest& digest, const ec_compressed& point, const ec_signature& signature, uint16_t id, const header_link& link) NOEXCEPT @@ -92,26 +70,6 @@ bool CLASS::set_signature(const hash_digest& digest, const ec_compressed& point, // ======================================================================== } -TEMPLATE -bool CLASS::set_signature(const hash_digest& digest, const ec_xonly& point, - const ec_signature& signature, uint16_t id, const header_link& link) NOEXCEPT -{ - // ======================================================================== - const auto scope = store_.get_transactor(); - - // Clean single allocation failure (e.g. disk full). - return store_.schnorr.put(table::schnorr::put_single_ref - { - {}, - digest, - point, - signature, - id, - link - }); - // ======================================================================== -} - TEMPLATE bool CLASS::set_signatures(const hash_digest& digest, const ec_compresseds& keys, const ec_signatures& sigs, uint16_t id, @@ -133,24 +91,6 @@ bool CLASS::set_signatures(const hash_digest& digest, // ======================================================================== } -TEMPLATE -bool CLASS::set_signatures(const threshold& batch, uint16_t id, - const header_link& link) NOEXCEPT -{ - // ======================================================================== - const auto scope = store_.get_transactor(); - - // Clean single allocation failure (e.g. disk full). - return store_.schnorr.put(table::schnorr::put_multiple_ref - { - {}, - batch, - id, - link - }); - // ======================================================================== -} - } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/query/batch/schnorr.ipp b/include/bitcoin/database/impl/query/batch/schnorr.ipp new file mode 100644 index 000000000..c165b55ad --- /dev/null +++ b/include/bitcoin/database/impl/query/batch/schnorr.ipp @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_QUERY_BATCH_SCHNORR_IPP +#define LIBBITCOIN_DATABASE_QUERY_BATCH_SCHNORR_IPP + +#include +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +bool CLASS::verify_schnorr_signatures(const stopper& cancel, + header_links& links) NOEXCEPT +{ + // False return only implies canceled. + using batch = system::schnorr::batch; + const auto count = store_.schnorr.count().value; + const auto ptr = store_.schnorr.get_memory(); + const auto rows = system::pointer_cast(ptr->data()); + links = batch::verify(cancel, { rows, count }); + return !cancel; +} + +// setters +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::purge_schnorr_signatures() NOEXCEPT +{ + // ======================================================================== + const auto scope = store_.get_transactor(); + return store_.schnorr.truncate(0); + // ======================================================================== +} + + +TEMPLATE +bool CLASS::set_signature(const hash_digest& digest, const ec_xonly& point, + const ec_signature& signature, uint16_t id, const header_link& link) NOEXCEPT +{ + // ======================================================================== + const auto scope = store_.get_transactor(); + + // Clean single allocation failure (e.g. disk full). + return store_.schnorr.put(table::schnorr::put_single_ref + { + {}, + digest, + point, + signature, + id, + link + }); + // ======================================================================== +} + +TEMPLATE +bool CLASS::set_signatures(const threshold& batch, uint16_t id, + const header_link& link) NOEXCEPT +{ + // ======================================================================== + const auto scope = store_.get_transactor(); + + // Clean single allocation failure (e.g. disk full). + return store_.schnorr.put(table::schnorr::put_multiple_ref + { + {}, + batch, + id, + link + }); + // ======================================================================== +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/query/batch/silent.ipp b/include/bitcoin/database/impl/query/batch/silent.ipp new file mode 100644 index 000000000..3fff7a61e --- /dev/null +++ b/include/bitcoin/database/impl/query/batch/silent.ipp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_QUERY_BATCH_SILENT_IPP +#define LIBBITCOIN_DATABASE_QUERY_BATCH_SILENT_IPP + +#include +#include + +namespace libbitcoin { +namespace database { + + +TEMPLATE +bool CLASS::scan_silent(const stopper& /*cancel*/, const ec_secret& /*scan_key*/, + const silent_handler& /*callback*/) NOEXCEPT +{ + // TODO: implement. + return {}; +} + +// setter +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::set_silent(const std::vector& prefixes, + const ec_compressed& compressed, const tx_link& link) NOEXCEPT +{ + // ======================================================================== + const auto scope = store_.get_transactor(); + + // Clean single allocation failure (e.g. disk full). + return store_.silent.put(table::silent::put_ref + { + {}, + prefixes, + compressed, + link + }); + // ======================================================================== +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 3df9ddaef..990a540ea 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -57,8 +57,12 @@ class query using ec_compressed = system::ec_compressed; using ec_signatures = system::ec_signatures; using ec_signature = system::ec_signature; + using ec_secret = system::ec_secret; using ec_xonly = system::ec_xonly; + /// Callback for long-running silent payment scan. + using silent_handler = std::function; + query(Store& store) NOEXCEPT; /// Store management from query-holder (not store owner) context. @@ -572,9 +576,13 @@ class query bool set_tx_connected(const tx_link& link, const context& ctx, uint64_t fee, size_t sigops) NOEXCEPT; - /// Signature Batching. + /// Batching. /// ----------------------------------------------------------------------- + /// Set silent payment rows. + bool set_silent(const std::vector& prefixes, + const ec_compressed& compressed, const tx_link& link) NOEXCEPT; + /// Set single ecdsa signature row. bool set_signature(const hash_digest& digest, const ec_compressed& point, const ec_signature& signature, uint16_t id, @@ -594,6 +602,13 @@ class query bool set_signatures(const threshold& batch, uint16_t id, const header_link& link) NOEXCEPT; + /// Scans the full silent payments table using the given scan private key. + /// Invokes callback for each candidate match with tx_link of match. Upon + /// cancellation or error returns associated code. Return indicates + /// completion of scan without store integrity error (cancel returns true). + bool scan_silent(const stopper& cancel, const ec_secret& scan_key, + const silent_handler& callback) NOEXCEPT; + /// Verify all signatures in table. bool verify_ecdsa_signatures(const stopper& cancel, header_links&) NOEXCEPT; bool verify_schnorr_signatures(const stopper& cancel, header_links&) NOEXCEPT; @@ -954,6 +969,10 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) #include #include +#include +#include +#include + #include #include #include @@ -984,7 +1003,6 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) #include #include #include -#include #include BC_POP_WARNING() diff --git a/include/bitcoin/database/tables/caches/silent.hpp b/include/bitcoin/database/tables/caches/silent.hpp index a549dfea9..ca1aa5922 100644 --- a/include/bitcoin/database/tables/caches/silent.hpp +++ b/include/bitcoin/database/tables/caches/silent.hpp @@ -73,7 +73,7 @@ struct silent tx::integer tx_fk{}; }; - struct records + struct put_ref : public schema::silent { inline link count() const NOEXCEPT @@ -98,8 +98,8 @@ struct silent return sink; } - const std::vector prefixes{}; - const system::ec_compressed compressed{}; + const std::vector& prefixes; + const system::ec_compressed& compressed; const tx::integer tx_fk{}; }; }; diff --git a/include/bitcoin/database/types/type.hpp b/include/bitcoin/database/types/type.hpp index 63a97f533..d0bbad305 100644 --- a/include/bitcoin/database/types/type.hpp +++ b/include/bitcoin/database/types/type.hpp @@ -43,6 +43,9 @@ using tx_link = table::transaction::link; using filter_link = table::filter_tx::link; using strong_link = table::strong_tx::link; using address_link = table::address::link; +using ecdsa_link = table::ecdsa_correlate::link; +using schnorr_link = table::schnorr_correlate::link; +using silent_link = table::silent_correlate::link; /// Multiples. using header_links = std::vector; From e2e3090210f16ea068fe27dff454be4436e16659 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 24 Jun 2026 21:42:16 -0400 Subject: [PATCH 2/3] Style, comments. --- .../bitcoin/database/impl/query/fee_rate.ipp | 23 +++++++++---------- .../bitcoin/database/impl/query/filters.ipp | 14 ++++++----- include/bitcoin/database/query.hpp | 9 +++----- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/include/bitcoin/database/impl/query/fee_rate.ipp b/include/bitcoin/database/impl/query/fee_rate.ipp index 44bb923b4..f50f894f6 100644 --- a/include/bitcoin/database/impl/query/fee_rate.ipp +++ b/include/bitcoin/database/impl/query/fee_rate.ipp @@ -81,22 +81,21 @@ bool CLASS::get_branch_fees(const stopper& cancel, fee_rate_sets& out, out.resize(count); stopper fail{}; - std::vector offsets(count); - std::iota(offsets.begin(), offsets.end(), zero); + std::vector it(count); + std::iota(it.begin(), it.end(), zero); constexpr auto parallel = poolstl::execution::par; constexpr auto relaxed = std::memory_order_relaxed; // Parallel execution saves ~50%. - std::for_each(parallel, offsets.cbegin(), offsets.cend(), - [&](size_t offset) NOEXCEPT - { - if (fail.load(relaxed)) - return; - - if (cancel.load(relaxed) || !get_block_fees(out.at(offset), - to_confirmed(start + offset))) - fail.store(true, relaxed); - }); + std::for_each(parallel, it.cbegin(), it.cend(), [&](size_t offset) NOEXCEPT + { + if (fail.load(relaxed)) + return; + + if (cancel.load(relaxed) || !get_block_fees(out.at(offset), + to_confirmed(start + offset))) + fail.store(true, relaxed); + }); const auto failed = fail.load(relaxed); if (failed) out.clear(); diff --git a/include/bitcoin/database/impl/query/filters.ipp b/include/bitcoin/database/impl/query/filters.ipp index 6ed785413..09728b246 100644 --- a/include/bitcoin/database/impl/query/filters.ipp +++ b/include/bitcoin/database/impl/query/filters.ipp @@ -37,7 +37,7 @@ bool CLASS::is_filtered_body(const header_link& link) const NOEXCEPT return store_.filter_tx.exists(to_filter_tx(link)); } -// node/fitler-out +// node/filter-out TEMPLATE bool CLASS::get_filter_body(filter& out, const header_link& link) const NOEXCEPT { @@ -58,7 +58,7 @@ bool CLASS::is_filtered_head(const header_link& link) const NOEXCEPT return store_.filter_bk.exists(to_filter_bk(link)); } -// node/fitler-out +// node/filter-out TEMPLATE bool CLASS::get_filter_head(hash_digest& out, const header_link& link) const NOEXCEPT @@ -71,7 +71,7 @@ bool CLASS::get_filter_head(hash_digest& out, return true; } -// node/fitler-out +// node/filter-out TEMPLATE bool CLASS::get_filter_hash(hash_digest& out, const header_link& link) const NOEXCEPT @@ -84,7 +84,7 @@ bool CLASS::get_filter_hash(hash_digest& out, return true; } -// node/fitler-out +// node/filter-out TEMPLATE bool CLASS::get_filter_hashes(hashes& filter_hashes, hash_digest& previous_header, const header_link& stop_link, @@ -98,7 +98,7 @@ bool CLASS::get_filter_hashes(hashes& filter_hashes, filter_hashes.resize(count); auto link = stop_link; - // Reversal allows ancenstry population into forward vector. + // Reversal allows ancestry population into forward vector. for (auto& hash: std::views::reverse(filter_hashes)) { // Implies that stop_link is not a filtered block. @@ -124,13 +124,15 @@ bool CLASS::get_filter_hashes(hashes& filter_hashes, return get_filter_head(previous_header, link); } -// node/fitler-out +// node/filter-out TEMPLATE bool CLASS::get_filter_heads(hashes& filter_heads, size_t stop_height, size_t interval) const NOEXCEPT { size_t height{}; filter_heads.resize(system::floored_divide(stop_height, interval)); + + // TODO: could be parallel. for (auto& head: filter_heads) if (!get_filter_head(head, to_confirmed((height += interval)))) return false; diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 990a540ea..c2db1a6d6 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -602,14 +602,11 @@ class query bool set_signatures(const threshold& batch, uint16_t id, const header_link& link) NOEXCEPT; - /// Scans the full silent payments table using the given scan private key. - /// Invokes callback for each candidate match with tx_link of match. Upon - /// cancellation or error returns associated code. Return indicates - /// completion of scan without store integrity error (cancel returns true). - bool scan_silent(const stopper& cancel, const ec_secret& scan_key, + /// Invoke callback for each candidate match, false implies cancel. + bool scan_silent(const stopper& cancel, const ec_compressed& scan_key, const silent_handler& callback) NOEXCEPT; - /// Verify all signatures in table. + /// Verify all signatures in table, false implies cancel. bool verify_ecdsa_signatures(const stopper& cancel, header_links&) NOEXCEPT; bool verify_schnorr_signatures(const stopper& cancel, header_links&) NOEXCEPT; From 21b5850bdde50ce205871b9753084bdb960e262b Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 24 Jun 2026 21:43:32 -0400 Subject: [PATCH 3/3] Refine set_silent based on system::silent::batch. --- .../database/impl/query/batch/silent.ipp | 66 ++++++++++++++++--- include/bitcoin/database/query.hpp | 9 +-- include/bitcoin/database/types/type.hpp | 1 + 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/include/bitcoin/database/impl/query/batch/silent.ipp b/include/bitcoin/database/impl/query/batch/silent.ipp index 3fff7a61e..ffd7941d4 100644 --- a/include/bitcoin/database/impl/query/batch/silent.ipp +++ b/include/bitcoin/database/impl/query/batch/silent.ipp @@ -25,22 +25,68 @@ namespace libbitcoin { namespace database { - TEMPLATE -bool CLASS::scan_silent(const stopper& /*cancel*/, const ec_secret& /*scan_key*/, - const silent_handler& /*callback*/) NOEXCEPT +bool CLASS::scan_silent(const stopper& cancel, const ec_compressed& scan_key, + const silent_handler& callback) NOEXCEPT { - // TODO: implement. - return {}; + // False return only implies canceled. + using batch = system::silent::batch; + const auto count = store_.silent.count().value; + const auto ptr = store_.silent.get_memory(); + const auto rows = system::pointer_cast(ptr->data()); + batch::scan(cancel, { rows, count }, scan_key, callback); + return !cancel; } -// setter +// setters // ---------------------------------------------------------------------------- +// Caller (node) controls which txs are indexed (e.g. by confirmed height). TEMPLATE -bool CLASS::set_silent(const std::vector& prefixes, - const ec_compressed& compressed, const tx_link& link) NOEXCEPT +bool CLASS::set_silent(const block& block, const header_link& link) NOEXCEPT { + const auto& txs = block.transactions_ptr(); + const auto count = txs->size(); + if (is_one(count)) + return true; + + const auto links = to_transactions(link); + if (links.size() != count) + return false; + + stopper fail{}; + std::vector it(sub1(count)); + std::iota(it.begin(), it.end(), one); + constexpr auto parallel = poolstl::execution::par; + constexpr auto relaxed = std::memory_order_relaxed; + + // TODO: parallel may or may not be optimal. + // TODO: alternatively could accumulate block results and write once. + std::for_each(parallel, it.cbegin(), it.cend(), [&](size_t index) NOEXCEPT + { + if (fail.load(relaxed)) + return; + + if (!set_silent(*txs->at(index), links.at(index))) + fail.store(true, relaxed); + }); + + return !fail.load(relaxed); +} + +TEMPLATE +bool CLASS::set_silent(const transaction& tx, const tx_link& link) NOEXCEPT +{ + BC_ASSERT(!tx.is_coinbase()); + if (link.is_terminal()) + return false; + + // Short-circuits with success on empty. + ////using namespace system::wallet; + ////silent_payment::scan_record record{}; + ////if (!silent_payment::compute_scan_record(record, tx)) + //// return true; + // ======================================================================== const auto scope = store_.get_transactor(); @@ -48,8 +94,8 @@ bool CLASS::set_silent(const std::vector& prefixes, return store_.silent.put(table::silent::put_ref { {}, - prefixes, - compressed, + {}, ////record.prefixes, + {}, ////record.summary, link }); // ======================================================================== diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index c2db1a6d6..4f4bf2d27 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -60,9 +60,6 @@ class query using ec_secret = system::ec_secret; using ec_xonly = system::ec_xonly; - /// Callback for long-running silent payment scan. - using silent_handler = std::function; - query(Store& store) NOEXCEPT; /// Store management from query-holder (not store owner) context. @@ -579,9 +576,9 @@ class query /// Batching. /// ----------------------------------------------------------------------- - /// Set silent payment rows. - bool set_silent(const std::vector& prefixes, - const ec_compressed& compressed, const tx_link& link) NOEXCEPT; + /// Set silent payment records. + bool set_silent(const transaction& tx, const tx_link& link) NOEXCEPT; + bool set_silent(const block& block, const header_link& link) NOEXCEPT; /// Set single ecdsa signature row. bool set_signature(const hash_digest& digest, const ec_compressed& point, diff --git a/include/bitcoin/database/types/type.hpp b/include/bitcoin/database/types/type.hpp index d0bbad305..3efe8c622 100644 --- a/include/bitcoin/database/types/type.hpp +++ b/include/bitcoin/database/types/type.hpp @@ -68,6 +68,7 @@ using hash_option = std::optional; using filter = system::data_chunk; using data_chunk = system::data_chunk; +using silent_handler = system::silent::batch::handler; /// Common system::chain aliases. /// ---------------------------------------------------------------------------