From 7f00796a3f96054569a28ba9b87902afae3bba8e Mon Sep 17 00:00:00 2001 From: evoskuil Date: Sun, 11 Feb 2024 12:42:52 -0500 Subject: [PATCH] Add chaser_candidate and stub in event handling. --- Makefile.am | 2 + builds/cmake/CMakeLists.txt | 1 + .../libbitcoin-node/libbitcoin-node.vcxproj | 2 + .../libbitcoin-node.vcxproj.filters | 6 ++ include/bitcoin/node.hpp | 1 + include/bitcoin/node/chasers/chaser.hpp | 67 ++++++++++++++++--- .../bitcoin/node/chasers/chaser_candidate.hpp | 45 +++++++++++++ include/bitcoin/node/chasers/chaser_check.hpp | 5 ++ .../bitcoin/node/chasers/chaser_confirm.hpp | 2 + .../bitcoin/node/chasers/chaser_connect.hpp | 4 +- .../bitcoin/node/chasers/chaser_header.hpp | 4 +- .../node/chasers/chaser_transaction.hpp | 4 +- include/bitcoin/node/chasers/chasers.hpp | 1 + include/bitcoin/node/error.hpp | 1 + include/bitcoin/node/full_node.hpp | 24 ++++--- src/chasers/chaser.cpp | 32 +++++++++ src/chasers/chaser_candidate.cpp | 39 +++++++++++ src/chasers/chaser_check.cpp | 26 +++++++ src/error.cpp | 1 + src/full_node.cpp | 45 +++++++++++-- test/error.cpp | 9 +++ 21 files changed, 295 insertions(+), 26 deletions(-) create mode 100644 include/bitcoin/node/chasers/chaser_candidate.hpp create mode 100644 src/chasers/chaser_candidate.cpp diff --git a/Makefile.am b/Makefile.am index 3a5330bc..5255bad4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ src_libbitcoin_node_la_SOURCES = \ src/parser.cpp \ src/settings.cpp \ src/chasers/chaser.cpp \ + src/chasers/chaser_candidate.cpp \ src/chasers/chaser_check.cpp \ src/chasers/chaser_confirm.cpp \ src/chasers/chaser_connect.cpp \ @@ -116,6 +117,7 @@ include_bitcoin_node_HEADERS = \ include_bitcoin_node_chasersdir = ${includedir}/bitcoin/node/chasers include_bitcoin_node_chasers_HEADERS = \ include/bitcoin/node/chasers/chaser.hpp \ + include/bitcoin/node/chasers/chaser_candidate.hpp \ include/bitcoin/node/chasers/chaser_check.hpp \ include/bitcoin/node/chasers/chaser_confirm.hpp \ include/bitcoin/node/chasers/chaser_connect.hpp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index f6b9248a..a995bea2 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -251,6 +251,7 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/parser.cpp" "../../src/settings.cpp" "../../src/chasers/chaser.cpp" + "../../src/chasers/chaser_candidate.cpp" "../../src/chasers/chaser_check.cpp" "../../src/chasers/chaser_confirm.cpp" "../../src/chasers/chaser_connect.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index ee997d55..5a1e5bbb 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -74,6 +74,7 @@ + @@ -101,6 +102,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index b97d84a4..31a4637e 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -45,6 +45,9 @@ src\chasers + + src\chasers + src\chasers @@ -122,6 +125,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 69a8361d..af936f4f 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index efb30da7..ee14cfc4 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -28,34 +28,81 @@ namespace node { class full_node; /// Abstract base chaser. - /// Chasers impose order on blockchain/pool construction as necessary. - /// Each chaser operates on its own strand, implemented here, allowing /// concurrent chaser operations to the extent that threads are available. - /// Events are passed between chasers using the full_node shared notifier. /// Notifications are bounced from sink (e.g. chaser) to its strand, and -/// full_node::notify bounces from source (e.g. chaser) to network strand. - +/// notify bounces from source (e.g. chaser) to network strand. class BCN_API chaser - : public network::enable_shared_from_base, - public network::reporter + : public network::reporter { public: + enum class chase + { + /// Initialize chaser state (no data). + start, + + /// A new strong branch exists (strong header link). + /// Issued by 'header' and handled by 'check'. + header, + + /// A block has been downloaded, checked and stored (header link). + /// Issued by 'check' and handled by 'connect'. + checked, + + /// A branch has been connected (header link). + /// Issued by 'connect' and handled by 'confirm'. + connected, + + /// A branch has been confirmed (top header link). + /// Issued by 'confirm' and handled by 'transaction'. + confirmed, + + /// A new transaction has been added to the pool (tx link). + /// Issued by 'transaction' and handled by 'candidate'. + transaction, + + /// A new candidate block has been created (data?). + /// Issued by 'candidate' and handled by miners. + candidate, + + /// Service is stopping (accompanied by error::service_stopped). + stop + }; + + typedef network::subscriber event_subscriber; + typedef event_subscriber::handler event_handler; DELETE_COPY_MOVE(chaser); - /// True if the current thread is on the chaser strand. - virtual bool stranded() const NOEXCEPT; + // TODO: public method to check/store a block. + // TODO: not stranded, thread safe, and posts checked event. protected: chaser(full_node& node) NOEXCEPT; - virtual ~chaser() NOEXCEPT; + ~chaser() NOEXCEPT; + + /// Close the node. + void close(const code& ec) NOEXCEPT; + + /// True if the current thread is on the chaser strand. + bool stranded() const NOEXCEPT; + + /// Subscribe to chaser events. + code subscribe(event_handler&& handler) NOEXCEPT; + + /// Set chaser event (does not require network strand). + void notify(const code& ec, chase value) NOEXCEPT; private: + void do_notify(const code& ec, chase value) NOEXCEPT; + // These are thread safe (mostly). full_node& node_; network::asio::strand strand_; + + // This is protected by the network strand. + event_subscriber& subscriber_; }; } // namespace node diff --git a/include/bitcoin/node/chasers/chaser_candidate.hpp b/include/bitcoin/node/chasers/chaser_candidate.hpp new file mode 100644 index 00000000..8e8f2e43 --- /dev/null +++ b/include/bitcoin/node/chasers/chaser_candidate.hpp @@ -0,0 +1,45 @@ +/** + * 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_CANDIDATE_HPP +#define LIBBITCOIN_NODE_CHASERS_CHASER_CANDIDATE_HPP + +#include +#include +#include + +namespace libbitcoin { +namespace node { + +class full_node; + +/// Construct candidate blocks upon modification of the transaction DAG. +/// Notify subscribers with "candidate" event. +class BCN_API chaser_candidate + : public chaser, protected network::tracker +{ +public: + typedef std::unique_ptr ptr; + + chaser_candidate(full_node& node) NOEXCEPT; +}; + +} // namespace node +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/chasers/chaser_check.hpp b/include/bitcoin/node/chasers/chaser_check.hpp index c103ddf2..c857cf15 100644 --- a/include/bitcoin/node/chasers/chaser_check.hpp +++ b/include/bitcoin/node/chasers/chaser_check.hpp @@ -34,7 +34,12 @@ class BCN_API chaser_check : public chaser, protected network::tracker { public: + typedef std::unique_ptr ptr; + chaser_check(full_node& node) NOEXCEPT; + +private: + void handle_event(const code& ec, chase value) NOEXCEPT; }; } // namespace node diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index 32da4213..959cbcaf 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -34,6 +34,8 @@ class BCN_API chaser_confirm : public chaser, protected network::tracker { public: + typedef std::unique_ptr ptr; + chaser_confirm(full_node& node) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chaser_connect.hpp b/include/bitcoin/node/chasers/chaser_connect.hpp index 925a2c6f..dc659974 100644 --- a/include/bitcoin/node/chasers/chaser_connect.hpp +++ b/include/bitcoin/node/chasers/chaser_connect.hpp @@ -31,9 +31,11 @@ class full_node; /// Chase down blocks in the the candidate header chain for validation. /// Notify subscribers with the "block connected" event. class BCN_API chaser_connect - : public chaser, protected network::tracker + : public chaser, protected network::tracker { public: + typedef std::unique_ptr ptr; + chaser_connect(full_node& node) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chaser_header.hpp b/include/bitcoin/node/chasers/chaser_header.hpp index 4e2658ac..ca7b0048 100644 --- a/include/bitcoin/node/chasers/chaser_header.hpp +++ b/include/bitcoin/node/chasers/chaser_header.hpp @@ -31,9 +31,11 @@ class full_node; /// Chase down stronger header branches for the candidate chain. /// Notify subscribers with "strong header" event. class BCN_API chaser_header - : public chaser, protected network::tracker + : public chaser, protected network::tracker { public: + typedef std::unique_ptr ptr; + chaser_header(full_node& node) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chaser_transaction.hpp b/include/bitcoin/node/chasers/chaser_transaction.hpp index dd5a5ac3..061d3022 100644 --- a/include/bitcoin/node/chasers/chaser_transaction.hpp +++ b/include/bitcoin/node/chasers/chaser_transaction.hpp @@ -30,9 +30,11 @@ class full_node; /// Chase down unconfirmed transactions. class BCN_API chaser_transaction - : public chaser, protected network::tracker + : public chaser, protected network::tracker { public: + typedef std::unique_ptr ptr; + chaser_transaction(full_node& node) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chasers.hpp b/include/bitcoin/node/chasers/chasers.hpp index b4b97f70..4fa00223 100644 --- a/include/bitcoin/node/chasers/chasers.hpp +++ b/include/bitcoin/node/chasers/chasers.hpp @@ -20,6 +20,7 @@ #define LIBBITCOIN_NODE_CHASERS_CHASERS_HPP #include +#include #include #include #include diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index 5d1caf8e..a0e383d2 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -38,6 +38,7 @@ enum error_t : uint8_t { success, unknown, + unexpected_event, // database store_uninitialized, diff --git a/include/bitcoin/node/full_node.hpp b/include/bitcoin/node/full_node.hpp index f787b243..0a79c0af 100644 --- a/include/bitcoin/node/full_node.hpp +++ b/include/bitcoin/node/full_node.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,6 @@ class BCN_API full_node typedef std::shared_ptr ptr; typedef database::store store; typedef database::query query; - typedef network::subscriber<> event_subscriber; /// Constructors. /// ----------------------------------------------------------------------- @@ -56,11 +56,6 @@ class BCN_API full_node /// Close the node. void close() NOEXCEPT override; - /// Events. - /// ----------------------------------------------------------------------- - - // TODO: subscribe/notify. - /// Properties. /// ----------------------------------------------------------------------- @@ -70,8 +65,15 @@ class BCN_API full_node /// Thread safe synchronous archival interface. query& archive() const NOEXCEPT; + /// Obtain reference to the chaser event subscriber. + chaser::event_subscriber& event_subscriber() NOEXCEPT; + protected: + virtual code create_chasers() NOEXCEPT; + virtual void stop_chasers() NOEXCEPT; + /// Session attachments. + /// ----------------------------------------------------------------------- network::session_manual::ptr attach_manual_session() NOEXCEPT override; network::session_inbound::ptr attach_inbound_session() NOEXCEPT override; network::session_outbound::ptr attach_outbound_session() NOEXCEPT override; @@ -85,8 +87,14 @@ class BCN_API full_node const configuration& config_; query& query_; - // This is protected by strand. - event_subscriber event_subscriber_; + // These are protected by strand. + chaser::event_subscriber event_subscriber_; + chaser_header::ptr chaser_header_{}; + chaser_check::ptr chaser_check_{}; + chaser_connect::ptr chaser_connect_{}; + chaser_confirm::ptr chaser_confirm_{}; + chaser_transaction::ptr chaser_transaction_{}; + chaser_candidate::ptr chaser_candidate_{}; }; } // namespace node diff --git a/src/chasers/chaser.cpp b/src/chasers/chaser.cpp index 3a75c8e6..f7bf9160 100644 --- a/src/chasers/chaser.cpp +++ b/src/chasers/chaser.cpp @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include namespace libbitcoin { @@ -30,6 +32,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser::chaser(full_node& node) NOEXCEPT : node_(node), strand_(node.service().get_executor()), + subscriber_(node.event_subscriber()), reporter(node.log) { } @@ -38,11 +41,40 @@ chaser::~chaser() NOEXCEPT { } + +void chaser::close(const code& ec) NOEXCEPT +{ + LOGF("Chaser fault, " << ec.message()); + node_.close(); +} + bool chaser::stranded() const NOEXCEPT { return strand_.running_in_this_thread(); } +// Must be non-virtual for constructor invoke. +// Requires network strand (call from node start). +code chaser::subscribe(event_handler&& handler) NOEXCEPT +{ + BC_ASSERT_MSG(node_.stranded(), "chaser"); + return subscriber_.subscribe(std::move(handler)); +} + +// Posts to network strand (call from chaser strands). +void chaser::notify(const code& ec, chase value) NOEXCEPT +{ + boost::asio::post(node_.strand(), + std::bind(&chaser::do_notify, this, ec, value)); +} + +// Executed on network strand (handler should bounce to chaser strand). +void chaser::do_notify(const code& ec, chase value) NOEXCEPT +{ + BC_ASSERT_MSG(node_.stranded(), "chaser"); + subscriber_.notify(ec, value); +} + BC_POP_WARNING() } // namespace database diff --git a/src/chasers/chaser_candidate.cpp b/src/chasers/chaser_candidate.cpp new file mode 100644 index 00000000..589abe4d --- /dev/null +++ b/src/chasers/chaser_candidate.cpp @@ -0,0 +1,39 @@ +/** + * 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 + +#include +#include +#include + +namespace libbitcoin { +namespace node { + +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +chaser_candidate::chaser_candidate(full_node& node) NOEXCEPT + : chaser(node), + tracker(node.log) +{ +} + +BC_POP_WARNING() + +} // namespace database +} // namespace libbitcoin diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index 19ca1cf6..06492666 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -18,19 +18,45 @@ */ #include +#include #include +#include #include #include namespace libbitcoin { namespace node { +using namespace std::placeholders; + BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_check::chaser_check(full_node& node) NOEXCEPT : chaser(node), tracker(node.log) { + subscribe(std::bind(&chaser_check::handle_event, this, _1, _2)); +} + +void chaser_check::handle_event(const code& ec, chase value) NOEXCEPT +{ + BC_ASSERT_MSG(stranded(), "chaser_check"); + + // The code should be error::service_stopped when error::stop is set. + if (ec) + return; + + switch (value) + { + case chase::start: + // TODO: initialize. + break; + case chase::header: + // TODO: handle the new strong branch (may issue 'checked'). + break; + default: + return; + } } BC_POP_WARNING() diff --git a/src/error.cpp b/src/error.cpp index ac1ae9d3..893f3656 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -29,6 +29,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) // general { success, "success" }, { unknown, "unknown error" }, + { unexpected_event, "unexpected event" }, // database { store_uninitialized, "store not initialized" }, diff --git a/src/full_node.cpp b/src/full_node.cpp index 7050f946..aa56e20e 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -61,7 +62,13 @@ void full_node::do_start(const result_handler& handler) NOEXCEPT { BC_ASSERT_MSG(stranded(), "full_node"); - // Do stuff here. + const auto ec = create_chasers(); + + if (ec) + { + handler(ec); + return; + } p2p::do_start(handler); } @@ -97,15 +104,37 @@ void full_node::do_close() NOEXCEPT { BC_ASSERT_MSG(stranded(), "full_node"); - // Do stuff here. - + stop_chasers(); p2p::do_close(); } -// Events. +// Chasers. // ---------------------------------------------------------------------------- -// TODO: subscribe/notify. +code full_node::create_chasers() NOEXCEPT +{ + BC_ASSERT_MSG(stranded(), "full_node"); + + // Create and subscribe all chasers. + chaser_header_ = std::make_unique(*this); + chaser_check_ = std::make_unique(*this); + chaser_connect_ = std::make_unique(*this); + chaser_confirm_ = std::make_unique(*this); + chaser_transaction_ = std::make_unique(*this); + chaser_candidate_ = std::make_unique(*this); + + // Post start event to all chasers. + event_subscriber_.notify(error::success, chaser::chase::start); + return error::success; +} + +void full_node::stop_chasers() NOEXCEPT +{ + BC_ASSERT_MSG(stranded(), "full_node"); + + event_subscriber_.stop(network::error::service_stopped, + chaser::chase::stop); +} // Properties. // ---------------------------------------------------------------------------- @@ -120,6 +149,12 @@ const configuration& full_node::config() const NOEXCEPT return config_; } +// protected +chaser::event_subscriber& full_node::event_subscriber() NOEXCEPT +{ + return event_subscriber_; +} + // Session attachments. // ---------------------------------------------------------------------------- diff --git a/test/error.cpp b/test/error.cpp index 5e1063ad..55150425 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -41,6 +41,15 @@ BOOST_AUTO_TEST_CASE(error_t__code__unknown__true_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "unknown error"); } +BOOST_AUTO_TEST_CASE(error_t__code__unexpected_event__true_exected_message) +{ + constexpr auto value = error::unexpected_event; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "unexpected event"); +} + BOOST_AUTO_TEST_CASE(error_t__code__store_uninitialized__true_exected_message) { constexpr auto value = error::store_uninitialized;