From e1c80c8c7afb5719ff25e082b5ad9c8a2ab99355 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 14:35:47 -0500 Subject: [PATCH 1/6] Split out electrum and sv1 channels. --- Makefile.am | 3 +- .../libbitcoin-node/libbitcoin-node.vcxproj | 3 +- .../libbitcoin-node.vcxproj.filters | 5 +- include/bitcoin/node.hpp | 3 +- .../node/channels/channel_electrum.hpp | 59 +++++++++++++++++++ .../{channel_rpc.hpp => channel_sv1.hpp} | 25 ++++---- include/bitcoin/node/channels/channels.hpp | 3 +- .../node/protocols/protocol_electrum.hpp | 4 +- .../bitcoin/node/protocols/protocol_rpc.hpp | 9 +-- .../node/protocols/protocol_stratum_v1.hpp | 5 +- src/protocols/protocol_electrum.cpp | 4 +- src/protocols/protocol_stratum_v1.cpp | 2 +- 12 files changed, 96 insertions(+), 29 deletions(-) create mode 100644 include/bitcoin/node/channels/channel_electrum.hpp rename include/bitcoin/node/channels/{channel_rpc.hpp => channel_sv1.hpp} (66%) diff --git a/Makefile.am b/Makefile.am index f1c3090f..fb7bd016 100644 --- a/Makefile.am +++ b/Makefile.am @@ -183,9 +183,10 @@ include_bitcoin_node_HEADERS = \ include_bitcoin_node_channelsdir = ${includedir}/bitcoin/node/channels include_bitcoin_node_channels_HEADERS = \ include/bitcoin/node/channels/channel.hpp \ + include/bitcoin/node/channels/channel_electrum.hpp \ include/bitcoin/node/channels/channel_http.hpp \ include/bitcoin/node/channels/channel_peer.hpp \ - include/bitcoin/node/channels/channel_rpc.hpp \ + include/bitcoin/node/channels/channel_sv1.hpp \ include/bitcoin/node/channels/channel_sv2.hpp \ include/bitcoin/node/channels/channel_ws.hpp \ include/bitcoin/node/channels/channels.hpp diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index d9899614..4a136c21 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -175,9 +175,10 @@ + - + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index 4adb9895..f7f8414f 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -221,13 +221,16 @@ include\bitcoin\node\channels + + include\bitcoin\node\channels + include\bitcoin\node\channels include\bitcoin\node\channels - + include\bitcoin\node\channels diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index 846c7491..c55b7823 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -28,9 +28,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include diff --git a/include/bitcoin/node/channels/channel_electrum.hpp b/include/bitcoin/node/channels/channel_electrum.hpp new file mode 100644 index 00000000..98be699f --- /dev/null +++ b/include/bitcoin/node/channels/channel_electrum.hpp @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2011-2025 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_CHANNELS_CHANNEL_ELECTRUM_HPP +#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_ELECTRUM_HPP + +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace node { + +// TODO: strip extraneous args before electrum version dispatch. +// TODO: move version_ and name_ members into channel with set/get. +// TODO: move to electrum version method to handshake protocol. +/// Channel for electrum channels (non-http json-rpc). +class BCN_API channel_electrum + : public node::channel, + public network::channel_rpc, + protected network::tracker +{ +public: + typedef std::shared_ptr ptr; + using interface_t = interface::electrum; + using options_t = typename network::channel_rpc::options_t; + + inline channel_electrum(const network::logger& log, + const network::socket::ptr& socket, uint64_t identifier, + const node::configuration& config, const options_t& options) NOEXCEPT + : node::channel(log, socket, identifier, config), + network::channel_rpc(log, socket, identifier, + config.network, options), + network::tracker(log) + { + } +}; + +} // namespace node +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/channels/channel_rpc.hpp b/include/bitcoin/node/channels/channel_sv1.hpp similarity index 66% rename from include/bitcoin/node/channels/channel_rpc.hpp rename to include/bitcoin/node/channels/channel_sv1.hpp index 2be67709..9315c30e 100644 --- a/include/bitcoin/node/channels/channel_rpc.hpp +++ b/include/bitcoin/node/channels/channel_sv1.hpp @@ -16,35 +16,36 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#ifndef LIBBITCOIN_NODE_CHANNELS_CHANNEL_RPC_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_RPC_HPP +#ifndef LIBBITCOIN_NODE_CHANNELS_CHANNEL_SV1_HPP +#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_SV1_HPP #include #include #include #include +#include namespace libbitcoin { namespace node { -/// Channel for electrum and stratum v1 channels (non-http json-rpc). -template -class BCN_API channel_rpc +/// Channel for stratum v1 channels (non-http json-rpc). +class BCN_API channel_sv1 : public node::channel, - public network::channel_rpc, - protected network::tracker> + public network::channel_rpc, + protected network::tracker { public: - typedef std::shared_ptr ptr; - using options_t = typename network::channel_rpc::options_t; + typedef std::shared_ptr ptr; + using interface_t = interface::stratum_v1; + using options_t = typename network::channel_rpc::options_t; - inline channel_rpc(const network::logger& log, + inline channel_sv1(const network::logger& log, const network::socket::ptr& socket, uint64_t identifier, const node::configuration& config, const options_t& options) NOEXCEPT : node::channel(log, socket, identifier, config), - network::channel_rpc(log, socket, identifier, + network::channel_rpc(log, socket, identifier, config.network, options), - network::tracker(log) + network::tracker(log) { } }; diff --git a/include/bitcoin/node/channels/channels.hpp b/include/bitcoin/node/channels/channels.hpp index 77a0110b..ad12fa05 100644 --- a/include/bitcoin/node/channels/channels.hpp +++ b/include/bitcoin/node/channels/channels.hpp @@ -20,9 +20,10 @@ #define LIBBITCOIN_NODE_CHANNELS_CHANNELS_HPP #include +#include #include #include -#include +#include #include #include diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index 0a39928f..8a24ccc1 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -30,7 +30,7 @@ namespace libbitcoin { namespace node { class BCN_API protocol_electrum - : public node::protocol_rpc, + : public node::protocol_rpc, protected network::tracker { public: @@ -40,7 +40,7 @@ class BCN_API protocol_electrum inline protocol_electrum(const auto& session, const network::channel::ptr& channel, const options_t& options) NOEXCEPT - : node::protocol_rpc(session, channel, options), + : node::protocol_rpc(session, channel, options), network::tracker(session->log) { } diff --git a/include/bitcoin/node/protocols/protocol_rpc.hpp b/include/bitcoin/node/protocols/protocol_rpc.hpp index 0720deec..e0e87433 100644 --- a/include/bitcoin/node/protocols/protocol_rpc.hpp +++ b/include/bitcoin/node/protocols/protocol_rpc.hpp @@ -28,13 +28,13 @@ namespace libbitcoin { namespace node { /// Abstract base for RPC protocols, thread safe. -template +template class BCN_API protocol_rpc : public node::protocol, - public network::protocol_rpc + public network::protocol_rpc { public: - using channel_t = node::channel_rpc; + using channel_t = Channel; using options_t = channel_t::options_t; protected: @@ -42,7 +42,8 @@ class BCN_API protocol_rpc const network::channel::ptr& channel, const options_t& options) NOEXCEPT : node::protocol(session, channel), - network::protocol_rpc(session, channel, options) + network::protocol_rpc( + session, channel, options) { } }; diff --git a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp index ef27250c..ae0a34ee 100644 --- a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp +++ b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp @@ -20,6 +20,7 @@ #define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_STRATUM_V1_HPP #include +#include #include #include #include @@ -28,7 +29,7 @@ namespace libbitcoin { namespace node { class BCN_API protocol_stratum_v1 - : public node::protocol_rpc, + : public node::protocol_rpc, protected network::tracker { public: @@ -38,7 +39,7 @@ class BCN_API protocol_stratum_v1 inline protocol_stratum_v1(const auto& session, const network::channel::ptr& channel, const options_t& options) NOEXCEPT - : node::protocol_rpc(session, channel, options), + : node::protocol_rpc(session, channel, options), network::tracker(session->log) { } diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index 66e6f2e4..c70065f3 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -76,7 +76,7 @@ void protocol_electrum::start() NOEXCEPT // Mempool methods. SUBSCRIBE_RPC(handle_mempool_get_fee_histogram, _1, _2); - node::protocol_rpc::start(); + node::protocol_rpc::start(); } // Handlers (blockchain). @@ -244,8 +244,6 @@ void protocol_electrum::handle_server_ping(const code& ec, send_code(error::not_implemented); } -// TODO: move to handshake protocol. -// TODO: strip extraneous args before dispatch. // Changed in version 1.6: server must tolerate and ignore extraneous args. void protocol_electrum::handle_server_version(const code& ec, rpc_interface::server_version, const std::string& client_name, diff --git a/src/protocols/protocol_stratum_v1.cpp b/src/protocols/protocol_stratum_v1.cpp index 94675375..94753f01 100644 --- a/src/protocols/protocol_stratum_v1.cpp +++ b/src/protocols/protocol_stratum_v1.cpp @@ -59,7 +59,7 @@ void protocol_stratum_v1::start() NOEXCEPT SUBSCRIBE_RPC(handle_client_reconnect, _1, _2, _3, _4, _5); SUBSCRIBE_RPC(handle_client_hello, _1, _2, _3); SUBSCRIBE_RPC(handle_client_rejected, _1, _2, _3, _4); - node::protocol_rpc::start(); + node::protocol_rpc::start(); } // Handlers (client requests). From 881a40a218709ad43a268ba13aacefbf33d98a0c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 14:38:07 -0500 Subject: [PATCH 2/6] Remove sv1 acronym (naming style). --- Makefile.am | 4 ++-- .../vs2022/libbitcoin-node/libbitcoin-node.vcxproj | 4 ++-- .../libbitcoin-node.vcxproj.filters | 4 ++-- include/bitcoin/node.hpp | 4 ++-- .../{channel_sv1.hpp => channel_stratum_v1.hpp} | 14 +++++++------- .../{channel_sv2.hpp => channel_stratum_v2.hpp} | 14 +++++++------- include/bitcoin/node/channels/channels.hpp | 4 ++-- .../bitcoin/node/protocols/protocol_stratum_v1.hpp | 4 ++-- .../bitcoin/node/protocols/protocol_stratum_v2.hpp | 2 +- src/protocols/protocol_stratum_v1.cpp | 2 +- 10 files changed, 28 insertions(+), 28 deletions(-) rename include/bitcoin/node/channels/{channel_sv1.hpp => channel_stratum_v1.hpp} (82%) rename include/bitcoin/node/channels/{channel_sv2.hpp => channel_stratum_v2.hpp} (80%) diff --git a/Makefile.am b/Makefile.am index fb7bd016..0d9c4c79 100644 --- a/Makefile.am +++ b/Makefile.am @@ -186,8 +186,8 @@ include_bitcoin_node_channels_HEADERS = \ include/bitcoin/node/channels/channel_electrum.hpp \ include/bitcoin/node/channels/channel_http.hpp \ include/bitcoin/node/channels/channel_peer.hpp \ - include/bitcoin/node/channels/channel_sv1.hpp \ - include/bitcoin/node/channels/channel_sv2.hpp \ + include/bitcoin/node/channels/channel_stratum_v1.hpp \ + include/bitcoin/node/channels/channel_stratum_v2.hpp \ include/bitcoin/node/channels/channel_ws.hpp \ include/bitcoin/node/channels/channels.hpp diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index 4a136c21..d3b1aba1 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -178,8 +178,8 @@ - - + + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index f7f8414f..e9a6da09 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -230,10 +230,10 @@ include\bitcoin\node\channels - + include\bitcoin\node\channels - + include\bitcoin\node\channels diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index c55b7823..7779a915 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -31,8 +31,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/include/bitcoin/node/channels/channel_sv1.hpp b/include/bitcoin/node/channels/channel_stratum_v1.hpp similarity index 82% rename from include/bitcoin/node/channels/channel_sv1.hpp rename to include/bitcoin/node/channels/channel_stratum_v1.hpp index 9315c30e..5fe39dde 100644 --- a/include/bitcoin/node/channels/channel_sv1.hpp +++ b/include/bitcoin/node/channels/channel_stratum_v1.hpp @@ -16,8 +16,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#ifndef LIBBITCOIN_NODE_CHANNELS_CHANNEL_SV1_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_SV1_HPP +#ifndef LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V1_HPP +#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V1_HPP #include #include @@ -29,23 +29,23 @@ namespace libbitcoin { namespace node { /// Channel for stratum v1 channels (non-http json-rpc). -class BCN_API channel_sv1 +class BCN_API channel_stratum_v1 : public node::channel, public network::channel_rpc, - protected network::tracker + protected network::tracker { public: - typedef std::shared_ptr ptr; + typedef std::shared_ptr ptr; using interface_t = interface::stratum_v1; using options_t = typename network::channel_rpc::options_t; - inline channel_sv1(const network::logger& log, + inline channel_stratum_v1(const network::logger& log, const network::socket::ptr& socket, uint64_t identifier, const node::configuration& config, const options_t& options) NOEXCEPT : node::channel(log, socket, identifier, config), network::channel_rpc(log, socket, identifier, config.network, options), - network::tracker(log) + network::tracker(log) { } }; diff --git a/include/bitcoin/node/channels/channel_sv2.hpp b/include/bitcoin/node/channels/channel_stratum_v2.hpp similarity index 80% rename from include/bitcoin/node/channels/channel_sv2.hpp rename to include/bitcoin/node/channels/channel_stratum_v2.hpp index 43fbcf32..faee09f0 100644 --- a/include/bitcoin/node/channels/channel_sv2.hpp +++ b/include/bitcoin/node/channels/channel_stratum_v2.hpp @@ -16,8 +16,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#ifndef LIBBITCOIN_NODE_CHANNELS_CHANNEL_SV2_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_SV2_HPP +#ifndef LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V2_HPP +#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V2_HPP #include #include @@ -28,20 +28,20 @@ namespace libbitcoin { namespace node { /// Channel for stratum v2 (custom protocol, not implemented). -class BCN_API channel_sv2 +class BCN_API channel_stratum_v2 : public node::channel, public network::channel, - protected network::tracker + protected network::tracker { public: - typedef std::shared_ptr ptr; + typedef std::shared_ptr ptr; - inline channel_sv2(const network::logger& log, + inline channel_stratum_v2(const network::logger& log, const network::socket::ptr& socket, uint64_t identifier, const node::configuration& config, const options_t& options) NOEXCEPT : node::channel(log, socket, identifier, config), network::channel(log, socket, identifier, config.network, options), - network::tracker(log) + network::tracker(log) { } }; diff --git a/include/bitcoin/node/channels/channels.hpp b/include/bitcoin/node/channels/channels.hpp index ad12fa05..10627939 100644 --- a/include/bitcoin/node/channels/channels.hpp +++ b/include/bitcoin/node/channels/channels.hpp @@ -23,8 +23,8 @@ #include #include #include -#include -#include +#include +#include #include #endif diff --git a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp index ae0a34ee..7d0ac220 100644 --- a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp +++ b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp @@ -29,7 +29,7 @@ namespace libbitcoin { namespace node { class BCN_API protocol_stratum_v1 - : public node::protocol_rpc, + : public node::protocol_rpc, protected network::tracker { public: @@ -39,7 +39,7 @@ class BCN_API protocol_stratum_v1 inline protocol_stratum_v1(const auto& session, const network::channel::ptr& channel, const options_t& options) NOEXCEPT - : node::protocol_rpc(session, channel, options), + : node::protocol_rpc(session, channel, options), network::tracker(session->log) { } diff --git a/include/bitcoin/node/protocols/protocol_stratum_v2.hpp b/include/bitcoin/node/protocols/protocol_stratum_v2.hpp index 0b3dc254..ad265c90 100644 --- a/include/bitcoin/node/protocols/protocol_stratum_v2.hpp +++ b/include/bitcoin/node/protocols/protocol_stratum_v2.hpp @@ -34,7 +34,7 @@ class BCN_API protocol_stratum_v2 { public: typedef std::shared_ptr ptr; - using channel_t = node::channel_sv2; + using channel_t = node::channel_stratum_v2; inline protocol_stratum_v2(const auto& session, const network::channel::ptr& channel, const options_t&) NOEXCEPT diff --git a/src/protocols/protocol_stratum_v1.cpp b/src/protocols/protocol_stratum_v1.cpp index 94753f01..eb406d43 100644 --- a/src/protocols/protocol_stratum_v1.cpp +++ b/src/protocols/protocol_stratum_v1.cpp @@ -59,7 +59,7 @@ void protocol_stratum_v1::start() NOEXCEPT SUBSCRIBE_RPC(handle_client_reconnect, _1, _2, _3, _4, _5); SUBSCRIBE_RPC(handle_client_hello, _1, _2, _3); SUBSCRIBE_RPC(handle_client_rejected, _1, _2, _3, _4); - node::protocol_rpc::start(); + node::protocol_rpc::start(); } // Handlers (client requests). From deea877dd3f5ccc4f32eeece96ff14fd4bb1957c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 18:32:23 -0500 Subject: [PATCH 3/6] Move electrum version method into its own handshake protocol. --- Makefile.am | 3 + builds/cmake/CMakeLists.txt | 1 + .../libbitcoin-node/libbitcoin-node.vcxproj | 3 + .../libbitcoin-node.vcxproj.filters | 9 + include/bitcoin/node.hpp | 2 + .../node/protocols/protocol_electrum.hpp | 76 +---- .../protocols/protocol_electrum_version.hpp | 128 ++++++++ include/bitcoin/node/protocols/protocols.hpp | 1 + .../node/sessions/session_handshake.hpp | 78 +++++ .../bitcoin/node/sessions/session_server.hpp | 27 +- include/bitcoin/node/sessions/sessions.hpp | 4 +- src/protocols/protocol_electrum.cpp | 197 +----------- src/protocols/protocol_electrum_version.cpp | 281 ++++++++++++++++++ 13 files changed, 522 insertions(+), 288 deletions(-) create mode 100644 include/bitcoin/node/protocols/protocol_electrum_version.hpp create mode 100644 include/bitcoin/node/sessions/session_handshake.hpp create mode 100644 src/protocols/protocol_electrum_version.cpp diff --git a/Makefile.am b/Makefile.am index 0d9c4c79..f6300f48 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,7 @@ src_libbitcoin_node_la_SOURCES = \ src/protocols/protocol_block_out_106.cpp \ src/protocols/protocol_block_out_70012.cpp \ src/protocols/protocol_electrum.cpp \ + src/protocols/protocol_electrum_version.cpp \ src/protocols/protocol_explore.cpp \ src/protocols/protocol_filter_out_70015.cpp \ src/protocols/protocol_header_in_31800.cpp \ @@ -239,6 +240,7 @@ include_bitcoin_node_protocols_HEADERS = \ include/bitcoin/node/protocols/protocol_block_out_106.hpp \ include/bitcoin/node/protocols/protocol_block_out_70012.hpp \ include/bitcoin/node/protocols/protocol_electrum.hpp \ + include/bitcoin/node/protocols/protocol_electrum_version.hpp \ include/bitcoin/node/protocols/protocol_explore.hpp \ include/bitcoin/node/protocols/protocol_filter_out_70015.hpp \ include/bitcoin/node/protocols/protocol_header_in_31800.hpp \ @@ -261,6 +263,7 @@ include_bitcoin_node_protocols_HEADERS = \ include_bitcoin_node_sessionsdir = ${includedir}/bitcoin/node/sessions include_bitcoin_node_sessions_HEADERS = \ include/bitcoin/node/sessions/session.hpp \ + include/bitcoin/node/sessions/session_handshake.hpp \ include/bitcoin/node/sessions/session_inbound.hpp \ include/bitcoin/node/sessions/session_manual.hpp \ include/bitcoin/node/sessions/session_outbound.hpp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 15f19bb9..15f2cd8d 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -277,6 +277,7 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/protocols/protocol_block_out_106.cpp" "../../src/protocols/protocol_block_out_70012.cpp" "../../src/protocols/protocol_electrum.cpp" + "../../src/protocols/protocol_electrum_version.cpp" "../../src/protocols/protocol_explore.cpp" "../../src/protocols/protocol_filter_out_70015.cpp" "../../src/protocols/protocol_header_in_31800.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index d3b1aba1..315be5fa 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -150,6 +150,7 @@ + @@ -222,6 +223,7 @@ + @@ -241,6 +243,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index e9a6da09..4b97483d 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -150,6 +150,9 @@ src\protocols + + src\protocols + src\protocols @@ -362,6 +365,9 @@ include\bitcoin\node\protocols + + include\bitcoin\node\protocols + include\bitcoin\node\protocols @@ -419,6 +425,9 @@ include\bitcoin\node\sessions + + include\bitcoin\node\sessions + include\bitcoin\node\sessions diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index 7779a915..d68905c0 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index 8a24ccc1..1a1bbf56 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -106,81 +106,19 @@ class BCN_API protocol_electrum rpc_interface::server_peers_subscribe) NOEXCEPT; void handle_server_ping(const code& ec, rpc_interface::server_ping) NOEXCEPT; - void handle_server_version(const code& ec, - rpc_interface::server_version, const std::string& client_name, - const interface::value_t& protocol_version) NOEXCEPT; + ////void handle_server_version(const code& ec, + //// rpc_interface::server_version, const std::string& client_name, + //// const interface::value_t& protocol_version) NOEXCEPT; /// Handlers (mempool). void handle_mempool_get_fee_histogram(const code& ec, rpc_interface::mempool_get_fee_histogram) NOEXCEPT; protected: - enum class protocol_version - { - /// Invalid version. - v0_0, - - /// 2011, initial protocol negotiation. - v0_6, - - /// 2012, enhanced protocol negotiation. - v0_8, - - /// 2012, added pruning limits and transport indicators. - v0_9, - - /// 2013, baseline for core methods in the official specification. - v0_10, - - /// 2014, 1.x series, deprecations of utxo and block number methods. - v1_0, - - /// 2015, updated version response and introduced scripthash methods. - v1_1, - - /// 2017, added optional parameters for transactions and headers. - v1_2, - - /// 2018, defaulted raw headers and introduced new block methods. - v1_3, - - /// 2019, removed deserialized headers and added merkle proof features. - v1_4, - - /// 2019, modifications for auxiliary proof-of-work handling. - v1_4_1, - - /// 2020, added scripthash unsubscribe functionality. - v1_4_2, - - /// 2022, updated response formats and added fee estimation modes. - v1_6 - }; - - static constexpr protocol_version minimum = protocol_version::v1_4; - static constexpr protocol_version maximum = protocol_version::v1_4_2; - - protocol_version version() const NOEXCEPT; - std::string_view get_version() const NOEXCEPT; - bool is_version(protocol_version version) const NOEXCEPT; - bool set_version(const interface::value_t& version) NOEXCEPT; - bool get_versions(protocol_version& min, protocol_version& max, - const interface::value_t& version) NOEXCEPT; - - static std::string_view get_server() NOEXCEPT; - std::string_view get_client() const NOEXCEPT; - std::string escape_client(const std::string& in) NOEXCEPT; - bool set_client(const std::string& name) NOEXCEPT; - -private: - static std::string_view version_to_string( - protocol_version version) NOEXCEPT; - static protocol_version version_from_string( - const std::string_view& version) NOEXCEPT; - - // These are protected by strand. - protocol_version version_{ protocol_version::v0_0 }; - std::string name_{}; + ////bool is_version(protocol_version version) const NOEXCEPT + ////{ + //// return version_ >= version; + ////} }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_electrum_version.hpp b/include/bitcoin/node/protocols/protocol_electrum_version.hpp new file mode 100644 index 00000000..6d49488a --- /dev/null +++ b/include/bitcoin/node/protocols/protocol_electrum_version.hpp @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2011-2025 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_PROTOCOLS_PROTOCOL_ELECTRUM_VERSION_HPP +#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_ELECTRUM_VERSION_HPP + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace node { + +class BCN_API protocol_electrum_version + : public node::protocol_rpc, + protected network::tracker +{ +public: + typedef std::shared_ptr ptr; + using rpc_interface = interface::electrum; + + inline protocol_electrum_version(const auto& session, + const network::channel::ptr& channel, + const options_t& options) NOEXCEPT + : node::protocol_rpc(session, channel, options), + network::tracker(session->log) + { + } + + virtual void shake(network::result_handler&& handler) NOEXCEPT; + virtual void complete(const code& ec, const code& shake) NOEXCEPT; + +protected: + void handle_server_version(const code& ec, + rpc_interface::server_version, const std::string& client_name, + const interface::value_t& protocol_version) NOEXCEPT; + +protected: + enum class protocol_version + { + /// Invalid version. + v0_0, + + /// 2011, initial protocol negotiation. + v0_6, + + /// 2012, enhanced protocol negotiation. + v0_8, + + /// 2012, added pruning limits and transport indicators. + v0_9, + + /// 2013, baseline for core methods in the official specification. + v0_10, + + /// 2014, 1.x series, deprecations of utxo and block number methods. + v1_0, + + /// 2015, updated version response and introduced scripthash methods. + v1_1, + + /// 2017, added optional parameters for transactions and headers. + v1_2, + + /// 2018, defaulted raw headers and introduced new block methods. + v1_3, + + /// 2019, removed deserialized headers and added merkle proof features. + v1_4, + + /// 2019, modifications for auxiliary proof-of-work handling. + v1_4_1, + + /// 2020, added scripthash unsubscribe functionality. + v1_4_2, + + /// 2022, updated response formats and added fee estimation modes. + v1_6 + }; + + static constexpr protocol_version minimum = protocol_version::v1_4; + static constexpr protocol_version maximum = protocol_version::v1_4_2; + + protocol_version version() const NOEXCEPT; + std::string_view get_version() const NOEXCEPT; + bool set_version(const interface::value_t& version) NOEXCEPT; + bool get_versions(protocol_version& min, protocol_version& max, + const interface::value_t& version) NOEXCEPT; + + static std::string_view get_server() NOEXCEPT; + std::string_view get_client() const NOEXCEPT; + std::string escape_client(const std::string& in) NOEXCEPT; + bool set_client(const std::string& name) NOEXCEPT; + +private: + static std::string_view version_to_string( + protocol_version version) NOEXCEPT; + static protocol_version version_from_string( + const std::string_view& version) NOEXCEPT; + + // These are protected by strand. + std::shared_ptr handler_{}; + protocol_version version_{ protocol_version::v0_0 }; + std::string name_{}; +}; + +} // namespace node +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/protocols/protocols.hpp b/include/bitcoin/node/protocols/protocols.hpp index 184ccf44..4d96db34 100644 --- a/include/bitcoin/node/protocols/protocols.hpp +++ b/include/bitcoin/node/protocols/protocols.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include diff --git a/include/bitcoin/node/sessions/session_handshake.hpp b/include/bitcoin/node/sessions/session_handshake.hpp new file mode 100644 index 00000000..d8699095 --- /dev/null +++ b/include/bitcoin/node/sessions/session_handshake.hpp @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2011-2025 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_SESSIONS_SESSION_HANDSHAKE_HPP +#define LIBBITCOIN_NODE_SESSIONS_SESSION_HANDSHAKE_HPP + +#include +#include +#include +#include + +namespace libbitcoin { +namespace node { + +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +class full_node; + +/// This is session_server<> with added support for single handshake protocol. +template +class session_handshake + : public node::session_server, + protected network::tracker> +{ +public: + typedef std::shared_ptr> ptr; + using base = node::session_server; + + /// Construct an instance (network should be started). + inline session_handshake(full_node& node, uint64_t identifier, + const base::options_t& options) NOEXCEPT + : node::session_server(node, identifier, options), + network::tracker>(node) + { + } + +protected: + /// Overridden to implement a connection handshake. Handshake protocol(s) + /// must invoke handler one time at completion. Use + /// std::dynamic_pointer_cast(channel) to obtain channel_t. + inline void attach_handshake(const base::channel_ptr& channel, + network::result_handler&& handler) NOEXCEPT override + { + using own = session_handshake; + const auto self = this->shared_from_base(); + channel->attach(self, this->options_)-> + shake(std::move(handler)); + } + + /// Enable handshake (network::session_server disables by default). + inline void do_attach_handshake(const base::channel_ptr& channel, + const network::result_handler& handshake) NOEXCEPT override + { + network::session::do_attach_handshake(channel, handshake); + } +}; + +BC_POP_WARNING() + +} // namespace node +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/sessions/session_server.hpp b/include/bitcoin/node/sessions/session_server.hpp index 74552222..9acdd209 100644 --- a/include/bitcoin/node/sessions/session_server.hpp +++ b/include/bitcoin/node/sessions/session_server.hpp @@ -83,34 +83,19 @@ class session_server inline channel_ptr create_channel( const socket_ptr& socket) NOEXCEPT override { - BC_ASSERT(stranded()); - const auto channel = std::make_shared(log, socket, - this->create_key(), this->config(), options_); + this->create_key(), this->config(), this->options_); return std::static_pointer_cast(channel); } - /// Override to implement a connection handshake as required. By default - /// this is bypassed, which applies to basic http services. Handshake - /// protocol(s) must invoke handler one time at completion. Use - /// std::dynamic_pointer_cast(channel) to obtain channel_t. - inline void attach_handshake(const channel_ptr& channel, - network::result_handler&& handler) NOEXCEPT override - { - BC_ASSERT(channel->stranded()); - BC_ASSERT(channel->paused()); - - network::session_server::attach_handshake(channel, std::move(handler)); - } - template = true> inline void attach_rest(const channel_ptr&, const ptr&) NOEXCEPT{} template inline void attach_rest(const channel_ptr& channel, const ptr& self) NOEXCEPT { - channel->attach(self, options_)->start(); + channel->attach(self, this->options_)->start(); attach_rest(channel, self); } @@ -120,14 +105,12 @@ class session_server /// Use std::dynamic_pointer_cast(channel) to obtain channel_t. inline void attach_protocols(const channel_ptr& channel) NOEXCEPT override { - BC_ASSERT(channel->stranded()); - BC_ASSERT(channel->paused()); - - const auto self = shared_from_base>(); + using base = session_server; + const auto self = this->shared_from_base(); attach_rest(channel, self); } -private: +protected: // This is thread safe. const options_t& options_; }; diff --git a/include/bitcoin/node/sessions/sessions.hpp b/include/bitcoin/node/sessions/sessions.hpp index 6a6877b7..935d5a0e 100644 --- a/include/bitcoin/node/sessions/sessions.hpp +++ b/include/bitcoin/node/sessions/sessions.hpp @@ -20,6 +20,7 @@ #define LIBBITCOIN_NODE_SESSIONS_SESSIONS_HPP #include +#include #include #include #include @@ -35,9 +36,10 @@ namespace node { using session_web = session_server; using session_explore = session_server; using session_bitcoind = session_server; -using session_electrum = session_server; using session_stratum_v1 = session_server; using session_stratum_v2 = session_server; +using session_electrum = session_handshake; } // namespace network } // namespace libbitcoin diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index c70065f3..0dabff44 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -72,7 +72,7 @@ void protocol_electrum::start() NOEXCEPT SUBSCRIBE_RPC(handle_server_features, _1, _2); SUBSCRIBE_RPC(handle_server_peers_subscribe, _1, _2); SUBSCRIBE_RPC(handle_server_ping, _1, _2); - SUBSCRIBE_RPC(handle_server_version, _1, _2, _3, _4); + ////SUBSCRIBE_RPC(handle_server_version, _1, _2, _3, _4); // Mempool methods. SUBSCRIBE_RPC(handle_mempool_get_fee_histogram, _1, _2); @@ -244,32 +244,6 @@ void protocol_electrum::handle_server_ping(const code& ec, send_code(error::not_implemented); } -// Changed in version 1.6: server must tolerate and ignore extraneous args. -void protocol_electrum::handle_server_version(const code& ec, - rpc_interface::server_version, const std::string& client_name, - const value_t& protocol_version) NOEXCEPT -{ - if (stopped(ec)) - return; - - // v0_0 implies version has not been set (first call). - if ((version() == protocol_version::v0_0) && - (!set_client(client_name) || !set_version(protocol_version))) - { - send_code(error::invalid_argument); - return; - } - - send_result(value_t - { - array_t - { - { string_t{ get_server() } }, - { string_t{ get_version() } } - } - }, 70); -} - // Handlers (mempool). // ---------------------------------------------------------------------------- @@ -280,175 +254,6 @@ void protocol_electrum::handle_mempool_get_fee_histogram(const code& ec, send_code(error::not_implemented); } -// Client/server names. -// ---------------------------------------------------------------------------- - -// static -std::string_view protocol_electrum::get_server() NOEXCEPT -{ - return BC_USER_AGENT; -} - -std::string_view protocol_electrum::get_client() const NOEXCEPT -{ - return name_; -} - -bool protocol_electrum::set_client(const std::string& name) NOEXCEPT -{ - // Avoid excess, empty name is allowed. - if (name.size() > max_client_name_length) - return false; - - // Do not put to log without escaping. - name_ = escape_client(name); - return true; -} - -std::string protocol_electrum::escape_client(const std::string& in) NOEXCEPT -{ - std::string out(in.size(), '*'); - std::transform(in.begin(), in.end(), out.begin(), [](char c) NOEXCEPT - { - using namespace system; - return is_ascii_character(c) && !is_ascii_whitespace(c) ? c : '*'; - }); - - return out; -} - -// Negotiated version. -// ---------------------------------------------------------------------------- - -bool protocol_electrum::is_version(protocol_version version) const NOEXCEPT -{ - return version_ >= version; -} - -protocol_electrum::protocol_version protocol_electrum::version() const NOEXCEPT -{ - return version_; -} - -std::string_view protocol_electrum::get_version() const NOEXCEPT -{ - return version_to_string(version_); -} - -bool protocol_electrum::set_version(const value_t& version) NOEXCEPT -{ - protocol_version client_min{}; - protocol_version client_max{}; - if (!get_versions(client_min, client_max, version)) - return false; - - const auto lower = std::max(client_min, minimum); - const auto upper = std::min(client_max, maximum); - if (lower > upper) - return false; - - LOGA("Electrum [" << authority() << "] version (" - << version_to_string(client_max) << ") " << get_client()); - - version_ = upper; - return true; -} - -bool protocol_electrum::get_versions(protocol_version& min, - protocol_version& max, const interface::value_t& version) NOEXCEPT -{ - // Optional value_t can be string_t or array_t of two string_t. - const auto& value = version.value(); - - // Default version (null_t is the default of value_t). - if (std::holds_alternative(value)) - { - // An interface default can't be set for optional. - max = min = protocol_version::v1_4; - return true; - } - - // One version. - if (std::holds_alternative(value)) - { - // A single value implies minimum is the same as maximum. - max = min = version_from_string(std::get(value)); - return min != protocol_version::v0_0; - } - - // Two versions. - if (std::holds_alternative(value)) - { - const auto& versions = std::get(value); - if (versions.size() != two) - return false; - - // First string is mimimum, second is maximum. - const auto& min_version = versions.at(0).value(); - const auto& max_version = versions.at(1).value(); - if (!std::holds_alternative(min_version) || - !std::holds_alternative(max_version)) - return false; - - min = version_from_string(std::get(min_version)); - max = version_from_string(std::get(max_version)); - return min != protocol_version::v0_0 - && max != protocol_version::v0_0; - } - - return false; -} - -// private/static -std::string_view protocol_electrum::version_to_string( - protocol_version version) NOEXCEPT -{ - static const std::unordered_map map - { - { protocol_version::v0_6, "0.6" }, - { protocol_version::v0_8, "0.8" }, - { protocol_version::v0_9, "0.9" }, - { protocol_version::v0_10, "0.10" }, - { protocol_version::v1_0, "1.0" }, - { protocol_version::v1_1, "1.1" }, - { protocol_version::v1_2, "1.2" }, - { protocol_version::v1_3, "1.3" }, - { protocol_version::v1_4, "1.4" }, - { protocol_version::v1_4_1, "1.4.1" }, - { protocol_version::v1_4_2, "1.4.2" }, - { protocol_version::v1_6, "1.6" }, - { protocol_version::v0_0, "0.0" } - }; - - const auto it = map.find(version); - return it != map.end() ? it->second : "0.0"; -} - -// private/static -protocol_electrum::protocol_version protocol_electrum::version_from_string( - const std::string_view& version) NOEXCEPT -{ - static const std::unordered_map map - { - { "0.6", protocol_version::v0_6 }, - { "0.8", protocol_version::v0_8 }, - { "0.9", protocol_version::v0_9 }, - { "0.10", protocol_version::v0_10 }, - { "1.0", protocol_version::v1_0 }, - { "1.1", protocol_version::v1_1 }, - { "1.2", protocol_version::v1_2 }, - { "1.3", protocol_version::v1_3 }, - { "1.4", protocol_version::v1_4 }, - { "1.4.1", protocol_version::v1_4_1 }, - { "1.4.2", protocol_version::v1_4_2 }, - { "1.6", protocol_version::v1_6 }, - { "0.0", protocol_version::v0_0 } - }; - - const auto it = map.find(version); - return it != map.end() ? it->second : protocol_version::v0_0; -} - BC_POP_WARNING() BC_POP_WARNING() BC_POP_WARNING() diff --git a/src/protocols/protocol_electrum_version.cpp b/src/protocols/protocol_electrum_version.cpp new file mode 100644 index 00000000..06671879 --- /dev/null +++ b/src/protocols/protocol_electrum_version.cpp @@ -0,0 +1,281 @@ +/** + * Copyright (c) 2011-2025 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 +#include + +namespace libbitcoin { +namespace node { + +#define CLASS protocol_electrum_version + +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) + +// Start/complete (handshake). +// ---------------------------------------------------------------------------- + +// Session resumes the channel following return from start(). +// Sends are not precluded, but no messages can be received while paused. +void protocol_electrum_version::shake(result_handler&& handler) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (started()) + { + handler(network::error::operation_failed); + return; + } + + handler_ = system::move_shared(std::move(handler)); + + SUBSCRIBE_RPC(handle_server_version, _1, _2, _3, _4); + protocol_rpc::start(); +} + +void protocol_electrum_version::complete(const code& ec, + const code& shake) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (stopped(ec)) + return; + + if (handler_) + { + // shake error will result in stopped channel. + (*handler_)(shake); + handler_.reset(); + } +} + +// Handler. +// ---------------------------------------------------------------------------- + +// 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& protocol_version) NOEXCEPT +{ + if (stopped(ec)) + return; + + // v0_0 implies version has not been set (first call). + if ((version() == protocol_version::v0_0) && + (!set_client(client_name) || !set_version(protocol_version))) + { + const auto reason = error::invalid_argument; + send_code(reason, BIND(complete, _1, reason)); + } + else + { + send_result(value_t + { + array_t + { + { string_t{ get_server() } }, + { string_t{ get_version() } } + } + }, 70, BIND(complete, _1, error::success)); + } +} + +// Client/server names. +// ---------------------------------------------------------------------------- + +// static +std::string_view protocol_electrum_version::get_server() NOEXCEPT +{ + return BC_USER_AGENT; +} + +std::string_view protocol_electrum_version::get_client() const NOEXCEPT +{ + return name_; +} + +bool protocol_electrum_version::set_client(const std::string& name) NOEXCEPT +{ + // Avoid excess, empty name is allowed. + if (name.size() > max_client_name_length) + return false; + + // Do not put to log without escaping. + name_ = escape_client(name); + return true; +} + +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 + { + using namespace system; + return is_ascii_character(c) && !is_ascii_whitespace(c) ? c : '*'; + }); + + return out; +} + +// Negotiated version. +// ---------------------------------------------------------------------------- + +protocol_electrum_version::protocol_version +protocol_electrum_version::version() const NOEXCEPT +{ + return version_; +} + +std::string_view protocol_electrum_version::get_version() const NOEXCEPT +{ + return version_to_string(version_); +} + +bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT +{ + protocol_version client_min{}; + protocol_version client_max{}; + if (!get_versions(client_min, client_max, version)) + return false; + + const auto lower = std::max(client_min, minimum); + const auto upper = std::min(client_max, maximum); + if (lower > upper) + return false; + + LOGA("Electrum [" << authority() << "] version (" + << version_to_string(client_max) << ") " << get_client()); + + version_ = upper; + return true; +} + +bool protocol_electrum_version::get_versions(protocol_version& min, + protocol_version& max, const interface::value_t& version) NOEXCEPT +{ + // Optional value_t can be string_t or array_t of two string_t. + const auto& value = version.value(); + + // Default version (null_t is the default of value_t). + if (std::holds_alternative(value)) + { + // An interface default can't be set for optional. + max = min = protocol_version::v1_4; + return true; + } + + // One version. + if (std::holds_alternative(value)) + { + // A single value implies minimum is the same as maximum. + max = min = version_from_string(std::get(value)); + return min != protocol_version::v0_0; + } + + // Two versions. + if (std::holds_alternative(value)) + { + const auto& versions = std::get(value); + if (versions.size() != two) + return false; + + // First string is mimimum, second is maximum. + const auto& min_version = versions.at(0).value(); + const auto& max_version = versions.at(1).value(); + if (!std::holds_alternative(min_version) || + !std::holds_alternative(max_version)) + return false; + + min = version_from_string(std::get(min_version)); + max = version_from_string(std::get(max_version)); + return min != protocol_version::v0_0 + && max != protocol_version::v0_0; + } + + return false; +} + +// private/static +std::string_view +protocol_electrum_version::version_to_string(protocol_version version) NOEXCEPT +{ + static const std::unordered_map map + { + { protocol_version::v0_6, "0.6" }, + { protocol_version::v0_8, "0.8" }, + { protocol_version::v0_9, "0.9" }, + { protocol_version::v0_10, "0.10" }, + { protocol_version::v1_0, "1.0" }, + { protocol_version::v1_1, "1.1" }, + { protocol_version::v1_2, "1.2" }, + { protocol_version::v1_3, "1.3" }, + { protocol_version::v1_4, "1.4" }, + { protocol_version::v1_4_1, "1.4.1" }, + { protocol_version::v1_4_2, "1.4.2" }, + { protocol_version::v1_6, "1.6" }, + { protocol_version::v0_0, "0.0" } + }; + + const auto it = map.find(version); + return it != map.end() ? it->second : "0.0"; +} + +// private/static +protocol_electrum_version::protocol_version +protocol_electrum_version::version_from_string( + const std::string_view& version) NOEXCEPT +{ + static const std::unordered_map map + { + { "0.6", protocol_version::v0_6 }, + { "0.8", protocol_version::v0_8 }, + { "0.9", protocol_version::v0_9 }, + { "0.10", protocol_version::v0_10 }, + { "1.0", protocol_version::v1_0 }, + { "1.1", protocol_version::v1_1 }, + { "1.2", protocol_version::v1_2 }, + { "1.3", protocol_version::v1_3 }, + { "1.4", protocol_version::v1_4 }, + { "1.4.1", protocol_version::v1_4_1 }, + { "1.4.2", protocol_version::v1_4_2 }, + { "1.6", protocol_version::v1_6 }, + { "0.0", protocol_version::v0_0 } + }; + + const auto it = map.find(version); + return it != map.end() ? it->second : protocol_version::v0_0; +} + +BC_POP_WARNING() +BC_POP_WARNING() +BC_POP_WARNING() + +} // namespace node +} // namespace libbitcoin From 6ff9b02ffa52d07b68e85b457ee52e05e335c8dc Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 19:21:35 -0500 Subject: [PATCH 4/6] Add missing template keywords. --- include/bitcoin/node/sessions/session_handshake.hpp | 4 ++-- include/bitcoin/node/sessions/session_server.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/bitcoin/node/sessions/session_handshake.hpp b/include/bitcoin/node/sessions/session_handshake.hpp index d8699095..b1b56cda 100644 --- a/include/bitcoin/node/sessions/session_handshake.hpp +++ b/include/bitcoin/node/sessions/session_handshake.hpp @@ -57,8 +57,8 @@ class session_handshake network::result_handler&& handler) NOEXCEPT override { using own = session_handshake; - const auto self = this->shared_from_base(); - channel->attach(self, this->options_)-> + const auto self = this->template shared_from_base(); + channel->template attach(self, this->options_)-> shake(std::move(handler)); } diff --git a/include/bitcoin/node/sessions/session_server.hpp b/include/bitcoin/node/sessions/session_server.hpp index 9acdd209..18029e67 100644 --- a/include/bitcoin/node/sessions/session_server.hpp +++ b/include/bitcoin/node/sessions/session_server.hpp @@ -95,7 +95,7 @@ class session_server template inline void attach_rest(const channel_ptr& channel, const ptr& self) NOEXCEPT { - channel->attach(self, this->options_)->start(); + channel->template attach(self, this->options_)->start(); attach_rest(channel, self); } @@ -105,8 +105,8 @@ class session_server /// Use std::dynamic_pointer_cast(channel) to obtain channel_t. inline void attach_protocols(const channel_ptr& channel) NOEXCEPT override { - using base = session_server; - const auto self = this->shared_from_base(); + using own = session_server; + const auto self = this->template shared_from_base(); attach_rest(channel, self); } From 41d193bad5ce1741e07043e1c53095127679622c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 19:22:10 -0500 Subject: [PATCH 5/6] Use network.user_agent setting for electrum server name. --- include/bitcoin/node/protocols/protocol_electrum_version.hpp | 2 +- src/protocols/protocol_electrum_version.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/bitcoin/node/protocols/protocol_electrum_version.hpp b/include/bitcoin/node/protocols/protocol_electrum_version.hpp index 6d49488a..abdf65f0 100644 --- a/include/bitcoin/node/protocols/protocol_electrum_version.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum_version.hpp @@ -105,7 +105,7 @@ class BCN_API protocol_electrum_version bool get_versions(protocol_version& min, protocol_version& max, const interface::value_t& version) NOEXCEPT; - static std::string_view get_server() NOEXCEPT; + std::string_view get_server() const NOEXCEPT; std::string_view get_client() const NOEXCEPT; std::string escape_client(const std::string& in) NOEXCEPT; bool set_client(const std::string& name) NOEXCEPT; diff --git a/src/protocols/protocol_electrum_version.cpp b/src/protocols/protocol_electrum_version.cpp index 06671879..328abcd8 100644 --- a/src/protocols/protocol_electrum_version.cpp +++ b/src/protocols/protocol_electrum_version.cpp @@ -110,10 +110,9 @@ void protocol_electrum_version::handle_server_version(const code& ec, // Client/server names. // ---------------------------------------------------------------------------- -// static -std::string_view protocol_electrum_version::get_server() NOEXCEPT +std::string_view protocol_electrum_version::get_server() const NOEXCEPT { - return BC_USER_AGENT; + return settings().user_agent; } std::string_view protocol_electrum_version::get_client() const NOEXCEPT From e0f227a9e3ca88887ee181be3682d57788f9757d Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 20:00:14 -0500 Subject: [PATCH 6/6] Move electrum_version to an enum for shared usage. --- Makefile.am | 1 + .../libbitcoin-node/libbitcoin-node.vcxproj | 1 + .../libbitcoin-node.vcxproj.filters | 3 + include/bitcoin/node.hpp | 1 + .../bitcoin/node/parsers/electrum_version.hpp | 72 +++++++++++++++ include/bitcoin/node/parsers/parsers.hpp | 1 + .../node/protocols/protocol_electrum.hpp | 1 + .../protocols/protocol_electrum_version.hpp | 57 ++---------- src/protocols/protocol_electrum_version.cpp | 92 +++++++++---------- 9 files changed, 134 insertions(+), 95 deletions(-) create mode 100644 include/bitcoin/node/parsers/electrum_version.hpp diff --git a/Makefile.am b/Makefile.am index f6300f48..1b47545e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,7 @@ include_bitcoin_node_parsersdir = ${includedir}/bitcoin/node/parsers include_bitcoin_node_parsers_HEADERS = \ include/bitcoin/node/parsers/bitcoind_query.hpp \ include/bitcoin/node/parsers/bitcoind_target.hpp \ + include/bitcoin/node/parsers/electrum_version.hpp \ include/bitcoin/node/parsers/explore_query.hpp \ include/bitcoin/node/parsers/explore_target.hpp \ include/bitcoin/node/parsers/parsers.hpp diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index 315be5fa..15549b8e 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -212,6 +212,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index 4b97483d..9ecd4a17 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -332,6 +332,9 @@ include\bitcoin\node\parsers + + include\bitcoin\node\parsers + include\bitcoin\node\parsers diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index d68905c0..0f58e11e 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/node/parsers/electrum_version.hpp b/include/bitcoin/node/parsers/electrum_version.hpp new file mode 100644 index 00000000..2a4b181b --- /dev/null +++ b/include/bitcoin/node/parsers/electrum_version.hpp @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2011-2025 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_PARSERS_ELECTRUM_VERSION_HPP +#define LIBBITCOIN_NODE_PARSERS_ELECTRUM_VERSION_HPP + +#include + +namespace libbitcoin { +namespace node { + +enum class electrum_version +{ + /// Invalid version. + v0_0, + + /// 2011, initial protocol negotiation. + v0_6, + + /// 2012, enhanced protocol negotiation. + v0_8, + + /// 2012, added pruning limits and transport indicators. + v0_9, + + /// 2013, baseline for core methods in the official specification. + v0_10, + + /// 2014, 1.x series, deprecations of utxo and block number methods. + v1_0, + + /// 2015, updated version response and introduced scripthash methods. + v1_1, + + /// 2017, added optional parameters for transactions and headers. + v1_2, + + /// 2018, defaulted raw headers and introduced new block methods. + v1_3, + + /// 2019, removed deserialized headers and added merkle proof features. + v1_4, + + /// 2019, modifications for auxiliary proof-of-work handling. + v1_4_1, + + /// 2020, added scripthash unsubscribe functionality. + v1_4_2, + + /// 2022, updated response formats and added fee estimation modes. + v1_6 +}; + +} // namespace network +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/parsers/parsers.hpp b/include/bitcoin/node/parsers/parsers.hpp index 5eee1700..5f4ba205 100644 --- a/include/bitcoin/node/parsers/parsers.hpp +++ b/include/bitcoin/node/parsers/parsers.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index 1a1bbf56..f7a5d823 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace libbitcoin { diff --git a/include/bitcoin/node/protocols/protocol_electrum_version.hpp b/include/bitcoin/node/protocols/protocol_electrum_version.hpp index abdf65f0..7c6c09b8 100644 --- a/include/bitcoin/node/protocols/protocol_electrum_version.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum_version.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace libbitcoin { @@ -54,55 +55,13 @@ class BCN_API protocol_electrum_version const interface::value_t& protocol_version) NOEXCEPT; protected: - enum class protocol_version - { - /// Invalid version. - v0_0, - - /// 2011, initial protocol negotiation. - v0_6, - - /// 2012, enhanced protocol negotiation. - v0_8, - - /// 2012, added pruning limits and transport indicators. - v0_9, - - /// 2013, baseline for core methods in the official specification. - v0_10, - - /// 2014, 1.x series, deprecations of utxo and block number methods. - v1_0, - - /// 2015, updated version response and introduced scripthash methods. - v1_1, - - /// 2017, added optional parameters for transactions and headers. - v1_2, - - /// 2018, defaulted raw headers and introduced new block methods. - v1_3, - - /// 2019, removed deserialized headers and added merkle proof features. - v1_4, - - /// 2019, modifications for auxiliary proof-of-work handling. - v1_4_1, - - /// 2020, added scripthash unsubscribe functionality. - v1_4_2, - - /// 2022, updated response formats and added fee estimation modes. - v1_6 - }; - - static constexpr protocol_version minimum = protocol_version::v1_4; - static constexpr protocol_version maximum = protocol_version::v1_4_2; + static constexpr electrum_version minimum = electrum_version::v1_4; + static constexpr electrum_version maximum = electrum_version::v1_4_2; - protocol_version version() const NOEXCEPT; + electrum_version version() const NOEXCEPT; std::string_view get_version() const NOEXCEPT; bool set_version(const interface::value_t& version) NOEXCEPT; - bool get_versions(protocol_version& min, protocol_version& max, + bool get_versions(electrum_version& min, electrum_version& max, const interface::value_t& version) NOEXCEPT; std::string_view get_server() const NOEXCEPT; @@ -112,13 +71,13 @@ class BCN_API protocol_electrum_version private: static std::string_view version_to_string( - protocol_version version) NOEXCEPT; - static protocol_version version_from_string( + electrum_version version) NOEXCEPT; + static electrum_version version_from_string( const std::string_view& version) NOEXCEPT; // These are protected by strand. std::shared_ptr handler_{}; - protocol_version version_{ protocol_version::v0_0 }; + electrum_version version_{ electrum_version::v0_0 }; std::string name_{}; }; diff --git a/src/protocols/protocol_electrum_version.cpp b/src/protocols/protocol_electrum_version.cpp index 328abcd8..fc817e8c 100644 --- a/src/protocols/protocol_electrum_version.cpp +++ b/src/protocols/protocol_electrum_version.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace libbitcoin { @@ -68,6 +69,7 @@ void protocol_electrum_version::complete(const code& ec, if (stopped(ec)) return; + // Calls after handshake completion are allowed and will skip this. if (handler_) { // shake error will result in stopped channel. @@ -82,14 +84,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& protocol_version) NOEXCEPT + const value_t& electrum_version) NOEXCEPT { if (stopped(ec)) return; // v0_0 implies version has not been set (first call). - if ((version() == protocol_version::v0_0) && - (!set_client(client_name) || !set_version(protocol_version))) + if ((version() == electrum_version::v0_0) && + (!set_client(client_name) || !set_version(electrum_version))) { const auto reason = error::invalid_argument; send_code(reason, BIND(complete, _1, reason)); @@ -146,8 +148,7 @@ std::string protocol_electrum_version::escape_client(const std::string& in) NOEX // Negotiated version. // ---------------------------------------------------------------------------- -protocol_electrum_version::protocol_version -protocol_electrum_version::version() const NOEXCEPT +electrum_version protocol_electrum_version::version() const NOEXCEPT { return version_; } @@ -159,8 +160,8 @@ std::string_view protocol_electrum_version::get_version() const NOEXCEPT bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT { - protocol_version client_min{}; - protocol_version client_max{}; + electrum_version client_min{}; + electrum_version client_max{}; if (!get_versions(client_min, client_max, version)) return false; @@ -176,8 +177,8 @@ bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT return true; } -bool protocol_electrum_version::get_versions(protocol_version& min, - protocol_version& max, const interface::value_t& version) NOEXCEPT +bool protocol_electrum_version::get_versions(electrum_version& min, + electrum_version& max, const interface::value_t& version) NOEXCEPT { // Optional value_t can be string_t or array_t of two string_t. const auto& value = version.value(); @@ -186,7 +187,7 @@ bool protocol_electrum_version::get_versions(protocol_version& min, if (std::holds_alternative(value)) { // An interface default can't be set for optional. - max = min = protocol_version::v1_4; + max = min = electrum_version::v1_4; return true; } @@ -195,7 +196,7 @@ bool protocol_electrum_version::get_versions(protocol_version& min, { // A single value implies minimum is the same as maximum. max = min = version_from_string(std::get(value)); - return min != protocol_version::v0_0; + return min != electrum_version::v0_0; } // Two versions. @@ -214,32 +215,32 @@ bool protocol_electrum_version::get_versions(protocol_version& min, min = version_from_string(std::get(min_version)); max = version_from_string(std::get(max_version)); - return min != protocol_version::v0_0 - && max != protocol_version::v0_0; + return min != electrum_version::v0_0 + && max != electrum_version::v0_0; } return false; } // private/static -std::string_view -protocol_electrum_version::version_to_string(protocol_version version) NOEXCEPT +std::string_view protocol_electrum_version::version_to_string( + electrum_version version) NOEXCEPT { - static const std::unordered_map map + static const std::unordered_map map { - { protocol_version::v0_6, "0.6" }, - { protocol_version::v0_8, "0.8" }, - { protocol_version::v0_9, "0.9" }, - { protocol_version::v0_10, "0.10" }, - { protocol_version::v1_0, "1.0" }, - { protocol_version::v1_1, "1.1" }, - { protocol_version::v1_2, "1.2" }, - { protocol_version::v1_3, "1.3" }, - { protocol_version::v1_4, "1.4" }, - { protocol_version::v1_4_1, "1.4.1" }, - { protocol_version::v1_4_2, "1.4.2" }, - { protocol_version::v1_6, "1.6" }, - { protocol_version::v0_0, "0.0" } + { electrum_version::v0_6, "0.6" }, + { electrum_version::v0_8, "0.8" }, + { electrum_version::v0_9, "0.9" }, + { electrum_version::v0_10, "0.10" }, + { electrum_version::v1_0, "1.0" }, + { electrum_version::v1_1, "1.1" }, + { electrum_version::v1_2, "1.2" }, + { electrum_version::v1_3, "1.3" }, + { electrum_version::v1_4, "1.4" }, + { electrum_version::v1_4_1, "1.4.1" }, + { electrum_version::v1_4_2, "1.4.2" }, + { electrum_version::v1_6, "1.6" }, + { electrum_version::v0_0, "0.0" } }; const auto it = map.find(version); @@ -247,29 +248,28 @@ protocol_electrum_version::version_to_string(protocol_version version) NOEXCEPT } // private/static -protocol_electrum_version::protocol_version -protocol_electrum_version::version_from_string( +electrum_version protocol_electrum_version::version_from_string( const std::string_view& version) NOEXCEPT { - static const std::unordered_map map + static const std::unordered_map map { - { "0.6", protocol_version::v0_6 }, - { "0.8", protocol_version::v0_8 }, - { "0.9", protocol_version::v0_9 }, - { "0.10", protocol_version::v0_10 }, - { "1.0", protocol_version::v1_0 }, - { "1.1", protocol_version::v1_1 }, - { "1.2", protocol_version::v1_2 }, - { "1.3", protocol_version::v1_3 }, - { "1.4", protocol_version::v1_4 }, - { "1.4.1", protocol_version::v1_4_1 }, - { "1.4.2", protocol_version::v1_4_2 }, - { "1.6", protocol_version::v1_6 }, - { "0.0", protocol_version::v0_0 } + { "0.6", electrum_version::v0_6 }, + { "0.8", electrum_version::v0_8 }, + { "0.9", electrum_version::v0_9 }, + { "0.10", electrum_version::v0_10 }, + { "1.0", electrum_version::v1_0 }, + { "1.1", electrum_version::v1_1 }, + { "1.2", electrum_version::v1_2 }, + { "1.3", electrum_version::v1_3 }, + { "1.4", electrum_version::v1_4 }, + { "1.4.1", electrum_version::v1_4_1 }, + { "1.4.2", electrum_version::v1_4_2 }, + { "1.6", electrum_version::v1_6 }, + { "0.0", electrum_version::v0_0 } }; const auto it = map.find(version); - return it != map.end() ? it->second : protocol_version::v0_0; + return it != map.end() ? it->second : electrum_version::v0_0; } BC_POP_WARNING()