diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index e0a168a1..c2b6bc64 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -141,9 +141,6 @@ class BCN_API chaser /// The confirmed chain is confirmed to maximum height or is current. bool is_recent() const NOEXCEPT; - /// Confirmation is limited to below the lowest banked silent block height. - size_t silent_limit() const NOEXCEPT; - /// The height is at or below the top checkpoint. bool is_under_checkpoint(size_t height) const NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_transaction.hpp b/include/bitcoin/node/chasers/chaser_transaction.hpp index e65d7fb3..4c92a186 100644 --- a/include/bitcoin/node/chasers/chaser_transaction.hpp +++ b/include/bitcoin/node/chasers/chaser_transaction.hpp @@ -37,6 +37,8 @@ class BCN_API chaser_transaction chaser_transaction(full_node& node) NOEXCEPT; code start() NOEXCEPT override; + void stopping(const code& ec) NOEXCEPT override; + void stop() NOEXCEPT override; /// Validate and archive a submitted package, accepted as a whole. /// The package is only validated when test, so nothing is archived. @@ -59,12 +61,21 @@ class BCN_API chaser_transaction const database::tx_links& links, const database::header_link& link, const submit_handler& handler) NOEXCEPT; - /// Recompute the pool context, closing the pool if not current. + /// Recompute the pool context, closing the pool if not current, and map + /// scan starts: built once current, extended by block until pooling, and + /// remapped when a block archived before the latch is organized. virtual void do_bump() NOEXCEPT; /// Pool the unpooled txs of a reorganized block. virtual void do_reorganized(header_t link) NOEXCEPT; + /// Map scan starts from the confirmed top (scan threadpool). + virtual void do_scan(const database::header_link& top, size_t bound, + bool remap) NOEXCEPT; + + /// Complete the scan start map. + virtual void do_scanned(bool mapped) NOEXCEPT; + private: static size_t to_rate(const system::chain::transaction& tx) NOEXCEPT; @@ -79,10 +90,16 @@ class BCN_API chaser_transaction const system::chain::context& ctx) NOEXCEPT; code unconfirmable(const database::header_link& link, size_t height, const code& ec) NOEXCEPT; + void start_scan(bool remap) NOEXCEPT; + + // These are thread safe. + network::threadpool scan_threadpool_; + stopper stopping_{}; // These are protected by strand. system::chain::context pool_{}; bool pooling_{}; + bool scanning_{}; }; } // namespace node diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index 5dc7eada..d8a4062f 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -41,9 +41,6 @@ class BCN_API chaser_validate void stopping(const code& ec) NOEXCEPT override; void stop() NOEXCEPT override; - /// Confirmation is limited to below the lowest banked silent block height. - size_t silent_limit() const NOEXCEPT; - protected: using header_link = database::header_link; using header_links = database::header_links; @@ -67,14 +64,15 @@ class BCN_API chaser_validate /// Validation. virtual void post_block(const header_link& link, bool bypass) NOEXCEPT; + virtual void post_scan(const header_link& link, bool bypass) NOEXCEPT; virtual void validate_block(const header_link& link, bool bypass) NOEXCEPT; + virtual void scan_block(const header_link& link, bool bypass) NOEXCEPT; virtual code validate(bool& batched, 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 code validate_silent(const header_link& link, - const system::chain::context& ctx) NOEXCEPT; + virtual code validate_silent(const header_link& link, bool bank) NOEXCEPT; virtual code validate_pooled(bool& pooled, const header_link& link, const system::chain::context& ctx) NOEXCEPT; virtual code complete_pooled(const header_link& link, @@ -108,9 +106,9 @@ class BCN_API chaser_validate virtual void process_silent_batch(bool residual) NOEXCEPT; virtual code do_process_silent_batch(bool bank) NOEXCEPT; virtual bool commit_silent_batch(const header_link& link, - const system::chain::block& block, size_t height) NOEXCEPT; + const system::chain::block& block) NOEXCEPT; virtual bool commit_silent_batch(const header_link& link, - const system::chain::view::block& block, size_t height) NOEXCEPT; + const system::chain::view::block& block) NOEXCEPT; virtual bool enter_silent_capture() NOEXCEPT; virtual void exit_silent_capture(bool bank) NOEXCEPT; @@ -119,9 +117,7 @@ class BCN_API chaser_validate bool stranded() const NOEXCEPT override; private: - using atomic_height = std::atomic; using atomic_counter = std::atomic; - using bank_heights = std::array; struct counters { atomic_counter ecdsa_{}; @@ -151,12 +147,13 @@ class BCN_API chaser_validate // Batching helpers. template - bool commit_silent_(const header_link& link, const Block& block, - size_t height) NOEXCEPT; + bool commit_silent_(const header_link& link, + const Block& block) NOEXCEPT; bool is_residual() NOEXCEPT; bool is_mature(bool residual) NOEXCEPT; bool is_silent_capturing(const header_link& link) NOEXCEPT; bool is_silent_mature(bool residual) NOEXCEPT; + bool is_silent_empty() const NOEXCEPT; std::string log_rate(const std::string& name, size_t signatures, size_t milliseconds, bool device) const NOEXCEPT; @@ -176,7 +173,6 @@ class BCN_API chaser_validate std::atomic_bool silent_draining_{}; std::atomic_bool silent_bank_{}; std::array silent_writers_{}; - bank_heights silent_heights_{ max_size_t, max_size_t }; counters counters_{}; stopper stopping_{}; diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index 5ab41158..01046684 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -122,6 +122,7 @@ enum error_t : uint8_t transaction5, transaction6, transaction7, + transaction8, estimates_initialize, estimates_push1, estimates_push2, diff --git a/include/bitcoin/node/full_node.hpp b/include/bitcoin/node/full_node.hpp index 16cb8230..63abeae9 100644 --- a/include/bitcoin/node/full_node.hpp +++ b/include/bitcoin/node/full_node.hpp @@ -165,9 +165,6 @@ class BCN_API full_node /// The confirmed chain is confirmed to maximum height or is current. virtual bool is_recent() const NOEXCEPT; - /// Confirmation is limited to below the lowest banked silent block height. - virtual size_t silent_limit() const NOEXCEPT; - /// Zulu time at which the node started. virtual time_t start_time() const NOEXCEPT; diff --git a/src/chasers/chaser.cpp b/src/chasers/chaser.cpp index d5bb9b6b..3e0ea12c 100644 --- a/src/chasers/chaser.cpp +++ b/src/chasers/chaser.cpp @@ -171,11 +171,6 @@ bool chaser::is_recent() const NOEXCEPT return node_.is_recent(); } -size_t chaser::silent_limit() const NOEXCEPT -{ - return node_.silent_limit(); -} - bool chaser::is_under_checkpoint(size_t height) const NOEXCEPT { return height <= checkpoint(); diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index 1ea8b49e..966d8517 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -161,7 +161,7 @@ void chaser_confirm::do_bumped(height_t) NOEXCEPT // Guarded by candidate interlock. size_t fork_point{}; const auto& query = archive(); - auto fork = query.get_validated_fork(fork_point, silent_limit()); + auto fork = query.get_validated_fork(fork_point); // Fork may be empty if candidates were reorganized. if (fork.empty()) diff --git a/src/chasers/chaser_transaction.cpp b/src/chasers/chaser_transaction.cpp index cc4fccc8..5f2c3317 100644 --- a/src/chasers/chaser_transaction.cpp +++ b/src/chasers/chaser_transaction.cpp @@ -35,7 +35,8 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) chaser_transaction::chaser_transaction(full_node& node) NOEXCEPT - : chaser(node) + : chaser(node), + scan_threadpool_(one, node.node_settings().thread_priority_()) { } @@ -49,6 +50,22 @@ code chaser_transaction::start() NOEXCEPT return error::success; } +void chaser_transaction::stopping(const code& ec) NOEXCEPT +{ + stopping_.store(true); + scan_threadpool_.stop(); + chaser::stopping(ec); +} + +void chaser_transaction::stop() NOEXCEPT +{ + if (!scan_threadpool_.join()) + { + BC_ASSERT_MSG(false, "failed to join threadpool"); + std::abort(); + } +} + // event handlers // ---------------------------------------------------------------------------- @@ -89,26 +106,79 @@ void chaser_transaction::do_bump() NOEXCEPT { BC_ASSERT(stranded()); pooling_ = false; + if (closed() || !is_current_chain(true)) + return; // Pooling is a permanent store property, so it requires relay. - if (closed() || !network_settings().enable_relay || !is_current_chain(true)) + auto& query = archive(); + if (network_settings().enable_relay) + { + const auto top = query.get_top_confirmed(); + const auto state = query.get_confirmed_chain_state(system_settings(), + query.to_confirmed(top), top); + + if (!state) + { + fault(error::transaction1); + return; + } + + // The context of the next block, in which a pool tx would confirm. + pool_ = chain_state{ *state, system_settings() }.context(); + query.set_pooling(); + pooling_ = true; + } + + // Scan starts are mapped after the latch, as pooled rows are unordered. + if (!query.silent_enabled() || scanning_) return; - auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto state = query.get_confirmed_chain_state(system_settings(), - query.to_confirmed(top), top); + // A complete map reaches genesis, and keeps up with the top until latched. + // Rows of a block archived before the latch may precede mapped starts. + const auto top = query.to_confirmed(query.get_top_confirmed()); + const auto unmapped = query.get_scan_start(top).is_terminal(); + if (query.get_scan_start(query.to_confirmed(zero)).is_terminal()) + start_scan(true); + else if (unmapped && !query.is_pooling()) + start_scan(false); + else if (unmapped && query.to_coinbase(top) < query.get_latch_tx()) + start_scan(true); +} + +void chaser_transaction::start_scan(bool remap) NOEXCEPT +{ + BC_ASSERT(stranded()); + scanning_ = true; - if (!state) + const auto& query = archive(); + const auto bound = query.is_pooling() ? query.get_scan_frontier() : + max_size_t; + const auto top = query.to_confirmed(query.get_top_confirmed()); + boost::asio::post(scan_threadpool_.service(), + BIND(do_scan, top, bound, remap)); +} + +void chaser_transaction::do_scan(const database::header_link& top, + size_t bound, bool remap) NOEXCEPT +{ + POST(do_scanned, archive().set_scan_starts(stopping_, top, bound, remap)); +} + +// Blocks organized during the map are mapped by the bump that follows. +void chaser_transaction::do_scanned(bool mapped) NOEXCEPT +{ + BC_ASSERT(stranded()); + scanning_ = false; + if (closed()) + return; + + if (!mapped) { - fault(error::transaction1); + fault(error::transaction8); return; } - // The context of the next block, in which a pool tx would confirm. - pool_ = chain_state{ *state, system_settings() }.context(); - query.set_pooling(); - pooling_ = true; + do_bump(); } // Pooled in block order under the block context, excluding any confirmed (by diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 6f2a559f..a67a6c4c 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -223,8 +223,26 @@ void chaser_validate::do_bumped(height_t height) NOEXCEPT return; const auto bypass = database::is_bypass(state); - if (database::is_validated(state) || - (bypass && !filter_ && !query.is_silent(link, height))) + if (database::is_validated(state)) + { + if (database::is_scanned(state)) + { + complete_block(error::success, link, height, true); + } + else if (!is_silent_empty()) + { + // Banked, scanned by the drain of its bank (which bumps). + process_silent_batch(true); + return; + } + else + { + // Its bank was dropped by restore. + ++validate_backlog_; + post_scan(link, bypass); + } + } + else if (bypass && !filter_ && !query.is_silent(link, height)) { complete_block(error::success, link, height, true); } @@ -247,6 +265,13 @@ void chaser_validate::post_block(const header_link& link, PARALLEL(validate_block, link, bypass); } +void chaser_validate::post_scan(const header_link& link, + bool bypass) NOEXCEPT +{ + BC_ASSERT(stranded()); + PARALLEL(scan_block, link, 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 capturing) NOEXCEPT diff --git a/src/chasers/chaser_validate_batch.cpp b/src/chasers/chaser_validate_batch.cpp index 964a6392..6bddb869 100644 --- a/src/chasers/chaser_validate_batch.cpp +++ b/src/chasers/chaser_validate_batch.cpp @@ -48,19 +48,6 @@ code chaser_validate::start_batch() NOEXCEPT is_zero(query.schnorr_records(bank))) continue; - // Prevalid is not a validation state, so dropped blocks validate in - // place. - if (!verify_enabled_) - { - LOGN("Dropping staged batch rows (" - << query.prevalid_records(bank) << ")."); - - if (const auto ec = purge_batch(bank)) - return ec; - - continue; - } - if (const auto ec = do_process_batch(bank, true)) return ec; } @@ -292,8 +279,24 @@ bool chaser_validate::is_silent_capturing(const header_link& link) NOEXCEPT bool chaser_validate::is_silent_mature(bool residual) NOEXCEPT { - const auto rows = archive().silent_records(silent_bank_.load()); - return is_nonzero(rows) && (residual || rows >= silent_target_); + const auto& query = archive(); + const auto bank = silent_bank_.load(); + const auto rows = query.silent_records(bank); + + // Nothing to compute and no links to release. + if (is_zero(rows) && is_zero(query.presilent_records(bank))) + return false; + + // Compute residuals whenever, and non-residuals when mature. + return residual || (rows >= silent_target_); +} + +// No block is banked while both banks are empty. +bool chaser_validate::is_silent_empty() const NOEXCEPT +{ + const auto& query = archive(); + return is_zero(query.presilent_records(false)) && + is_zero(query.presilent_records(true)); } std::string chaser_validate::log_rate(const std::string& name, @@ -308,13 +311,13 @@ std::string chaser_validate::log_rate(const std::string& name, // ---------------------------------------------------------------------------- // protected -// Silent batch is not validation state, so it drains whether or not batching -// is enabled. code chaser_validate::start_silent_batch() NOEXCEPT { + auto& query = archive(); for (const auto bank: { false, true }) { - if (is_zero(archive().silent_records(bank))) + if (is_zero(query.presilent_records(bank)) && + is_zero(query.silent_records(bank))) continue; if (const auto ec = do_process_silent_batch(bank)) @@ -396,61 +399,53 @@ code chaser_validate::do_process_silent_batch(bool bank) NOEXCEPT if (ec) return error::batch7; - const auto elapsed = network::logger::now() - start; - LOGN(log_rate("Compute silent...", rows, - duration_cast(elapsed).count(), device)); + if (is_nonzero(rows)) + { + const auto elapsed = network::logger::now() - start; + fire(events::silent_secs, duration_cast(elapsed).count()); + LOGN(log_rate("Compute silent...", rows, + duration_cast(elapsed).count(), device)); + } - if (!query.purge_silents(bank)) + // Purge presilents before silents. + if (!query.purge_presilents(bank) || !query.purge_silents(bank)) return error::batch8; // Blocks of the bank may now be confirmable. - silent_heights_.at(to_int(bank)).store(max_size_t); notify(error::success, chases::bump{}); return error::success; } -// The bank height is lowered before exit, so that its drain observes it. +// Upon store decline the records are set directly, within the commit epoch, +// so that a drain of the bank cannot precede them. template bool chaser_validate::commit_silent_(const header_link& link, - const Block& block, size_t height) NOEXCEPT + const Block& block) NOEXCEPT { size_t rows{}; + auto& query = archive(); const auto bank = enter_silent_capture(); - const auto committed = archive().set_silents(rows, link, block, bank); - if (committed && is_nonzero(rows)) - { - auto& lowest = silent_heights_.at(to_int(bank)); - auto current = lowest.load(); - while (height < current) - if (lowest.compare_exchange_weak(current, height)) - break; - } - - exit_silent_capture(bank); + const auto committed = query.set_silents(rows, link, block, bank); // Store decline (e.g. disk full), recoverable once faulted. if (!committed) fault(error::batch6); - return committed; + const auto written = committed || query.set_silent(link, block); + exit_silent_capture(bank); + return written; } bool chaser_validate::commit_silent_batch(const header_link& link, - const chain::block& block, size_t height) NOEXCEPT + const chain::block& block) NOEXCEPT { - return commit_silent_(link, block, height); + return commit_silent_(link, block); } bool chaser_validate::commit_silent_batch(const header_link& link, - const chain::view::block& block, size_t height) NOEXCEPT -{ - return commit_silent_(link, block, height); -} - -size_t chaser_validate::silent_limit() const NOEXCEPT + const chain::view::block& block) NOEXCEPT { - return std::min(silent_heights_.front().load(), - silent_heights_.back().load()); + return commit_silent_(link, block); } // A writer admitted to a bank that is then switched retries on the other, so diff --git a/src/chasers/chaser_validate_parallel.cpp b/src/chasers/chaser_validate_parallel.cpp index 0086139c..070fd10d 100644 --- a/src/chasers/chaser_validate_parallel.cpp +++ b/src/chasers/chaser_validate_parallel.cpp @@ -80,7 +80,7 @@ void chaser_validate::validate_block(const header_link& link, } else if (bypass && !filter_ && query.is_silent(link, ctx.height)) { - ec = validate_silent(link, ctx); + ec = validate_silent(link, is_silent_capturing(link)); } else { @@ -159,10 +159,25 @@ code chaser_validate::populate(bool bypass, const chain::block& block, return error::success; } +// Scan a validated block whose silent bank was dropped by restore. +void chaser_validate::scan_block(const header_link& link, + bool bypass) NOEXCEPT +{ + if (closed()) + return; + + size_t height{}; + const auto ec = archive().get_height(height, link) ? + validate_silent(link, false) : error::validate3; + + --validate_backlog_; + complete_block(ec, link, height, bypass); +} + // Bypassed without client filters, a block is read only for its silent // payment records, so it is not materialized. code chaser_validate::validate_silent(const header_link& link, - const chain::context& ctx) NOEXCEPT + bool bank) NOEXCEPT { auto& query = archive(); auto wire = query.get_wire_block(link, node_witness_); @@ -179,10 +194,8 @@ code chaser_validate::validate_silent(const header_link& link, return query.set_block_unconfirmable(link) ? code{ system::error::missing_previous_output } : error::validate4; - const auto committed = is_silent_capturing(link) && - commit_silent_batch(link, block, ctx.height); - - if (!committed && !query.set_silent(link, block)) + if (bank ? !commit_silent_batch(link, block) : + !query.set_silent(link, block)) return error::validate9; return query.set_block_valid(link) ? error::success : error::validate10; @@ -226,7 +239,10 @@ code chaser_validate::complete_pooled(const header_link& link, return ec; } - // Valid must be set after set_prevouts and set_filter_body. + // Pooled txs are scanned, and valid must be set after set_filter_body. + if (!query.set_block_scanned(link)) + return error::validate9; + return query.set_block_valid(link) ? error::success : error::validate10; } @@ -277,14 +293,9 @@ code chaser_validate::validate(bool& batched, bool& capturing, bool bypass, return error::validate8; // Banked records are set when the bank drains. - if (query.is_silent(link, ctx.height)) - { - const auto committed = is_silent_capturing(link) && - commit_silent_batch(link, block, ctx.height); - - if (!committed && !query.set_silent(link, block)) - return error::validate9; - } + if (query.is_silent(link, ctx.height) && (is_silent_capturing(link) ? + !commit_silent_batch(link, block) : !query.set_silent(link, block))) + return error::validate9; // Defer block state change when batched. // Valid must be set after set_prevouts and set_filter_body. diff --git a/src/error.cpp b/src/error.cpp index 1b0508c3..0fffa1da 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -112,6 +112,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { transaction5, "transaction5" }, { transaction6, "transaction6" }, { transaction7, "transaction7" }, + { transaction8, "transaction8" }, { estimates_initialize, "estimates_initialize" }, { estimates_push1, "estimates_push1" }, { estimates_push2, "estimates_push2" }, diff --git a/src/full_node.cpp b/src/full_node.cpp index aec3bc83..8cf80e57 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -460,11 +460,6 @@ bool full_node::is_current_header(const header_link& link) const NOEXCEPT return is_current_time(timestamp); } -size_t full_node::silent_limit() const NOEXCEPT -{ - return chaser_validate_.silent_limit(); -} - bool full_node::is_recent() const NOEXCEPT { if (is_nonzero(config_.node.maximum_height) && diff --git a/test/chasers/chaser_confirm.cpp b/test/chasers/chaser_confirm.cpp index 7784d7d9..5f37a648 100644 --- a/test/chasers/chaser_confirm.cpp +++ b/test/chasers/chaser_confirm.cpp @@ -173,15 +173,15 @@ static bool confirm_candidates(node::query& query) confirm_candidate(query, confirm_d2(), 2); } -static bool confirm_unfiltered(node::query& query) +static bool confirm_filtered(node::query& query, const chain::block& block) { - return confirm_candidate(query, confirm_d1(), 1) && - query.set_block_valid(query.to_header(confirm_d1().hash())); + return query.set_filter_body(query.to_header(block.hash()), block); } static bool confirm_confirmable(node::query& query) { return confirm_candidate(query, confirm_d1(), 1) && + confirm_filtered(query, confirm_d1()) && query.set_block_confirmable(query.to_header(confirm_d1().hash())); } @@ -189,6 +189,7 @@ static bool confirm_confirmables(node::query& query) { return confirm_confirmable(query) && confirm_candidate(query, confirm_d2(), 2) && + confirm_filtered(query, confirm_d2()) && query.set_block_confirmable(query.to_header(confirm_d2().hash())); } @@ -202,16 +203,6 @@ static bool confirm_headerless_confirmed(node::query& query) confirm_confirmables(query); } -static bool confirm_headerless_candidate(node::query& query) -{ - const auto& genesis = confirm_genesis().header(); - const database::context context{ 0, 1, genesis.timestamp() }; - const auto work = genesis.proof() * 2u; - return query.set(confirm_d1().header(), context, work, false) && - query.push_candidate(query.to_header(confirm_d1().hash())) && - query.set_block_confirmable(query.to_header(confirm_d1().hash())); -} - static bool confirm_unheaded(node::query& query) { return confirm_confirmed(query, confirm_c1(), 1) && @@ -223,6 +214,7 @@ static bool confirm_unprevouted(node::query& query) { return confirm_confirmable(query) && confirm_candidate(query, confirm_e2(), 2) && + confirm_filtered(query, confirm_e2()) && query.set_block_valid(query.to_header(confirm_e2().hash())); } @@ -236,11 +228,6 @@ static void confirm_uncheckpointed(configuration& config) config.bitcoin.checkpoints.clear(); } -static bool is_valid_only(database::block_state state) -{ - return database::is_validated(state) && !database::is_confirmable(state); -} - struct confirm_setup_fixture { DELETE_COPY_MOVE(confirm_setup_fixture); @@ -389,15 +376,6 @@ struct confirm_current_fixture } }; -struct confirm_unfiltered_fixture - : confirm_setup_fixture -{ - confirm_unfiltered_fixture() - : confirm_setup_fixture(confirm_unfiltered, confirm_uncheckpointed) - { - } -}; - ////struct confirm_windowed_fixture //// : confirm_setup_fixture ////{ @@ -457,15 +435,6 @@ struct confirm_headerless_confirmed_fixture } }; -struct confirm_headerless_candidate_fixture - : confirm_setup_fixture -{ - confirm_headerless_candidate_fixture() - : confirm_setup_fixture(confirm_headerless_candidate, confirm_uncheckpointed) - { - } -}; - struct confirm_unheaded_fixture : confirm_setup_fixture { @@ -521,13 +490,6 @@ BOOST_FIXTURE_TEST_CASE(chaser_confirm__start__current_candidates__organized, co BOOST_REQUIRE(stated(confirm_d2(), database::is_confirmable)); } -BOOST_FIXTURE_TEST_CASE(chaser_confirm__start__valid_candidate_without_filter_body__suspended, confirm_unfiltered_fixture) -{ - BOOST_REQUIRE(await([&]() { return node_.suspended(); })); - BOOST_REQUIRE(stated(confirm_d1(), is_valid_only)); - BOOST_REQUIRE_EQUAL(query_.get_top_confirmed(), zero); -} - ////BOOST_FIXTURE_TEST_CASE(chaser_confirm__bump__recent_confirmable_candidate__organized_stale, confirm_windowed_fixture) ////{ //// const auto block = confirm_recent(); @@ -550,6 +512,7 @@ BOOST_FIXTURE_TEST_CASE(chaser_confirm__bump__unstored_confirmed_top__suspended, { BOOST_REQUIRE(query_.push_confirmed(database::header_link{ 42 }, false)); BOOST_REQUIRE(confirm_candidate(query_, confirm_d1(), 1)); + BOOST_REQUIRE(confirm_filtered(query_, confirm_d1())); BOOST_REQUIRE(query_.set_block_valid(link(confirm_d1()))); node_.notify(node::error::success, chases::bump{ 0 }); BOOST_REQUIRE(await([&]() { return node_.suspended(); })); @@ -564,12 +527,6 @@ BOOST_FIXTURE_TEST_CASE(chaser_confirm__bump__unstored_confirmed_top__suspended, //// BOOST_REQUIRE_EQUAL(query_.get_top_confirmed(), 1u); ////} -BOOST_FIXTURE_TEST_CASE(chaser_confirm__start__headerless_confirmable_candidate__suspended, confirm_headerless_candidate_fixture) -{ - BOOST_REQUIRE(await([&]() { return node_.suspended(); })); - BOOST_REQUIRE_EQUAL(query_.get_top_confirmed(), zero); -} - BOOST_FIXTURE_TEST_CASE(chaser_confirm__start__bypassed_candidate_unheaded_parent__suspended, confirm_unheaded_fixture) { BOOST_REQUIRE(await([&]() { return node_.suspended(); })); diff --git a/test/chasers/chaser_validate.cpp b/test/chasers/chaser_validate.cpp index 277b1ed1..9445b5be 100644 --- a/test/chasers/chaser_validate.cpp +++ b/test/chasers/chaser_validate.cpp @@ -155,6 +155,25 @@ static bool validate_store(node::query& query, const chain::block& block, return query.set(block, context, work, false, false); } +static bool validate_silent_store(node::query& query, + const chain::block& block, uint32_t height) +{ + const auto& genesis = validate_genesis().header(); + const database::context context + { + chain::flags::bip341_rule, height, genesis.timestamp() + }; + const auto work = genesis.proof() * add1(height); + return query.set(block, context, work, false, false); +} + +static bool validate_unscanned(node::query& query) +{ + return validate_silent_store(query, validate_a1(), 1) && + query.push_candidate(query.to_header(validate_a1().hash())) && + query.set_block_valid(query.to_header(validate_a1().hash())); +} + static bool validate_candidate(node::query& query, const chain::block& block, uint32_t height) { @@ -345,6 +364,19 @@ struct validate_current_fixture } }; +struct validate_unscanned_fixture + : validate_setup_fixture +{ + validate_unscanned_fixture() + : validate_setup_fixture(validate_unscanned, [](configuration& config) + { + config.bitcoin.checkpoints.clear(); + config.node.silent_start_height = 0; + }) + { + } +}; + struct validate_overspent_fixture : validate_setup_fixture { @@ -568,16 +600,23 @@ BOOST_FIXTURE_TEST_CASE(chaser_validate__start__maximum_height__confirmable, val BOOST_REQUIRE(stated(validate_a1(), database::is_confirmable)); } -BOOST_FIXTURE_TEST_CASE(chaser_validate__start__staged_prevalid__validated_iff_accelerated, validate_staged_fixture) +BOOST_FIXTURE_TEST_CASE(chaser_validate__start__staged_prevalid__validated, validate_staged_fixture) { BOOST_REQUIRE_EQUAL(query_.prevalid0_records(), zero); - BOOST_REQUIRE_EQUAL(is_valid_only(state(validate_x1())), system::batched::accelerated()); + BOOST_REQUIRE(is_valid_only(state(validate_x1()))); } -BOOST_FIXTURE_TEST_CASE(chaser_validate__start__staged_second_bank__validated_iff_accelerated, validate_staged_second_fixture) +BOOST_FIXTURE_TEST_CASE(chaser_validate__start__staged_second_bank__validated, validate_staged_second_fixture) { BOOST_REQUIRE_EQUAL(query_.prevalid_records(true), zero); - BOOST_REQUIRE_EQUAL(is_valid_only(state(validate_x1())), system::batched::accelerated()); + BOOST_REQUIRE(is_valid_only(state(validate_x1()))); +} + +BOOST_FIXTURE_TEST_CASE(chaser_validate__start__validated_unscanned__scanned, validate_unscanned_fixture) +{ + BOOST_REQUIRE(stated(validate_a1(), database::is_scanned)); + BOOST_REQUIRE_EQUAL(query_.presilent_records(false), zero); + BOOST_REQUIRE_EQUAL(query_.presilent_records(true), zero); } BOOST_FIXTURE_TEST_CASE(chaser_validate__process_batch__residual_commit_during_drain__drained, validate_noncandidates_fixture)