diff --git a/Makefile.am b/Makefile.am
index f1c3090f..1b47545e 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 \
@@ -183,10 +184,11 @@ 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_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
@@ -224,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
@@ -238,6 +241,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 \
@@ -260,6 +264,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 d9899614..15549b8e 100644
--- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
@@ -150,6 +150,7 @@
+
@@ -175,10 +176,11 @@
+
-
-
+
+
@@ -210,6 +212,7 @@
+
@@ -221,6 +224,7 @@
+
@@ -240,6 +244,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
index 4adb9895..9ecd4a17 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
@@ -221,16 +224,19 @@
include\bitcoin\node\channels
+
+ include\bitcoin\node\channels
+
include\bitcoin\node\channels
include\bitcoin\node\channels
-
+
include\bitcoin\node\channels
-
+
include\bitcoin\node\channels
@@ -326,6 +332,9 @@
include\bitcoin\node\parsers
+
+ include\bitcoin\node\parsers
+
include\bitcoin\node\parsers
@@ -359,6 +368,9 @@
include\bitcoin\node\protocols
+
+ include\bitcoin\node\protocols
+
include\bitcoin\node\protocols
@@ -416,6 +428,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 846c7491..0f58e11e 100644
--- a/include/bitcoin/node.hpp
+++ b/include/bitcoin/node.hpp
@@ -28,10 +28,11 @@
#include
#include
#include
+#include
#include
#include
-#include
-#include
+#include
+#include
#include
#include
#include
@@ -56,6 +57,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -67,6 +69,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -86,6 +89,7 @@
#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_stratum_v1.hpp
similarity index 64%
rename from include/bitcoin/node/channels/channel_rpc.hpp
rename to include/bitcoin/node/channels/channel_stratum_v1.hpp
index 2be67709..5fe39dde 100644
--- a/include/bitcoin/node/channels/channel_rpc.hpp
+++ b/include/bitcoin/node/channels/channel_stratum_v1.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_STRATUM_V1_HPP
+#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V1_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_stratum_v1
: 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_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,
+ 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 77a0110b..10627939 100644
--- a/include/bitcoin/node/channels/channels.hpp
+++ b/include/bitcoin/node/channels/channels.hpp
@@ -20,10 +20,11 @@
#define LIBBITCOIN_NODE_CHANNELS_CHANNELS_HPP
#include
+#include
#include
#include
-#include
-#include
+#include
+#include
#include
#endif
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 0a39928f..f7a5d823 100644
--- a/include/bitcoin/node/protocols/protocol_electrum.hpp
+++ b/include/bitcoin/node/protocols/protocol_electrum.hpp
@@ -24,13 +24,14 @@
#include
#include
#include
+#include
#include
namespace libbitcoin {
namespace node {
class BCN_API protocol_electrum
- : public node::protocol_rpc,
+ : public node::protocol_rpc,
protected network::tracker
{
public:
@@ -40,7 +41,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)
{
}
@@ -106,81 +107,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..7c6c09b8
--- /dev/null
+++ b/include/bitcoin/node/protocols/protocol_electrum_version.hpp
@@ -0,0 +1,87 @@
+/**
+ * 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
+#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:
+ 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;
+ 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 escape_client(const std::string& in) NOEXCEPT;
+ bool set_client(const std::string& name) NOEXCEPT;
+
+private:
+ static std::string_view version_to_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_{};
+ electrum_version version_{ electrum_version::v0_0 };
+ std::string name_{};
+};
+
+} // namespace node
+} // namespace libbitcoin
+
+#endif
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..7d0ac220 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/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/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..b1b56cda
--- /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->template shared_from_base();
+ channel->template 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..18029e67 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->template 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 own = session_server;
+ const auto self = this->template 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 66e6f2e4..0dabff44 100644
--- a/src/protocols/protocol_electrum.cpp
+++ b/src/protocols/protocol_electrum.cpp
@@ -72,11 +72,11 @@ 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);
- node::protocol_rpc::start();
+ node::protocol_rpc::start();
}
// Handlers (blockchain).
@@ -244,34 +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,
- 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).
// ----------------------------------------------------------------------------
@@ -282,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..fc817e8c
--- /dev/null
+++ b/src/protocols/protocol_electrum_version.cpp
@@ -0,0 +1,280 @@
+/**
+ * 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
+#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;
+
+ // Calls after handshake completion are allowed and will skip this.
+ 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& electrum_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)))
+ {
+ 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.
+// ----------------------------------------------------------------------------
+
+std::string_view protocol_electrum_version::get_server() const NOEXCEPT
+{
+ return settings().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.
+// ----------------------------------------------------------------------------
+
+electrum_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
+{
+ electrum_version client_min{};
+ electrum_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(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();
+
+ // 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 = electrum_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 != electrum_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 != electrum_version::v0_0
+ && max != electrum_version::v0_0;
+ }
+
+ return false;
+}
+
+// private/static
+std::string_view protocol_electrum_version::version_to_string(
+ electrum_version version) NOEXCEPT
+{
+ static const std::unordered_map map
+ {
+ { 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);
+ return it != map.end() ? it->second : "0.0";
+}
+
+// private/static
+electrum_version protocol_electrum_version::version_from_string(
+ const std::string_view& version) NOEXCEPT
+{
+ static const std::unordered_map map
+ {
+ { "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 : electrum_version::v0_0;
+}
+
+BC_POP_WARNING()
+BC_POP_WARNING()
+BC_POP_WARNING()
+
+} // namespace node
+} // namespace libbitcoin
diff --git a/src/protocols/protocol_stratum_v1.cpp b/src/protocols/protocol_stratum_v1.cpp
index 94675375..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).