From ee37e0fd7ac078b894d435b9b63c73a94955fec4 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 May 2026 01:36:14 -0400 Subject: [PATCH 1/6] Use chase::block as fee estimator trigger, comments. --- include/bitcoin/node/chase.hpp | 6 +++--- src/chasers/chaser_estimate.cpp | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/include/bitcoin/node/chase.hpp b/include/bitcoin/node/chase.hpp index 557e3143..891be398 100644 --- a/include/bitcoin/node/chase.hpp +++ b/include/bitcoin/node/chase.hpp @@ -134,15 +134,15 @@ enum class chase /// ----------------------------------------------------------------------- /// A current block has been organized (header_t). - /// Issued by 'transaction' and handled by 'protocol_header/block_out'. + /// Issued by 'confirm' and handled by 'protocol_header/block_out/estimator'. block, /// A confirmable block has been confirmed (header_t). - /// Issued by 'confirm' [and handled by 'transaction']. + /// Issued by 'confirm' and handled by 'transaction'. organized, /// A previously confirmed block has been unconfirmed (header_t). - /// Issued by 'confirm' [and handled by 'transaction']. + /// Issued by 'confirm' and handled by 'transaction'. reorganized, /// Mining. diff --git a/src/chasers/chaser_estimate.cpp b/src/chasers/chaser_estimate.cpp index cbb9b0b5..59d52e81 100644 --- a/src/chasers/chaser_estimate.cpp +++ b/src/chasers/chaser_estimate.cpp @@ -113,12 +113,10 @@ bool chaser_estimate::handle_chase(const code&, chase event_, ////if (suspended()) //// return true; - if (!is_current(true)) - return true; - switch (event_) { - case chase::organized: + // chase::block is only sent when current (may need to file gaps). + case chase::block: { BC_ASSERT(std::holds_alternative(value)); POST(do_organized, std::get(value)); @@ -147,7 +145,8 @@ void chaser_estimate::do_organized(header_t) NOEXCEPT { BC_ASSERT(stranded()); - if (initialize()) + // TODO: make gap safe (get estimator top and adjust). + if (initialized() || initialize()) estimator_->push(archive()); } @@ -155,7 +154,8 @@ void chaser_estimate::do_reorganized(header_t) NOEXCEPT { BC_ASSERT(stranded()); - if (initialize()) + // TODO: make gap safe (get estimator top and adjust). + if (initialized()) estimator_->pop(archive()); } @@ -167,9 +167,6 @@ bool chaser_estimate::initialize() NOEXCEPT { BC_ASSERT(stranded()); - if (initialized()) - return true; - // Preempt initialize fault when horizon exceeds chain length. const auto horizon = node_settings().fee_estimate_horizon_(); if (horizon > add1(archive().get_top_confirmed())) From 25f52dbc6442127df3cfabe8ef65d0c2f182fd3e Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 May 2026 12:13:56 -0400 Subject: [PATCH 2/6] Fix estimator enable in chaser start(). --- src/chasers/chaser_estimate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chasers/chaser_estimate.cpp b/src/chasers/chaser_estimate.cpp index 59d52e81..93da183e 100644 --- a/src/chasers/chaser_estimate.cpp +++ b/src/chasers/chaser_estimate.cpp @@ -46,7 +46,7 @@ code chaser_estimate::start() NOEXCEPT { BC_ASSERT(stranded()); - if (is_zero(node_settings().fee_estimate_enabled())) + if (node_settings().fee_estimate_enabled()) { SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); } From 7673ef755ee0f2606ceb850565e4bdd8724ecb68 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 May 2026 12:56:53 -0400 Subject: [PATCH 3/6] Add estimate failure code. --- include/bitcoin/node/error.hpp | 1 + src/error.cpp | 1 + test/error.cpp | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index b9db21af..a4562997 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -98,6 +98,7 @@ enum error_t : uint8_t confirm10, confirm11, confirm12, + estimate_failed, estimates_failed, estimates_disabled, estimates_premature diff --git a/src/error.cpp b/src/error.cpp index 33fafa00..173935ea 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -88,6 +88,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { confirm10, "confirm10" }, { confirm11, "confirm11" }, { confirm12, "confirm12" }, + { estimate_failed, "estimate_failed" }, { estimates_failed, "estimates_failed" }, { estimates_disabled, "estimates_disabled" }, { estimates_premature, "estimates_premature" } diff --git a/test/error.cpp b/test/error.cpp index cb4699ba..9b385bbc 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -225,6 +225,15 @@ BOOST_AUTO_TEST_CASE(error_t__code__confirm1__true_expected_message) BOOST_REQUIRE_EQUAL(ec.message(), "confirm1"); } +BOOST_AUTO_TEST_CASE(error_t__code__estimate_failed__true_expected_message) +{ + constexpr auto value = error::estimate_failed; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "estimate_failed"); +} + BOOST_AUTO_TEST_CASE(error_t__code__estimates_failed__true_expected_message) { constexpr auto value = error::estimates_failed; From 8a6b003904448a9e278edffaa5e95d992f7f0705 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 May 2026 12:57:39 -0400 Subject: [PATCH 4/6] Handle estimator max_size sentinel, guard int64 cast. --- src/chasers/chaser_estimate.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/chasers/chaser_estimate.cpp b/src/chasers/chaser_estimate.cpp index 93da183e..c1bd521e 100644 --- a/src/chasers/chaser_estimate.cpp +++ b/src/chasers/chaser_estimate.cpp @@ -86,7 +86,13 @@ void chaser_estimate::do_estimate(size_t target, estimator::mode mode, const estimate_handler& handler) NOEXCEPT { BC_ASSERT(stranded()); - handler(error::success, estimator_->estimate(target, mode)); + + const auto value = estimator_->estimate(target, mode); + const auto ec = (value < to_unsigned(max_int64) ? error::success : + error::estimate_failed); + + // Successful value is always castable to int64_t. + handler(ec, value); } size_t chaser_estimate::top_height() const NOEXCEPT From 853d5eeef892798a6399bed6cd7fb9950d488e80 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 May 2026 13:15:47 -0400 Subject: [PATCH 5/6] Reorder estimator init for simpler testing. --- src/chasers/chaser_estimate.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/chasers/chaser_estimate.cpp b/src/chasers/chaser_estimate.cpp index c1bd521e..79e2fc6f 100644 --- a/src/chasers/chaser_estimate.cpp +++ b/src/chasers/chaser_estimate.cpp @@ -72,12 +72,6 @@ void chaser_estimate::estimate(size_t target, estimator::mode mode, return; } - if (!initialized()) - { - handler(error::estimates_premature, {}); - return; - } - POST(do_estimate, target, mode, std::move(handler)); } @@ -87,6 +81,13 @@ void chaser_estimate::do_estimate(size_t target, estimator::mode mode, { BC_ASSERT(stranded()); + // Check this under strand so that chase can initialize first. + if (!initialized()) + { + handler(error::estimates_premature, {}); + return; + } + const auto value = estimator_->estimate(target, mode); const auto ec = (value < to_unsigned(max_int64) ? error::success : error::estimate_failed); From 098679e4b13bd144cff87723d833ce2cdcdf7372 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 18 May 2026 14:29:19 -0400 Subject: [PATCH 6/6] Revert "Assert stranded() in chaser::start() overrides." --- include/bitcoin/node/chasers/chaser.hpp | 2 +- src/chasers/chaser_check.cpp | 1 - src/chasers/chaser_confirm.cpp | 1 - src/chasers/chaser_estimate.cpp | 2 -- src/chasers/chaser_header.cpp | 1 - src/chasers/chaser_snapshot.cpp | 2 -- src/chasers/chaser_storage.cpp | 2 -- src/chasers/chaser_template.cpp | 1 - src/chasers/chaser_transaction.cpp | 1 - 9 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index 6d9cab57..bea55dc7 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -39,7 +39,7 @@ class BCN_API chaser public: DELETE_COPY_MOVE_DESTRUCT(chaser); - /// Must be called from node strand. + /// Should be called from node strand. virtual code start() NOEXCEPT = 0; /// Override to capture non-blocking stopping. diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index 768ce4af..68e72f98 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -90,7 +90,6 @@ map_ptr chaser_check::split(const map_ptr& map) NOEXCEPT code chaser_check::start() NOEXCEPT { - BC_ASSERT(stranded()); start_tracking(); set_position(archive().get_fork()); requested_ = advanced_ = position(); diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index 56ee6a9e..d1fb50b7 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -42,7 +42,6 @@ chaser_confirm::chaser_confirm(full_node& node) NOEXCEPT code chaser_confirm::start() NOEXCEPT { - BC_ASSERT(stranded()); const auto& query = archive(); set_position(query.get_fork()); diff --git a/src/chasers/chaser_estimate.cpp b/src/chasers/chaser_estimate.cpp index 79e2fc6f..5573985d 100644 --- a/src/chasers/chaser_estimate.cpp +++ b/src/chasers/chaser_estimate.cpp @@ -44,8 +44,6 @@ chaser_estimate::chaser_estimate(full_node& node) NOEXCEPT code chaser_estimate::start() NOEXCEPT { - BC_ASSERT(stranded()); - if (node_settings().fee_estimate_enabled()) { SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index 97a78388..0b0da56c 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -35,7 +35,6 @@ chaser_header::chaser_header(full_node& node) NOEXCEPT code chaser_header::start() NOEXCEPT { - BC_ASSERT(stranded()); if (!initialize_milestone()) return fault(error::header1); diff --git a/src/chasers/chaser_snapshot.cpp b/src/chasers/chaser_snapshot.cpp index 01a487d4..9eefc134 100644 --- a/src/chasers/chaser_snapshot.cpp +++ b/src/chasers/chaser_snapshot.cpp @@ -52,8 +52,6 @@ chaser_snapshot::chaser_snapshot(full_node& node) NOEXCEPT code chaser_snapshot::start() NOEXCEPT { - BC_ASSERT(stranded()); - // Initial values assume all stops or starts are snapped. // get_top_validated is an expensive scan. diff --git a/src/chasers/chaser_storage.cpp b/src/chasers/chaser_storage.cpp index 306caa45..7abbd33d 100644 --- a/src/chasers/chaser_storage.cpp +++ b/src/chasers/chaser_storage.cpp @@ -47,8 +47,6 @@ chaser_storage::chaser_storage(full_node& node) NOEXCEPT code chaser_storage::start() NOEXCEPT { - BC_ASSERT(stranded()); - // Construct is too early to create the unstarted timer. disk_timer_ = std::make_shared(log, strand(), seconds{1}); diff --git a/src/chasers/chaser_template.cpp b/src/chasers/chaser_template.cpp index 80d45e03..b6aa2077 100644 --- a/src/chasers/chaser_template.cpp +++ b/src/chasers/chaser_template.cpp @@ -43,7 +43,6 @@ chaser_template::chaser_template(full_node& node) NOEXCEPT // TODO: initialize template state. code chaser_template::start() NOEXCEPT { - BC_ASSERT(stranded()); SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); return error::success; } diff --git a/src/chasers/chaser_transaction.cpp b/src/chasers/chaser_transaction.cpp index 347a5336..ee6a5fcb 100644 --- a/src/chasers/chaser_transaction.cpp +++ b/src/chasers/chaser_transaction.cpp @@ -43,7 +43,6 @@ chaser_transaction::chaser_transaction(full_node& node) NOEXCEPT // TODO: initialize tx graph from store, log and stop on error. code chaser_transaction::start() NOEXCEPT { - BC_ASSERT(stranded()); SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); return error::success; }