diff --git a/Makefile.am b/Makefile.am index d39b89b33..813860848 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ src_libbitcoin_node_la_SOURCES = \ src/chasers/chaser_check.cpp \ src/chasers/chaser_confirm.cpp \ src/chasers/chaser_header.cpp \ + src/chasers/chaser_populate.cpp \ src/chasers/chaser_snapshot.cpp \ src/chasers/chaser_storage.cpp \ src/chasers/chaser_template.cpp \ @@ -89,6 +90,7 @@ test_libbitcoin_node_test_SOURCES = \ test/chasers/chaser_check.cpp \ test/chasers/chaser_confirm.cpp \ test/chasers/chaser_header.cpp \ + test/chasers/chaser_populate.cpp \ test/chasers/chaser_template.cpp \ test/chasers/chaser_transaction.cpp \ test/chasers/chaser_validate.cpp \ @@ -140,6 +142,7 @@ include_bitcoin_node_chasers_HEADERS = \ include/bitcoin/node/chasers/chaser_confirm.hpp \ include/bitcoin/node/chasers/chaser_header.hpp \ include/bitcoin/node/chasers/chaser_organize.hpp \ + include/bitcoin/node/chasers/chaser_populate.hpp \ include/bitcoin/node/chasers/chaser_snapshot.hpp \ include/bitcoin/node/chasers/chaser_storage.hpp \ include/bitcoin/node/chasers/chaser_template.hpp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 6bb6e5f42..bcbcb815f 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -255,6 +255,7 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/chasers/chaser_check.cpp" "../../src/chasers/chaser_confirm.cpp" "../../src/chasers/chaser_header.cpp" + "../../src/chasers/chaser_populate.cpp" "../../src/chasers/chaser_snapshot.cpp" "../../src/chasers/chaser_storage.cpp" "../../src/chasers/chaser_template.cpp" @@ -329,6 +330,7 @@ if (with-tests) "../../test/chasers/chaser_check.cpp" "../../test/chasers/chaser_confirm.cpp" "../../test/chasers/chaser_header.cpp" + "../../test/chasers/chaser_populate.cpp" "../../test/chasers/chaser_template.cpp" "../../test/chasers/chaser_transaction.cpp" "../../test/chasers/chaser_validate.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj index f6c0ac61d..a3ac41477 100644 --- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj @@ -75,6 +75,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters index b8dd021e6..0fc4ebb1d 100644 --- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters @@ -36,6 +36,9 @@ src\chasers + + src\chasers + src\chasers diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index 01ab576a1..d1974246c 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -78,6 +78,7 @@ + @@ -114,6 +115,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index 8d44ffe59..549c30b1a 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -63,6 +63,9 @@ src\chasers + + src\chasers + src\chasers @@ -167,6 +170,9 @@ include\bitcoin\node\chasers + + include\bitcoin\node\chasers + include\bitcoin\node\chasers diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index ed2a916e7..09b6025ff 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/node/chasers/chaser_block.hpp b/include/bitcoin/node/chasers/chaser_block.hpp index 421116f09..a445df14e 100644 --- a/include/bitcoin/node/chasers/chaser_block.hpp +++ b/include/bitcoin/node/chasers/chaser_block.hpp @@ -41,33 +41,33 @@ class BCN_API chaser_block protected: /// Get header from Block instance. - virtual const system::chain::header& get_header( - const system::chain::block& block) const NOEXCEPT; + const system::chain::header& get_header( + const system::chain::block& block) const NOEXCEPT override; /// Query store for const pointer to Block instance by candidate height. - virtual bool get_block(system::chain::block::cptr& out, - size_t height) const NOEXCEPT; + bool get_block(system::chain::block::cptr& out, + size_t height) const NOEXCEPT override; /// Determine if Block is a duplicate (success for not duplicate). - virtual code duplicate(size_t& height, - const system::hash_digest& hash) const NOEXCEPT; + code duplicate(size_t& height, + const system::hash_digest& hash) const NOEXCEPT override; /// Determine if Block is valid. - virtual code validate(const system::chain::block& block, - const chain_state& state) const NOEXCEPT; + code validate(const system::chain::block& block, + const chain_state& state) const NOEXCEPT override; /// Notify check chaser to redownload the block (nop). - virtual void do_malleated(header_t link) NOEXCEPT; + void do_malleated(header_t link) NOEXCEPT override; /// Determine if state is top of a storable branch (always true). - virtual bool is_storable(const chain_state& state) const NOEXCEPT; + bool is_storable(const chain_state& state) const NOEXCEPT override; /// True if Block is on a milestone-covered branch. - virtual bool is_under_milestone(size_t height) const NOEXCEPT; + bool is_under_milestone(size_t height) const NOEXCEPT override; /// Milestone tracking. - virtual void update_milestone(const system::chain::header& header, - size_t height, size_t branch_point) NOEXCEPT; + void update_milestone(const system::chain::header& header, + size_t height, size_t branch_point) NOEXCEPT override; private: void set_prevout(const system::chain::input& input) const NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index 1876cf0c3..1e3dc3617 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -49,9 +49,7 @@ class BCN_API chaser_confirm event_value value) NOEXCEPT; virtual void do_validated(height_t height) NOEXCEPT; -#if defined(SEQUENTIAL) - virtual void do_organize(size_t height) NOEXCEPT; -#else + virtual void do_reorganize(size_t height) NOEXCEPT; 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, @@ -63,17 +61,21 @@ class BCN_API chaser_confirm virtual void confirm_block(const code& ec, const database::header_link& link, size_t height) NOEXCEPT; virtual void next_block(size_t height) NOEXCEPT; -#endif // SEQUENTIAL private: + void reset() NOEXCEPT; + bool busy() const NOEXCEPT; + bool set_organized(const database::header_link& link, height_t height) NOEXCEPT; bool reset_organized(const database::header_link& link, height_t height) NOEXCEPT; bool set_reorganized(const database::header_link& link, height_t height) NOEXCEPT; - bool roll_back(header_links& popped, const database::header_link& link, - size_t fork_point, size_t top) NOEXCEPT; + bool roll_back(const header_links& popped, size_t fork_point, + size_t top) NOEXCEPT; + bool roll_back(const header_links& popped, size_t fork_point, + size_t top, const database::header_link& link) NOEXCEPT; bool get_fork_work(uint256_t& fork_work, header_links& fork, height_t fork_top) const NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_header.hpp b/include/bitcoin/node/chasers/chaser_header.hpp index fa3d1efed..c36fe4b5c 100644 --- a/include/bitcoin/node/chasers/chaser_header.hpp +++ b/include/bitcoin/node/chasers/chaser_header.hpp @@ -44,33 +44,33 @@ class BCN_API chaser_header protected: /// Get header from Block instance. - virtual const system::chain::header& get_header( - const system::chain::header& header) const NOEXCEPT; + const system::chain::header& get_header( + const system::chain::header& header) const NOEXCEPT override; /// Query store for const pointer to Block instance by candidate height. - virtual bool get_block(system::chain::header::cptr& out, - size_t height) const NOEXCEPT; + bool get_block(system::chain::header::cptr& out, + size_t height) const NOEXCEPT override; /// Determine if Block is a duplicate (success for not duplicate). - virtual code duplicate(size_t& height, - const system::hash_digest& hash) const NOEXCEPT; + code duplicate(size_t& height, + const system::hash_digest& hash) const NOEXCEPT override; /// Determine if Block is valid. - virtual code validate(const system::chain::header& header, - const chain_state& state) const NOEXCEPT; + code validate(const system::chain::header& header, + const chain_state& state) const NOEXCEPT override; /// Notify check chaser to redownload the block. - virtual void do_malleated(header_t link) NOEXCEPT; + void do_malleated(header_t link) NOEXCEPT override; /// Determine if state is top of a storable branch. - virtual bool is_storable(const chain_state& state) const NOEXCEPT; + bool is_storable(const chain_state& state) const NOEXCEPT override; /// True if Block is on a milestone-covered branch. - virtual bool is_under_milestone(size_t height) const NOEXCEPT; + bool is_under_milestone(size_t height) const NOEXCEPT override; /// Milestone tracking. - virtual void update_milestone(const system::chain::header& header, - size_t height, size_t branch_point) NOEXCEPT; + void update_milestone(const system::chain::header& header, + size_t height, size_t branch_point) NOEXCEPT override; private: bool is_checkpoint(const chain_state& state) const NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_populate.hpp b/include/bitcoin/node/chasers/chaser_populate.hpp new file mode 100644 index 000000000..35212925c --- /dev/null +++ b/include/bitcoin/node/chasers/chaser_populate.hpp @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_NODE_CHASERS_CHASER_POPULATE_HPP +#define LIBBITCOIN_NODE_CHASERS_CHASER_POPULATE_HPP + +#include +#include + +namespace libbitcoin { +namespace node { + +class full_node; + +/// Order and populate downloaded non-bypass blocks for validation. +class BCN_API chaser_populate + : public chaser +{ +public: + DELETE_COPY_MOVE_DESTRUCT(chaser_populate); + + chaser_populate(full_node& node) NOEXCEPT; + + /// Initialize chaser state. + code start() NOEXCEPT override; + +protected: + virtual bool handle_event(const code& ec, chase event_, + event_value value) NOEXCEPT; + + virtual void do_checked(height_t height) NOEXCEPT; + +private: + // TODO: +}; + +} // namespace node +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/chasers/chasers.hpp b/include/bitcoin/node/chasers/chasers.hpp index 248e0412d..0948c632c 100644 --- a/include/bitcoin/node/chasers/chasers.hpp +++ b/include/bitcoin/node/chasers/chasers.hpp @@ -25,10 +25,11 @@ #include #include #include -#include +#include #include #include #include #include +#include #endif diff --git a/include/bitcoin/node/define.hpp b/include/bitcoin/node/define.hpp index 4d91f90a0..d4f432499 100644 --- a/include/bitcoin/node/define.hpp +++ b/include/bitcoin/node/define.hpp @@ -23,6 +23,7 @@ #include #include #include +#include /// Pulls in common /node headers (excluding settings/config/parser/full_node). #include @@ -63,25 +64,51 @@ typedef std::shared_ptr map_ptr; typedef std::function map_handler; -/// Node events. -typedef uint64_t object_key; -typedef uint64_t event_value; -typedef network::desubscriber event_subscriber; -typedef event_subscriber::handler event_notifier; -typedef event_subscriber::completer event_completer; +/// Event desubscriber key type. +using object_key = uint64_t; -/// Use for event_value variants (all unsigned integral integers). -/// std::variant is inconsistent with interpretation of size_t as redundant or -/// unique with respect to uint32_t and/or uint64_t (specifically macOS). So -/// instead these are implicitly casted to event_value (uint64_t) and explicitly -/// casted from event_value using system::possible_narrow_cast. This is no less -/// type-safe as using std::variant in cases where the types are overloaded. +/// Event value types. using count_t = size_t; using height_t = size_t; using channel_t = uint64_t; using object_t = object_key; using header_t = database::header_link::integer; using transaction_t = database::tx_link::integer; +typedef system::chain::block::cptr block_t; +////typedef struct +////{ +//// size_t height; +//// database::header_link link; +//// system::chain::block::cptr block; +////} xblock_t; + +/// std::variant types must be distinct, and xcode size_t is neither uint32_t +/// nor uint64_t, so this ensures we have the distinct set of necessary types. +using event_value = + iif, + std::variant, + iif, + std::variant, + std::variant>>; +////using xevent_value = +//// iif, +//// std::variant, +//// iif, +//// std::variant, +//// std::variant>>; + +/// Event desubscriber. +typedef network::desubscriber event_subscriber; +////typedef network::desubscriber xevent_subscriber; +typedef event_subscriber::handler event_notifier; +typedef event_subscriber::completer event_completer; + +// NDEBUG MSVC +////static_assert(sizeof(uint64_t) == 8u); +////static_assert(sizeof(block_t) == 16u); +////static_assert(sizeof(xblock_t) == 32u); +////static_assert(sizeof(event_value) == 24u); +////static_assert(sizeof(xevent_value) == 40u); } // namespace node } // namespace libbitcoin diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 9e26e846f..00a7d7dc5 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -96,13 +96,15 @@ bool CLASS::handle_event(const code&, chase event_, event_value value) NOEXCEPT case chase::unconfirmable: { // Roll back the candidate chain to confirmed top (via fork point). - POST(do_disorganize, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_disorganize, std::get(value)); break; } case chase::malleated: { // Re-obtain the malleated block if it is still a candidate (virtual). - POST(do_malleated, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_malleated, std::get(value)); break; } case chase::stop: diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp index 08aa4621d..a11c274e7 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp @@ -39,7 +39,6 @@ class BCN_API protocol_block_in_31800 protocol_block_in_31800(const SessionPtr& session, const channel_ptr& channel) NOEXCEPT : protocol_performer(session, channel), - maximum_concurrency_(session->config().node.maximum_concurrency_()), top_checkpoint_height_( session->config().bitcoin.top_checkpoint().height()), block_type_(session->config().network.witness_node() ? @@ -92,7 +91,6 @@ class BCN_API protocol_block_in_31800 const job::ptr& job) NOEXCEPT; // These are thread safe. - const size_t maximum_concurrency_; const size_t top_checkpoint_height_; const network::messages::inventory::type_id block_type_; diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index b281032fa..25401fa41 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -106,28 +107,33 @@ bool chaser_check::handle_event(const code&, chase event_, } case chase::checked: { - POST(do_checked, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_checked, std::get(value)); break; } case chase::regressed: { - POST(do_regressed, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_regressed, std::get(value)); break; } case chase::disorganized: { - POST(do_regressed, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_regressed, std::get(value)); break; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ case chase::header: { - POST(do_header, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_header, std::get(value)); break; } case chase::headers: { - POST(do_headers, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_headers, std::get(value)); break; } case chase::stop: diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index 58de908a6..7b333899e 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -66,13 +67,15 @@ bool chaser_confirm::handle_event(const code&, chase event_, case chase::blocks: { // TODO: value is branch point. - POST(do_validated, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_validated, std::get(value)); break; } case chase::valid: { // value is individual height. - POST(do_validated, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_validated, std::get(value)); break; } case chase::stop: @@ -90,22 +93,20 @@ 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. + +// Compute relative work, set fork_ and fork_point_. void chaser_confirm::do_validated(height_t height) NOEXCEPT { BC_ASSERT(stranded()); - if (closed() || !fork_.empty()) + if (closed() || busy()) return; - // Compute relative work. - // ........................................................................ - bool strong{}; uint256_t work{}; @@ -131,28 +132,29 @@ 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(); + reset(); return; } - // Reorganize confirmed chain. - // ........................................................................ + do_reorganize(archive().get_top_confirmed()); +} - auto& query = archive(); - const auto top = query.get_top_confirmed(); - if (top < fork_point_) +// Pop confirmed chain from height down to above fork point, save popped_. +void chaser_confirm::do_reorganize(size_t height) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (height < fork_point_) { fault(error::invalid_fork_point); return; } - // Pop down to the fork point. - auto index = top; popped_.clear(); - - while (index > fork_point_) + auto& query = archive(); + while (height > fork_point_) { - const auto link = query.to_confirmed(index); + const auto link = query.to_confirmed(height); if (link.is_terminal()) { fault(error::to_confirmed); @@ -160,7 +162,7 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT } popped_.push_back(link); - if (!query.set_unstrong_parallel(link)) + if (!query.set_unstrong(link)) { fault(error::set_unstrong); return; @@ -173,173 +175,68 @@ void chaser_confirm::do_validated(height_t height) NOEXCEPT } notify(error::success, chase::reorganized, popped_.back()); - fire(events::block_reorganized, index--); + fire(events::block_reorganized, height--); } - // Push candidate headers to confirmed chain. - // ........................................................................ - - do_organize(add1(fork_point_)); + do_organize(add1(height)); } -#if defined(SEQUENTIAL) - +// Push candidate headers from above fork point to confirmed chain. void chaser_confirm::do_organize(size_t height) NOEXCEPT { + BC_ASSERT(stranded()); + if (fork_.empty()) + return; + auto& query = archive(); + bool confirmable = false; + const auto& link = fork_.back(); + const auto bypass = is_under_checkpoint(height) || + query.is_milestone(link); - // Push candidate headers to confirmed chain. - for (const auto& link: views_reverse(fork_)) + if (!bypass) { - if (closed()) - return; - // 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); + const auto ec = query.get_block_state(link); + + // Previously unconfirmable block. if (ec == database::error::block_unconfirmable) { notify(ec, chase::unconfirmable, link); fire(events::block_unconfirmable, height); - if (!roll_back(popped_, link, fork_point_, height)) + // Roll back previously confirmed blocks. + if (!roll_back(popped_, fork_point_, sub1(height))) fault(error::node_roll_back); + reset(); return; } - const auto checked = is_under_checkpoint(height) || - query.is_milestone(link); - - // Required for block_confirmable and all confirmed blocks. - if (!checked && !query.set_strong_parallel(link)) + // Previously evaluated and set confirmable block. + if (ec == database::error::block_confirmable) { - fault(error::set_strong); - return; - } - - if (ec == database::error::block_confirmable || checked) - { - // TODO: compute fees from validation records. - if ((ec != database::error::block_confirmable) && - !query.set_block_confirmable(link, uint64_t{})) - { - fault(error::set_block_confirmable); - return; - } - - notify(ec, chase::confirmable, height); - ////fire(events::confirm_bypassed, height); - - if (!set_organized(link, height)) - { - fault(error::set_organized); - return; - } - } - else - { - ec = query.block_confirmable(link); - if (ec == database::error::integrity) - { - fault(error::get_block_confirmable); - return; - } - - if (ec) - { - if (!query.set_block_unconfirmable(link)) - { - fault(error::set_block_unconfirmable); - return; - } - - LOGR("Unconfirmable block [" << height << "] " << ec.message()); - notify(ec, chase::unconfirmable, link); - fire(events::block_unconfirmable, height); - - if (!roll_back(popped_, link, fork_point_, height)) - fault(error::node_roll_back); - - return; - } - - // TODO: compute fees from validation records. - if (!query.set_block_confirmable(link, uint64_t{})) + // Required of all confirmed, and before checking confirmable. + // Checked blocks are set at download, cannot be unset (reorged). + // Milestone blocks are set/unset strong by header organization. + if (!query.set_strong(link)) { - fault(error::set_block_confirmable); + fault(error::set_strong); return; } - notify(error::success, chase::confirmable, height); - fire(events::block_confirmed, height); - - if (!set_organized(link, height)) - { - fault(error::set_organized); - return; - } + confirmable = true; } - - LOGV("Block confirmed and organized: " << height); - ++height; } -} - -#else - -// CONCURRENT -void chaser_confirm::do_organize(size_t height) NOEXCEPT -{ - if (fork_.empty()) - return; - - auto& query = archive(); - const auto& link = fork_.back(); - // 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) + if (bypass || confirmable) { - notify(ec, chase::unconfirmable, link); - fire(events::block_unconfirmable, height); - - if (!roll_back(popped_, link, fork_point_, height)) - fault(error::node_roll_back); - - return; - } - - const auto checked = is_under_checkpoint(height) || - query.is_milestone(link); - - // Required for block_confirmable and all confirmed blocks. - if (!checked && !query.set_strong_parallel(link)) - { - fault(error::set_strong); - return; - } - - if (ec == database::error::block_confirmable || checked) - { - // TODO: compute fees from validation records. - if ((ec != database::error::block_confirmable) && - !query.set_block_confirmable(link, uint64_t{})) - { - fault(error::set_block_confirmable); - return; - } - - notify(ec, chase::confirmable, height); + notify(error::success, chase::confirmable, height); ////fire(events::confirm_bypassed, height); if (!set_organized(link, height)) @@ -460,17 +357,20 @@ void chaser_confirm::confirm_block(const code& ec, const header_link& link, LOGR("Unconfirmable block [" << height << "] " << ec.message()); notify(ec, chase::unconfirmable, link); fire(events::block_unconfirmable, height); - if (!roll_back(popped_, link, fork_point_, height)) + + // Roll back current and previously confirmed blocks. + if (!roll_back(popped_, fork_point_, height, link)) { fault(error::node_roll_back); return; } - fork_.clear(); + reset(); return; } - // TODO: compute fees from validation records. + // TODO: move fee setter to set_block_valid (transitory) and propagate to + // TODO: set_block_confirmable (final). Bypassed do not have the fee cache. if (!query.set_block_confirmable(link, uint64_t{})) { fault(error::set_block_confirmable); @@ -493,8 +393,8 @@ void chaser_confirm::confirm_block(const code& ec, const header_link& link, void chaser_confirm::next_block(size_t height) NOEXCEPT { BC_ASSERT(stranded()); - auto& query = archive(); + // Continue until fork is empty. fork_.pop_back(); if (!fork_.empty()) { @@ -502,6 +402,9 @@ void chaser_confirm::next_block(size_t height) NOEXCEPT return; } + reset(); + const auto& query = archive(); + // Prevent stall by bumping, as the event may have been missed. const auto code = query.get_block_state(query.to_candidate(height)); if ((code == database::error::block_valid) || @@ -511,11 +414,21 @@ void chaser_confirm::next_block(size_t height) NOEXCEPT } } -#endif // SEQUENTIAL - // Private // ---------------------------------------------------------------------------- +void chaser_confirm::reset() NOEXCEPT +{ + fork_.clear(); + popped_.clear(); + fork_point_ = zero; +} + +bool chaser_confirm::busy() const NOEXCEPT +{ + return !fork_.empty(); +} + bool chaser_confirm::set_organized(const header_link& link, height_t) NOEXCEPT { auto& query = archive(); @@ -531,7 +444,7 @@ bool chaser_confirm::reset_organized(const header_link& link, height_t height) NOEXCEPT { auto& query = archive(); - if (!query.set_strong_parallel(link) || !query.push_confirmed(link)) + if (!query.set_strong(link) || !query.push_confirmed(link)) return false; notify(error::success, chase::organized, link); @@ -543,7 +456,7 @@ bool chaser_confirm::set_reorganized(const header_link& link, height_t height) NOEXCEPT { auto& query = archive(); - if (!query.pop_confirmed() || !query.set_unstrong_parallel(link)) + if (!query.pop_confirmed() || !query.set_unstrong(link)) return false; notify(error::success, chase::reorganized, link); @@ -551,14 +464,17 @@ bool chaser_confirm::set_reorganized(const header_link& link, return true; } -bool chaser_confirm::roll_back(header_links& popped, - const header_link& link, size_t fork_point, size_t top) NOEXCEPT +bool chaser_confirm::roll_back(const header_links& popped, + size_t fork_point, size_t top, const header_link& link) NOEXCEPT { - auto& query = archive(); + // The current block (top/link) is set_strong but is not confirmable. + return archive().set_unstrong(link) && roll_back(popped, fork_point, top); +} - // The current block is set_strong but not confirmed. - if (!query.set_unstrong_parallel(link)) - return false; +bool chaser_confirm::roll_back(const header_links& popped, size_t fork_point, + size_t top) NOEXCEPT +{ + const auto& query = archive(); for (auto height = top; height > fork_point; --height) if (!set_reorganized(query.to_confirmed(height), height)) @@ -568,7 +484,6 @@ bool chaser_confirm::roll_back(header_links& popped, if (!reset_organized(fk, ++fork_point)) return false; - popped.clear(); return true; } diff --git a/src/chasers/chaser_populate.cpp b/src/chasers/chaser_populate.cpp new file mode 100644 index 000000000..b790ec9f5 --- /dev/null +++ b/src/chasers/chaser_populate.cpp @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2011-2024 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +#include +#include +#include +#include + +namespace libbitcoin { +namespace node { + +#define CLASS chaser_populate + +////using namespace system; +////using namespace system::chain; +////using namespace database; +////using namespace network; +using namespace std::placeholders; + +////// Shared pointers required for lifetime in handler parameters. +////BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) +////BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) +////BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +chaser_populate::chaser_populate(full_node& node) NOEXCEPT + : chaser(node) +{ +} + +// start/stop +// ---------------------------------------------------------------------------- + +code chaser_populate::start() NOEXCEPT +{ + SUBSCRIBE_EVENTS(handle_event, _1, _2, _3); + return error::success; +} + +bool chaser_populate::handle_event(const code&, chase event_, + event_value) NOEXCEPT +{ + if (closed()) + return false; + + switch (event_) + { + case chase::checked: + { + POST(do_checked, height_t{}); + break; + } + case chase::stop: + { + return false; + } + default: + { + break; + } + } + + return true; +} + +void chaser_populate::do_checked(height_t) NOEXCEPT +{ + BC_ASSERT(stranded()); +} + +////BC_POP_WARNING() +////BC_POP_WARNING() +////BC_POP_WARNING() + +} // namespace node +} // namespace libbitcoin diff --git a/src/chasers/chaser_snapshot.cpp b/src/chasers/chaser_snapshot.cpp index f171ca7d3..938b37004 100644 --- a/src/chasers/chaser_snapshot.cpp +++ b/src/chasers/chaser_snapshot.cpp @@ -82,7 +82,8 @@ bool chaser_snapshot::handle_event(const code& ec, chase event_, if (!enabled_bytes_ || ec) break; // Checked blocks are out of order, so this is probalistic. - POST(do_archive, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_archive, std::get(value)); break; } case chase::confirmable: @@ -91,7 +92,8 @@ bool chaser_snapshot::handle_event(const code& ec, chase event_, if (!enabled_valid_ || ec) break; // Confirmable covers all validation except set_confirmed (link). - POST(do_confirm, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_confirm, std::get(value)); break; } default: diff --git a/src/chasers/chaser_template.cpp b/src/chasers/chaser_template.cpp index c30b98efa..411c44605 100644 --- a/src/chasers/chaser_template.cpp +++ b/src/chasers/chaser_template.cpp @@ -62,7 +62,8 @@ bool chaser_template::handle_event(const code&, chase event_, { case chase::transaction: { - POST(do_transaction, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_transaction, std::get(value)); break; } case chase::stop: diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 8659f097b..fc240e2b3 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -79,17 +79,20 @@ bool chaser_validate::handle_event(const code&, chase event_, } case chase::checked: { - POST(do_checked, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_checked, std::get(value)); break; } case chase::regressed: { - POST(do_regressed, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_regressed, std::get(value)); break; } case chase::disorganized: { - POST(do_regressed, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_regressed, std::get(value)); break; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -180,6 +183,7 @@ void chaser_validate::do_bump(height_t) NOEXCEPT } else { + // Validation is currently bypassed in all cases. ////// TODO: the quantity of work must be throttled. ////// Will very rapidly pump outstanding work in asio queue. ////if (!enqueue_block(link)) @@ -361,6 +365,8 @@ void chaser_validate::validate_block(const code& ec, return; } + // TODO: move fee setter to set_block_valid (transitory) and propagate to + // TODO: set_block_confirmable (final). Bypassed do not have the fee cache. if (!query.set_block_valid(link)) { fault(error::set_block_valid); diff --git a/src/full_node.cpp b/src/full_node.cpp index 15c8fae5a..175538100 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index e378edd89..4edd81fc6 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -147,12 +147,14 @@ bool protocol_block_in_31800::handle_event(const code&, chase event_, // There are count blocks to download at/above given header. // chase::headers is only sent for current candidate chain, and this // chase::download is only sent as a consequence of chase::headers. - POST(do_get_downloads, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_get_downloads, std::get(value)); break; } case chase::report: { - POST(do_report, possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + POST(do_report, std::get(value)); break; } case chase::stop: diff --git a/src/sessions/session.cpp b/src/sessions/session.cpp index 467a3b22d..b1c3d3700 100644 --- a/src/sessions/session.cpp +++ b/src/sessions/session.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include diff --git a/src/sessions/session_outbound.cpp b/src/sessions/session_outbound.cpp index d781e9969..6fafd2a02 100644 --- a/src/sessions/session_outbound.cpp +++ b/src/sessions/session_outbound.cpp @@ -82,7 +82,8 @@ bool session_outbound::handle_event(const code&, chase event_, case chase::starved: { // When a channel becomes starved notify other(s) to split work. - do_starved(possible_narrow_cast(value)); + BC_ASSERT(std::holds_alternative(value)); + do_starved(std::get(value)); break; } case chase::stop: diff --git a/test/chasers/chaser_populate.cpp b/test/chasers/chaser_populate.cpp new file mode 100644 index 000000000..b1cbc3337 --- /dev/null +++ b/test/chasers/chaser_populate.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +BOOST_AUTO_TEST_SUITE(chaser_populate_tests) + +BOOST_AUTO_TEST_CASE(chaser_populate_test) +{ + BOOST_REQUIRE(true); +} + +BOOST_AUTO_TEST_SUITE_END()