From 961e391510eaf0dac67b8f69431bda952aa2770c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 30 Dec 2025 17:59:04 -0500 Subject: [PATCH 1/6] Style, includes. --- include/bitcoin/node/protocols/protocol_electrum.hpp | 4 ++-- src/protocols/protocol_electrum.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index d4c96e86..1a69422d 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -94,7 +94,7 @@ class BCN_API protocol_electrum /// Handlers (server). bool handle_server_add_peer(const code& ec, rpc_interface::server_add_peer, - const network::rpc::object_t& features) NOEXCEPT; + const interface::object_t& features) NOEXCEPT; bool handle_server_banner(const code& ec, rpc_interface::server_banner) NOEXCEPT; bool handle_server_donation_address(const code& ec, @@ -107,7 +107,7 @@ class BCN_API protocol_electrum rpc_interface::server_ping) NOEXCEPT; bool handle_server_version(const code& ec, rpc_interface::server_version, const std::string& client_name, - const network::rpc::value_t& protocol_version) NOEXCEPT; + const interface::value_t& protocol_version) NOEXCEPT; /// Handlers (mempool). bool handle_mempool_get_fee_histogram(const code& ec, diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index 151aa5e2..4eb5a7f1 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include namespace libbitcoin { @@ -25,7 +27,7 @@ namespace node { #define CLASS protocol_electrum -using namespace network; +using namespace interface; using namespace std::placeholders; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) @@ -212,7 +214,7 @@ bool protocol_electrum::handle_blockchain_transaction_id_from_pos(const code& ec // ---------------------------------------------------------------------------- bool protocol_electrum::handle_server_add_peer(const code& ec, - rpc_interface::server_add_peer, const rpc::object_t& ) NOEXCEPT + rpc_interface::server_add_peer, const interface::object_t& ) NOEXCEPT { if (stopped(ec)) return false; send_code(error::not_implemented); @@ -262,7 +264,7 @@ bool protocol_electrum::handle_server_ping(const code& ec, // value_t can be string_t or array_t of two string_t. bool protocol_electrum::handle_server_version(const code& ec, rpc_interface::server_version, const std::string& , - const rpc::value_t& ) NOEXCEPT + const value_t& ) NOEXCEPT { if (stopped(ec)) return false; send_code(error::not_implemented); From 6f8ff124ecf6ec4cc3f8b35917bd9cd3e9f445b0 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 30 Dec 2025 22:15:12 -0500 Subject: [PATCH 2/6] Stub in stratum_v1 interface. --- include/bitcoin/node/define.hpp | 31 +++++ .../node/protocols/protocol_stratum_v1.hpp | 36 +++++- src/protocols/protocol_stratum_v1.cpp | 119 +++++++++++++++++- 3 files changed, 182 insertions(+), 4 deletions(-) diff --git a/include/bitcoin/node/define.hpp b/include/bitcoin/node/define.hpp index 59f583d9..9f8a643e 100644 --- a/include/bitcoin/node/define.hpp +++ b/include/bitcoin/node/define.hpp @@ -102,6 +102,37 @@ using type_id = network::messages::peer::inventory_item::type_id; } // namespace node } // namespace libbitcoin +/// Augment limited xcode placeholder defines (10 vs. common 20). +/// --------------------------------------------------------------------------- +#if defined (HAVE_XCODE) + +/// Define custom placeholder types in the global namespace to avoid conflicts. +struct placeholder_11 {}; +struct placeholder_12 {}; +struct placeholder_13 {}; + +/// Specialize std::is_placeholder within the std namespace. +namespace std +{ + template <> + struct is_placeholder<::placeholder_11> : integral_constant {}; + template <> + struct is_placeholder<::placeholder_12> : integral_constant {}; + template <> + struct is_placeholder<::placeholder_13> : integral_constant {}; +} + +/// Add instances to std::placeholders for standard usage syntax. +namespace std::placeholders +{ + inline constexpr ::placeholder_11 _11{}; + inline constexpr ::placeholder_12 _12{}; + inline constexpr ::placeholder_13 _13{}; +} + +#endif // HAVE_XCODE +/// --------------------------------------------------------------------------- + #endif // define.hpp is the common include for /node. diff --git a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp index d4cd93ad..ef27250c 100644 --- a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp +++ b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp @@ -46,9 +46,43 @@ class BCN_API protocol_stratum_v1 void start() NOEXCEPT override; protected: - /// Handlers. + /// Handlers (client requests). + bool handle_mining_subscribe(const code& ec, + rpc_interface::mining_subscribe, const std::string& user_agent, + double extranonce1_size) NOEXCEPT; + bool handle_mining_authorize(const code& ec, + rpc_interface::mining_authorize, const std::string& username, + const std::string& password) NOEXCEPT; + bool handle_mining_submit(const code& ec, + rpc_interface::mining_submit, const std::string& worker_name, + const std::string& job_id, const std::string& extranonce2, + double ntime, const std::string& nonce) NOEXCEPT; bool handle_mining_extranonce_subscribe(const code& ec, rpc_interface::mining_extranonce_subscribe) NOEXCEPT; + bool handle_mining_extranonce_unsubscribe(const code& ec, + rpc_interface::mining_extranonce_unsubscribe, double id) NOEXCEPT; + + /// Handlers (server notifications). + bool handle_mining_configure(const code& ec, + rpc_interface::mining_configure, + const interface::object_t& extensions) NOEXCEPT; + bool handle_mining_set_difficulty(const code& ec, + rpc_interface::mining_set_difficulty, double difficulty) NOEXCEPT; + bool handle_mining_notify(const code& ec, + rpc_interface::mining_notify, const std::string& job_id, + const std::string& prevhash, const std::string& coinb1, + const std::string& coinb2, const interface::array_t& merkle_branch, + double version, double nbits, double ntime, bool clean_jobs, + bool hash1, bool hash2) NOEXCEPT; + bool handle_client_reconnect(const code& ec, + rpc_interface::client_reconnect, const std::string& url, double port, + double id) NOEXCEPT; + bool handle_client_hello(const code& ec, + rpc_interface::client_hello, + const interface::object_t& protocol) NOEXCEPT; + bool handle_client_rejected(const code& ec, + rpc_interface::client_rejected, const std::string& job_id, + const std::string& reject_reason) NOEXCEPT; }; } // namespace node diff --git a/src/protocols/protocol_stratum_v1.cpp b/src/protocols/protocol_stratum_v1.cpp index 658e94e5..94675375 100644 --- a/src/protocols/protocol_stratum_v1.cpp +++ b/src/protocols/protocol_stratum_v1.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include namespace libbitcoin { @@ -25,6 +27,7 @@ namespace node { #define CLASS protocol_stratum_v1 +using namespace interface; using namespace std::placeholders; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) @@ -41,18 +44,128 @@ void protocol_stratum_v1::start() NOEXCEPT if (started()) return; + // Client requests. + SUBSCRIBE_RPC(handle_mining_subscribe, _1, _2, _3, _4); + SUBSCRIBE_RPC(handle_mining_authorize, _1, _2, _3, _4); + SUBSCRIBE_RPC(handle_mining_submit, _1, _2, _3, _4, _5, _6, _7); SUBSCRIBE_RPC(handle_mining_extranonce_subscribe, _1, _2); + SUBSCRIBE_RPC(handle_mining_extranonce_unsubscribe, _1, _2, _3); + + // Server notifications. + SUBSCRIBE_RPC(handle_mining_configure, _1, _2, _3); + SUBSCRIBE_RPC(handle_mining_set_difficulty, _1, _2, _3); + SUBSCRIBE_RPC(handle_mining_notify, _1, _2, _3, _4, _5, _6, _7, _8, _9, + _10, _11, _12, _13); + 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(); } -// Handlers. +// Handlers (client requests). // ---------------------------------------------------------------------------- +bool protocol_stratum_v1::handle_mining_subscribe(const code& ec, + rpc_interface::mining_subscribe, const std::string& , + double ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_mining_authorize(const code& ec, + rpc_interface::mining_authorize, const std::string& , + const std::string& ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_mining_submit(const code& ec, + rpc_interface::mining_submit, const std::string& , + const std::string& , const std::string& , + double , const std::string& ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + bool protocol_stratum_v1::handle_mining_extranonce_subscribe(const code& ec, rpc_interface::mining_extranonce_subscribe) NOEXCEPT { - // TODO: - return !ec; + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_mining_extranonce_unsubscribe(const code& ec, + rpc_interface::mining_extranonce_unsubscribe, double ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +// Handlers (server notifications). +// ---------------------------------------------------------------------------- + +bool protocol_stratum_v1::handle_mining_configure(const code& ec, + rpc_interface::mining_configure, + const interface::object_t& ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_mining_set_difficulty(const code& ec, + rpc_interface::mining_set_difficulty, double ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_mining_notify(const code& ec, + rpc_interface::mining_notify, const std::string& , + const std::string& , const std::string& , + const std::string& , const interface::array_t& , + double , double , double , bool , + bool , bool ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_client_reconnect(const code& ec, + rpc_interface::client_reconnect, const std::string& , double , + double ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_client_hello(const code& ec, + rpc_interface::client_hello, + const interface::object_t& ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; +} + +bool protocol_stratum_v1::handle_client_rejected(const code& ec, + rpc_interface::client_rejected, const std::string& , + const std::string& ) NOEXCEPT +{ + if (stopped(ec)) return false; + send_code(error::not_implemented); + return true; } BC_POP_WARNING() From 6af3b93a74c44347a01aa2d19788b2e9bdebb3b3 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 30 Dec 2025 22:15:34 -0500 Subject: [PATCH 3/6] Comments. --- include/bitcoin/node/sessions/session_server.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/bitcoin/node/sessions/session_server.hpp b/include/bitcoin/node/sessions/session_server.hpp index 4e8af041..74552222 100644 --- a/include/bitcoin/node/sessions/session_server.hpp +++ b/include/bitcoin/node/sessions/session_server.hpp @@ -92,10 +92,9 @@ class session_server } /// Override to implement a connection handshake as required. By default - /// this is bypassed, which applies to basic http services. A handshake - /// is used to implement TLS and WebSocket upgrade from http (for example). - /// Handshake protocol(s) must invoke handler one time at completion. - /// Use std::dynamic_pointer_cast(channel) to obtain channel_t. + /// 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 { From 2af74f9a85978a710bceabf9a6a229da60d056fa Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 30 Dec 2025 22:15:48 -0500 Subject: [PATCH 4/6] Delint. --- src/protocols/protocol_bitcoind_rest.cpp | 6 +++--- src/protocols/protocol_explore.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protocols/protocol_bitcoind_rest.cpp b/src/protocols/protocol_bitcoind_rest.cpp index 058c9e90..4aaafa4a 100644 --- a/src/protocols/protocol_bitcoind_rest.cpp +++ b/src/protocols/protocol_bitcoind_rest.cpp @@ -98,9 +98,9 @@ void protocol_bitcoind_rest::handle_receive_get(const code& ec, // Handlers. // ---------------------------------------------------------------------------- -constexpr auto data = to_value(media_type::application_octet_stream); -constexpr auto json = to_value(media_type::application_json); -constexpr auto text = to_value(media_type::text_plain); +////constexpr auto data = to_value(media_type::application_octet_stream); +////constexpr auto json = to_value(media_type::application_json); +////constexpr auto text = to_value(media_type::text_plain); bool protocol_bitcoind_rest::handle_get_block(const code& ec, rest_interface::block, uint8_t , system::hash_cptr ) NOEXCEPT diff --git a/src/protocols/protocol_explore.cpp b/src/protocols/protocol_explore.cpp index a50da8ab..4e1cc2bc 100644 --- a/src/protocols/protocol_explore.cpp +++ b/src/protocols/protocol_explore.cpp @@ -714,7 +714,7 @@ bool protocol_explore::handle_get_inputs(const code& ec, interface::inputs, // Wire serialization of input does not include witness. const auto size = std::accumulate(inputs->begin(), inputs->end(), zero, - [&witness](size_t total, const auto& input) NOEXCEPT + [&](size_t total, const auto& input) NOEXCEPT { return total + input->serialized_size(false); }); switch (media) From 51cd6556187bd8cf064702d3816842a2678ad8c0 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 00:34:01 -0500 Subject: [PATCH 5/6] Implement electrum handshake. --- include/bitcoin/node/interfaces/electrum.hpp | 4 +- include/bitcoin/node/interfaces/types.hpp | 1 + .../node/protocols/protocol_electrum.hpp | 89 +++-- src/protocols/protocol_electrum.cpp | 304 +++++++++++++----- 4 files changed, 301 insertions(+), 97 deletions(-) diff --git a/include/bitcoin/node/interfaces/electrum.hpp b/include/bitcoin/node/interfaces/electrum.hpp index 4994e0bf..2e2a32fd 100644 --- a/include/bitcoin/node/interfaces/electrum.hpp +++ b/include/bitcoin/node/interfaces/electrum.hpp @@ -55,14 +55,14 @@ struct electrum_methods method<"server.features">{}, method<"server.peers.subscribe">{}, method<"server.ping">{}, - method<"server.version", string_t, value_t>{ "client_name", "protocol_version" }, + method<"server.version", string_t, optional>{ "client_name", "protocol_version" }, /// Mempool methods. method<"mempool.get_fee_histogram">{} }; template - using subscriber = network::unsubscriber; + using subscriber = network::subscriber; template using at = method_at; diff --git a/include/bitcoin/node/interfaces/types.hpp b/include/bitcoin/node/interfaces/types.hpp index c887479e..6510acb8 100644 --- a/include/bitcoin/node/interfaces/types.hpp +++ b/include/bitcoin/node/interfaces/types.hpp @@ -45,6 +45,7 @@ using number_t = network::rpc::number_t; using object_t = network::rpc::object_t; using array_t = network::rpc::array_t; using value_t = network::rpc::value_t; +using null_t = network::rpc::null_t; namespace empty { constexpr auto array = network::rpc::empty::array; }; namespace empty { constexpr auto object = network::rpc::empty::object; }; diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index 1a69422d..46af433b 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -20,6 +20,7 @@ #define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_ELECTRUM_HPP #include +#include #include #include #include @@ -48,70 +49,112 @@ class BCN_API protocol_electrum protected: /// Handlers (blockchain). - bool handle_blockchain_block_header(const code& ec, + void handle_blockchain_block_header(const code& ec, rpc_interface::blockchain_block_header, double height, double cp_height) NOEXCEPT; - bool handle_blockchain_block_headers(const code& ec, + void handle_blockchain_block_headers(const code& ec, rpc_interface::blockchain_block_headers, double start_height, double count, double cp_height) NOEXCEPT; - bool handle_blockchain_headers_subscribe(const code& ec, + void handle_blockchain_headers_subscribe(const code& ec, rpc_interface::blockchain_headers_subscribe) NOEXCEPT; - bool handle_blockchain_estimatefee(const code& ec, + void handle_blockchain_estimatefee(const code& ec, rpc_interface::blockchain_estimatefee, double) NOEXCEPT; - bool handle_blockchain_relayfee(const code& ec, + void handle_blockchain_relayfee(const code& ec, rpc_interface::blockchain_relayfee) NOEXCEPT; - bool handle_blockchain_scripthash_get_balance(const code& ec, + void handle_blockchain_scripthash_get_balance(const code& ec, rpc_interface::blockchain_scripthash_get_balance, const std::string& scripthash) NOEXCEPT; - bool handle_blockchain_scripthash_get_history(const code& ec, + void handle_blockchain_scripthash_get_history(const code& ec, rpc_interface::blockchain_scripthash_get_history, const std::string& scripthash) NOEXCEPT; - bool handle_blockchain_scripthash_get_mempool(const code& ec, + void handle_blockchain_scripthash_get_mempool(const code& ec, rpc_interface::blockchain_scripthash_get_mempool, const std::string& scripthash) NOEXCEPT; - bool handle_blockchain_scripthash_listunspent(const code& ec, + void handle_blockchain_scripthash_listunspent(const code& ec, rpc_interface::blockchain_scripthash_listunspent, const std::string& scripthash) NOEXCEPT; - bool handle_blockchain_scripthash_subscribe(const code& ec, + void handle_blockchain_scripthash_subscribe(const code& ec, rpc_interface::blockchain_scripthash_subscribe, const std::string& scripthash) NOEXCEPT; - bool handle_blockchain_scripthash_unsubscribe(const code& ec, + void handle_blockchain_scripthash_unsubscribe(const code& ec, rpc_interface::blockchain_scripthash_unsubscribe, const std::string& scripthash) NOEXCEPT; - bool handle_blockchain_transaction_broadcast(const code& ec, + void handle_blockchain_transaction_broadcast(const code& ec, rpc_interface::blockchain_transaction_broadcast, const std::string& raw_tx) NOEXCEPT; - bool handle_blockchain_transaction_get(const code& ec, + void handle_blockchain_transaction_get(const code& ec, rpc_interface::blockchain_transaction_get, const std::string& tx_hash, bool verbose) NOEXCEPT; - bool handle_blockchain_transaction_get_merkle(const code& ec, + void handle_blockchain_transaction_get_merkle(const code& ec, rpc_interface::blockchain_transaction_get_merkle, const std::string& tx_hash, double height) NOEXCEPT; - bool handle_blockchain_transaction_id_from_pos(const code& ec, + void handle_blockchain_transaction_id_from_pos(const code& ec, rpc_interface::blockchain_transaction_id_from_pos, double height, double tx_pos, bool merkle) NOEXCEPT; /// Handlers (server). - bool handle_server_add_peer(const code& ec, + void handle_server_add_peer(const code& ec, rpc_interface::server_add_peer, const interface::object_t& features) NOEXCEPT; - bool handle_server_banner(const code& ec, + void handle_server_banner(const code& ec, rpc_interface::server_banner) NOEXCEPT; - bool handle_server_donation_address(const code& ec, + void handle_server_donation_address(const code& ec, rpc_interface::server_donation_address) NOEXCEPT; - bool handle_server_features(const code& ec, + void handle_server_features(const code& ec, rpc_interface::server_features) NOEXCEPT; - bool handle_server_peers_subscribe(const code& ec, + void handle_server_peers_subscribe(const code& ec, rpc_interface::server_peers_subscribe) NOEXCEPT; - bool handle_server_ping(const code& ec, + void handle_server_ping(const code& ec, rpc_interface::server_ping) NOEXCEPT; - bool handle_server_version(const code& ec, + 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). - bool handle_mempool_get_fee_histogram(const code& ec, + void handle_mempool_get_fee_histogram(const code& ec, rpc_interface::mempool_get_fee_histogram) NOEXCEPT; + +protected: + enum class protocol_version + { + v0_0, + v0_6, + v0_8, + v0_9, + v0_10, + v1_0, + v1_1, + v1_2, + v1_3, + v1_4, + v1_4_1, + v1_4_2, + v1_6 + }; + + static constexpr protocol_version minimum = protocol_version::v1_4; + static constexpr protocol_version maximum = protocol_version::v1_6; + + 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. + protocol_version version_{ protocol_version::v0_0 }; + std::string name_{}; }; } // namespace node diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index 4eb5a7f1..e2a8b18e 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include #include #include @@ -30,6 +32,8 @@ namespace node { 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) @@ -78,210 +82,366 @@ void protocol_electrum::start() NOEXCEPT // Handlers (blockchain). // ---------------------------------------------------------------------------- -bool protocol_electrum::handle_blockchain_block_header(const code& ec, +void protocol_electrum::handle_blockchain_block_header(const code& ec, rpc_interface::blockchain_block_header, double , double ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_block_headers(const code& ec, +void protocol_electrum::handle_blockchain_block_headers(const code& ec, rpc_interface::blockchain_block_headers, double , double , double ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_headers_subscribe(const code& ec, +void protocol_electrum::handle_blockchain_headers_subscribe(const code& ec, rpc_interface::blockchain_headers_subscribe) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_estimatefee(const code& ec, +void protocol_electrum::handle_blockchain_estimatefee(const code& ec, rpc_interface::blockchain_estimatefee, double ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_relayfee(const code& ec, +void protocol_electrum::handle_blockchain_relayfee(const code& ec, rpc_interface::blockchain_relayfee) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_scripthash_get_balance(const code& ec, +void protocol_electrum::handle_blockchain_scripthash_get_balance(const code& ec, rpc_interface::blockchain_scripthash_get_balance, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_scripthash_get_history(const code& ec, +void protocol_electrum::handle_blockchain_scripthash_get_history(const code& ec, rpc_interface::blockchain_scripthash_get_history, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_scripthash_get_mempool(const code& ec, +void protocol_electrum::handle_blockchain_scripthash_get_mempool(const code& ec, rpc_interface::blockchain_scripthash_get_mempool, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_scripthash_listunspent(const code& ec, +void protocol_electrum::handle_blockchain_scripthash_listunspent(const code& ec, rpc_interface::blockchain_scripthash_listunspent, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_scripthash_subscribe(const code& ec, +void protocol_electrum::handle_blockchain_scripthash_subscribe(const code& ec, rpc_interface::blockchain_scripthash_subscribe, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_scripthash_unsubscribe(const code& ec, +void protocol_electrum::handle_blockchain_scripthash_unsubscribe(const code& ec, rpc_interface::blockchain_scripthash_unsubscribe, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_transaction_broadcast(const code& ec, +void protocol_electrum::handle_blockchain_transaction_broadcast(const code& ec, rpc_interface::blockchain_transaction_broadcast, const std::string& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_transaction_get(const code& ec, +void protocol_electrum::handle_blockchain_transaction_get(const code& ec, rpc_interface::blockchain_transaction_get, const std::string& , bool ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_transaction_get_merkle(const code& ec, +void protocol_electrum::handle_blockchain_transaction_get_merkle(const code& ec, rpc_interface::blockchain_transaction_get_merkle, const std::string& , double ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_blockchain_transaction_id_from_pos(const code& ec, +void protocol_electrum::handle_blockchain_transaction_id_from_pos(const code& ec, rpc_interface::blockchain_transaction_id_from_pos, double , double , bool ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } // Handlers (server). // ---------------------------------------------------------------------------- -bool protocol_electrum::handle_server_add_peer(const code& ec, +void protocol_electrum::handle_server_add_peer(const code& ec, rpc_interface::server_add_peer, const interface::object_t& ) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_server_banner(const code& ec, +void protocol_electrum::handle_server_banner(const code& ec, rpc_interface::server_banner) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_server_donation_address(const code& ec, +void protocol_electrum::handle_server_donation_address(const code& ec, rpc_interface::server_donation_address) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_server_features(const code& ec, +void protocol_electrum::handle_server_features(const code& ec, rpc_interface::server_features) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_server_peers_subscribe(const code& ec, +void protocol_electrum::handle_server_peers_subscribe(const code& ec, rpc_interface::server_peers_subscribe) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -bool protocol_electrum::handle_server_ping(const code& ec, +void protocol_electrum::handle_server_ping(const code& ec, rpc_interface::server_ping) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; send_code(error::not_implemented); - return true; } -// value_t can be string_t or array_t of two string_t. -bool protocol_electrum::handle_server_version(const code& ec, - rpc_interface::server_version, const std::string& , - const value_t& ) NOEXCEPT +// 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 false; - send_code(error::not_implemented); - return true; + 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() } } + } + }, 100); } // Handlers (mempool). // ---------------------------------------------------------------------------- -bool protocol_electrum::handle_mempool_get_fee_histogram(const code& ec, +void protocol_electrum::handle_mempool_get_fee_histogram(const code& ec, rpc_interface::mempool_get_fee_histogram) NOEXCEPT { - if (stopped(ec)) return false; + if (stopped(ec)) return; 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. +// ---------------------------------------------------------------------------- + +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; + + 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() From cf91b46ede8febdb9441094b36f5cf29001390d1 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 31 Dec 2025 00:46:58 -0500 Subject: [PATCH 6/6] Add protocol_electrum::is_version, reset max version, comments. --- .../node/protocols/protocol_electrum.hpp | 28 ++++++++++++++++++- src/protocols/protocol_electrum.cpp | 5 ++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp index 46af433b..0a39928f 100644 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ b/include/bitcoin/node/protocols/protocol_electrum.hpp @@ -117,26 +117,52 @@ class BCN_API protocol_electrum 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_6; + 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; diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index e2a8b18e..8b49bfab 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -321,6 +321,11 @@ std::string protocol_electrum::escape_client(const std::string& in) NOEXCEPT // 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_;