diff --git a/console/executor_store.cpp b/console/executor_store.cpp index f6fd8dd1c..d0d2b95ef 100644 --- a/console/executor_store.cpp +++ b/console/executor_store.cpp @@ -59,7 +59,7 @@ bool executor::check_store_path(bool create) const if (create) { logger(format(BN_INITIALIZING_CHAIN) % store); - if (auto ec = database::file::create_directory_ex(store)) + if (const auto ec = database::file::create_directory_ex(store)) { logger(format(BN_INITCHAIN_DIRECTORY_ERROR) % store % ec.message()); return false; diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index 16447a017..4c3894d02 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -107,6 +107,15 @@ class BCN_API chaser virtual void notify_one(object_key key, const code& ec, chase event_, event_value value) const NOEXCEPT; + /// Strand. + /// ----------------------------------------------------------------------- + + /// The chaser's strand (on the network threadpool). + virtual network::asio::strand& strand() NOEXCEPT; + + /// True if the current thread is on the chaser strand. + virtual bool stranded() const NOEXCEPT; + /// Properties. /// ----------------------------------------------------------------------- @@ -116,12 +125,6 @@ class BCN_API chaser /// Thread safe synchronous archival interface. query& archive() const NOEXCEPT; - /// The chaser's strand. - network::asio::strand& strand() NOEXCEPT; - - /// True if the current thread is on the chaser strand. - bool stranded() const NOEXCEPT; - /// Top candidate is within configured span from current time. bool is_current() const NOEXCEPT; @@ -153,6 +156,9 @@ class BCN_API chaser #define SUBSCRIBE_EVENTS(method, ...) \ subscribe_events(BIND(method, __VA_ARGS__)) +#define PARALLEL(method, ...) \ + boost::asio::post(threadpool_.service(), BIND(method, __VA_ARGS__)); + } // namespace node } // namespace libbitcoin diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index d2ce58f23..a5f18a4b8 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -59,7 +59,20 @@ class BCN_API chaser_confirm ////virtual void do_organize(header_links& fork, const header_links& popped, //// size_t fork_point) NOEXCEPT; + // Override base class strand because it sits on the network thread pool. + network::asio::strand& strand() NOEXCEPT override; + bool stranded() const NOEXCEPT override; + private: + struct neutrino_header + { + system::hash_digest head{}; + database::header_link link{}; + }; + + void reset_position(size_t confirmed_height) NOEXCEPT; + bool update_neutrino(const database::header_link& link) NOEXCEPT; + bool set_organized(const database::header_link& link, height_t height) NOEXCEPT; bool reset_organized(const database::header_link& link, @@ -74,12 +87,14 @@ class BCN_API chaser_confirm bool get_is_strong(bool& strong, const uint256_t& fork_work, size_t fork_point) const NOEXCEPT; - // This is thread safe. + // These are thread safe. const bool concurrent_; + network::asio::strand independent_strand_; // These are protected by strand. network::threadpool threadpool_; - network::asio::strand strand_; + neutrino_header neutrino_{}; + bool filters_{}; bool mature_{}; }; diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index 5d99d5720..dd5a98fcf 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -56,12 +56,20 @@ class BCN_API chaser_validate virtual void complete_block(const code& ec, const database::header_link& link, size_t height) NOEXCEPT; + // Override base class strand because it sits on the network thread pool. + network::asio::strand& strand() NOEXCEPT override; + bool stranded() const NOEXCEPT override; + private: - // neutrino - void update_position(size_t height) NOEXCEPT; - system::hash_digest get_neutrino(size_t height) const NOEXCEPT; - bool update_neutrino(const database::header_link& link) NOEXCEPT; - bool update_neutrino(const database::header_link& link, + inline bool unfilled() const NOEXCEPT + { + return backlog_ < maximum_backlog_; + } + + bool set_neutrino(const database::header_link& link, + const system::chain::block& block) NOEXCEPT; + + bool set_prevouts(size_t height, const system::chain::block& block) NOEXCEPT; // These are thread safe. @@ -69,12 +77,12 @@ class BCN_API chaser_validate const size_t maximum_backlog_; const uint64_t initial_subsidy_; const uint32_t subsidy_interval_; + network::asio::strand independent_strand_; // These are protected by strand. network::threadpool threadpool_; - network::asio::strand strand_; - system::hash_digest neutrino_{}; - size_t validation_backlog_{}; + size_t backlog_{}; + bool filters_{}; bool mature_{}; }; diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index c22376fe1..a24afedc6 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -82,6 +82,7 @@ enum error_t : uint8_t validate4, validate5, validate6, + validate7, confirm1, confirm2, confirm3, diff --git a/include/bitcoin/node/settings.hpp b/include/bitcoin/node/settings.hpp index 816cd2dfc..cdcafa337 100644 --- a/include/bitcoin/node/settings.hpp +++ b/include/bitcoin/node/settings.hpp @@ -88,10 +88,12 @@ class BCN_API settings uint32_t threads; /// Helpers. + virtual size_t threads_() const NOEXCEPT; virtual size_t maximum_height_() const NOEXCEPT; virtual size_t maximum_concurrency_() const NOEXCEPT; virtual network::steady_clock::duration sample_period() const NOEXCEPT; virtual network::wall_clock::duration currency_window() const NOEXCEPT; + virtual network::thread_priority priority_() const NOEXCEPT; }; } // namespace node diff --git a/src/chasers/chaser.cpp b/src/chasers/chaser.cpp index 04e8277fa..a9cde6c7d 100644 --- a/src/chasers/chaser.cpp +++ b/src/chasers/chaser.cpp @@ -102,27 +102,30 @@ void chaser::notify_one(object_key key, const code& ec, chase event_, node_.notify_one(key, ec, event_, value); } -// Properties. +// Strand. // ---------------------------------------------------------------------------- -const node::configuration& chaser::config() const NOEXCEPT +asio::strand& chaser::strand() NOEXCEPT { - return node_.config(); + return strand_; } -query& chaser::archive() const NOEXCEPT +bool chaser::stranded() const NOEXCEPT { - return node_.archive(); + return strand_.running_in_this_thread(); } -asio::strand& chaser::strand() NOEXCEPT +// Properties. +// ---------------------------------------------------------------------------- + +const node::configuration& chaser::config() const NOEXCEPT { - return strand_; + return node_.config(); } -bool chaser::stranded() const NOEXCEPT +query& chaser::archive() const NOEXCEPT { - return strand_.running_in_this_thread(); + return node_.archive(); } bool chaser::is_current() const NOEXCEPT diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index f0ecc56ac..8c4559b25 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -42,15 +42,16 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_confirm::chaser_confirm(full_node& node) NOEXCEPT : chaser(node), concurrent_(node.config().node.concurrent_confirmation), - threadpool_(1_u32, node.config().node.priority_validation ? - network::thread_priority::high : network::thread_priority::normal), - strand_(threadpool_.service().get_executor()) + threadpool_(one, node.config().node.priority_()), + independent_strand_(threadpool_.service().get_executor()) { } code chaser_confirm::start() NOEXCEPT { - set_position(archive().get_fork()); + const auto& query = archive(); + filters_ = query.neutrino_enabled(); + reset_position(query.get_fork()); SUBSCRIBE_EVENTS(handle_event, _1, _2, _3); return error::success; } @@ -71,9 +72,6 @@ void chaser_confirm::stop() NOEXCEPT } } -// Protected -// ---------------------------------------------------------------------------- - bool chaser_confirm::handle_event(const code&, chase event_, event_value value) NOEXCEPT { @@ -106,9 +104,7 @@ bool chaser_confirm::handle_event(const code&, chase event_, { if (concurrent_ || mature_) { - ////POST(do_bump, height_t{}); - boost::asio::post(strand_, - BIND(do_bump, height_t{})); + POST(do_bump, height_t{}); } break; @@ -120,9 +116,7 @@ bool chaser_confirm::handle_event(const code&, chase event_, if (concurrent_ || mature_) { - ////POST(do_validated, std::get(value)); - boost::asio::post(strand_, - BIND(do_validated, std::get(value))); + POST(do_validated, std::get(value)); } break; @@ -130,17 +124,13 @@ bool chaser_confirm::handle_event(const code&, chase event_, case chase::regressed: { BC_ASSERT(std::holds_alternative(value)); - ////POST(do_regressed, std::get(value)); - boost::asio::post(strand_, - BIND(do_regressed, std::get(value))); + POST(do_regressed, std::get(value)); break; } case chase::disorganized: { BC_ASSERT(std::holds_alternative(value)); - ////POST(do_regressed, std::get(value)); - boost::asio::post(strand_, - BIND(do_regressed, std::get(value))); + POST(do_regressed, std::get(value)); break; } case chase::stop: @@ -156,6 +146,9 @@ bool chaser_confirm::handle_event(const code&, chase event_, return true; } +// track validation +// ---------------------------------------------------------------------------- + void chaser_confirm::do_regressed(height_t branch_point) NOEXCEPT { BC_ASSERT(stranded()); @@ -170,14 +163,14 @@ void chaser_confirm::do_regressed(height_t branch_point) NOEXCEPT } } - set_position(branch_point); + reset_position(branch_point); } +// Candidate block validated at given height, if next then confirm/advance. void chaser_confirm::do_validated(height_t height) NOEXCEPT { BC_ASSERT(stranded()); - // Candidate block was validated at the given height, confirm/advance. if (height == add1(position())) do_bump(height); } @@ -244,9 +237,8 @@ void chaser_confirm::do_bump(height_t) NOEXCEPT << ec.message()); return; } - - // TODO: fees. - if (!query.set_block_confirmable(link, {})) + + if (!query.set_block_confirmable(link)) { fault(error::confirm6); return; @@ -283,6 +275,7 @@ void chaser_confirm::do_bump(height_t) NOEXCEPT return; } + update_neutrino(link); set_position(height); } } @@ -617,6 +610,56 @@ bool chaser_confirm::get_is_strong(bool& strong, const uint256_t& fork_work, return true; } +// neutrino +// ---------------------------------------------------------------------------- + +// This can only fail if prevouts are not fully populated. +bool chaser_confirm::update_neutrino(const header_link& link) NOEXCEPT +{ + // neutrino_.link is only used for this assertion, should compile away. + BC_ASSERT(archive().get_height(link) == + add1(archive().get_height(neutrino_.link))); + + if (!filters_) + return true; + + data_chunk filter{}; + auto& query = archive(); + if (!query.get_filter_body(filter, link)) + return false; + + neutrino_.link = link; + neutrino_.head = neutrino::compute_filter_header(neutrino_.head, filter); + return query.set_filter_head(link, neutrino_.head); +} + +// Expects confirmed height. +// Use for startup and regression, to read current filter header from store. +void chaser_confirm::reset_position(size_t confirmed_height) NOEXCEPT +{ + set_position(confirmed_height); + + if (filters_) + { + const auto& query = archive(); + neutrino_.link = query.to_confirmed(confirmed_height); + query.get_filter_head(neutrino_.head, neutrino_.link); + } +} + +// Strand. +// ---------------------------------------------------------------------------- + +network::asio::strand& chaser_confirm::strand() NOEXCEPT +{ + return independent_strand_; +} + +bool chaser_confirm::stranded() const NOEXCEPT +{ + return independent_strand_.running_in_this_thread(); +} + BC_POP_WARNING() } // namespace node diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 12e3f76b6..87eb1e378 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -31,12 +31,9 @@ namespace node { #define CLASS chaser_validate using namespace system; -using namespace system::neutrino; using namespace database; using namespace std::placeholders; -// TODO: update specialized fault codes, reintegrate neutrino. - // Shared pointer is required to keep the race object alive in bind closure. BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) @@ -50,16 +47,16 @@ chaser_validate::chaser_validate(full_node& node) NOEXCEPT maximum_backlog_(node.config().node.maximum_concurrency_()), initial_subsidy_(node.config().bitcoin.initial_subsidy()), subsidy_interval_(node.config().bitcoin.subsidy_interval_blocks), - threadpool_(std::max(node.config().node.threads, 1_u32), - node.config().node.priority_validation ? - network::thread_priority::high : network::thread_priority::normal), - strand_(threadpool_.service().get_executor()) + threadpool_(node.config().node.threads_(), node.config().node.priority_()), + independent_strand_(threadpool_.service().get_executor()) { } code chaser_validate::start() NOEXCEPT { - update_position(archive().get_fork()); + const auto& query = archive(); + filters_ = query.neutrino_enabled(); + set_position(query.get_fork()); SUBSCRIBE_EVENTS(handle_event, _1, _2, _3); return error::success; } @@ -100,9 +97,7 @@ bool chaser_validate::handle_event(const code&, chase event_, { if (concurrent_ || mature_) { - ////POST(do_bump, height_t{}); - boost::asio::post(strand_, - BIND(do_bump, height_t{})); + POST(do_bump, height_t{}); } break; @@ -114,9 +109,7 @@ bool chaser_validate::handle_event(const code&, chase event_, if (concurrent_ || mature_) { - ////POST(do_checked, std::get(value)); - boost::asio::post(strand_, - BIND(do_checked, std::get(value))); + POST(do_checked, std::get(value)); } break; @@ -124,17 +117,13 @@ bool chaser_validate::handle_event(const code&, chase event_, case chase::regressed: { BC_ASSERT(std::holds_alternative(value)); - ////POST(do_regressed, std::get(value)); - boost::asio::post(strand_, - BIND(do_regressed, std::get(value))); + POST(do_regressed, std::get(value)); break; } case chase::disorganized: { BC_ASSERT(std::holds_alternative(value)); - ////POST(do_regressed, std::get(value)); - boost::asio::post(strand_, - BIND(do_regressed, std::get(value))); + POST(do_regressed, std::get(value)); break; } case chase::stop: @@ -150,7 +139,7 @@ bool chaser_validate::handle_event(const code&, chase event_, return true; } -// track downloaded in order (to validate) +// track downloaded // ---------------------------------------------------------------------------- void chaser_validate::do_regressed(height_t branch_point) NOEXCEPT @@ -161,14 +150,14 @@ void chaser_validate::do_regressed(height_t branch_point) NOEXCEPT return; // Update position and wait. - update_position(branch_point); + set_position(branch_point); } +// Candidate block checked at given height, if next then validate/advance. void chaser_validate::do_checked(height_t height) NOEXCEPT { BC_ASSERT(stranded()); - // Candidate block was checked at the given height, validate/advance. if (height == add1(position())) do_bump(height); } @@ -179,8 +168,7 @@ void chaser_validate::do_bump(height_t) NOEXCEPT const auto& query = archive(); // Bypass until next event if validation backlog is full. - for (auto height = add1(position()); - (validation_backlog_ < maximum_backlog_) && !closed(); ++height) + for (auto height = add1(position()); unfilled() && !closed(); ++height) { const auto link = query.to_candidate(height); const auto ec = query.get_block_state(link); @@ -190,7 +178,7 @@ void chaser_validate::do_bump(height_t) NOEXCEPT return; // The size of the job is not relevant to the backlog cost. - ++validation_backlog_; + ++backlog_; if (ec == database::error::block_unconfirmable) { @@ -208,12 +196,11 @@ void chaser_validate::do_bump(height_t) NOEXCEPT continue; } - // concurrent by block - boost::asio::post(threadpool_.service(), - BIND(validate_block, link)); + PARALLEL(validate_block, link); } } +// unstranded (concurrent by block) void chaser_validate::validate_block(const header_link& link) NOEXCEPT { if (closed()) @@ -223,27 +210,29 @@ void chaser_validate::validate_block(const header_link& link) NOEXCEPT const auto block = query.get_block(link); if (!block) { - ////POST(complete_block, error::validate1, link, zero); - boost::asio::post(strand_, - BIND(complete_block, error::validate1, link, zero)); + POST(complete_block, error::validate1, link, zero); return; } chain::context ctx{}; if (!query.get_context(ctx, link)) { - ////POST(complete_block, error::validate2, link, zero); - boost::asio::post(strand_, - BIND(complete_block, error::validate2, link, zero)); + POST(complete_block, error::validate2, link, zero); return; } - block->populate(); - if (!query.populate(*block)) + if (!block->populate()) { - ////POST(complete_block, error::validate3, link, ctx.height); - boost::asio::post(strand_, - BIND(complete_block, error::validate3, link, ctx.height)); + // input.metadata.locked set invalid for relative locktime (bip68). + // Otherwise internal spends do not require confirmability checks. + POST(complete_block, error::validate3, link, ctx.height); + return; + } + else if (!query.populate(*block)) + { + // Prepopulated prevouts are bypassed here, including metadata. + // .input.metadata.parent (tx link) and .coinbase (bool) are set here. + POST(complete_block, error::validate4, link, ctx.height); return; } @@ -252,20 +241,21 @@ void chaser_validate::validate_block(const header_link& link) NOEXCEPT ((ec = block->connect(ctx)))) { if (!query.set_block_unconfirmable(link)) - ec = error::validate4; + ec = error::validate5; } - else if (!query.set_block_valid(link)) + else if (!query.set_block_valid(link, block->fees())) { - ec = error::validate5; + ec = error::validate6; } else { + set_neutrino(link, *block); + set_prevouts(ctx.height, *block); + fire(events::block_validated, ctx.height); } - ////POST(complete_block, ec, link, ctx.height); - boost::asio::post(strand_, - BIND(complete_block, ec, link, ctx.height)); + POST(complete_block, ec, link, ctx.height); } void chaser_validate::complete_block(const code& ec, const header_link& link, @@ -274,11 +264,11 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, BC_ASSERT(stranded()); // The size of the job is not relevant to the backlog cost. - --validation_backlog_; + --backlog_; if (ec) { - if (ec == error::validate6) + if (ec == error::validate7) { fault(ec); return; @@ -293,79 +283,51 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, notify(ec, chase::valid, possible_wide_cast(height)); // Prevent stall by posting internal event, avoid hitting external handlers. - if (is_zero(validation_backlog_)) + if (is_zero(backlog_)) handle_event(ec, chase::bump, height_t{}); } -// neutrino +// setters // ---------------------------------------------------------------------------- -// Since neutrino filters must be computed in block order, and require full -// block and previous output deserialization, configuration of this option -// implies either ordered validation or re-deserialization of 115% of the store -// in another chaser or populated block caching in that chaser (which implies a -// controlled/narrow window of concurrent validation). - -// Returns null_hash if not found, intended for genesis block. -hash_digest chaser_validate::get_neutrino(size_t height) const NOEXCEPT -{ - hash_digest neutrino{}; - const auto& query = archive(); - if (query.neutrino_enabled()) - { - // candidate regression race may result in null_hash, which is ok, as - // in that case position will subsequently be reset to below height. - query.get_filter_head(neutrino, query.to_candidate(height)); - } +// unstranded (concurrent by block) - return neutrino; -} - -// This can only fail if block missing or prevouts are not fully populated. -bool chaser_validate::update_neutrino(const header_link& link) NOEXCEPT +bool chaser_validate::set_neutrino(const header_link& link, + const chain::block& block) NOEXCEPT { - const auto& query = archive(); - if (!query.neutrino_enabled()) + if (!filters_) return true; // Avoid computing the filter if already stored. + auto& query = archive(); if (!query.to_filter(link).is_terminal()) return true; - const auto block_ptr = query.get_block(link); - if (!block_ptr) + // Only fails if prevouts are not fully populated. + data_chunk filter{}; + if (!neutrino::compute_filter(filter, block)) return false; - const auto& block = *block_ptr; - return query.populate(block) && update_neutrino(link, block); + return query.set_filter_body(link, filter); } -// This can only fail if prevouts are not fully populated. -bool chaser_validate::update_neutrino(const header_link& link, - const chain::block& block) NOEXCEPT +bool chaser_validate::set_prevouts(size_t, const chain::block&) NOEXCEPT { - auto& query = archive(); - if (!query.neutrino_enabled()) - return true; - - // Filters are computed during validate, in parallel. - // Validation skipped under checkpoint/milestone to only compute filter. - data_chunk filter{}; - if (!compute_filter(filter, block)) - return false; - - // TODO: move filter header computation to confirmation chaser (ordered). - // TODO: this requires distinct storage array[n, 256] for filter headers. - neutrino_ = compute_filter_header(neutrino_, filter); - return query.set_filter(link, neutrino_, filter); + // TODO: need to be able to differentiate internal vs. store. + // This tells us what to cache, skip internal and set store populated. + return {}; } -// position +// Strand. // ---------------------------------------------------------------------------- -void chaser_validate::update_position(size_t height) NOEXCEPT +network::asio::strand& chaser_validate::strand() NOEXCEPT +{ + return independent_strand_; +} + +bool chaser_validate::stranded() const NOEXCEPT { - set_position(height); - neutrino_ = get_neutrino(position()); + return independent_strand_.running_in_this_thread(); } BC_POP_WARNING() diff --git a/src/error.cpp b/src/error.cpp index 6eda9964b..cb61db997 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -72,6 +72,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { validate4, "validate4" }, { validate5, "validate5" }, { validate6, "validate6" }, + { validate7, "validate7" }, { confirm1, "confirm1" }, { confirm2, "confirm2" }, { confirm3, "confirm3" }, diff --git a/src/settings.cpp b/src/settings.cpp index 74b276fbc..c594c78dd 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -98,6 +98,11 @@ settings::settings(chain::selection) NOEXCEPT // TODO: testnet, etc. maximum_concurrency, snapshot_bytes. } +size_t settings::threads_() const NOEXCEPT +{ + return std::max(threads, one); +} + size_t settings::maximum_height_() const NOEXCEPT { return to_bool(maximum_height) ? maximum_height : max_size_t; @@ -118,5 +123,11 @@ network::wall_clock::duration settings::currency_window() const NOEXCEPT return network::minutes(currency_window_minutes); } +network::thread_priority settings::priority_() const NOEXCEPT +{ + return priority_validation ? network::thread_priority::high : + network::thread_priority::normal; +} + } // namespace node } // namespace libbitcoin diff --git a/test/settings.cpp b/test/settings.cpp index 0458c7f52..9a7c350a7 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -68,10 +68,15 @@ BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) BOOST_REQUIRE_EQUAL(node.maximum_concurrency, 50000_u32); BOOST_REQUIRE_EQUAL(node.maximum_concurrency_(), 50000_size); BOOST_REQUIRE_EQUAL(node.sample_period_seconds, 10_u16); + BOOST_REQUIRE_EQUAL(node.currency_window_minutes, 60_u32); BOOST_REQUIRE_EQUAL(node.threads, 1_u32); + + BOOST_REQUIRE_EQUAL(node.threads_(), one); + BOOST_REQUIRE_EQUAL(node.maximum_height_(), max_size_t); + BOOST_REQUIRE_EQUAL(node.maximum_concurrency_(), 50'000_size); BOOST_REQUIRE(node.sample_period() == steady_clock::duration(seconds(10))); - BOOST_REQUIRE_EQUAL(node.currency_window_minutes, 60_u32); BOOST_REQUIRE(node.currency_window() == steady_clock::duration(minutes(60))); + BOOST_REQUIRE(node.priority_() == network::thread_priority::normal); } BOOST_AUTO_TEST_SUITE_END()