From e403010ad8293ddfaf04c2f410ede75476a61390 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 2 Jun 2026 01:09:34 -0400 Subject: [PATCH 1/3] Add batch_signatures setting. --- include/bitcoin/node/settings.hpp | 1 + src/settings.cpp | 1 + test/settings.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/include/bitcoin/node/settings.hpp b/include/bitcoin/node/settings.hpp index dd887844..ae6ff022 100644 --- a/include/bitcoin/node/settings.hpp +++ b/include/bitcoin/node/settings.hpp @@ -39,6 +39,7 @@ class BCN_API settings bool thread_priority; bool memory_priority; bool allow_overlapped; + bool batch_signatures; bool defer_validation; bool defer_confirmation; float allowed_deviation; diff --git a/src/settings.cpp b/src/settings.cpp index 2066d5d5..02bbd06e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -35,6 +35,7 @@ settings::settings() NOEXCEPT memory_priority{ true }, thread_priority{ true }, allow_overlapped{ true }, + batch_signatures{ true }, defer_validation{ false }, defer_confirmation{ false }, minimum_fee_rate{ 0.0 }, diff --git a/test/settings.cpp b/test/settings.cpp index 57b44143..38739f21 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -35,6 +35,7 @@ BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) BOOST_REQUIRE_EQUAL(node.memory_priority, true); BOOST_REQUIRE_EQUAL(node.thread_priority, true); BOOST_REQUIRE_EQUAL(node.allow_overlapped, true); + BOOST_REQUIRE_EQUAL(node.batch_signatures, true); BOOST_REQUIRE_EQUAL(node.defer_validation, false); BOOST_REQUIRE_EQUAL(node.defer_confirmation, false); BOOST_REQUIRE_EQUAL(node.minimum_fee_rate, 0.0); From a30d50986075008ce6add521a9e81933dc26312e Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 3 Jun 2026 00:42:55 -0400 Subject: [PATCH 2/3] Stub in batched signatures. --- .../bitcoin/node/chasers/chaser_validate.hpp | 7 +++ src/chasers/chaser_validate.cpp | 48 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index 5ba698d2..6968f362 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -82,11 +82,18 @@ class BCN_API chaser_validate network::threadpool validation_threadpool_; // These are thread safe. + std::atomic batched_ecdsa_{}; + std::atomic unbatched_ecdsa_{}; + std::atomic batched_schnorr_{}; + std::atomic unbatched_schnorr_{}; + std::atomic batched_multisig_{}; + std::atomic unbatched_multisig_{}; std::atomic backlog_{}; network::asio::strand validation_strand_; const uint32_t subsidy_interval_; const uint64_t initial_subsidy_; const size_t maximum_backlog_; + const bool batch_signatures_; const bool node_witness_; const bool defer_; const bool filter_; diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index f73a19be..6d45bcc0 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -43,6 +43,7 @@ chaser_validate::chaser_validate(full_node& node) NOEXCEPT subsidy_interval_(node.system_settings().subsidy_interval_blocks), initial_subsidy_(node.system_settings().initial_subsidy()), maximum_backlog_(node.node_settings().maximum_concurrency_()), + batch_signatures_(node.node_settings().batch_signatures), node_witness_(node.network_settings().witness_node()), defer_(node.node_settings().defer_validation), filter_(!defer_ && node.archive().filter_enabled()) @@ -295,8 +296,51 @@ code chaser_validate::validate(bool bypass, const chain::block& block, if ((ec = block.accept(ctx, subsidy_interval_, initial_subsidy_))) return ec; - if ((ec = block.connect(ctx))) - return ec; + if (batch_signatures_) + { + const chain::signatures capture + { + .ecdsa = [&](const hash_digest& , + const ec_compressed& , const ec_signature& ) NOEXCEPT + { + ////query.set_signature(digest, point, sign, link); + }, + + .schnorr = [&](const hash_digest& , + const ec_xonly& , const ec_signature& ) NOEXCEPT + { + ////query.set_signature(digest, point, sign, link); + }, + + .enabled = batch_signatures_ + }; + + if ((ec = block.connect(ctx, capture))) + return ec; + + batched_ecdsa_ += capture.batched_ecdsa; + unbatched_ecdsa_ += capture.unbatched_ecdsa; + batched_schnorr_ += capture.batched_schnorr; + unbatched_schnorr_ += capture.unbatched_schnorr; + batched_multisig_ += capture.batched_multisig; + unbatched_multisig_ += capture.unbatched_multisig; + { + LOGV("Bypass ecdsa " << batched_ecdsa_ << " / (" << batched_ecdsa_ << " + " << unbatched_ecdsa_ << ")"); + } + if (to_bool(batched_schnorr_.load()) || to_bool(unbatched_schnorr_.load())) + { + LOGV("Bypass schnorr " << batched_schnorr_ << " / (" << batched_schnorr_ << " + " << unbatched_schnorr_ << ")"); + } + if (to_bool(batched_multisig_.load()) || to_bool(unbatched_multisig_.load())) + { + LOGV("Bypass multisig " << batched_multisig_ << " / (" << batched_multisig_ << " + " << unbatched_multisig_ << ")"); + } + } + else + { + if ((ec = block.connect(ctx))) + return ec; + } // Prevouts optimize confirmation. if (!query.set_prevouts(link, block)) From 324821c5e775fb3565f65941bd28a8651f6dc16b Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 3 Jun 2026 19:21:56 -0400 Subject: [PATCH 3/3] Style, expose estimator::estimate_failed. --- include/bitcoin/node/estimator.hpp | 1 + src/estimator.cpp | 21 +++++++-------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/include/bitcoin/node/estimator.hpp b/include/bitcoin/node/estimator.hpp index 685bc10b..f849269a 100644 --- a/include/bitcoin/node/estimator.hpp +++ b/include/bitcoin/node/estimator.hpp @@ -37,6 +37,7 @@ class BCN_API estimator public: typedef std::unique_ptr ptr; static constexpr size_t maximum_horizon = 1008; + static constexpr size_t estimate_failed = max_uint64; DELETE_COPY_MOVE_DESTRUCT(estimator); diff --git a/src/estimator.cpp b/src/estimator.cpp index 039e9b5f..02394caf 100644 --- a/src/estimator.cpp +++ b/src/estimator.cpp @@ -34,11 +34,9 @@ using namespace system; uint64_t estimator::estimate(size_t target, mode mode) const NOEXCEPT { - // max_uint64 is failure sentinel (and unachievable/invalid as a fee). - auto estimate = max_uint64; constexpr size_t large = horizon::large; if (target >= large) - return estimate; + return estimate_failed; // Valid results are effectively limited to at least 1 sat/vb. // threshold_fee is thread safe but values are affected during update. @@ -46,13 +44,11 @@ uint64_t estimator::estimate(size_t target, mode mode) const NOEXCEPT { case mode::basic: { - estimate = compute(target, confidence::high); - break; + return compute(target, confidence::high); } case mode::geometric: { - estimate = compute(target, confidence::high, true); - break; + return compute(target, confidence::high, true); } case mode::economical: { @@ -62,8 +58,7 @@ uint64_t estimator::estimate(size_t target, mode mode) const NOEXCEPT const auto fee1 = compute(target1, confidence::low); const auto fee2 = compute(target2, confidence::mid); const auto fee3 = compute(target3, confidence::high); - estimate = std::max({ fee1, fee2, fee3 }); - break; + return std::max({ fee1, fee2, fee3 }); } case mode::conservative: { @@ -73,16 +68,14 @@ uint64_t estimator::estimate(size_t target, mode mode) const NOEXCEPT const auto fee1 = compute(target1, confidence::low); const auto fee2 = compute(target2, confidence::mid); const auto fee3 = compute(target3, confidence::high); - estimate = std::max({ fee1, fee2, fee3 }); - break; + return std::max({ fee1, fee2, fee3 }); } + default: case mode::unknown: { - break; + return estimate_failed; } } - - return estimate; } bool estimator::initialize(const std::atomic_bool& cancel, const query& query,