diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index bb18f05b..452b1b98 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -82,14 +82,17 @@ 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 batched_threshold_{}; - std::atomic unbatched_threshold_{}; + + std::atomic ecdsa_{}; + std::atomic schnorr_{}; + std::atomic multisig_{}; + std::atomic threshold_{}; + + std::atomic missed_ecdsa_{}; + std::atomic missed_schnorr_{}; + std::atomic missed_multisig_{}; + std::atomic missed_threshold_{}; + std::atomic backlog_{}; network::asio::strand validation_strand_; const uint32_t subsidy_interval_; diff --git a/include/bitcoin/node/events.hpp b/include/bitcoin/node/events.hpp index 76a07e4b..25ab537a 100644 --- a/include/bitcoin/node/events.hpp +++ b/include/bitcoin/node/events.hpp @@ -51,17 +51,39 @@ enum events : uint8_t block_reorganized, // block popped /// Mining. - template_issued, // block template issued for mining + template_issued, // block template issued for mining /// Timespans. - snapshot_secs, // snapshot timespan in seconds. - prune_msecs, // prune timespan in milliseconds. - reload_msecs, // store reload timespan in milliseconds. - block_usecs, // getblock timespan in microseconds. - ancestry_msecs, // getancestry timespan in milliseconds. - filter_msecs, // getfilter timespan in milliseconds. - filterhashes_msecs, // getfilterhashes timespan in milliseconds. - filterchecks_msecs // getcfcheckpt timespan in milliseconds. + snapshot_secs, // snapshot timespan in seconds. + prune_msecs, // prune timespan in milliseconds. + reload_msecs, // store reload timespan in milliseconds. + block_usecs, // getblock timespan in microseconds. + ancestry_msecs, // getancestry timespan in milliseconds. + filter_msecs, // getfilter timespan in milliseconds. + filterhashes_msecs, // getfilterhashes timespan in milliseconds. + filterchecks_msecs, // getcfcheckpt timespan in milliseconds. + + /// Batching (missed). + missed_ecdsa, // Failed to capture ecdsa signature. + missed_multisig, // Failed to capture ecdsa signatures. + missed_schnorr, // Failed to capture schnorr sig (single|multiple). + missed_overflow, // Failed to capture schnorr sigs (multiple >= 2^16). + + /// Batching (captured). + checksigverify, // ecdsa single (checksig/verify). + checkmultisigverify, // ecdsa multiple (checkmultisig/verify). + checksigadd, // schnorr single (op_checksigadd|op_checksig/verify). + checksig, // schnorr multiple (multisig). + numequal, // schnorr multiple (threshold). + numequalverify, // schnorr multiple (threshold). + numnotequal, // schnorr multiple (threshold). + lessthan, // schnorr multiple (threshold). + greaterthan, // schnorr multiple (threshold). + lessthanorequal, // schnorr multiple (threshold). + greaterthanorequal, // schnorr multiple (threshold). + within, // schnorr multiple (threshold). + + unknown }; } // namespace node diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 8c674e18..fc20dde8 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -285,7 +285,7 @@ code chaser_validate::validate(bool bypass, const chain::block& block, code ec{}; // Skips identity validation (performed in downloader). - // This can be performed in donwnloader as well but moving it here with + // This can be performed in downloader as well but moving it here with // more order than required allows the downloader to avoid block parse // which significantly reduces memory consumption and CPU during sync. // This slightly increases check() computation under full validation @@ -298,78 +298,140 @@ code chaser_validate::validate(bool bypass, const chain::block& block, if (batch_signatures_) { - const chain::signatures capture + using namespace chain; + std::atomic set{}; + + const auto to_events = [](opcode op) NOEXCEPT + { + switch (op) + { + // ecdsa single (checksig/verify). + case opcode::checksigverify: + return events::checksigverify; + + // ecdsa multiple (checkmultisig/verify). + case opcode::checkmultisigverify: + return events::checkmultisigverify; + + // schnorr single (op_checksigadd|op_checksig/verify). + case opcode::checksigadd: + return events::checksigadd; + + // schnorr multiple (multisig pattern). + case opcode::checksig: + return events::checksig; + + // schnorr multiple (is_threshold). + case opcode::numequal: + return events::numequal; + case opcode::numequalverify: + return events::numequalverify; + case opcode::numnotequal: + return events::numnotequal; + case opcode::lessthan: + return events::lessthan; + case opcode::greaterthan: + return events::greaterthan; + case opcode::lessthanorequal: + return events::lessthanorequal; + case opcode::greaterthanorequal: + return events::greaterthanorequal; + case opcode::within: + return events::within; + + // should be no path to this. + default: + return events::unknown; + } + }; + + const signatures capture { // Enable/disable capture. .enabled = batch_signatures_, - .ecdsa = [&](const hash_digest& , - const ec_compressed& , const ec_signature& ) NOEXCEPT + .log = [&](const script& missed) NOEXCEPT { - ////query.set_signature(digest, point, sign, link); + LOGA("Sigop @ " << ctx.height << " -> " + << missed.to_string(chain::flags::all_rules)); }, - .schnorr = [&](const hash_digest& , - const ec_xonly& , const ec_signature& ) NOEXCEPT + .fire = [&](signatures::miss miss) NOEXCEPT { - ////query.set_signature(digest, point, sign, link); + switch (miss) + { + case signatures::miss::ecdsa: + ++missed_ecdsa_; + fire(events::missed_ecdsa, ctx.height); + break; + case signatures::miss::multisig: + ++missed_multisig_; + fire(events::missed_multisig, ctx.height); + break; + case signatures::miss::schnorr: + ++missed_schnorr_; + fire(events::missed_schnorr, ctx.height); + break; + case signatures::miss::overflow: + ++missed_threshold_; + fire(events::missed_overflow, ctx.height); + break; + + // should be no path to this. + default: + BC_ASSERT_MSG(false, "unknown signatures::miss"); + } }, - .multisig = [&](const hash_digest& , - const ec_compresseds& , const ec_signatures& , - uint16_t ) NOEXCEPT + .ecdsa = [&](const hash_digest& digest, + const ec_compressed& point, + const ec_signature& sign) NOEXCEPT { - ////query.set_signatures(digest, points, signs, group, link); + ++ecdsa_; + fire(to_events(opcode::checksigverify), ctx.height); + return query.set_signature(digest, point, sign, link); }, - .threshold = [&](const chain::signatures::threshold_group& , - uint16_t ) NOEXCEPT + .schnorr = [&](const hash_digest& digest, const ec_xonly& point, + const ec_signature& sign) NOEXCEPT { - ////query.set_signatures(digest, points, signs, group, link); + ++schnorr_; + fire(to_events(opcode::checksigadd), ctx.height); + return query.set_signature(digest, point, sign, link); + }, + .multisig = [&](const hash_digest& digest, + const ec_compresseds& points, + const ec_signatures& signs) NOEXCEPT + { + BC_ASSERT(points.size() == signs.size()); + multisig_ += points.size(); + fire(to_events(opcode::checkmultisigverify), ctx.height); + return query.set_signatures(digest, points, signs, set, link); + }, + .threshold = [&]( + const signatures::threshold_entries& group) NOEXCEPT + { + // 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); } }; if ((ec = block.connect(ctx, capture))) return ec; - if (is_limited(capture.group.load())) + const auto log_capture = [&](std::string_view name, size_t captured, + size_t missed) NOEXCEPT { - LOGF("Multisig capture bypassed because correlation overflow (" - << capture.group << ")."); - } - - // Diagnostics (ecdsa). - batched_ecdsa_ += capture.batched_ecdsa; - unbatched_ecdsa_ += capture.unbatched_ecdsa; - batched_multisig_ += capture.batched_multisig; - unbatched_multisig_ += capture.unbatched_multisig; - - if (to_bool(batched_ecdsa_.load()) || to_bool(unbatched_ecdsa_.load())) - { - LOGV("Efficiency ecdsa " << batched_ecdsa_ << " / (" - << batched_ecdsa_ << " + " << unbatched_ecdsa_ << ")"); - } - - if (to_bool(batched_multisig_.load()) || to_bool(unbatched_multisig_.load())) - { - LOGV("Efficiency multisig " << batched_multisig_ << " / (" - << batched_multisig_ << " + " << unbatched_multisig_ << ")"); - } - - // Diagnostics (schnorr). - batched_schnorr_ += capture.batched_schnorr; - unbatched_schnorr_ += capture.unbatched_schnorr; - batched_threshold_ += capture.batched_threshold; - unbatched_threshold_ += capture.unbatched_threshold; - - if (to_bool(batched_schnorr_.load()) || to_bool(unbatched_schnorr_.load())) - { - LOGV("Efficiency schnorr " << batched_schnorr_ << " / (" - << batched_schnorr_ << " + " << unbatched_schnorr_ << ")"); - } + if (!to_bool(captured) && !to_bool(missed)) return; + const auto ratio = (100.0f * captured) / (captured + missed); + const auto rate = (boost_format("%.2f") % ratio).str(); + LOGA("Efficiency " << name << rate << "% = " << captured + << "/(" << captured << "+" << missed << ")"); + }; - if (to_bool(batched_threshold_.load()) || to_bool(unbatched_threshold_.load())) - { - LOGV("Efficiency threshold " << batched_threshold_ << " / (" - << batched_threshold_ << " + " << unbatched_threshold_ << ")"); - } + log_capture("ecdsa.... ", ecdsa_, missed_ecdsa_); + log_capture("multisig. ", multisig_, missed_multisig_); + log_capture("schnorr.. ", schnorr_, missed_schnorr_); + log_capture("threshold ", threshold_,missed_threshold_); } else {