diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index 1ae6db77..1876cf0c 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -23,6 +23,8 @@ #include #include +////#define SEQUENTIAL + namespace libbitcoin { namespace node { @@ -47,7 +49,9 @@ class BCN_API chaser_confirm event_value value) NOEXCEPT; virtual void do_validated(height_t height) NOEXCEPT; -#if defined(UNDEFINED) +#if defined(SEQUENTIAL) + virtual void do_organize(size_t height) NOEXCEPT; +#else virtual void do_organize(size_t height) NOEXCEPT; virtual bool enqueue_block(const database::header_link& link) NOEXCEPT; virtual void confirm_tx(const database::context& ctx, @@ -58,7 +62,8 @@ class BCN_API chaser_confirm const database::header_link& link, size_t height)NOEXCEPT; virtual void confirm_block(const code& ec, const database::header_link& link, size_t height) NOEXCEPT; -#endif // UNDEFINED + virtual void next_block(size_t height) NOEXCEPT; +#endif // SEQUENTIAL private: bool set_organized(const database::header_link& link, @@ -67,9 +72,8 @@ class BCN_API chaser_confirm height_t height) NOEXCEPT; bool set_reorganized(const database::header_link& link, height_t height) NOEXCEPT; - bool roll_back(const header_links& popped, - const database::header_link& link, size_t fork_point, - size_t top) NOEXCEPT; + bool roll_back(header_links& popped, const database::header_link& link, + size_t fork_point, size_t top) NOEXCEPT; bool get_fork_work(uint256_t& fork_work, header_links& fork, height_t fork_top) const NOEXCEPT; diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 4500d44b..9e26e846 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -306,6 +306,7 @@ void CLASS::do_organize(typename Block::cptr block_ptr, // Checking currency before notify also avoids excessive work backlog. if (is_block() || is_current(header.timestamp())) { + // TODO: this should probably be sent only once for the process. // If at start the fork point is top of both chains, and next candidate // is already downloaded, then new header will arrive and download will // be skipped, resulting in stall until restart at which time the start diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index 84253756..b281032f 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -348,10 +348,8 @@ size_t chaser_check::set_unassociated() NOEXCEPT // Inventory size gets set only once. if (is_zero(inventory_)) - { - inventory_ = get_inventory_size(); - if (is_zero(inventory_)) return zero; - } + if (is_zero((inventory_ = get_inventory_size()))) + return zero; // Due to previous downloads, validation can race ahead of last request. // The last request (requested_) stops at the last gap in the window, but @@ -394,11 +392,11 @@ size_t chaser_check::get_inventory_size() const NOEXCEPT if (is_zero(peers) || !is_current()) return zero; - const auto step = config().node.maximum_concurrency_(); - const auto fork = archive().get_fork(); - const auto scan = archive().get_unassociated_count_above(fork, step); - const auto span = std::min(step, scan); - const auto inventory = std::min(span, messages::max_inventory); + const auto& query = archive(); + const auto fork = query.get_fork(); + const auto window = config().node.maximum_concurrency_(); + const auto step = std::min(window, messages::max_inventory); + const auto inventory = query.get_unassociated_count_above(fork, step); return system::ceilinged_divide(inventory, peers); } diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index 4cae4858..58de908a 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -91,11 +91,16 @@ bool chaser_confirm::handle_event(const code&, chase event_, // confirm // ---------------------------------------------------------------------------- +// Blocks are either confirmed (blocks first) or validated/confirmed +// (headers first) at this point. An unconfirmable block may not land here. +// Candidate chain reorganizations will result in reported heights moving +// in any direction. Each is treated as independent and only one representing +// a stronger chain is considered. void chaser_confirm::do_validated(height_t height) NOEXCEPT { BC_ASSERT(stranded()); - if (closed()) + if (closed() || !fork_.empty()) return; // Compute relative work. @@ -103,22 +108,21 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT bool strong{}; uint256_t work{}; - header_links fork{}; // Scan down from height to first confirmed, accumulate links and sum work. - if (!get_fork_work(work, fork, height)) + if (!get_fork_work(work, fork_, height)) { fault(error::get_fork_work); return; } // No longer a candidate fork (heights are not candidates). - if (fork.empty()) + if (fork_.empty()) return; // fork_point is the highest candidate-confirmed common block. - const auto fork_point = height - fork.size(); - if (!get_is_strong(strong, work, fork_point)) + fork_point_ = height - fork_.size(); + if (!get_is_strong(strong, work, fork_point_)) { fault(error::get_is_strong); return; @@ -126,14 +130,17 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT // Not yet a strong fork (confirmed branch has at least as much work). if (!strong) + { + fork_.clear(); return; + } // Reorganize confirmed chain. // ........................................................................ auto& query = archive(); const auto top = query.get_top_confirmed(); - if (top < fork_point) + if (top < fork_point_) { fault(error::invalid_fork_point); return; @@ -141,8 +148,9 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT // Pop down to the fork point. auto index = top; - header_links popped{}; - while (index > fork_point) + popped_.clear(); + + while (index > fork_point_) { const auto link = query.to_confirmed(index); if (link.is_terminal()) @@ -151,7 +159,7 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT return; } - popped.push_back(link); + popped_.push_back(link); if (!query.set_unstrong_parallel(link)) { fault(error::set_unstrong); @@ -164,19 +172,24 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT return; } - notify(error::success, chase::reorganized, popped.back()); + notify(error::success, chase::reorganized, popped_.back()); fire(events::block_reorganized, index--); } - // Above may shrink indexes and below may cancel. - // This may result in an inability to restore. - // TODO: copy height indexes in backup. + // Push candidate headers to confirmed chain. + // ........................................................................ + + do_organize(add1(fork_point_)); +} - // fork_point + 1 - ++index; +#if defined(SEQUENTIAL) + +void chaser_confirm::do_organize(size_t height) NOEXCEPT +{ + auto& query = archive(); // Push candidate headers to confirmed chain. - for (const auto& link: views_reverse(fork)) + for (const auto& link: views_reverse(fork_)) { if (closed()) return; @@ -191,15 +204,15 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT if (ec == database::error::block_unconfirmable) { notify(ec, chase::unconfirmable, link); - fire(events::block_unconfirmable, index); + fire(events::block_unconfirmable, height); - if (!roll_back(popped, link, fork_point, index)) + if (!roll_back(popped_, link, fork_point_, height)) fault(error::node_roll_back); return; } - const auto checked = is_under_checkpoint(index) || + const auto checked = is_under_checkpoint(height) || query.is_milestone(link); // Required for block_confirmable and all confirmed blocks. @@ -219,10 +232,10 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT return; } - notify(ec, chase::confirmable, index); - ////fire(events::confirm_bypassed, index); + notify(ec, chase::confirmable, height); + ////fire(events::confirm_bypassed, height); - if (!set_organized(link, index)) + if (!set_organized(link, height)) { fault(error::set_organized); return; @@ -245,11 +258,11 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT return; } - LOGR("Unconfirmable block [" << index << "] " << ec.message()); + LOGR("Unconfirmable block [" << height << "] " << ec.message()); notify(ec, chase::unconfirmable, link); - fire(events::block_unconfirmable, index); + fire(events::block_unconfirmable, height); - if (!roll_back(popped, link, fork_point, index)) + if (!roll_back(popped_, link, fork_point_, height)) fault(error::node_roll_back); return; @@ -262,111 +275,24 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT return; } - notify(error::success, chase::confirmable, index); - fire(events::block_confirmed, index); + notify(error::success, chase::confirmable, height); + fire(events::block_confirmed, height); - if (!set_organized(link, index)) + if (!set_organized(link, height)) { fault(error::set_organized); return; } } - LOGV("Block confirmed and organized: " << index); - ++index; + LOGV("Block confirmed and organized: " << height); + ++height; } } -#if defined(DISABLED) - -// Blocks are either confirmed (blocks first) or validated/confirmed -// (headers first) at this point. An unconfirmable block may not land here. -// Candidate chain reorganizations will result in reported heights moving -// in any direction. Each is treated as independent and only one representing -// a stronger chain is considered. Currently total work at a given block is not -// archived, so this organization (like in the organizer) requires scanning to -// the fork point from the block and to the top confirmed from the fork point. -// The scans are extremely fast and tiny in all typical scnearios, so it may -// not improve performance or be worth spending 32 bytes per header to store -// work, especially since individual header work is obtained from 4 bytes. -void chaser_confirm::do_validated(height_t height) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (closed() || !fork_.empty()) - return; - - // TODO: update specialized fault codes. - - // Compute relative work. - // ........................................................................ - - bool strong{}; - uint256_t work{}; - - if (!get_fork_work(work, fork_, height)) - { - fault(error::get_fork_work); - return; - } - - if (!get_is_strong(strong, work, height)) - { - fault(error::get_is_strong); - return; - } - - if (!strong) - { - fork_.clear(); - return; - } - - // Pop down to the fork point. - // ........................................................................ - - auto& query = archive(); - const auto top = query.get_top_confirmed(); - fork_point_ = height - fork_.size(); - if (top < fork_point_) - { - fault(error::invalid_fork_point); - return; - } - - popped_.clear(); - for (auto index = top; index > fork_point_; --index) - { - const auto link = query.to_confirmed(index); - if (link.is_terminal()) - { - fault(error::to_confirmed); - return; - } - - popped_.push_back(link); - if (!query.set_unstrong_parallel(link)) - { - fault(error::set_unstrong); - return; - } - - if (!query.pop_confirmed()) - { - fault(error::pop_confirmed); - return; - } - - notify(error::success, chase::reorganized, popped_.back()); - fire(events::block_reorganized, index); - } - - // Push candidate headers to confirmed chain. - // ........................................................................ - - do_organize(add1(fork_point_)); -} +#else +// CONCURRENT void chaser_confirm::do_organize(size_t height) NOEXCEPT { if (fork_.empty()) @@ -374,18 +300,22 @@ void chaser_confirm::do_organize(size_t height) NOEXCEPT auto& query = archive(); const auto& link = fork_.back(); - const auto ec = query.get_block_state(link); + + // database::error::unassociated + // database::error::block_unconfirmable + // database::error::block_confirmable + // database::error::block_valid + // database::error::unknown_state + // database::error::unvalidated + auto ec = query.get_block_state(link); if (ec == database::error::block_unconfirmable) { notify(ec, chase::unconfirmable, link); fire(events::block_unconfirmable, height); + if (!roll_back(popped_, link, fork_point_, height)) - { fault(error::node_roll_back); - return; - } - fork_.clear(); return; } @@ -393,7 +323,6 @@ void chaser_confirm::do_organize(size_t height) NOEXCEPT query.is_milestone(link); // Required for block_confirmable and all confirmed blocks. - // Checkpoint and milestone guarantee set_strong is always set. if (!checked && !query.set_strong_parallel(link)) { fault(error::set_strong); @@ -416,9 +345,10 @@ void chaser_confirm::do_organize(size_t height) NOEXCEPT if (!set_organized(link, height)) { fault(error::set_organized); + return; } - fork_.clear(); + POST(next_block, add1(height)); return; } @@ -556,25 +486,32 @@ void chaser_confirm::confirm_block(const code& ec, const header_link& link, } LOGV("Block confirmed and organized: " << height); + next_block(add1(height)); +} + +// SHARED ITERATE +void chaser_confirm::next_block(size_t height) NOEXCEPT +{ + BC_ASSERT(stranded()); + auto& query = archive(); - const auto next = add1(height); fork_.pop_back(); if (!fork_.empty()) { - do_organize(next); + do_organize(height); return; } // Prevent stall by bumping, as the event may have been missed. - const auto code = query.get_block_state(query.to_candidate(next)); + const auto code = query.get_block_state(query.to_candidate(height)); if ((code == database::error::block_valid) || (code == database::error::block_confirmable)) { - do_validated(next); + do_validated(height); } } -#endif // DISABLED +#endif // SEQUENTIAL // Private // ---------------------------------------------------------------------------- @@ -614,7 +551,7 @@ bool chaser_confirm::set_reorganized(const header_link& link, return true; } -bool chaser_confirm::roll_back(const header_links& popped, +bool chaser_confirm::roll_back(header_links& popped, const header_link& link, size_t fork_point, size_t top) NOEXCEPT { auto& query = archive(); @@ -631,6 +568,7 @@ bool chaser_confirm::roll_back(const header_links& popped, if (!reset_organized(fk, ++fork_point)) return false; + popped.clear(); return true; }