diff --git a/console/executor.cpp b/console/executor.cpp index b35bf68a..d81f15b6 100644 --- a/console/executor.cpp +++ b/console/executor.cpp @@ -42,8 +42,9 @@ using namespace system; using namespace std::placeholders; // const executor statics -const std::string executor::quit_{ "q" }; +// "c" avoids conflict with network "quit" messages. const std::string executor::name_{ "bn" }; +const std::string executor::close_{ "c" }; const std::unordered_map executor::defined_ { { levels::application, true }, @@ -68,7 +69,7 @@ const std::unordered_map executor::display_ { levels::wire, "toggle Wire shark" }, // not implemented { levels::remote, "toggle Remote fault" }, { levels::fault, "toggle internal Fault" }, - { levels::quit, "Quit" } + { levels::quit, "toggle Quitting" } }; const std::unordered_map executor::keys_ { @@ -81,7 +82,7 @@ const std::unordered_map executor::keys_ { "w", levels::wire }, { "r", levels::remote }, { "f", levels::fault }, - { quit_, levels::quit } + { "q", levels::quit } }; const std::unordered_map executor::events_ { @@ -1601,6 +1602,15 @@ void executor::subscribe_capture() capture_.subscribe([&](const code& ec, const std::string& line) { const auto token = system::trim_copy(line); + + // Close (this isn't a toggle). + if (token == close_) + { + logger("CONSOLE: Close"); + stop(error::success); + return false; + } + if (!keys_.contains(token)) { logger("CONSOLE: '" + line + "'"); @@ -1608,15 +1618,6 @@ void executor::subscribe_capture() } const auto index = keys_.at(token); - - // Quit (this level isn't a toggle). - if (index == levels::quit) - { - logger("CONSOLE: " + display_.at(index)); - stop(error::success); - return false; - } - if (defined_.at(index)) { toggle_.at(index) = !toggle_.at(index); diff --git a/console/executor.hpp b/console/executor.hpp index 816b3ee3..da63aba4 100644 --- a/console/executor.hpp +++ b/console/executor.hpp @@ -83,8 +83,8 @@ class executor void read_test() const; void write_test(); - static const std::string quit_; static const std::string name_; + static const std::string close_; static const std::unordered_map defined_; static const std::unordered_map display_; static const std::unordered_map keys_; @@ -103,7 +103,7 @@ class executor std::istream& input_; std::ostream& output_; network::logger log_{}; - network::capture capture_{ input_, quit_ }; + network::capture capture_{ input_, close_ }; std_array toggle_ { true, // application diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index 5935a219..6a134f65 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -47,44 +47,51 @@ class BCN_API chaser public: enum class chase { - /// A new strong branch exists (strong height_t). - /// Issued by 'block' and handled by 'confirm'. - /// The block chaser works with the blocks-first protocol. - /// Bocks first performs header/checked/connected stages. - block, - - /// A new strong branch exists (strong height_t). + /// A new candidate branch exists (height_t). /// Issued by 'header' and handled by 'check'. - /// The block chaser works with the header-first protocol. header, - /// New unassociated blocks exist in the strong branch. - unassociated, + /// New candidate headers without txs exist (count_t). + /// Issued by 'check' and handled by 'block_in_31800'. + download, - /// A block has been downloaded, checked and stored (header_t). - /// Issued by 'check' and handled by 'connect'. + /// A block has been downloaded, checked and stored (height_t). + /// Issued by 'block_in_31800' and handled by 'connect'. checked, + + /// A downloaded block has failed check (header_t). + /// Issued by 'block_in_31800' and handled by 'header'. unchecked, - /// A branch has been connected (header_t|height_t). + /// A branch has been connected (height_t). /// Issued by 'connect' and handled by 'confirm'. connected, + + /// A checked block has failed connect (header_t). + /// Issued by 'connect' and handled by 'header'. unconnected, - /// A branch has been confirmed (fork header_t|height_t). + /// A branch has been confirmed (height_t). /// Issued by 'confirm' and handled by 'transaction'. confirmed, + + /// A connected block has failed confirm (header_t). + /// Issued by 'confirm' and handled by 'header' (and 'block'). unconfirmed, /// A new transaction has been added to the pool (transaction_t). /// Issued by 'transaction' and handled by 'candidate'. transaction, - /// A new candidate block has been created (?). - /// Issued by 'candidate' and handled by miners. + /// A new candidate block (template) has been created (). + /// Issued by 'candidate' and handled by [miners]. candidate, - /// Service is stopping (accompanied by error::service_stopped). + /// Legacy: A new strong branch exists (branch height_t). + /// Issued by 'block' and handled by 'confirm'. + block, + + /// Service is stopping (accompanied by error::service_stopped), (). stop }; @@ -92,6 +99,7 @@ class BCN_API chaser using header_t = database::header_link::integer; using transaction_t = database::tx_link::integer; using flags_t = database::context::flag::integer; + using count_t = height_t; typedef std::function organize_handler; typedef std::variant link; @@ -106,6 +114,12 @@ class BCN_API chaser virtual code start() NOEXCEPT = 0; protected: + ////using channel_notifier = network::p2p::channel_notifier; + ////using channel_completer = network::p2p::channel_completer; + using stop_handler = network::p2p::stop_handler; + using stop_completer = network::p2p::stop_completer; + using key = network::p2p::object_key; + /// Bind a method (use BIND). template auto bind(Method&& method, Args&&... args) NOEXCEPT @@ -148,6 +162,16 @@ class BCN_API chaser /// Set chaser event (does not require node strand). void notify(const code& ec, chase event_, link value) NOEXCEPT; + /////// Subscribe to connection creation. + /////// A call after close invokes handlers with error::subscriber_stopped. + ////void subscribe_connect(channel_notifier&& handler, + //// channel_completer&& complete) NOEXCEPT; + + /// Subscribe to service stop. + /// A call after close invokes handlers with error::subscriber_stopped. + void subscribe_close(stop_handler&& handler, + stop_completer&& complete) NOEXCEPT; + private: void do_notify(const code& ec, chase event_, link value) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_block.hpp b/include/bitcoin/node/chasers/chaser_block.hpp index cf345ecb..e20d2791 100644 --- a/include/bitcoin/node/chasers/chaser_block.hpp +++ b/include/bitcoin/node/chasers/chaser_block.hpp @@ -65,7 +65,7 @@ class BCN_API chaser_block link value) NOEXCEPT; // Handle events. - virtual void handle_unconnected(height_t height) NOEXCEPT; + virtual void handle_unconnected(header_t height) NOEXCEPT; /// Sum of work from header to fork point (excluded). virtual bool get_branch_work(uint256_t& work, size_t& point, @@ -87,12 +87,12 @@ class BCN_API chaser_block const system::chain::chain_state::ptr& state) NOEXCEPT; /// Store block to database and push to top of candidate chain. - virtual database::header_link push( + virtual database::header_link push_block( const system::chain::block::cptr& block, const system::chain::context& context) const NOEXCEPT; /// Move tree header to database and push to top of candidate chain. - virtual bool push(const system::hash_digest& key) NOEXCEPT; + virtual bool push_block(const system::hash_digest& key) NOEXCEPT; /// Populate block prevouts and metadata from block tree. virtual void populate(const system::chain::block& block) const NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_check.hpp b/include/bitcoin/node/chasers/chaser_check.hpp index 5e8e70a1..e88a7958 100644 --- a/include/bitcoin/node/chasers/chaser_check.hpp +++ b/include/bitcoin/node/chasers/chaser_check.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -37,6 +38,7 @@ class BCN_API chaser_check public: typedef std::shared_ptr map_ptr; typedef std::function handler; + typedef std::list maps; DELETE_COPY_MOVE(chaser_check); @@ -50,6 +52,8 @@ class BCN_API chaser_check network::result_handler&& handler) NOEXCEPT; protected: + virtual bool handle_close(const code& ec) NOEXCEPT; + virtual void handle_subscribed(const code& ec, const key& id) NOEXCEPT; virtual void handle_put_hashes(const code&) NOEXCEPT; virtual void handle_header(height_t branch_point) NOEXCEPT; virtual void handle_event(const code& ec, chase event_, @@ -59,11 +63,18 @@ class BCN_API chaser_check virtual void do_put_hashes(const map_ptr& map, const network::result_handler& handler) NOEXCEPT; - // This is thread safe. +private: + void initialize_map(maps& table) const NOEXCEPT; + size_t count_map(const maps& table) const NOEXCEPT; + map_ptr make_map(size_t start, size_t count=max_size_t) const NOEXCEPT; + map_ptr get_map(maps& table) NOEXCEPT; + + // These are thread safe. + const size_t connections_; const size_t inventory_; // This is protected by strand. - database::associations map_{}; + maps map_table_{}; }; } // namespace node diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index a0b7f9b1..33fb3bcd 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -42,7 +42,7 @@ class BCN_API chaser_confirm virtual code start() NOEXCEPT; protected: - virtual void handle_connected(header_t block) NOEXCEPT; + virtual void handle_connected(height_t block) NOEXCEPT; virtual void handle_event(const code& ec, chase event_, link value) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chaser_connect.hpp b/include/bitcoin/node/chasers/chaser_connect.hpp index 71554bab..46cb2244 100644 --- a/include/bitcoin/node/chasers/chaser_connect.hpp +++ b/include/bitcoin/node/chasers/chaser_connect.hpp @@ -42,7 +42,7 @@ class BCN_API chaser_connect virtual code start() NOEXCEPT; protected: - virtual void handle_checked(header_t block) NOEXCEPT; + virtual void handle_checked(height_t block) NOEXCEPT; virtual void handle_event(const code& ec, chase event_, link value) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chaser_header.hpp b/include/bitcoin/node/chasers/chaser_header.hpp index df7ab990..20fb29f8 100644 --- a/include/bitcoin/node/chasers/chaser_header.hpp +++ b/include/bitcoin/node/chasers/chaser_header.hpp @@ -65,7 +65,7 @@ class BCN_API chaser_header link value) NOEXCEPT; // Handle events. - virtual void handle_unchecked(height_t height) NOEXCEPT; + virtual void handle_unchecked(header_t height) NOEXCEPT; /// Sum of work from header to branch point (excluded). virtual bool get_branch_work(uint256_t& work, size_t& point, @@ -90,12 +90,12 @@ class BCN_API chaser_header const system::chain::chain_state::ptr& state) NOEXCEPT; /// Store header to database and push to top of candidate chain. - virtual database::header_link push( + virtual database::header_link push_header( const system::chain::header::cptr& header, const system::chain::context& context) const NOEXCEPT; /// Move tree header to database and push to top of candidate chain. - virtual bool push(const system::hash_digest& key) NOEXCEPT; + virtual bool push_header(const system::hash_digest& key) NOEXCEPT; /// Validate and organize next header in sequence relative to caller peer. virtual void do_organize(const system::chain::header::cptr& header, diff --git a/include/bitcoin/node/chasers/chaser_transaction.hpp b/include/bitcoin/node/chasers/chaser_transaction.hpp index 114f2ef3..81f3b96a 100644 --- a/include/bitcoin/node/chasers/chaser_transaction.hpp +++ b/include/bitcoin/node/chasers/chaser_transaction.hpp @@ -43,7 +43,7 @@ class BCN_API chaser_transaction virtual void store(const system::chain::transaction::cptr& block) NOEXCEPT; protected: - virtual void handle_confirmed(header_t block) NOEXCEPT; + virtual void handle_confirmed(height_t block) NOEXCEPT; virtual void handle_event(const code& ec, chase event_, link value) NOEXCEPT; diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index 4de70a00..0e683e4d 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -47,6 +47,7 @@ enum error_t : uint8_t // network slow_channel, stalled_channel, + exhausted_channel, // blockchain orphan_block, diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp index 427c5fe3..1487ca76 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp @@ -61,32 +61,27 @@ class BCN_API protocol_block_in_31800 void stopping(const code& ec) NOEXCEPT override; protected: - /// Request blocks from peer. + /// Methods. + virtual void restore(const map_ptr& map) NOEXCEPT; + virtual void start_performance() NOEXCEPT; + virtual void pause_performance() NOEXCEPT; + virtual void stop_performance() NOEXCEPT; + virtual void send_performance(uint64_t rate) NOEXCEPT; virtual void send_get_data(const map_ptr& map) NOEXCEPT; - /// Recieved incoming block message. + /// Handlers. + virtual void handle_performance_timer(const code& ec) NOEXCEPT; + virtual void handle_send_performance(const code& ec) NOEXCEPT; virtual bool handle_receive_block(const code& ec, const network::messages::block::cptr& message) NOEXCEPT; - - /// Handle performance timer event. - virtual void handle_performance_timer(const code& ec) NOEXCEPT; - - /// Handle result of performance reporting. - virtual void handle_performance(const code& ec) NOEXCEPT; - - virtual void handle_unassociated(chaser::header_t block) NOEXCEPT; + virtual void handle_download(chaser::count_t count) NOEXCEPT; virtual void handle_event(const code& ec, chaser::chase event_, chaser::link value) NOEXCEPT; - - /// Manage download queue. virtual void handle_put_hashes(const code& ec) NOEXCEPT; virtual void handle_get_hashes(const code& ec, const map_ptr& map) NOEXCEPT; private: - void reset_performance() NOEXCEPT; - void set_performance(uint64_t rate) NOEXCEPT; - void do_handle_performance(const code& ec) NOEXCEPT; network::messages::get_data create_get_data( const map_ptr& map) const NOEXCEPT; diff --git a/src/chasers/chaser.cpp b/src/chasers/chaser.cpp index a65c6b93..01528836 100644 --- a/src/chasers/chaser.cpp +++ b/src/chasers/chaser.cpp @@ -100,6 +100,18 @@ void chaser::do_notify(const code& ec, chase event_, link value) NOEXCEPT subscriber_.notify(ec, event_, value); } +////void chaser::subscribe_connect(channel_notifier&& handler, +//// channel_completer&& complete) NOEXCEPT +////{ +//// node_.subscribe_connect(std::move(handler), std::move(complete)); +////} + +void chaser::subscribe_close(stop_handler&& handler, + stop_completer&& complete) NOEXCEPT +{ + node_.subscribe_close(std::move(handler), std::move(complete)); +} + BC_POP_WARNING() } // namespace database diff --git a/src/chasers/chaser_block.cpp b/src/chasers/chaser_block.cpp index 730336cc..30ea3407 100644 --- a/src/chasers/chaser_block.cpp +++ b/src/chasers/chaser_block.cpp @@ -70,13 +70,13 @@ code chaser_block::start() NOEXCEPT void chaser_block::handle_event(const code&, chase event_, link value) NOEXCEPT { - if (event_ == chase::unconnected) + if (event_ == chase::unconfirmed) { - POST(handle_unconnected, std::get(value)); + POST(handle_unconnected, std::get(value)); } } -void chaser_block::handle_unconnected(height_t) NOEXCEPT +void chaser_block::handle_unconnected(header_t) NOEXCEPT { BC_ASSERT(stranded()); } @@ -117,10 +117,10 @@ void chaser_block::do_organize(const block::cptr& block_ptr, } // If header exists test for prior invalidity as a block. - const auto id = query.to_header(hash); - if (!id.is_terminal()) + const auto link = query.to_header(hash); + if (!link.is_terminal()) { - const auto ec = query.get_block_state(id); + const auto ec = query.get_block_state(link); if (ec == database::error::block_unconfirmable) { handler(ec, {}); @@ -255,9 +255,9 @@ void chaser_block::do_organize(const block::cptr& block_ptr, } // Push stored strong block headers to candidate chain. - for (const auto& link: views_reverse(store_branch)) + for (const auto& id: views_reverse(store_branch)) { - if (!query.push_candidate(link)) + if (!query.push_candidate(id)) { handler(error::store_integrity, height); return; @@ -267,7 +267,7 @@ void chaser_block::do_organize(const block::cptr& block_ptr, // Store strong tree blocks and push headers to candidate chain. for (const auto& key: views_reverse(tree_branch)) { - if (!push(key)) + if (!push_block(key)) { handler(error::store_integrity, height); return; @@ -275,15 +275,17 @@ void chaser_block::do_organize(const block::cptr& block_ptr, } // Push new block as top of candidate chain. - if (push(block_ptr, state->context()).is_terminal()) + if (push_block(block_ptr, state->context()).is_terminal()) { handler(error::store_integrity, height); return; } + // ------------------------------------------------------------------------ + top_state_ = state; const auto branch_point = possible_narrow_cast(point); - notify(error::success, chase::block, { branch_point }); + notify(error::success, chase::block, branch_point); handler(error::success, height); } @@ -380,7 +382,7 @@ void chaser_block::cache(const block::cptr& block, tree_.insert({ block->hash(), { block, state } }); } -database::header_link chaser_block::push(const block::cptr& block, +database::header_link chaser_block::push_block(const block::cptr& block, const context& context) const NOEXCEPT { auto& query = archive(); @@ -394,7 +396,7 @@ database::header_link chaser_block::push(const block::cptr& block, return query.push_candidate(link) ? link : database::header_link{}; } -bool chaser_block::push(const hash_digest& key) NOEXCEPT +bool chaser_block::push_block(const hash_digest& key) NOEXCEPT { const auto value = tree_.extract(key); BC_ASSERT_MSG(!value.empty(), "missing tree value"); diff --git a/src/chasers/chaser_candidate.cpp b/src/chasers/chaser_candidate.cpp index fa5a215b..01b42fbf 100644 --- a/src/chasers/chaser_candidate.cpp +++ b/src/chasers/chaser_candidate.cpp @@ -60,6 +60,7 @@ code chaser_candidate::start() NOEXCEPT void chaser_candidate::handle_event(const code&, chase event_, link value) NOEXCEPT { + // TODO: also handle confirmed/unconfirmed. if (event_ == chase::transaction) { POST(handle_transaction, std::get(value)); diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index 057b463d..77753322 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -38,10 +38,14 @@ using namespace system; using namespace system::chain; using namespace std::placeholders; +// Shared pointers required for lifetime in handler parameters. BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) +BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) +BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) chaser_check::chaser_check(full_node& node) NOEXCEPT : chaser(node), + connections_(node.network_settings().outbound_connections), inventory_(system::lesser(node.node_settings().maximum_inventory, network::messages::max_inventory)) { @@ -51,20 +55,76 @@ chaser_check::~chaser_check() NOEXCEPT { } +// utility +// ---------------------------------------------------------------------------- +// private + +size_t chaser_check::count_map(const maps& table) const NOEXCEPT +{ + return std::accumulate(table.begin(), table.end(), zero, + [](size_t sum, const map_ptr& map) NOEXCEPT + { + return sum + map->size(); + }); +} + +void chaser_check::initialize_map(maps& table) const NOEXCEPT +{ + auto start = archive().get_fork(); + while (true) + { + const auto map = make_map(start, inventory_); + if (map->empty()) break; + table.push_front(map); + start = map->top().height; + } +} + +chaser_check::map_ptr chaser_check::make_map(size_t start, + size_t count) const NOEXCEPT +{ + return std::make_shared( + archive().get_unassociated_above(start, count)); +} + +chaser_check::map_ptr chaser_check::get_map(maps& table) NOEXCEPT +{ + return table.empty() ? std::make_shared() : + pop(table); +} + // start // ---------------------------------------------------------------------------- +// TODO: node.subscribe_close(handle_close, handle_subscribed); +// Complete start/stats in handle_subscribed, report stats in handle_close. + code chaser_check::start() NOEXCEPT { BC_ASSERT(node_stranded()); - // Initialize map to all unassociated blocks starting at genesis. - map_ = archive().get_all_unassociated_above(zero); - LOGN("Unassociated candidates (" << map_.size() << ")."); + subscribe_close(BIND(handle_close, _1), BIND(handle_subscribed, _1, _2)); + + LOGN("Candidate fork (" << archive().get_fork() << ")."); + + initialize_map(map_table_); + + LOGN("Unassociated candidates (" << count_map(map_table_) << ")."); return SUBSCRIBE_EVENTS(handle_event, _1, _2, _3); } +bool chaser_check::handle_close(const code&) NOEXCEPT +{ + // There may still be channels running, so this isn't exact. + LOGN("Top associated (" << archive().get_last_associated() << ")."); + return false; +} + +void chaser_check::handle_subscribed(const code&, const key&) NOEXCEPT +{ +} + // event handlers // ---------------------------------------------------------------------------- @@ -73,21 +133,20 @@ void chaser_check::handle_event(const code&, chase event_, { if (event_ == chase::header) { - handle_header(std::get(value)); + BC_ASSERT(std::holds_alternative(value)); + ////LOGN("get chase::header " << std::get(value)); + POST(handle_header, std::get(value)); } } // Stale branches are just be allowed to complete (still downloaded). void chaser_check::handle_header(height_t branch_point) NOEXCEPT { - const auto map = std::make_shared( - archive().get_all_unassociated_above(branch_point)); - - if (map->empty()) - return; + BC_ASSERT(stranded()); - network::result_handler handler = BIND(handle_put_hashes, _1); - POST(do_put_hashes, map, std::move(handler)); + // This can produce duplicate downloads in relation to those outstanding, + // which is ok. That implies a rerg and then a reorg back before complete. + do_put_hashes(make_map(branch_point), BIND(handle_put_hashes, _1)); } void chaser_check::handle_put_hashes(const code&) NOEXCEPT @@ -113,39 +172,40 @@ void chaser_check::put_hashes(const map_ptr& map, this, map, std::move(handler))); } -// TODO: post event causing channels to put some? -// TODO: otherwise channels may ,monopolize work. void chaser_check::do_get_hashes(const handler& handler) NOEXCEPT { BC_ASSERT(stranded()); - auto& index = map_.get(); - const auto size = index.size(); - const auto count = std::min(size, inventory_); - const auto map = std::make_shared(); + const auto map = get_map(map_table_); - /// Merge "moves" elements from one table to another. - map->merge(index, index.begin(), std::next(index.begin(), count)); - LOGN("Hashes: (" - << size << " - " - << map->size() << " = " - << map_.size() << ")."); + ////LOGN("Hashes -" << map->size() << " (" + //// << count_map(map_table_) << ") remain."); handler(error::success, map); } -// TODO: post event causing channels to get some? -// TODO: otherwise completed channels remain idle until header event. void chaser_check::do_put_hashes(const map_ptr& map, const network::result_handler& handler) NOEXCEPT { BC_ASSERT(stranded()); - /// Merge "moves" elements from one table to another. - map_.merge(*map); + if (!map->empty()) + { + map_table_.push_back(map); + + ////LOGN("set chase::download " << map->size()); + notify(error::success, chase::download, + system::possible_narrow_cast(map->size())); + } + + ////LOGN("Hashes +" << map->size() << " (" + //// << count_map(map_table_) << ") remain."); + handler(error::success); } +BC_POP_WARNING() +BC_POP_WARNING() BC_POP_WARNING() } // namespace database diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index db3afe56..84387d04 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -60,12 +60,12 @@ void chaser_confirm::handle_event(const code&, chase event_, { if (event_ == chase::connected) { - POST(handle_connected, std::get(value)); + POST(handle_connected, std::get(value)); } } -// TODO: handle new strong connected branch (may issue 'confirmed'). -void chaser_confirm::handle_connected(header_t) NOEXCEPT +// TODO: handle new strong connected branch (may issue 'confirmed'/'unconfirmed'). +void chaser_confirm::handle_connected(height_t) NOEXCEPT { BC_ASSERT(stranded()); } diff --git a/src/chasers/chaser_connect.cpp b/src/chasers/chaser_connect.cpp index 2a7a9e27..8f2913f8 100644 --- a/src/chasers/chaser_connect.cpp +++ b/src/chasers/chaser_connect.cpp @@ -58,14 +58,14 @@ code chaser_connect::start() NOEXCEPT void chaser_connect::handle_event(const code&, chase event_, link value) NOEXCEPT { - if (event_ == chase::connected) + if (event_ == chase::checked) { - POST(handle_checked, std::get(value)); + POST(handle_checked, std::get(value)); } } -// TODO: handle the new checked blocks (may issue 'connected'). -void chaser_connect::handle_checked(header_t) NOEXCEPT +// TODO: handle the new checked blocks (may issue 'connected'/'unconnected'). +void chaser_connect::handle_checked(height_t) NOEXCEPT { BC_ASSERT(stranded()); diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index db24566a..4ef074b9 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -68,6 +68,9 @@ code chaser_header::start() NOEXCEPT top_state_ = archive().get_candidate_chain_state( config().bitcoin, archive().get_top_candidate()); + LOGN("Candidate top ["<< encode_hash(top_state_->hash()) << ":" + << top_state_->height() << "]."); + return SUBSCRIBE_EVENTS(handle_event, _1, _2, _3); } @@ -78,25 +81,31 @@ code chaser_header::start() NOEXCEPT void chaser_header::handle_event(const code&, chase event_, link value) NOEXCEPT { - if (event_ == chase::unchecked) + // Posted due to block/header invalidation. + if (event_ == chase::unchecked || + event_ == chase::unconnected || + event_ == chase::unconfirmed) { - POST(handle_unchecked, std::get(value)); + ////LOGN("get chase::invalid " << std::get(value)); + POST(handle_unchecked, std::get(value)); } } // TODO: chaser_header controls canididate organization for headers first // TODO: mark all headers above as invalid and pop from candidate chain. -// TODO: if weaker than confirmed chain reorg into confirmed. There may also -// TODO: be a stronger cached chain but there is no marker for its top. +// TODO: if weaker than confirmed chain reorg from confirmed. There may also +// TODO: be a stronger cached chain now but there is no marker for its top, +// TODO: but that self-corrects with the next ancestor announcement, restart. // TODO: candidate is weaker than confirmed, since it didn't reorg prior. -// TODO: so just close conformed to candidate and wait for next block. This +// TODO: so just clone confirmed to candidate and wait for next block. This // TODO: might result in a material delay in the case where there is a strong // TODO: but also invalid block (extremely rare) but this is easily recovered. -// TODO: (1) from invalid key mark all above as invalid and pop. -// TODO: (2) pop down to current common (fork point) into the header cache. -// TODO: (3) push confirmed into candidate until equal and notify. +// TODO: (1) pop down to invalid and mark all as unconfirmable. +// TODO: (2) pop down to current common (fork point) into the header tree. +// TODO: (3) push up to top confirmed into candidate and notify (?). +// TODO: (4) candidate chain is now confirmed so all pending work is void. // TODO: chaser_check must reset header as its top. -void chaser_header::handle_unchecked(height_t) NOEXCEPT +void chaser_header::handle_unchecked(header_t) NOEXCEPT { BC_ASSERT(stranded()); } @@ -136,10 +145,10 @@ void chaser_header::do_organize(const header::cptr& header_ptr, } // If header exists test for prior invalidity as a block. - const auto id = query.to_header(hash); - if (!id.is_terminal()) + const auto link = query.to_header(hash); + if (!link.is_terminal()) { - const auto ec = query.get_header_state(id); + const auto ec = query.get_header_state(link); if (ec == database::error::block_unconfirmable) { handler(ec, {}); @@ -161,29 +170,23 @@ void chaser_header::do_organize(const header::cptr& header_ptr, state.reset(new chain_state{ *state, header, coin }); const auto height = state->height(); - // Validate header. + // Check/Accept header. // ------------------------------------------------------------------------ // Header validations are not bypassed when under checkpoint/milestone. - // Checkpoints are considered chain not block/header validation. - if (checkpoint::is_conflict(coin.checkpoints, hash, height)) - { - handler(system::error::checkpoint_conflict, height); - return; - } - if (const auto error = header.check(coin.timestamp_limit_seconds, - coin.proof_of_work_limit, coin.scrypt_proof_of_work)) + code error{ system::error::checkpoint_conflict }; + if (checkpoint::is_conflict(coin.checkpoints, hash, height) || + ((error = header.check(coin.timestamp_limit_seconds, + coin.proof_of_work_limit,coin.scrypt_proof_of_work))) || + ((error = header.accept(state->context())))) { + // There is no storage or notification of an invalid header. handler(error, height); return; } - if (const auto error = header.accept(state->context())) - { - handler(error, height); - return; - } + // ------------------------------------------------------------------------ // A checkpointed or milestoned branch always gets disk stored. Otherwise // branch must be both current and of sufficient chain work to be stored. @@ -245,9 +248,9 @@ void chaser_header::do_organize(const header::cptr& header_ptr, } // Push stored strong headers to candidate chain. - for (const auto& link: views_reverse(store_branch)) + for (const auto& id: views_reverse(store_branch)) { - if (!query.push_candidate(link)) + if (!query.push_candidate(id)) { handler(error::store_integrity, height); return; @@ -257,7 +260,7 @@ void chaser_header::do_organize(const header::cptr& header_ptr, // Store strong tree headers and push to candidate chain. for (const auto& key: views_reverse(tree_branch)) { - if (!push(key)) + if (!push_header(key)) { handler(error::store_integrity, height); return; @@ -265,15 +268,18 @@ void chaser_header::do_organize(const header::cptr& header_ptr, } // Push new header as top of candidate chain. - if (push(header_ptr, state->context()).is_terminal()) + if (push_header(header_ptr, state->context()).is_terminal()) { handler(error::store_integrity, height); return; } + // ------------------------------------------------------------------------ + top_state_ = state; const auto branch_point = possible_narrow_cast(point); - notify(error::success, chase::header, { branch_point }); + ////LOGN("set chase::header " << branch_point); + notify(error::success, chase::header, branch_point ); handler(error::success, height); } @@ -381,7 +387,7 @@ void chaser_header::cache(const header::cptr& header, tree_.insert({ header->hash(), { header, state } }); } -database::header_link chaser_header::push(const header::cptr& header, +database::header_link chaser_header::push_header(const header::cptr& header, const context& context) const NOEXCEPT { auto& query = archive(); @@ -395,7 +401,7 @@ database::header_link chaser_header::push(const header::cptr& header, return query.push_candidate(link) ? link : database::header_link{}; } -bool chaser_header::push(const hash_digest& key) NOEXCEPT +bool chaser_header::push_header(const hash_digest& key) NOEXCEPT { const auto value = tree_.extract(key); BC_ASSERT_MSG(!value.empty(), "missing tree value"); diff --git a/src/chasers/chaser_transaction.cpp b/src/chasers/chaser_transaction.cpp index 045b3e29..320cf120 100644 --- a/src/chasers/chaser_transaction.cpp +++ b/src/chasers/chaser_transaction.cpp @@ -61,12 +61,12 @@ void chaser_transaction::handle_event(const code&, chase event_, { if (event_ == chase::confirmed) { - POST(handle_confirmed, std::get(value)); + POST(handle_confirmed, std::get(value)); } } // TODO: handle the new confirmed blocks (may issue 'transaction'). -void chaser_transaction::handle_confirmed(header_t) NOEXCEPT +void chaser_transaction::handle_confirmed(height_t) NOEXCEPT { BC_ASSERT(stranded()); } diff --git a/src/configuration.cpp b/src/configuration.cpp index 279f3faa..c4d7c6ba 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -40,7 +40,6 @@ configuration::configuration(system::chain::selection context) NOEXCEPT write(false), log(context), node(context), - ////chain(context), database(context), network(context), bitcoin(context) diff --git a/src/error.cpp b/src/error.cpp index 26b1edad..baf9ec9b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -38,6 +38,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) // network { slow_channel, "slow channel" }, { stalled_channel, "stalled channel" }, + { exhausted_channel, "exhausted channel" }, // blockchain { orphan_block, "orphan block" }, diff --git a/src/protocols/protocol_block_in.cpp b/src/protocols/protocol_block_in.cpp index 1eefa317..ec2b4d1c 100644 --- a/src/protocols/protocol_block_in.cpp +++ b/src/protocols/protocol_block_in.cpp @@ -72,13 +72,12 @@ bool protocol_block_in::handle_receive_inventory(const code& ec, const inventory::cptr& message) NOEXCEPT { BC_ASSERT(stranded()); - constexpr auto block_id = inventory::type_id::block; if (stopped(ec)) return false; - LOGP("Received (" << message->count(block_id) << ") block inventory from [" - << authority() << "]."); + LOGP("Received (" << message->count(inventory::type_id::block) + << ") block inventory from [" << authority() << "]."); const auto getter = create_get_data(*message); @@ -170,11 +169,11 @@ bool protocol_block_in::handle_receive_block(const code& ec, void protocol_block_in::complete() NOEXCEPT { BC_ASSERT(stranded()); - LOGN("Blocks from [" << authority() << "] exhausted."); + LOGP("Blocks from [" << authority() << "] exhausted."); } -void protocol_block_in::handle_organize(const code& ec, size_t height, - const chain::block::cptr& block_ptr) NOEXCEPT +void protocol_block_in::handle_organize(const code& ec, size_t LOG_ONLY(height), + const chain::block::cptr& LOG_ONLY(block_ptr)) NOEXCEPT { if (ec == network::error::service_stopped || ec == error::duplicate_block) return; diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index 7e4f90dc..859209f1 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -36,6 +36,7 @@ using namespace system; using namespace network; using namespace network::messages; using namespace std::placeholders; +using namespace std::chrono; // Shared pointers required for lifetime in handler parameters. BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) @@ -45,55 +46,77 @@ BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) // Performance polling. // ---------------------------------------------------------------------------- +void protocol_block_in_31800::start_performance() NOEXCEPT +{ + if (stopped()) + return; + + if (report_performance_) + { + bytes_ = zero; + start_ = steady_clock::now(); + performance_timer_->start(BIND(handle_performance_timer, _1)); + } +} + void protocol_block_in_31800::handle_performance_timer(const code& ec) NOEXCEPT { BC_ASSERT(stranded()); - if (stopped() || ec == network::error::operation_canceled) + if (ec == network::error::operation_canceled) + return; + + if (stopped()) return; if (ec) { - LOGF("Performance timer error, " << ec.message()); + LOGF("Performance timer failure, " << ec.message()); stop(ec); return; } - // Compute rate in bytes per second. - const auto now = steady_clock::now(); - const auto gap = std::chrono::duration_cast(now - start_).count(); - const auto rate = floored_divide(bytes_, gap); + if (map_->empty()) + { + // Channel is exhausted, performance no longer relevant. + pause_performance(); + return; + } - // Reset counters. - bytes_ = zero; - start_ = now; - set_performance(rate); + const auto delta = duration_cast(steady_clock::now() - start_); + const auto unsigned_delta = sign_cast(delta.count()); + const auto non_zero_period = system::greater(unsigned_delta, one); + const auto rate = floored_divide(bytes_, non_zero_period); + send_performance(rate); } -// private -// Removes channel from performance tracking. -void protocol_block_in_31800::reset_performance() NOEXCEPT +void protocol_block_in_31800::pause_performance() NOEXCEPT { - BC_ASSERT(stranded()); - set_performance(zero); + send_performance(max_uint64); } -// private -// Bounces to network strand, performs computation, then calls handler. -// Channel will continue to process and count blocks while this call excecutes -// on the network strand. Timer will not be restarted until cycle completes. -void protocol_block_in_31800::set_performance(uint64_t rate) NOEXCEPT +void protocol_block_in_31800::stop_performance() NOEXCEPT +{ + send_performance(zero); +} + +// [0...slow...fast] => [stalled_channel...slow_channel...success] +void protocol_block_in_31800::send_performance(uint64_t rate) NOEXCEPT { BC_ASSERT(stranded()); - performance(identifier(), rate, BIND(handle_performance, _1)); + + if (report_performance_) + { + performance_timer_->stop(); + performance(identifier(), rate, BIND(handle_send_performance, _1)); + } } -void protocol_block_in_31800::handle_performance(const code& ec) NOEXCEPT +void protocol_block_in_31800::handle_send_performance(const code& ec) NOEXCEPT { POST(do_handle_performance, ec); } -// private void protocol_block_in_31800::do_handle_performance(const code& ec) NOEXCEPT { BC_ASSERT(stranded()); @@ -101,21 +124,28 @@ void protocol_block_in_31800::do_handle_performance(const code& ec) NOEXCEPT if (stopped()) return; - // stalled_channel or slow_channel - if (ec) + // Caused only by performance(max) - had no outstanding work. + // Timer stopped until chaser::download event restarts it. + if (ec == error::exhausted_channel) + return; + + // Caused only by performance(zero|xxx) - had outstanding work. + if (ec == error::stalled_channel || ec == error::slow_channel) { + LOGP("Performance [" << authority() << "] " << ec.message()); stop(ec); return; - }; + } - // Stop performance tracking without channel stop. New work restarts. - if (map_->empty()) + if (ec) { - reset_performance(); + LOGF("Performance failure [" << authority() << "] " << ec.message()); + stop(ec); return; } - performance_timer_->start(BIND(handle_performance_timer, _1)); + // Restart performance timing cycle. + start_performance(); } // Start/stop. @@ -128,88 +158,106 @@ void protocol_block_in_31800::start() NOEXCEPT if (started()) return; - if (report_performance_) - { - start_ = steady_clock::now(); - performance_timer_->start(BIND(handle_performance_timer, _1)); - } - - // This subscription is asynchronous without completion handler. So there - // is no completion time guarantee, best efforts completion only (ok). + // Events subscription is asynchronous. async_subscribe_events(BIND(handle_event, _1, _2, _3)); - SUBSCRIBE_CHANNEL(block, handle_receive_block, _1, _2); + + // Start performance timing cycle. + start_performance(); get_hashes(BIND(handle_get_hashes, _1, _2)); protocol::start(); } +void protocol_block_in_31800::stopping(const code& ec) NOEXCEPT +{ + BC_ASSERT(stranded()); + + restore(map_); + map_ = std::make_shared(); + stop_performance(); + + protocol::stopping(ec); +} + +// Idempotent cleanup. +void protocol_block_in_31800::restore(const map_ptr& map) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (!map->empty()) + put_hashes(map, BIND(handle_put_hashes, _1)); +} + void protocol_block_in_31800::handle_event(const code&, chaser::chase event_, chaser::link value) NOEXCEPT { - if (event_ == chaser::chase::unassociated) + if (stopped()) + return; + + // There are count blocks to download at/above the given header. + if (event_ == chaser::chase::download) { - BC_ASSERT(std::holds_alternative(value)); - POST(handle_unassociated, std::get(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(handle_download, std::get(value)); } } -// TODO: handle chaser::chase::unassociated (new downloads). -void protocol_block_in_31800::handle_unassociated(chaser::header_t) NOEXCEPT +void protocol_block_in_31800::handle_download(chaser::count_t count) NOEXCEPT { BC_ASSERT(stranded()); -} -void protocol_block_in_31800::stopping(const code& ec) NOEXCEPT -{ - BC_ASSERT(stranded()); + if (stopped()) + return; - performance_timer_->stop(); - reset_performance(); - put_hashes(map_, BIND(handle_put_hashes, _1)); - protocol::stopping(ec); + if (map_->empty() && !is_zero(count)) + { + // Assume performance was stopped due to exhaustion. + start_performance(); + get_hashes(BIND(handle_get_hashes, _1, _2)); + } } // Inbound (blocks). // ---------------------------------------------------------------------------- -void protocol_block_in_31800::handle_get_hashes(const code& ec, - const map_ptr& map) NOEXCEPT +void protocol_block_in_31800::send_get_data(const map_ptr& map) NOEXCEPT { - BC_ASSERT_MSG(map->size() < max_inventory, "inventory overflow"); + BC_ASSERT(stranded()); - if (ec) + if (stopped()) { - LOGF("Error getting block hashes for [" << authority() << "] " - << ec.message()); - stop(ec); + restore(map); return; } if (map->empty()) + return; + + if (map_->empty()) { - LOGP("Exhausted block hashes at [" << authority() << "] " - << ec.message()); + SEND(create_get_data((map_ = map)), handle_send, _1); return; } - POST(send_get_data, map); + // There are two populated maps, return the new and leave the old alone. + restore(map); } -void protocol_block_in_31800::send_get_data(const map_ptr& map) NOEXCEPT +// private +// clang has emplace_back bug (no matching constructor). +// bip144: get_data uses witness constant but inventory does not. +get_data protocol_block_in_31800::create_get_data( + const map_ptr& map) const NOEXCEPT { - BC_ASSERT(stranded()); - - map_ = map; - SEND(create_get_data(map_), handle_send, _1); -} + get_data getter{}; + getter.items.reserve(map->size()); + std::for_each(map->pos_begin(), map->pos_end(), + [&](const auto& item) NOEXCEPT + { + getter.items.push_back({ block_type_, item.hash }); + }); -void protocol_block_in_31800::handle_put_hashes(const code& ec) NOEXCEPT -{ - if (ec) - { - LOGF("Error putting block hashes for [" << authority() << "] " - << ec.message()); - } + return getter; } bool protocol_block_in_31800::handle_receive_block(const code& ec, @@ -233,18 +281,18 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, return true; } + // Check block. + // ------------------------------------------------------------------------ + code error{}; + const auto& link = it->link; const auto& ctx = it->context; const auto height = possible_narrow_cast(ctx.height); if (((error = block.check())) || ((error = block.check(ctx)))) { - // Set stored header state to 'block_unconfirmable'. - query.set_block_unconfirmable(query.to_header(hash)); - - // Notify that a candidate is 'unchecked' (candidates reorganize). - notify(error::success, chaser::chase::unchecked, { height }); + query.set_block_unconfirmable(link); + notify(error::success, chaser::chase::unchecked, link); - // TODO: include context in log message. LOGR("Invalid block [" << encode_hash(hash) << "] at (" << ctx.height << ") from [" << authority() << "] " << error.message()); @@ -253,9 +301,11 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, return false; } - // TODO: optimize using header_fk? - // Commit the block (txs) to the store, failure may stall the node. - if (query.set_link(block).is_terminal()) + // Commit block.txs. + // ------------------------------------------------------------------------ + + // Commit block.txs to store, failure may stall the node. + if (query.set_link(*block.transactions_ptr(), link).is_terminal()) { LOGF("Failure storing block [" << encode_hash(hash) << "] at (" << ctx.height << ") from [" << authority() << "] " @@ -264,10 +314,12 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, return false; } + // ------------------------------------------------------------------------ + LOGP("Downloaded block [" << encode_hash(hash) << "] at (" << ctx.height << ") from [" << authority() << "]."); - notify(error::success, chaser::chase::checked, { height }); + notify(error::success, chaser::chase::checked, height); bytes_ += message->cached_size; map_->erase(it); @@ -281,25 +333,41 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, return true; } -// private -// ---------------------------------------------------------------------------- +void protocol_block_in_31800::handle_put_hashes(const code& ec) NOEXCEPT +{ + if (ec) + { + LOGF("Error putting block hashes for [" << authority() << "] " + << ec.message()); + } +} -get_data protocol_block_in_31800::create_get_data( - const map_ptr& map) const NOEXCEPT +void protocol_block_in_31800::handle_get_hashes(const code& ec, + const map_ptr& map) NOEXCEPT { - BC_ASSERT(stranded()); + BC_ASSERT_MSG(map->size() < max_inventory, "inventory overflow"); - get_data getter{}; - getter.items.reserve(map->size()); - std::for_each(map->pos_begin(), map->pos_end(), - [&](const auto& item) NOEXCEPT - { - // clang has emplace_back bug (no matching constructor). - // bip144: get_data uses witness constant but inventory does not. - getter.items.push_back({ block_type_, item.hash }); - }); + if (stopped()) + { + restore(map); + return; + } - return getter; + if (ec) + { + LOGF("Error getting block hashes for [" << authority() << "] " + << ec.message()); + stop(ec); + return; + } + + if (map->empty()) + { + ////LOGP("Block hashes for [" << authority() << "] exhausted."); + return; + } + + POST(send_get_data, map); } BC_POP_WARNING() diff --git a/src/protocols/protocol_header_in_31800.cpp b/src/protocols/protocol_header_in_31800.cpp index 851f6d3b..26f6b7fe 100644 --- a/src/protocols/protocol_header_in_31800.cpp +++ b/src/protocols/protocol_header_in_31800.cpp @@ -106,13 +106,14 @@ bool protocol_header_in_31800::handle_receive_headers(const code& ec, void protocol_header_in_31800::complete() NOEXCEPT { BC_ASSERT(stranded()); - LOGN("Headers from [" << authority() << "] exhausted."); + LOGP("Headers from [" << authority() << "] exhausted."); } -void protocol_header_in_31800::handle_organize(const code& ec, size_t height, - const chain::header::cptr& header_ptr) NOEXCEPT +void protocol_header_in_31800::handle_organize(const code& ec, + size_t LOG_ONLY(height), + const chain::header::cptr& LOG_ONLY(header_ptr)) NOEXCEPT { - if (ec == network::error::service_stopped || ec == error::duplicate_header) + if (stopped() || ec == error::duplicate_header) return; if (ec) diff --git a/src/sessions/session_outbound.cpp b/src/sessions/session_outbound.cpp index f8eb62e8..8094ec87 100644 --- a/src/sessions/session_outbound.cpp +++ b/src/sessions/session_outbound.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -31,11 +32,17 @@ namespace node { #define CLASS session_outbound +/// Three required to measure deviation. +constexpr auto minimum_deviation_set = 3_size; constexpr auto to_kilobits_per_second = [](auto value) NOEXCEPT { - constexpr auto kilo = 1'000; + // There are no system wrappers for floating point conversion. + BC_PUSH_WARNING(NO_STATIC_CAST) + BC_PUSH_WARNING(NO_CASTS_FOR_ARITHMETIC_CONVERSION) return system::encode_base10(static_cast( - value * byte_bits / kilo)); + (value * byte_bits) / std::kilo::num)); + BC_POP_WARNING() + BC_POP_WARNING() }; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) @@ -60,6 +67,13 @@ void session_outbound::do_performance(uint64_t channel, uint64_t speed, { BC_ASSERT(stranded()); + if (speed == max_uint64) + { + speeds_.erase(channel); + handler(error::exhausted_channel); + return; + } + // Always remove record on stalled channel (and channel close). if (is_zero(speed)) { @@ -71,35 +85,47 @@ void session_outbound::do_performance(uint64_t channel, uint64_t speed, speeds_[channel] = static_cast(speed); const auto count = speeds_.size(); + if (count <= minimum_deviation_set) + { + handler(error::success); + return; + } + const auto rate = std::accumulate(speeds_.begin(), speeds_.end(), 0.0, [](double sum, const auto& element) NOEXCEPT { return sum + element.second; }); + + // This bypasses unnecessary computation but prevents consistent logging. // Keep this channel if its performance deviation is at/above average. const auto mean = rate / count; - if (speed >= mean) - { - handler(error::success); - return; - } + ////if (speed >= mean) + ////{ + //// handler(error::success); + //// return; + ////} const auto variance = std::accumulate(speeds_.begin(), speeds_.end(), 0.0, [mean](double sum, const auto& element) NOEXCEPT { - const auto difference = element.second - mean; - return sum + (difference * difference); - }) / count; + return sum + std::pow(element.second - mean, two); + }) / (sub1(count)); const auto sdev = std::sqrt(variance); const auto slow = (mean - speed) > (allowed_deviation_ * sdev); - - LOGS("Block download channels (" << count << ") rate (" + + ////system::string_list out{}; + ////for (const auto& value: speeds_) + //// out.push_back(system::serialize(to_kilobits_per_second(value.second))); + + LOGN("Block download channels (" << count << ") rate (" << to_kilobits_per_second(rate) << ") mean (" << to_kilobits_per_second(mean) << ") sdev (" << to_kilobits_per_second(sdev) << ") Kbps [" << (slow ? "*" : "") << to_kilobits_per_second(speed) << "]."); + ////<< system::join(out)); if (slow) { diff --git a/src/settings.cpp b/src/settings.cpp index c32f4b23..6b15e40b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -62,7 +62,7 @@ std::filesystem::path settings::events_file() NOEXCEPT namespace node { settings::settings() NOEXCEPT - : allowed_deviation{ 1.0 }, + : allowed_deviation{ 1.5 }, maximum_inventory{ 500 }, sample_period_seconds{ 5 }, currency_window_minutes{ 60 } diff --git a/test/error.cpp b/test/error.cpp index 4fd4e65c..05bc559a 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -86,6 +86,15 @@ BOOST_AUTO_TEST_CASE(error_t__code__stalled_channel__true_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "stalled channel"); } +BOOST_AUTO_TEST_CASE(error_t__code__exhausted_channel__true_exected_message) +{ + constexpr auto value = error::exhausted_channel; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "exhausted channel"); +} + BOOST_AUTO_TEST_CASE(error_t__code__orphan_block__true_exected_message) { constexpr auto value = error::orphan_block; diff --git a/test/settings.cpp b/test/settings.cpp index bac354f9..49bcc023 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(settings__log__default_context__expected) BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) { node::settings configuration{}; - BOOST_REQUIRE_EQUAL(configuration.allowed_deviation, 1.0); + BOOST_REQUIRE_EQUAL(configuration.allowed_deviation, 1.5); BOOST_REQUIRE_EQUAL(configuration.maximum_inventory, 500); BOOST_REQUIRE_EQUAL(configuration.sample_period_seconds, 5_u16); BOOST_REQUIRE_EQUAL(configuration.currency_window_minutes, 60_u32);