From d6058c8ecd9b3d9ecad868e2c1ad498a9cb1324a Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 19 Jun 2026 03:08:41 -0400 Subject: [PATCH] Fix batch residual and accelerate cancellation. --- .../bitcoin/node/chasers/chaser_validate.hpp | 10 +++--- src/chasers/chaser_validate.cpp | 33 +++++++++++-------- src/chasers/chaser_validate_batch.cpp | 9 ++--- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index 369904c9..046a2a34 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -67,14 +67,14 @@ class BCN_API chaser_validate /// Validation. virtual void post_block(const header_link& link, bool bypass) NOEXCEPT; virtual void validate_block(const header_link& link, bool bypass) NOEXCEPT; - virtual code validate(bool& batched, bool& faulted, bool bypass, - const system::chain::block& block, const header_link& link, - const system::chain::context& ctx) NOEXCEPT; + virtual code validate(bool& batched, bool& faulted, bool& capturing, + bool bypass, const system::chain::block& block, + const header_link& link, const system::chain::context& ctx) NOEXCEPT; virtual code populate(bool bypass, const system::chain::block& block, const system::chain::context& ctx) NOEXCEPT; virtual void complete_block(const code& ec, const header_link& link, size_t height, bool bypass, bool batched=false, - bool faulted=false) NOEXCEPT; + bool faulted=false, bool capturing=false) NOEXCEPT; virtual void notify_block(const code& ec, size_t height, const header_link& link, bool bypass) NOEXCEPT; @@ -92,8 +92,6 @@ class BCN_API chaser_validate private: static constexpr auto relaxed = std::memory_order_relaxed; - using shared_lock = const std::shared_lock; - using shared_lock_cptr = std::shared_ptr; using atomic_counter = std::atomic; using atomic_counter_ptr = std::shared_ptr; using threshold_group = signatures::threshold_group; diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 6574d57f..fa1d9899 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -219,7 +219,7 @@ void chaser_validate::validate_block(const header_link& link, code ec{}; chain::context ctx{}; - bool batched{}, faulted{}; + bool batched{}, faulted{}, enabled{}; auto& query = archive(); // TODO: implement allocator parameter resulting in full allocation to @@ -239,13 +239,13 @@ void chaser_validate::validate_block(const header_link& link, if (!query.set_block_unconfirmable(link)) ec = error::validate4; } - else if ((ec = validate(batched, faulted, bypass, *block, link, ctx))) + else if ((ec = validate(batched, faulted, enabled, bypass, *block, link, ctx))) { if (!query.set_block_unconfirmable(link)) ec = error::validate5; } - complete_block(ec, link, ctx.height, bypass, batched, faulted); + complete_block(ec, link, ctx.height, bypass, batched, faulted, enabled); // Prevent stall by posting internal event, avoiding external handlers. if (is_one(backlog_.fetch_sub(one, relaxed))) @@ -279,8 +279,8 @@ code chaser_validate::populate(bool bypass, const chain::block& block, return error::success; } -code chaser_validate::validate(bool& batched, bool& faulted, bool bypass, - const chain::block& block, const header_link& link, +code chaser_validate::validate(bool& batched, bool& faulted, bool& capturing, + bool bypass, const chain::block& block, const header_link& link, const chain::context& ctx) NOEXCEPT { auto& query = archive(); @@ -294,13 +294,17 @@ code chaser_validate::validate(bool& batched, bool& faulted, bool bypass, if ((ec = block.accept(ctx, subsidy_interval_, initial_subsidy_))) return ec; + // Initialize block capture. + const auto capture = get_capture(link); + + // Signature capture is enabled. + capturing = capture.enabled; + // This critical section is mutually-exclusive with batch verification. // ==================================================================== { - const std::shared_lock lock{ mutex_ }; - - // Initialize block capture. - const auto capture = get_capture(link); + std::shared_lock lock{ mutex_, std::defer_lock }; + if (capturing) lock.lock(); // Sequentially connect block with signature capture (if enabled). // There is not stop during connect, so shutdown will wait on the @@ -314,8 +318,8 @@ code chaser_validate::validate(bool& batched, bool& faulted, bool bypass, // Threshold batch commit failed, block otherwise passed (retry). faulted = capture.faulted; - // ================================================================ } + // ================================================================ // Prevouts optimize confirmation. // Block will be retried if batch is faulted. @@ -337,7 +341,8 @@ code chaser_validate::validate(bool& batched, bool& faulted, bool bypass, // May be either concurrent or stranded. void chaser_validate::complete_block(const code& ec, const header_link& link, - size_t height, bool bypass, bool batched, bool faulted) NOEXCEPT + size_t height, bool bypass, bool batched, bool faulted, + bool capturing) NOEXCEPT { // Node errors are fatal (or disk full recoverable). if (ec && node::error::error_category::contains(ec)) @@ -379,9 +384,9 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, // Not failed/invalid/batched/faulted, so block is complete (maybe valid). notify_block({}, height, link, bypass); - // Batch enabled not bypassed implies that the block is current and was - // not batched. Each such block triggers residual batch processing. - if (!bypass && batch_enabled_) + // Batch enabled not bypassed implies that the block is current and not + // batched. Each such block triggers residual batch processing (no push). + if (batch_enabled_ && !stopping_ && !capturing && !bypass) { POST(process_batch, true); } diff --git a/src/chasers/chaser_validate_batch.cpp b/src/chasers/chaser_validate_batch.cpp index 7cb50466..a397a7c0 100644 --- a/src/chasers/chaser_validate_batch.cpp +++ b/src/chasers/chaser_validate_batch.cpp @@ -18,8 +18,8 @@ */ #include -#include -#include +#include +#include #include namespace libbitcoin { @@ -70,9 +70,9 @@ void chaser_validate::process_batch(bool residual) NOEXCEPT // Test outside of lock to prevent reader contention for nearly all calls. auto& query = archive(); - if (!residual && + if (stopping_ || (!residual && (query.ecdsa_records() < batch_target_) && - (query.schnorr_records() < batch_target_)) + (query.schnorr_records() < batch_target_))) return; // Unique lock prevents batch table updates during evaluation, allowing the @@ -82,6 +82,7 @@ void chaser_validate::process_batch(bool residual) NOEXCEPT const std::unique_lock lock{ mutex_ }; // Must retest inside the lock as table updates are running concurrently. + if (stopping_) return; const auto ecdsa = query.ecdsa_records(); const auto schnorr = query.schnorr_records(); if (!residual && (ecdsa < batch_target_) && (schnorr < batch_target_))