From 1058c0b9e3667166c1b98513038fd022ea87698c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 10 Jun 2026 19:19:51 -0400 Subject: [PATCH 1/4] Log text. --- src/full_node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/full_node.cpp b/src/full_node.cpp index b278bae3..b610de7a 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -284,7 +284,7 @@ void full_node::fault(const code& ec) NOEXCEPT } else if (query_.is_fault()) { - LOGF("Store faulted '" << query_.get_fault().message() + LOGF("Store fault (unrecoverable) '" << query_.get_fault().message() << "' following, " << ec.message()); } else if (ec) From ae815177dc24fb4c3f0c1e8e4410d134f012adb6 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 10 Jun 2026 19:48:46 -0400 Subject: [PATCH 2/4] Add error::capture_fault code. --- include/bitcoin/node/error.hpp | 3 ++- src/error.cpp | 3 ++- test/error.cpp | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index 065e6c94..e3004567 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -107,7 +107,8 @@ enum error_t : uint8_t estimates_push1, estimates_push2, estimates_pop1, - estimates_pop2 + estimates_pop2, + capture_fault }; // No current need for error_code equivalence mapping. diff --git a/src/error.cpp b/src/error.cpp index 894f823d..6746ca2d 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -97,7 +97,8 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { estimates_push1, "estimates_push1" }, { estimates_push2, "estimates_push2" }, { estimates_pop1, "estimates_pop1" }, - { estimates_pop2, "estimates_pop2" } + { estimates_pop2, "estimates_pop2" }, + { capture_fault, "capture_fault" } }; DEFINE_ERROR_T_CATEGORY(error, "node", "node code") diff --git a/test/error.cpp b/test/error.cpp index 52539775..ff94bc49 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -297,4 +297,13 @@ BOOST_AUTO_TEST_CASE(error_t__code__estimates_pop2__true_expected_message) BOOST_REQUIRE_EQUAL(ec.message(), "estimates_pop2"); } +BOOST_AUTO_TEST_CASE(error_t__code__capture_fault__true_expected_message) +{ + constexpr auto value = error::capture_fault; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "capture_fault"); +} + BOOST_AUTO_TEST_SUITE_END() From b1faf226923d2198550801c2ce746c001a3f604a Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 10 Jun 2026 19:59:00 -0400 Subject: [PATCH 3/4] Document and stub in batching disk full recovery design. --- src/chasers/chaser_validate.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index fc20dde8..f81981ed 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -300,6 +300,7 @@ code chaser_validate::validate(bool bypass, const chain::block& block, { using namespace chain; std::atomic set{}; + code capture_ec{}; const auto to_events = [](opcode op) NOEXCEPT { @@ -411,13 +412,30 @@ code chaser_validate::validate(bool bypass, const chain::block& block, // Sets condition to opcode::checksig for all required. threshold_ += group.entries.size(); fire(to_events(group.condition), ctx.height); - return query.set_signatures(group, set, link); + + // Script always processing proceeds as if batch succeeded. + if (!query.set_signatures(group, set, link)) + capture_ec = fault(error::capture_fault); } }; if ((ec = block.connect(ctx, capture))) return ec; + // TODO: capture the fault ec from closure (with block link). + // TODO: return failure here with dedicated ec (batch write fail). + // TODO: if was threshold then block must be resubmitted to queue. + // TODO: for others there is no special action. In all cases must + // TODO: regard a validation failure here (unconfirmable) and when + // TODO: batching, must defer setting valid state until batch post + // TODO: processing. chaser startup must drain the batch tables. + if (capture_ec) + { + // TODO: repost block (link) to work queue in complete_block + // TODO: based on error::capture_fault. + return capture_ec; + } + const auto log_capture = [&](std::string_view name, size_t captured, size_t missed) NOEXCEPT { @@ -463,6 +481,7 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, // Node errors are fatal. if (node::error::error_category::contains(ec)) { + // fault(ec) initiates recovery if caused by disk full condition. LOGF("Fault validating [" << height << "] " << ec.message()); fault(ec); return; From 9a7fb4e96c6752c73d2eb4667c4c96c7fbc82f8c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 10 Jun 2026 22:26:39 -0400 Subject: [PATCH 4/4] Disable script level capture event dispatch (perf). --- src/chasers/chaser_validate.cpp | 36 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index f81981ed..2ec34d94 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -362,19 +362,19 @@ code chaser_validate::validate(bool bypass, const chain::block& block, { case signatures::miss::ecdsa: ++missed_ecdsa_; - fire(events::missed_ecdsa, ctx.height); + ////fire(events::missed_ecdsa, ctx.height); break; case signatures::miss::multisig: ++missed_multisig_; - fire(events::missed_multisig, ctx.height); + ////fire(events::missed_multisig, ctx.height); break; case signatures::miss::schnorr: ++missed_schnorr_; - fire(events::missed_schnorr, ctx.height); + ////fire(events::missed_schnorr, ctx.height); break; case signatures::miss::overflow: ++missed_threshold_; - fire(events::missed_overflow, ctx.height); + ////fire(events::missed_overflow, ctx.height); break; // should be no path to this. @@ -387,14 +387,14 @@ code chaser_validate::validate(bool bypass, const chain::block& block, const ec_signature& sign) NOEXCEPT { ++ecdsa_; - fire(to_events(opcode::checksigverify), ctx.height); + ////fire(to_events(opcode::checksigverify), ctx.height); return query.set_signature(digest, point, sign, link); }, .schnorr = [&](const hash_digest& digest, const ec_xonly& point, const ec_signature& sign) NOEXCEPT { ++schnorr_; - fire(to_events(opcode::checksigadd), ctx.height); + ////fire(to_events(opcode::checksigadd), ctx.height); return query.set_signature(digest, point, sign, link); }, .multisig = [&](const hash_digest& digest, @@ -403,7 +403,7 @@ code chaser_validate::validate(bool bypass, const chain::block& block, { BC_ASSERT(points.size() == signs.size()); multisig_ += points.size(); - fire(to_events(opcode::checkmultisigverify), ctx.height); + ////fire(to_events(opcode::checkmultisigverify), ctx.height); return query.set_signatures(digest, points, signs, set, link); }, .threshold = [&]( @@ -411,7 +411,7 @@ code chaser_validate::validate(bool bypass, const chain::block& block, { // Sets condition to opcode::checksig for all required. threshold_ += group.entries.size(); - fire(to_events(group.condition), ctx.height); + ////fire(to_events(group.condition), ctx.height); // Script always processing proceeds as if batch succeeded. if (!query.set_signatures(group, set, link)) @@ -419,29 +419,21 @@ code chaser_validate::validate(bool bypass, const chain::block& block, } }; + // Prioritize validation failure over capture failure. if ((ec = block.connect(ctx, capture))) return ec; - // TODO: capture the fault ec from closure (with block link). - // TODO: return failure here with dedicated ec (batch write fail). - // TODO: if was threshold then block must be resubmitted to queue. - // TODO: for others there is no special action. In all cases must - // TODO: regard a validation failure here (unconfirmable) and when - // TODO: batching, must defer setting valid state until batch post - // TODO: processing. chaser startup must drain the batch tables. + // TODO: repost block (link) to work queue in complete_block + // TODO: based on error::capture_fault. if (capture_ec) - { - // TODO: repost block (link) to work queue in complete_block - // TODO: based on error::capture_fault. return capture_ec; - } - const auto log_capture = [&](std::string_view name, size_t captured, - size_t missed) NOEXCEPT + const auto log_capture = [&](std::string_view name, + size_t captured, size_t missed) NOEXCEPT { if (!to_bool(captured) && !to_bool(missed)) return; const auto ratio = (100.0f * captured) / (captured + missed); - const auto rate = (boost_format("%.2f") % ratio).str(); + const auto rate = (boost_format("%.4f") % ratio).str(); LOGA("Efficiency " << name << rate << "% = " << captured << "/(" << captured << "+" << missed << ")"); };