diff --git a/include/bitcoin/node/channels/channel_electrum.hpp b/include/bitcoin/node/channels/channel_electrum.hpp index 98be699f..71dbebd3 100644 --- a/include/bitcoin/node/channels/channel_electrum.hpp +++ b/include/bitcoin/node/channels/channel_electrum.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace libbitcoin { namespace node { @@ -51,6 +52,34 @@ class BCN_API channel_electrum network::tracker(log) { } + + /// Properties. + /// ----------------------------------------------------------------------- + + inline void set_client(const std::string& name) NOEXCEPT + { + name_ = name; + } + + inline const std::string& client() const NOEXCEPT + { + return name_; + } + + inline void set_version(electrum_version version) NOEXCEPT + { + version_ = version; + } + + inline electrum_version version() const NOEXCEPT + { + return version_; + } + +private: + // These are protected by strand. + electrum_version version_{ electrum_version::v0_0 }; + std::string name_{}; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index f7a5d823..c489212c 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -42,6 +42,7 @@ class BCN_API protocol_electrum const network::channel::ptr& channel, const options_t& options) NOEXCEPT : node::protocol_rpc(session, channel, options), + channel_(std::dynamic_pointer_cast(channel)), network::tracker(session->log) { } @@ -116,10 +117,14 @@ class BCN_API protocol_electrum rpc_interface::mempool_get_fee_histogram) NOEXCEPT; protected: - ////bool is_version(protocol_version version) const NOEXCEPT - ////{ - //// return version_ >= version; - ////} + inline bool is_version(electrum_version version) const NOEXCEPT + { + return channel_->version() >= version; + } + +private: + // This is mostly thread safe, and used in a thread safe manner. + const channel_t::ptr channel_; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_electrum_version.hpp b/include/bitcoin/node/protocols/protocol_electrum_version.hpp index 7c6c09b8..0356508c 100644 --- a/include/bitcoin/node/protocols/protocol_electrum_version.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum_version.hpp @@ -42,6 +42,7 @@ class BCN_API protocol_electrum_version const network::channel::ptr& channel, const options_t& options) NOEXCEPT : node::protocol_rpc(session, channel, options), + channel_(std::dynamic_pointer_cast(channel)), network::tracker(session->log) { } @@ -50,22 +51,22 @@ class BCN_API protocol_electrum_version virtual void complete(const code& ec, const code& shake) NOEXCEPT; protected: + static constexpr electrum_version minimum = electrum_version::v1_4; + static constexpr electrum_version maximum = electrum_version::v1_4_2; + static constexpr size_t max_client_name_length = 1024; + void handle_server_version(const code& ec, rpc_interface::server_version, const std::string& client_name, const interface::value_t& protocol_version) NOEXCEPT; -protected: - static constexpr electrum_version minimum = electrum_version::v1_4; - static constexpr electrum_version maximum = electrum_version::v1_4_2; - electrum_version version() const NOEXCEPT; - std::string_view get_version() const NOEXCEPT; + std::string_view negotiated_version() const NOEXCEPT; bool set_version(const interface::value_t& version) NOEXCEPT; bool get_versions(electrum_version& min, electrum_version& max, const interface::value_t& version) NOEXCEPT; - std::string_view get_server() const NOEXCEPT; - std::string_view get_client() const NOEXCEPT; + std::string_view server_name() const NOEXCEPT; + std::string_view client_name() const NOEXCEPT; std::string escape_client(const std::string& in) NOEXCEPT; bool set_client(const std::string& name) NOEXCEPT; @@ -75,10 +76,11 @@ class BCN_API protocol_electrum_version static electrum_version version_from_string( const std::string_view& version) NOEXCEPT; - // These are protected by strand. + // This is mostly thread safe, and used in a thread safe manner. + const channel_t::ptr channel_; + + // This is protected by strand. std::shared_ptr handler_{}; - electrum_version version_{ electrum_version::v0_0 }; - std::string name_{}; }; } // namespace node diff --git a/src/protocols/protocol_electrum_version.cpp b/src/protocols/protocol_electrum_version.cpp index fc817e8c..c32ba1a1 100644 --- a/src/protocols/protocol_electrum_version.cpp +++ b/src/protocols/protocol_electrum_version.cpp @@ -34,8 +34,6 @@ using namespace network; using namespace interface; using namespace std::placeholders; -constexpr auto max_client_name_length = 1024u; - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) @@ -72,7 +70,7 @@ void protocol_electrum_version::complete(const code& ec, // Calls after handshake completion are allowed and will skip this. if (handler_) { - // shake error will result in stopped channel. + // Invoke handshake completion, error will result in stopped channel. (*handler_)(shake); handler_.reset(); } @@ -84,14 +82,14 @@ void protocol_electrum_version::complete(const code& ec, // Changed in version 1.6: server must tolerate and ignore extraneous args. void protocol_electrum_version::handle_server_version(const code& ec, rpc_interface::server_version, const std::string& client_name, - const value_t& electrum_version) NOEXCEPT + const value_t& protocol_version) NOEXCEPT { if (stopped(ec)) return; // v0_0 implies version has not been set (first call). - if ((version() == electrum_version::v0_0) && - (!set_client(client_name) || !set_version(electrum_version))) + if ((channel_->version() == electrum_version::v0_0) && + (!set_client(client_name) || !set_version(protocol_version))) { const auto reason = error::invalid_argument; send_code(reason, BIND(complete, _1, reason)); @@ -102,24 +100,27 @@ void protocol_electrum_version::handle_server_version(const code& ec, { array_t { - { string_t{ get_server() } }, - { string_t{ get_version() } } + { string_t{ server_name() } }, + { string_t{ negotiated_version() } } } }, 70, BIND(complete, _1, error::success)); } + + // Handshake must leave channel paused, before leaving stranded handler. + if (handler_) pause(); } // Client/server names. // ---------------------------------------------------------------------------- -std::string_view protocol_electrum_version::get_server() const NOEXCEPT +std::string_view protocol_electrum_version::server_name() const NOEXCEPT { return settings().user_agent; } -std::string_view protocol_electrum_version::get_client() const NOEXCEPT +std::string_view protocol_electrum_version::client_name() const NOEXCEPT { - return name_; + return channel_->client(); } bool protocol_electrum_version::set_client(const std::string& name) NOEXCEPT @@ -129,11 +130,12 @@ bool protocol_electrum_version::set_client(const std::string& name) NOEXCEPT return false; // Do not put to log without escaping. - name_ = escape_client(name); + channel_->set_client(escape_client(name)); return true; } -std::string protocol_electrum_version::escape_client(const std::string& in) NOEXCEPT +std::string protocol_electrum_version::escape_client( + const std::string& in) NOEXCEPT { std::string out(in.size(), '*'); std::transform(in.begin(), in.end(), out.begin(), [](char c) NOEXCEPT @@ -148,14 +150,9 @@ std::string protocol_electrum_version::escape_client(const std::string& in) NOEX // Negotiated version. // ---------------------------------------------------------------------------- -electrum_version protocol_electrum_version::version() const NOEXCEPT -{ - return version_; -} - -std::string_view protocol_electrum_version::get_version() const NOEXCEPT +std::string_view protocol_electrum_version::negotiated_version() const NOEXCEPT { - return version_to_string(version_); + return version_to_string(channel_->version()); } bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT @@ -171,9 +168,9 @@ bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT return false; LOGA("Electrum [" << authority() << "] version (" - << version_to_string(client_max) << ") " << get_client()); + << version_to_string(client_max) << ") " << client_name()); - version_ = upper; + channel_->set_version(upper); return true; }