diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index 93fd1dce..5c4572b6 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -79,9 +79,8 @@ class BCN_API chaser_validate const header_link& link, bool bypass, bool startup=false) NOEXCEPT; /// Batching. - virtual bool is_residual() NOEXCEPT; - virtual bool is_mature(bool residual) NOEXCEPT; virtual code start_batch() NOEXCEPT; + virtual void close_batch() NOEXCEPT; virtual void push_batch(const header_link& link, size_t height) NOEXCEPT; virtual void process_batch(bool residual) NOEXCEPT; virtual code do_process_batch(bool startup) NOEXCEPT; @@ -118,12 +117,16 @@ class BCN_API chaser_validate // Capture helpers. signatures get_capture(const header_link& link) NOEXCEPT; - std::string log_rate(const std::string& name, size_t numerator, - size_t denominator) const NOEXCEPT; std::string log_ratio(const std::string& name, size_t numerator, size_t denominator) const NOEXCEPT; void log_captures() const NOEXCEPT; + // Batching helpers. + bool is_residual() NOEXCEPT; + bool is_mature(bool residual) NOEXCEPT; + std::string log_rate(const std::string& name, size_t numerator, + size_t denominator) const NOEXCEPT; + // These are protected by strand. header_links batched_{}; header_links invalids_{}; diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 4bf58444..f51be840 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -179,7 +179,7 @@ void CLASS::do_organize(typename Block::cptr block, // TODO: If any checkpoint is reached then reject non-candidates below. // TODO: because checkpoints are storable (and therefore stored) along with - // TODO: all ancestor blocks, which therefore much be candidates as well. + // TODO: all ancestor blocks, which therefore must be candidates as well. // TODO: When a checkpoint is pushed and after its branch is reorganized, // TODO: purge all blocks in the tree with height at/below that checkpoint. // TODO: The combination strongly mitigates low pow sybil attacks against diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index df8d86ab..5687bc9c 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -206,10 +206,6 @@ void chaser_validate::do_bumped(height_t height) NOEXCEPT complete_block(error::success, link, height, true); break; } - case database::error::block_prevalid: - { - break; - } case database::error::block_unconfirmable: { return; @@ -319,6 +315,9 @@ void chaser_validate::notify_block(const code& ec, size_t height, void chaser_validate::stopping(const code& ec) NOEXCEPT { + // closed() is now true so time to bump batched_ drain. + POST(close_batch); + // Stop long-running batch validations. stopping_.store(true); diff --git a/src/chasers/chaser_validate_batch.cpp b/src/chasers/chaser_validate_batch.cpp index bb05f811..f1afce5b 100644 --- a/src/chasers/chaser_validate_batch.cpp +++ b/src/chasers/chaser_validate_batch.cpp @@ -38,24 +38,6 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) // ---------------------------------------------------------------------------- // protected -bool chaser_validate::is_residual() NOEXCEPT -{ - // Verify residuals when recent. - return maximum_posted_.load() && - is_zero(batch_backlog_.load()) && - is_zero(validate_backlog_.load()); -} - -bool chaser_validate::is_mature(bool residual) NOEXCEPT -{ - const auto& query = archive(); - const auto ecdsa = query.ecdsa_records(); - const auto schnorr = query.schnorr_records(); - - // Verify non-residuals when mature. - return residual || (ecdsa >= batch_target_) || (schnorr >= batch_target_); -} - // If there was a non-empty batch at startup, process it for invalids and set // their states normally, then scan from fork point (position()) to association // gap for all prevalids. Iterate over these setting their states to valid. @@ -65,15 +47,32 @@ code chaser_validate::start_batch() NOEXCEPT if (!batch_enabled_) return {}; - const auto& query = archive(); + auto& query = archive(); + batched_ = query.get_prevalids(); + if (!query.purge_prevalids()) + return error::batch1; + if (is_zero(query.ecdsa_records()) && is_zero(query.schnorr_records())) return {}; - // Accumulate all prevalid block links above the fork point. - batched_ = query.get_prevalids(position()); return do_process_batch(true); } +// Shutdown drains batched_ to block prevalid states, recovered on startup. +// Snapshot restoration purges batch backlog as the tables are not append-only. +void chaser_validate::close_batch() NOEXCEPT +{ + BC_ASSERT(stranded()); + BC_ASSERT(closed()); + + // Set even if signature batch tables are empty. + if (is_zero(batch_backlog_.load())) + { + archive().set_prevalids(batched_); + batched_.clear(); + } +} + // batched_ is redundant with the combined set of ecdsa/schnorr unfailed block // identifiers stored in the two batch tables, so just a sort optimization. void chaser_validate::push_batch(const header_link& link, @@ -81,12 +80,16 @@ void chaser_validate::push_batch(const header_link& link, { BC_ASSERT(stranded()); - if (closed()) - return; - + // Accumulate even if closed. Sacrifices stop speed to save validations. batched_.push_back(link); --batch_backlog_; + if (closed()) + { + close_batch(); + return; + } + // Unblocks check chaser for download while verifying. notify({}, chase::prevalid, possible_wide_cast(height)); @@ -112,22 +115,27 @@ void chaser_validate::process_batch(bool residual) NOEXCEPT // tables to be fully purged upon completion, and ensuring that evaluation // does not operate over partial block records in the batch tables. // ======================================================================== - const std::unique_lock lock{ mutex_ }; + { + const std::unique_lock lock{ mutex_ }; - // Must retest inside the lock as table updates are running concurrently. - if (closed() || !is_mature(residual)) - return; + // Must retest inside lock as table updates are running concurrently. + if (closed() || !is_mature(residual)) + return; - if (const auto ec = do_process_batch(false)) - fault(ec); + if (const auto ec = do_process_batch(false)) + { + fault(ec); + return; + } + } // ======================================================================== + + // Log outside of lock, oand nly when batch executes (non-verbose). + log_captures(); } code chaser_validate::do_process_batch(bool startup) NOEXCEPT { - if (!startup) - log_captures(); - auto& query = archive(); const auto ecdsa = query.ecdsa_records(); @@ -252,6 +260,36 @@ bool chaser_validate::mark_valids(bool startup) NOEXCEPT return !fault.load(); } +// Batch helpers. +// ---------------------------------------------------------------------------- +// private + +bool chaser_validate::is_residual() NOEXCEPT +{ + // Verify residuals when recent. + return maximum_posted_.load() && + is_zero(batch_backlog_.load()) && + is_zero(validate_backlog_.load()); +} + +bool chaser_validate::is_mature(bool residual) NOEXCEPT +{ + const auto& query = archive(); + const auto ecdsa = query.ecdsa_records(); + const auto schnorr = query.schnorr_records(); + + // Verify non-residuals when mature. + return residual || (ecdsa >= batch_target_) || (schnorr >= batch_target_); +} + +std::string chaser_validate::log_rate(const std::string& name, + size_t numerator, size_t denominator) const NOEXCEPT +{ + const auto rate = numerator / greater(denominator, one); + return (boost_format("%1% (%2% / %3%) = %4% sps") % + name % numerator % denominator % rate).str(); +} + BC_POP_WARNING() } // namespace node diff --git a/src/chasers/chaser_validate_capture.cpp b/src/chasers/chaser_validate_capture.cpp index 85575a33..15ff20ef 100644 --- a/src/chasers/chaser_validate_capture.cpp +++ b/src/chasers/chaser_validate_capture.cpp @@ -117,14 +117,6 @@ bool chaser_validate::do_threshold(const threshold& batch, return set; } -std::string chaser_validate::log_rate(const std::string& name, - size_t numerator, size_t denominator) const NOEXCEPT -{ - const auto rate = numerator / greater(denominator, one); - return (boost_format("%1% (%2% / %3%) = %4% sps") % - name % numerator % denominator % rate).str(); -} - // Capture helpers. // ---------------------------------------------------------------------------- // private @@ -160,10 +152,10 @@ std::string chaser_validate::log_ratio(const std::string& name, void chaser_validate::log_captures() const NOEXCEPT { - LOGV(log_ratio("Capture rate ecdsa.... ", ecdsa_, ecdsa_ + missed_ecdsa_)); - LOGV(log_ratio("Capture rate multisig. ", multisig_, multisig_ + missed_multisig_)); - LOGV(log_ratio("Capture rate schnorr.. ", schnorr_, schnorr_ + missed_schnorr_)); - LOGV(log_ratio("Capture rate threshold ", threshold_, threshold_ + zero)); + LOGN(log_ratio("Capture ecdsa.... ", ecdsa_, ecdsa_ + missed_ecdsa_)); + LOGN(log_ratio("Capture multisig. ", multisig_, multisig_ + missed_multisig_)); + LOGN(log_ratio("Capture schnorr.. ", schnorr_, schnorr_ + missed_schnorr_)); + LOGN(log_ratio("Capture threshold ", threshold_, threshold_ + zero)); } BC_POP_WARNING() diff --git a/src/chasers/chaser_validate_parallel.cpp b/src/chasers/chaser_validate_parallel.cpp index 2cf87593..f28578a7 100644 --- a/src/chasers/chaser_validate_parallel.cpp +++ b/src/chasers/chaser_validate_parallel.cpp @@ -64,11 +64,6 @@ void chaser_validate::validate_block(const header_link& link, if (!query.set_block_unconfirmable(link)) ec = error::validate5; } - else if (batched && !faulted) - { - if (!query.set_block_prevalid(link)) - ec = error::batch1; - } --validate_backlog_; complete_block(ec, link, ctx.height, bypass, batched, faulted, capturing);