From dfcf568aefbe30ea84863161cbf129940efa9819 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 11 Mar 2026 20:07:04 -0400 Subject: [PATCH 1/3] Delint (incorrect warnings in msvc on electrum:: ambiguity). --- include/bitcoin/server/channels/channel_electrum.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bitcoin/server/channels/channel_electrum.hpp b/include/bitcoin/server/channels/channel_electrum.hpp index c9915a76..c42f77c2 100644 --- a/include/bitcoin/server/channels/channel_electrum.hpp +++ b/include/bitcoin/server/channels/channel_electrum.hpp @@ -65,12 +65,12 @@ class BCS_API channel_electrum return name_; } - inline void set_version(electrum::version version) NOEXCEPT + inline void set_version(server::electrum::version version) NOEXCEPT { version_ = version; } - inline electrum::version version() const NOEXCEPT + inline server::electrum::version version() const NOEXCEPT { return version_; } @@ -85,7 +85,7 @@ class BCS_API channel_electrum const options_t& options_; // These are protected by strand. - electrum::version version_{ electrum::version::v0_0 }; + server::electrum::version version_{ server::electrum::version::v0_0 }; std::string name_{}; }; From 1499c47a45e0bb9b4e00feb64b744cec4e3f5c0c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 11 Mar 2026 23:43:25 -0400 Subject: [PATCH 2/3] Add headers_subscribe tests, fix test setup. --- Makefile.am | 2 + builds/cmake/CMakeLists.txt | 2 + .../libbitcoin-server-test.vcxproj | 2 + .../libbitcoin-server-test.vcxproj.filters | 6 + .../server/protocols/protocol_electrum.hpp | 8 +- src/protocols/protocol_electrum.cpp | 66 ++++-- test/protocols/blocks.cpp | 2 +- .../electrum/electrum_block_headers.cpp | 211 ++++++++++++++++++ .../electrum/electrum_headers_subscribe.cpp | 65 ++++++ 9 files changed, 341 insertions(+), 23 deletions(-) create mode 100644 test/protocols/electrum/electrum_block_headers.cpp create mode 100644 test/protocols/electrum/electrum_headers_subscribe.cpp diff --git a/Makefile.am b/Makefile.am index ec5fca00..b2a9a9ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -82,6 +82,8 @@ test_libbitcoin_server_test_SOURCES = \ test/protocols/electrum/electrum.cpp \ test/protocols/electrum/electrum.hpp \ test/protocols/electrum/electrum_block_header.cpp \ + test/protocols/electrum/electrum_block_headers.cpp \ + test/protocols/electrum/electrum_headers_subscribe.cpp \ test/protocols/electrum/electrum_server.cpp \ test/protocols/electrum/electrum_server_version.cpp diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 4183cdda..6e041596 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -311,6 +311,8 @@ if (with-tests) "../../test/protocols/electrum/electrum.cpp" "../../test/protocols/electrum/electrum.hpp" "../../test/protocols/electrum/electrum_block_header.cpp" + "../../test/protocols/electrum/electrum_block_headers.cpp" + "../../test/protocols/electrum/electrum_headers_subscribe.cpp" "../../test/protocols/electrum/electrum_server.cpp" "../../test/protocols/electrum/electrum_server_version.cpp" ) diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj index cf12bd0c..ba0e1a63 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj @@ -134,6 +134,8 @@ $(IntDir)test_protocols_electrum_electrum.obj + + diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters index 8ae34192..88bb9ad5 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters @@ -60,6 +60,12 @@ src\protocols\electrum + + src\protocols\electrum + + + src\protocols\electrum + src\protocols\electrum diff --git a/include/bitcoin/server/protocols/protocol_electrum.hpp b/include/bitcoin/server/protocols/protocol_electrum.hpp index a66d830e..65b30789 100644 --- a/include/bitcoin/server/protocols/protocol_electrum.hpp +++ b/include/bitcoin/server/protocols/protocol_electrum.hpp @@ -128,7 +128,10 @@ class BCS_API protocol_electrum void blockchain_block_headers(size_t starting, size_t quantity, size_t waypoint, bool multiplicity) NOEXCEPT; - inline bool is_version(electrum::version version) const NOEXCEPT + /// Notify client of new header. + void do_header(node::header_t link) NOEXCEPT; + + inline bool is_version(server::electrum::version version) const NOEXCEPT { return channel_->version() >= version; } @@ -139,8 +142,9 @@ class BCS_API protocol_electrum } private: - // This is thread safe. + // These are thread safe. const options_t& options_; + std::atomic_bool subscribed_{}; // This is mostly thread safe, and used in a thread safe manner. const channel_t::ptr channel_; diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index 00c4a72e..733c1a70 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -94,7 +94,7 @@ void protocol_electrum::stopping(const code& ec) NOEXCEPT // ---------------------------------------------------------------------------- bool protocol_electrum::handle_event(const code&, node::chase event_, - node::event_value) NOEXCEPT + node::event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped()) @@ -102,8 +102,14 @@ bool protocol_electrum::handle_event(const code&, node::chase event_, switch (event_) { - case node::chase::suspend: + case node::chase::organized: { + if (subscribed_.load(std::memory_order_relaxed)) + { + BC_ASSERT(std::holds_alternative(value)); + POST(do_header, std::get(value)); + } + break; } default: @@ -297,19 +303,39 @@ void protocol_electrum::handle_blockchain_headers_subscribe(const code& ec, return; } - // TODO: signal header subscription. + subscribed_.store(true, std::memory_order_relaxed); + send_result( + { + object_t + { + { "height", uint64_t{ top } }, + { "hex", to_hex(*header, chain::header::serialized_size()) } + } + }, 256, BIND(complete, _1)); +} - // TODO: idempotent subscribe to chase::organized via session/chaser/node. - // TODO: upon notification send just the header notified by the link. - // TODO: it is client responsibility to deal with reorgs and race gaps. - send_result(value_t +void protocol_electrum::do_header(node::header_t link) NOEXCEPT +{ + BC_ASSERT(stranded()); + + const auto& query = archive(); + const auto height = query.get_height(link); + const auto header = query.get_header(link); + + if (height.is_terminal() || !header) + { + LOGF("Electrum::do_header, object not found (" << link << ")."); + return; + } + + send_notification("blockchain.headers.subscribe", + { + object_t { - object_t - { - { "height", uint64_t{ top } }, - { "hex", to_hex(*header, chain::header::serialized_size()) } - } - }, 256, BIND(complete, _1)); + { "height", height.value }, + { "hex", to_hex(*header, chain::header::serialized_size()) } + } + }, 100, BIND(complete, _1)); } void protocol_electrum::handle_blockchain_estimate_fee(const code& ec, @@ -477,14 +503,14 @@ void protocol_electrum::handle_mempool_get_fee_histogram(const code& ec, // TODO: requires tx pool metadata graph. send_result(value_t + { + array_t { - array_t - { - array_t{ 1, 1024 }, - array_t{ 2, 2048 }, - array_t{ 4, 4096 } - } - }, 256, BIND(complete, _1)); + array_t{ 1, 1024 }, + array_t{ 2, 2048 }, + array_t{ 4, 4096 } + } + }, 256, BIND(complete, _1)); } BC_POP_WARNING() diff --git a/test/protocols/blocks.cpp b/test/protocols/blocks.cpp index eadeb40b..6a6a7fcd 100644 --- a/test/protocols/blocks.cpp +++ b/test/protocols/blocks.cpp @@ -75,7 +75,7 @@ const chain::block block5{ block5_data, true }; const chain::block block6{ block6_data, true }; const chain::block block7{ block7_data, true }; const chain::block block8{ block8_data, true }; -const chain::block block9{ block8_data, true }; +const chain::block block9{ block9_data, true }; const server::settings::embedded_pages admin{}; const server::settings::embedded_pages native{}; diff --git a/test/protocols/electrum/electrum_block_headers.cpp b/test/protocols/electrum/electrum_block_headers.cpp new file mode 100644 index 00000000..753f5079 --- /dev/null +++ b/test/protocols/electrum/electrum_block_headers.cpp @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2011-2026 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 "../../test.hpp" +#include "electrum.hpp" + +BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) + +// blockchain.block.headers + +using namespace system; +static const code not_found{ server::error::not_found }; +static const code target_overflow{ server::error::target_overflow }; +static const code invalid_argument{ server::error::invalid_argument }; + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__genesis_count1_no_checkpoint__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":60,"method":"blockchain.block.headers","params":[0,1]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("max").as_int64(), 5); + BOOST_CHECK_EQUAL(result.at("count").as_int64(), 1); + BOOST_CHECK(result.at("headers").is_array()); + BOOST_CHECK_EQUAL(result.at("headers").as_array().size(), 1u); + BOOST_CHECK_EQUAL(result.at("headers").as_array().at(0).as_string(), encode_base16(header0_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__block1to3_no_checkpoint__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":61,"method":"blockchain.block.headers","params":[1,3]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("max").as_int64(), 5); + BOOST_CHECK_EQUAL(result.at("count").as_int64(), 3); + BOOST_CHECK(result.at("headers").is_array()); + + const auto& headers = result.at("headers").as_array(); + BOOST_CHECK_EQUAL(headers.size(), 3u); + BOOST_CHECK_EQUAL(headers.at(0).as_string(), encode_base16(header1_data)); + BOOST_CHECK_EQUAL(headers.at(1).as_string(), encode_base16(header2_data)); + BOOST_CHECK_EQUAL(headers.at(2).as_string(), encode_base16(header3_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__count_exceeds_max__capped) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":62,"method":"blockchain.block.headers","params":[0,10]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("max").as_int64(), 5); + BOOST_CHECK_EQUAL(result.at("count").as_int64(), 5); + BOOST_CHECK_EQUAL(result.at("headers").as_array().size(), 5u); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__count_zero__empty_headers) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":63,"method":"blockchain.block.headers","params":[5,0]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("count").as_int64(), 0); + BOOST_CHECK(result.at("headers").as_array().empty()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__proof_no_offset__expected) +{ + BOOST_CHECK(handshake()); + + const auto expected_root = encode_hash(merkle_root( + { + block0_hash, + block1_hash, + block2_hash, + block3_hash, + block4_hash, + block5_hash, + block6_hash, + block7_hash, + block8_hash + })); + + const string_list expected_branch + { + encode_hash(block4_hash), + encode_hash(root67), + encode_hash(root03), + encode_hash(root88) + }; + + const auto response = get(R"({"id":64,"method":"blockchain.block.headers","params":[5,1,8]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("max").as_int64(), 5); + BOOST_CHECK_EQUAL(result.at("count").as_int64(), 1); + BOOST_CHECK_EQUAL(result.at("headers").as_array().size(), 1u); + BOOST_CHECK_EQUAL(result.at("root").as_string(), expected_root); + + const auto& branch = result.at("branch").as_array(); + BOOST_CHECK_EQUAL(branch.size(), expected_branch.size()); + BOOST_CHECK_EQUAL(branch.at(0).as_string(), expected_branch[0]); + BOOST_CHECK_EQUAL(branch.at(1).as_string(), expected_branch[1]); + BOOST_CHECK_EQUAL(branch.at(2).as_string(), expected_branch[2]); + BOOST_CHECK_EQUAL(branch.at(3).as_string(), expected_branch[3]); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__proof_offset__expected) +{ + BOOST_CHECK(handshake()); + + const auto expected_root = encode_hash(merkle_root( + { + block0_hash, + block1_hash, + block2_hash, + block3_hash, + block4_hash, + block5_hash, + block6_hash, + block7_hash, + block8_hash + })); + + const string_list expected_branch + { + encode_hash(block6_hash), + encode_hash(root45), + encode_hash(root03), + encode_hash(root88) + }; + + const auto response = get(R"({"id":64,"method":"blockchain.block.headers","params":[5,3,8]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("max").as_int64(), 5); + BOOST_CHECK_EQUAL(result.at("count").as_int64(), 3); + BOOST_CHECK_EQUAL(result.at("headers").as_array().size(), 3u); + BOOST_CHECK_EQUAL(result.at("root").as_string(), expected_root); + + const auto& branch = result.at("branch").as_array(); + BOOST_CHECK_EQUAL(branch.size(), expected_branch.size()); + BOOST_CHECK_EQUAL(branch.at(0).as_string(), expected_branch[0]); + BOOST_CHECK_EQUAL(branch.at(1).as_string(), expected_branch[1]); + BOOST_CHECK_EQUAL(branch.at(2).as_string(), expected_branch[2]); + BOOST_CHECK_EQUAL(branch.at(3).as_string(), expected_branch[3]); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__start_above_top__not_found) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":65,"method":"blockchain.block.headers","params":[10,1]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__target_exceeds_waypoint__target_overflow) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":66,"method":"blockchain.block.headers","params":[2,3,1]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), target_overflow.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__waypoint_above_top__not_found) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":67,"method":"blockchain.block.headers","params":[0,1,10]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__negative_start__invalid_argument) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":68,"method":"blockchain.block.headers","params":[-1,1]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__fractional_count__invalid_argument) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":69,"method":"blockchain.block.headers","params":[0,1.5]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__start_plus_count_huge__not_found) +{ + BOOST_CHECK(handshake()); + + // argument_overflow is not actually reachable via json due to its integer limits. + const auto response = get(R"({"id":70,"method":"blockchain.block.headers","params":[9007199254740991,2]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_headers_subscribe.cpp b/test/protocols/electrum/electrum_headers_subscribe.cpp new file mode 100644 index 00000000..a2d43549 --- /dev/null +++ b/test/protocols/electrum/electrum_headers_subscribe.cpp @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2011-2026 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 "../../test.hpp" +#include "electrum.hpp" + +BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) + +// blockchain.headers.subscribe + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__default__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":80,"method":"blockchain.headers.subscribe","params":[]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); + BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__jsonrpc_2__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"jsonrpc":"2.0","id":81,"method":"blockchain.headers.subscribe"})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); + BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__id_preserved__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":123,"method":"blockchain.headers.subscribe","params":[]})" "\n"); + BOOST_CHECK_EQUAL(response.at("id").as_int64(), 123); + BOOST_CHECK(response.at("result").is_object()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__empty_params__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":82,"method":"blockchain.headers.subscribe","params":[]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); + BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 627190fb35d2c053c93c27a6558f847691ba96f6 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 12 Mar 2026 00:50:48 -0400 Subject: [PATCH 3/3] Add electrum blockchain_relay_fee test, stub in test files. --- Makefile.am | 9 +- builds/cmake/CMakeLists.txt | 9 +- .../libbitcoin-server-test.vcxproj | 7 +- .../libbitcoin-server-test.vcxproj.filters | 9 +- src/protocols/protocol_electrum.cpp | 2 + test/protocols/electrum/electrum.cpp | 3 +- .../protocols/electrum/electrum_addresses.cpp | 24 +++ .../electrum/electrum_block_header.cpp | 162 ---------------- test/protocols/electrum/electrum_fees.cpp | 37 ++++ ...block_headers.cpp => electrum_headers.cpp} | 178 +++++++++++++++++- .../electrum/electrum_headers_subscribe.cpp | 65 ------- .../electrum/electrum_transactions.cpp | 24 +++ 12 files changed, 285 insertions(+), 244 deletions(-) create mode 100644 test/protocols/electrum/electrum_addresses.cpp delete mode 100644 test/protocols/electrum/electrum_block_header.cpp create mode 100644 test/protocols/electrum/electrum_fees.cpp rename test/protocols/electrum/{electrum_block_headers.cpp => electrum_headers.cpp} (54%) delete mode 100644 test/protocols/electrum/electrum_headers_subscribe.cpp create mode 100644 test/protocols/electrum/electrum_transactions.cpp diff --git a/Makefile.am b/Makefile.am index b2a9a9ed..7fddf4a6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,11 +81,12 @@ test_libbitcoin_server_test_SOURCES = \ test/protocols/blocks.hpp \ test/protocols/electrum/electrum.cpp \ test/protocols/electrum/electrum.hpp \ - test/protocols/electrum/electrum_block_header.cpp \ - test/protocols/electrum/electrum_block_headers.cpp \ - test/protocols/electrum/electrum_headers_subscribe.cpp \ + test/protocols/electrum/electrum_addresses.cpp \ + test/protocols/electrum/electrum_fees.cpp \ + test/protocols/electrum/electrum_headers.cpp \ test/protocols/electrum/electrum_server.cpp \ - test/protocols/electrum/electrum_server_version.cpp + test/protocols/electrum/electrum_server_version.cpp \ + test/protocols/electrum/electrum_transactions.cpp endif WITH_TESTS diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 6e041596..7e4e4f7b 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -310,11 +310,12 @@ if (with-tests) "../../test/protocols/blocks.hpp" "../../test/protocols/electrum/electrum.cpp" "../../test/protocols/electrum/electrum.hpp" - "../../test/protocols/electrum/electrum_block_header.cpp" - "../../test/protocols/electrum/electrum_block_headers.cpp" - "../../test/protocols/electrum/electrum_headers_subscribe.cpp" + "../../test/protocols/electrum/electrum_addresses.cpp" + "../../test/protocols/electrum/electrum_fees.cpp" + "../../test/protocols/electrum/electrum_headers.cpp" "../../test/protocols/electrum/electrum_server.cpp" - "../../test/protocols/electrum/electrum_server_version.cpp" ) + "../../test/protocols/electrum/electrum_server_version.cpp" + "../../test/protocols/electrum/electrum_transactions.cpp" ) add_test( NAME libbitcoin-server-test COMMAND libbitcoin-server-test --run_test=* diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj index ba0e1a63..a5b4ee32 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj @@ -133,11 +133,12 @@ $(IntDir)test_protocols_electrum_electrum.obj - - - + + + + $(IntDir)test_test.obj diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters index 88bb9ad5..ebe5bf8e 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters @@ -57,13 +57,13 @@ src\protocols\electrum - + src\protocols\electrum - + src\protocols\electrum - + src\protocols\electrum @@ -72,6 +72,9 @@ src\protocols\electrum + + src\protocols\electrum + src diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index 733c1a70..4df677ee 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -407,6 +407,8 @@ void protocol_electrum::handle_blockchain_scripthash_unsubscribe(const code& ec, send_code(error::not_implemented); } +// TODO: requires tx pool in order to validate against unconfirmed txs. +// TODO: requires that p2p channels subscribe to transaction broadcast. void protocol_electrum::handle_blockchain_transaction_broadcast(const code& ec, rpc_interface::blockchain_transaction_broadcast, const std::string& ) NOEXCEPT diff --git a/test/protocols/electrum/electrum.cpp b/test/protocols/electrum/electrum.cpp index a1850f4e..dfbad074 100644 --- a/test/protocols/electrum/electrum.cpp +++ b/test/protocols/electrum/electrum.cpp @@ -52,6 +52,7 @@ electrum_setup_fixture::electrum_setup_fixture() electrum.connections = 1; database_settings.interval_depth = 2; node_settings.delay_inbound = false; + node_settings.minimum_fee_rate = 99.0; network_settings.inbound.connections = 0; network_settings.outbound.connections = 0; auto ec = store_.create([](auto, auto) {}); @@ -127,4 +128,4 @@ bool electrum_setup_fixture::handshake(const std::string& version, (result.at(0).is_string() && result.at(1).is_string()) && (result.at(0).as_string() == config().server.electrum.server_name) && (result.at(1).as_string() == version); -} \ No newline at end of file +} diff --git a/test/protocols/electrum/electrum_addresses.cpp b/test/protocols/electrum/electrum_addresses.cpp new file mode 100644 index 00000000..3a32de00 --- /dev/null +++ b/test/protocols/electrum/electrum_addresses.cpp @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2011-2026 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 "../../test.hpp" +#include "electrum.hpp" + +BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_block_header.cpp b/test/protocols/electrum/electrum_block_header.cpp deleted file mode 100644 index 7d3d625a..00000000 --- a/test/protocols/electrum/electrum_block_header.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (c) 2011-2026 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 "../../test.hpp" -#include "electrum.hpp" - -BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) - -// blockchain.block.header - -using namespace system; -static const code not_found{ server::error::not_found }; -static const code target_overflow{ server::error::target_overflow }; -static const code invalid_argument{ server::error::invalid_argument }; - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__genesis_no_checkpoint__expected_no_proof) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":43,"method":"blockchain.block.header","params":[0]})" "\n"); - BOOST_CHECK_EQUAL(response.at("result").as_object().at("header").as_string(), encode_base16(header0_data)); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__block1_no_checkpoint__expected_no_proof) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":44,"method":"blockchain.block.header","params":[1]})" "\n"); - BOOST_CHECK_EQUAL(response.at("result").as_object().at("header").as_string(), encode_base16(header1_data)); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__genesis_zero_checkpoint__expected_no_proof) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":45,"method":"blockchain.block.header","params":[0,0]})" "\n"); - const auto& result = response.at("result").as_object(); - BOOST_CHECK_EQUAL(result.at("header").as_string(), encode_base16(header0_data)); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__proof_self_block1__expected) -{ - BOOST_CHECK(handshake()); - const auto expected_header = encode_base16(header1_data); - const auto expected_root = encode_hash(merkle_root( - { - block0_hash, - block1_hash - })); - - const auto response = get(R"({"id":46,"method":"blockchain.block.header","params":[1,1]})" "\n"); - const auto& result = response.at("result").as_object(); - BOOST_CHECK_EQUAL(result.at("header").as_string(), expected_header); - BOOST_CHECK_EQUAL(result.at("root").as_string(), expected_root); - - const auto& branch = result.at("branch").as_array(); - BOOST_CHECK_EQUAL(branch.size(), 1u); - BOOST_CHECK_EQUAL(branch.at(0).as_string(), encode_hash(block0_hash)); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__proof_example__expected) -{ - BOOST_CHECK(handshake()); - - const auto expected_root = encode_hash(merkle_root( - { - block0_hash, - block1_hash, - block2_hash, - block3_hash, - block4_hash, - block5_hash, - block6_hash, - block7_hash, - block8_hash - })); - - const string_list expected_branch - { - encode_hash(block4_hash), - encode_hash(root67), - encode_hash(root03), - encode_hash(root88) - }; - - const auto response = get(R"({"id":50,"method":"blockchain.block.header","params":[5,8]})" "\n"); - const auto& result = response.at("result").as_object(); - BOOST_CHECK_EQUAL(result.at("header").as_string(), encode_base16(header5_data)); - BOOST_CHECK_EQUAL(result.at("root").as_string(), expected_root); - - const auto& branch = result.at("branch").as_array(); - BOOST_CHECK_EQUAL(branch.size(), expected_branch.size()); - BOOST_CHECK_EQUAL(branch.at(0).as_string(), expected_branch[0]); - BOOST_CHECK_EQUAL(branch.at(1).as_string(), expected_branch[1]); - BOOST_CHECK_EQUAL(branch.at(2).as_string(), expected_branch[2]); - BOOST_CHECK_EQUAL(branch.at(3).as_string(), expected_branch[3]); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__checkpoint_below_height__target_overflow) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":51,"method":"blockchain.block.header","params":[2,1]})" "\n"); - BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), target_overflow.value()); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__above_top__not_found) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":52,"method":"blockchain.block.header","params":[10]})" "\n"); - BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__checkpoint_above_top__not_found) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":53,"method":"blockchain.block.header","params":[1,10]})" "\n"); - BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__negative_height__invalid_argument) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":54,"method":"blockchain.block.header","params":[-1]})" "\n"); - BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__fractional_height__invalid_argument) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":55,"method":"blockchain.block.header","params":[1.5]})" "\n"); - BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__over_top_height__not_found) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":56,"method":"blockchain.block.header","params":[4294967296]})" "\n"); - BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_fees.cpp b/test/protocols/electrum/electrum_fees.cpp new file mode 100644 index 00000000..bdc2622d --- /dev/null +++ b/test/protocols/electrum/electrum_fees.cpp @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2011-2026 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 "../../test.hpp" +#include "electrum.hpp" + +BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) + +// blockchain.relay_fee + +BOOST_AUTO_TEST_CASE(electrum__blockchain_relay_fee__default__expected) +{ + BOOST_CHECK(handshake()); + + constexpr auto expected = 99.0; + const auto response = get(R"({"id":90,"method":"blockchain.relayfee","params":[]})" "\n"); + BOOST_CHECK_EQUAL(response.at("id").as_int64(), 90); + BOOST_CHECK(response.at("result").is_number()); + BOOST_CHECK_EQUAL(response.at("result").as_double(), expected); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_block_headers.cpp b/test/protocols/electrum/electrum_headers.cpp similarity index 54% rename from test/protocols/electrum/electrum_block_headers.cpp rename to test/protocols/electrum/electrum_headers.cpp index 753f5079..3247c379 100644 --- a/test/protocols/electrum/electrum_block_headers.cpp +++ b/test/protocols/electrum/electrum_headers.cpp @@ -21,13 +21,146 @@ BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) -// blockchain.block.headers - using namespace system; static const code not_found{ server::error::not_found }; static const code target_overflow{ server::error::target_overflow }; static const code invalid_argument{ server::error::invalid_argument }; +// blockchain.block.header + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__genesis_no_checkpoint__expected_no_proof) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":43,"method":"blockchain.block.header","params":[0]})" "\n"); + BOOST_CHECK_EQUAL(response.at("result").as_object().at("header").as_string(), encode_base16(header0_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__block1_no_checkpoint__expected_no_proof) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":44,"method":"blockchain.block.header","params":[1]})" "\n"); + BOOST_CHECK_EQUAL(response.at("result").as_object().at("header").as_string(), encode_base16(header1_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__genesis_zero_checkpoint__expected_no_proof) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":45,"method":"blockchain.block.header","params":[0,0]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("header").as_string(), encode_base16(header0_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__proof_self_block1__expected) +{ + BOOST_CHECK(handshake()); + const auto expected_header = encode_base16(header1_data); + const auto expected_root = encode_hash(merkle_root( + { + block0_hash, + block1_hash + })); + + const auto response = get(R"({"id":46,"method":"blockchain.block.header","params":[1,1]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("header").as_string(), expected_header); + BOOST_CHECK_EQUAL(result.at("root").as_string(), expected_root); + + const auto& branch = result.at("branch").as_array(); + BOOST_CHECK_EQUAL(branch.size(), 1u); + BOOST_CHECK_EQUAL(branch.at(0).as_string(), encode_hash(block0_hash)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__proof_example__expected) +{ + BOOST_CHECK(handshake()); + + const auto expected_root = encode_hash(merkle_root( + { + block0_hash, + block1_hash, + block2_hash, + block3_hash, + block4_hash, + block5_hash, + block6_hash, + block7_hash, + block8_hash + })); + + const string_list expected_branch + { + encode_hash(block4_hash), + encode_hash(root67), + encode_hash(root03), + encode_hash(root88) + }; + + const auto response = get(R"({"id":50,"method":"blockchain.block.header","params":[5,8]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("header").as_string(), encode_base16(header5_data)); + BOOST_CHECK_EQUAL(result.at("root").as_string(), expected_root); + + const auto& branch = result.at("branch").as_array(); + BOOST_CHECK_EQUAL(branch.size(), expected_branch.size()); + BOOST_CHECK_EQUAL(branch.at(0).as_string(), expected_branch[0]); + BOOST_CHECK_EQUAL(branch.at(1).as_string(), expected_branch[1]); + BOOST_CHECK_EQUAL(branch.at(2).as_string(), expected_branch[2]); + BOOST_CHECK_EQUAL(branch.at(3).as_string(), expected_branch[3]); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__checkpoint_below_height__target_overflow) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":51,"method":"blockchain.block.header","params":[2,1]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), target_overflow.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__above_top__not_found) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":52,"method":"blockchain.block.header","params":[10]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__checkpoint_above_top__not_found) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":53,"method":"blockchain.block.header","params":[1,10]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__negative_height__invalid_argument) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":54,"method":"blockchain.block.header","params":[-1]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__fractional_height__invalid_argument) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":55,"method":"blockchain.block.header","params":[1.5]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_block_header__over_top_height__not_found) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":56,"method":"blockchain.block.header","params":[4294967296]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +// blockchain.block.headers + BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__genesis_count1_no_checkpoint__expected) { BOOST_CHECK(handshake()); @@ -208,4 +341,45 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_block_headers__start_plus_count_huge__ BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); } +// blockchain.headers.subscribe + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__default__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":80,"method":"blockchain.headers.subscribe","params":[]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); + BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__jsonrpc_2__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"jsonrpc":"2.0","id":81,"method":"blockchain.headers.subscribe"})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); + BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__id_preserved__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":123,"method":"blockchain.headers.subscribe","params":[]})" "\n"); + BOOST_CHECK_EQUAL(response.at("id").as_int64(), 123); + BOOST_CHECK(response.at("result").is_object()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__empty_params__expected) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":82,"method":"blockchain.headers.subscribe","params":[]})" "\n"); + const auto& result = response.at("result").as_object(); + BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); + BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_headers_subscribe.cpp b/test/protocols/electrum/electrum_headers_subscribe.cpp deleted file mode 100644 index a2d43549..00000000 --- a/test/protocols/electrum/electrum_headers_subscribe.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2011-2026 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 "../../test.hpp" -#include "electrum.hpp" - -BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) - -// blockchain.headers.subscribe - -BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__default__expected) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":80,"method":"blockchain.headers.subscribe","params":[]})" "\n"); - const auto& result = response.at("result").as_object(); - BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); - BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__jsonrpc_2__expected) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"jsonrpc":"2.0","id":81,"method":"blockchain.headers.subscribe"})" "\n"); - const auto& result = response.at("result").as_object(); - BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); - BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__id_preserved__expected) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":123,"method":"blockchain.headers.subscribe","params":[]})" "\n"); - BOOST_CHECK_EQUAL(response.at("id").as_int64(), 123); - BOOST_CHECK(response.at("result").is_object()); -} - -BOOST_AUTO_TEST_CASE(electrum__blockchain_headers_subscribe__empty_params__expected) -{ - BOOST_CHECK(handshake()); - - const auto response = get(R"({"id":82,"method":"blockchain.headers.subscribe","params":[]})" "\n"); - const auto& result = response.at("result").as_object(); - BOOST_CHECK_EQUAL(result.at("height").as_int64(), 9); - BOOST_CHECK_EQUAL(result.at("hex").as_string(), system::encode_base16(header9_data)); -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test/protocols/electrum/electrum_transactions.cpp b/test/protocols/electrum/electrum_transactions.cpp new file mode 100644 index 00000000..3a32de00 --- /dev/null +++ b/test/protocols/electrum/electrum_transactions.cpp @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2011-2026 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 "../../test.hpp" +#include "electrum.hpp" + +BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) + +BOOST_AUTO_TEST_SUITE_END()