diff --git a/include/bitcoin/server/protocols/protocol_electrum.hpp b/include/bitcoin/server/protocols/protocol_electrum.hpp index 6cdc89c0..dd5a1d28 100644 --- a/include/bitcoin/server/protocols/protocol_electrum.hpp +++ b/include/bitcoin/server/protocols/protocol_electrum.hpp @@ -211,7 +211,7 @@ class BCS_API protocol_electrum using history = database::history; using unspents = database::unspents; using histories = database::histories; - using cursor_t = database::address_link; + using cursor_t = database::height_link; using midstate = system::accumulator; enum class notify_t { address, scripthash, scriptpubkey }; @@ -274,7 +274,7 @@ class BCS_API protocol_electrum void do_scripthash_subscribe(const hash_digest& hash, notify_t type) NOEXCEPT; void complete_scripthash_subscribe(const code& ec, - const hash_digest& status, const hash_digest& hash) NOEXCEPT; + const hash_digest& status) NOEXCEPT; void scripthash_unsubscribe(const hash_digest& hash) NOEXCEPT; void do_scripthash_unsubscribe(const hash_digest& hash) NOEXCEPT; void complete_scripthash_unsubscribe(bool found) NOEXCEPT; @@ -282,7 +282,7 @@ class BCS_API protocol_electrum notify_t type) NOEXCEPT; code get_scripthash_history(address_subscription& sub, - const hash_digest& hash, size_t limit=max_size_t) NOEXCEPT; + const hash_digest& hash, size_t limit) NOEXCEPT; /// Outpoint. /// ----------------------------------------------------------------------- diff --git a/src/protocols/electrum/protocol_electrum_scripthash.cpp b/src/protocols/electrum/protocol_electrum_scripthash.cpp index 6f78cc2e..88a42f2a 100644 --- a/src/protocols/electrum/protocol_electrum_scripthash.cpp +++ b/src/protocols/electrum/protocol_electrum_scripthash.cpp @@ -152,8 +152,11 @@ void protocol_electrum::do_get_history(const hash_digest& hash) NOEXCEPT { BC_ASSERT(!stranded()); histories histories{}; + database::height_link cursor{}; const auto& query = archive(); - const auto ec = query.get_history(stopping_, histories, hash, turbo_); + const auto ec = query.get_history(stopping_, cursor, histories, hash, + options().maximum_history, turbo_); + POST(complete_get_history, ec, std::move(histories)); } @@ -223,7 +226,9 @@ void protocol_electrum::do_get_mempool(const hash_digest& hash) NOEXCEPT BC_ASSERT(!stranded()); histories histories{}; const auto& query = archive(); - auto ec = query.get_unconfirmed_history(stopping_, histories, hash, turbo_); + auto ec = query.get_unconfirmed_history(stopping_, histories, hash, + options().maximum_history, turbo_); + POST(complete_get_mempool, ec, std::move(histories)); } diff --git a/src/protocols/electrum/protocol_electrum_subscribe.cpp b/src/protocols/electrum/protocol_electrum_subscribe.cpp index 020a3be6..89e8148e 100644 --- a/src/protocols/electrum/protocol_electrum_subscribe.cpp +++ b/src/protocols/electrum/protocol_electrum_subscribe.cpp @@ -104,11 +104,11 @@ void protocol_electrum::do_scripthash_subscribe(const hash_digest& hash, subscribed_address_.store(true, relaxed); } - POST(complete_scripthash_subscribe, ec, hash, std::move(status)); + POST(complete_scripthash_subscribe, ec, std::move(status)); } void protocol_electrum::complete_scripthash_subscribe(const code& ec, - const hash_digest& status, const hash_digest& hash) NOEXCEPT + const hash_digest& status) NOEXCEPT { BC_ASSERT(stranded()); @@ -123,11 +123,8 @@ void protocol_electrum::complete_scripthash_subscribe(const code& ec, return; } - send_result(array_t - { - encode_hash(hash), - status == null_hash ? value_t{} : value_t{ encode_hash(status) } - }, 128, BIND(complete, _1)); + send_result(status == null_hash ? value_t{} : + value_t{ encode_hash(status) }, 128, BIND(complete, _1)); } // unsubscribe @@ -202,7 +199,7 @@ void protocol_electrum::do_scripthash(node::header_t) NOEXCEPT for (auto& [key, sub]: address_subscriptions_) { // Depth limit is never imposed once a subscription is accepted. - if (const auto ec = get_scripthash_history(sub, key)) + if (const auto ec = get_scripthash_history(sub, key, max_size_t)) { if (ec == database::error::query_canceled) return; @@ -234,9 +231,8 @@ void protocol_electrum::scripthash_notify(const hash_digest& status, // utility // ---------------------------------------------------------------------------- -// private -// static +// private/static // Convert enumeration to json-rpc notification method name. std::string protocol_electrum::to_method_name(notify_t type) NOEXCEPT { @@ -252,7 +248,7 @@ std::string protocol_electrum::to_method_name(notify_t type) NOEXCEPT } } -// static +// private/static // Height is zero (rooted) or max_size_t for unconfirmed history txs. void protocol_electrum::write_status(midstate& accumulator, const history& history) NOEXCEPT @@ -263,77 +259,31 @@ void protocol_electrum::write_status(midstate& accumulator, accumulator.write(":"); } +// protected code protocol_electrum::get_scripthash_history(address_subscription& sub, const hash_digest& hash, size_t limit) NOEXCEPT { - histories records{}; + BC_ASSERT(notification_strand_.running_in_this_thread()); + + histories history{}; const auto& query = archive(); + if (const auto ec = query.get_history(stopping_, sub.cursor, history, + hash, limit, turbo_)) + return ec; - if (sub.cursor.is_terminal()) - { - // Initial scan queries all confirmed and unconfired together. - // Initial scan is depth-limited (based on config), others are not. - if (const auto ec = query.get_history(stopping_, sub.cursor, records, - hash, limit, turbo_)) - return ec; - - // Accumulate confirmed status in order. - auto it = records.cbegin(); - const auto cend = records.cend(); - while (it != cend && it->confirmed()) - write_status(sub.accumulator, *it++); - - BC_ASSERT(std::none_of(it, cend, [](const auto& at) - { return at.confirmed(); })); - - // Copy midstate accumulator and write unconfirmeds. - midstate copy = sub.accumulator; - while (it != cend) - write_status(copy, *it++); - - // Flush, cache and return status (always updated on initial). - sub.status = copy.flush(); + if (history.empty()) return error::success; - } - else - { - // Update scan queries new (cursor) confirmed independently. - if (const auto ec = query.get_confirmed_history(stopping_, sub.cursor, - records, hash, max_size_t, turbo_)) - return ec; - - // Accumulate confirmed status in order. - auto it = records.cbegin(); - auto cend = records.cend(); - while (it != cend && it->confirmed()) - write_status(sub.accumulator, *it++); - - // Copy midstate accumulator for write of unconfirmeds. - midstate copy = sub.accumulator; - records.clear(); - - // Update scan queries all unconfirmed independently. - if (const auto ec = query.get_unconfirmed_history(stopping_, records, - hash, turbo_)) - return ec; - - // Reinitialize iterator for unconfirmed writer. - it = records.cbegin(); - cend = records.cend(); - - // Accumulate unconfirmed status in order. - while (it != cend) - write_status(copy, *it++); - - // Flush, cache and return not found if no writes. - auto status = copy.flush(); - if (sub.status == status) - return error::not_found; - - // Set cache into midstate object for next run. - sub.status = std::move(status); - return error::success; - } + + auto it = history.cbegin(); + while (it != history.cend() && it->confirmed()) + write_status(sub.accumulator, *it++); + + midstate copy = sub.accumulator; + while (it != history.cend()) + write_status(copy, *it++); + + sub.status = copy.flush(); + return error::success; } BC_POP_WARNING() diff --git a/test/protocols/electrum/electrum.cpp b/test/protocols/electrum/electrum.cpp index f447d09d..215b30c2 100644 --- a/test/protocols/electrum/electrum.cpp +++ b/test/protocols/electrum/electrum.cpp @@ -108,6 +108,11 @@ int64_t electrum_setup_fixture::get_error(const std::string& request) boost::json::value electrum_setup_fixture::get(const std::string& request) { socket_.send(boost::asio::buffer(request)); + return receive(); +} + +boost::json::value electrum_setup_fixture::receive() +{ boost::asio::streambuf stream{}; try diff --git a/test/protocols/electrum/electrum.hpp b/test/protocols/electrum/electrum.hpp index 929c2464..337fe615 100644 --- a/test/protocols/electrum/electrum.hpp +++ b/test/protocols/electrum/electrum.hpp @@ -33,6 +33,7 @@ struct electrum_setup_fixture bool address_index=true); ~electrum_setup_fixture(); + boost::json::value receive(); int64_t get_error(const std::string& request); boost::json::value get(const std::string& request); void notify(node::chase event_, node::event_value value); diff --git a/test/protocols/electrum/electrum_addresses.cpp b/test/protocols/electrum/electrum_addresses.cpp index 77d338b7..adfdbd6b 100644 --- a/test/protocols/electrum/electrum_addresses.cpp +++ b/test/protocols/electrum/electrum_addresses.cpp @@ -23,6 +23,8 @@ using namespace system; static const code not_found{ server::error::not_found }; static const code wrong_version{ server::error::wrong_version }; static const code invalid_argument{ server::error::invalid_argument }; +static const std::string bogus_address{ "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn" }; +static const std::string found_address{ "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM" }; BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_ten_block_setup_fixture) @@ -41,18 +43,16 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_balance__obsoleted_version { BOOST_REQUIRE(handshake(electrum::version::v1_3)); - const auto response = get(R"({"id":902,"method":"blockchain.address.get_balance","params":[""]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":902,"method":"blockchain.address.get_balance","params":[""]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_balance__invalid_address__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - const auto response = get(R"({"id":903,"method":"blockchain.address.get_balance","params":["invalid"]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":903,"method":"blockchain.address.get_balance","params":["invalid"]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_balance__not_found_address__zero) @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_balance__not_found_address BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":904,"method":"blockchain.address.get_balance","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + const auto response = get((boost::format(request) % bogus_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_object()); const auto& result = response.at("result").as_object(); @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_balance__confirmed_and_unc BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); const auto request = R"({"id":905,"method":"blockchain.address.get_balance","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM").str()); + const auto response = get((boost::format(request) % found_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_object()); const auto& result = response.at("result").as_object(); @@ -108,18 +108,16 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__obsoleted_version { BOOST_REQUIRE(handshake(electrum::version::v1_3)); - const auto response = get(R"({"id":1003,"method":"blockchain.address.get_history","params":[""]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":1003,"method":"blockchain.address.get_history","params":[""]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__invalid_address__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - const auto response = get(R"({"id":1004,"method":"blockchain.address.get_history","params":["invalid"]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":1004,"method":"blockchain.address.get_history","params":["invalid"]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__not_found_address__empty) @@ -127,7 +125,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__not_found_address BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":1005,"method":"blockchain.address.get_history","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + const auto response = get((boost::format(request) % bogus_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").as_array().empty()); } @@ -142,7 +140,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__confirmed_and_unc BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); const auto request = R"({"id":1006,"method":"blockchain.address.get_history","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM").str()); + const auto response = get((boost::format(request) % found_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_array()); const auto& history = response.at("result").as_array(); @@ -188,18 +186,16 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__obsoleted_version { BOOST_REQUIRE(handshake(electrum::version::v1_3)); - const auto response = get(R"({"id":1003,"method":"blockchain.address.get_mempool","params":[""]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":1003,"method":"blockchain.address.get_mempool","params":[""]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__invalid_address__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - const auto response = get(R"({"id":1004,"method":"blockchain.address.get_mempool","params":["invalid"]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":1004,"method":"blockchain.address.get_mempool","params":["invalid"]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__not_found_address__empty) @@ -207,7 +203,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__not_found_address BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":1005,"method":"blockchain.address.get_mempool","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + const auto response = get((boost::format(request) % bogus_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").as_array().empty()); } @@ -222,7 +218,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__confirmed_and_unc BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); const auto request = R"({"id":1006,"method":"blockchain.address.get_mempool","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM").str()); + const auto response = get((boost::format(request) % found_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_array()); const auto& history = response.at("result").as_array(); @@ -260,18 +256,16 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__obsoleted_versio { BOOST_REQUIRE(handshake(electrum::version::v1_3)); - const auto response = get(R"({"id":1003,"method":"blockchain.address.listunspent","params":[""]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":1003,"method":"blockchain.address.listunspent","params":[""]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__invalid_address__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - const auto response = get(R"({"id":1004,"method":"blockchain.address.listunspent","params":["invalid"]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":1004,"method":"blockchain.address.listunspent","params":["invalid"]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__not_found_address__empty) @@ -279,7 +273,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__not_found_addres BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":1005,"method":"blockchain.address.listunspent","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + const auto response = get((boost::format(request) % bogus_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").as_array().empty()); } @@ -294,7 +288,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__confirmed_and_un BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); const auto request = R"({"id":1006,"method":"blockchain.address.listunspent","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM").str()); + const auto response = get((boost::format(request) % found_address).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_array()); const auto& unspent = response.at("result").as_array(); diff --git a/test/protocols/electrum/electrum_disabled.cpp b/test/protocols/electrum/electrum_disabled.cpp index 691f4245..dafd940b 100644 --- a/test/protocols/electrum/electrum_disabled.cpp +++ b/test/protocols/electrum/electrum_disabled.cpp @@ -37,9 +37,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_balance__no_address_index_ BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":901,"method":"blockchain.address.get_balance","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__no_address_index__not_implemented) @@ -48,9 +47,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_history__no_address_index_ BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":1001,"method":"blockchain.address.get_history","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__no_address_index__not_implemented) @@ -59,9 +57,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_get_mempool__no_address_index_ BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":1001,"method":"blockchain.address.get_mempool","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__no_address_index__not_implemented) @@ -70,9 +67,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_list_unspent__no_address_index BOOST_REQUIRE(handshake(electrum::version::v1_0)); const auto request = R"({"id":1001,"method":"blockchain.address.listunspent","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn").str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } // scripthash @@ -165,9 +161,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_get_balance__no_address_i BOOST_REQUIRE(handshake(electrum::version::v1_7)); const auto request = R"({"id":901,"method":"blockchain.scriptpubkey.get_balance","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % bogus_script).str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % bogus_script).str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_get_history__no_address_index__not_implemented) @@ -176,9 +171,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_get_history__no_address_i BOOST_REQUIRE(handshake(electrum::version::v1_7)); const auto request = R"({"id":1001,"method":"blockchain.scriptpubkey.get_history","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % bogus_script).str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % bogus_script).str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_get_mempool__no_address_index__not_implemented) @@ -187,9 +181,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_get_mempool__no_address_i BOOST_REQUIRE(handshake(electrum::version::v1_7)); const auto request = R"({"id":1001,"method":"blockchain.scriptpubkey.get_mempool","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % bogus_script).str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % bogus_script).str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_list_unspent__no_address_index__not_implemented) @@ -198,9 +191,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_list_unspent__no_address_ BOOST_REQUIRE(handshake(electrum::version::v1_7)); const auto request = R"({"id":1001,"method":"blockchain.scriptpubkey.listunspent","params":["%1%"]})" "\n"; - const auto response = get((boost::format(request) % bogus_script).str()); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error((boost::format(request) % bogus_script).str()); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_fees.cpp b/test/protocols/electrum/electrum_fees.cpp index 690275b4..2020a209 100644 --- a/test/protocols/electrum/electrum_fees.cpp +++ b/test/protocols/electrum/electrum_fees.cpp @@ -40,27 +40,24 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__float_number__invalid_ar { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[42.42]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42.42]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__mode_invalid_version__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_4)); - const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__valid__not_implemented) { BOOST_REQUIRE(handshake(electrum::version::v1_6)); - const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); + const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n"); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } // blockchain.relayfee @@ -81,9 +78,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_relay_fee__obsoleted__wrong_version) { BOOST_REQUIRE(handshake(electrum::version::v1_6)); - const auto response = get(R"({"id":801,"method":"blockchain.relayfee","params":[]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":801,"method":"blockchain.relayfee","params":[]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/protocols/electrum/electrum_headers.cpp b/test/protocols/electrum/electrum_headers.cpp index e8f5cb12..d44ab2e4 100644 --- a/test/protocols/electrum/electrum_headers.cpp +++ b/test/protocols/electrum/electrum_headers.cpp @@ -33,9 +33,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_number_of_blocks_subscribe__obsoleted_ { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - const auto response = get(R"({"id":1001,"method":"blockchain.numblocks.subscribe","params":[]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":1001,"method":"blockchain.numblocks.subscribe","params":[]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_number_of_blocks_subscribe__9_block_store__returns_9) @@ -53,18 +52,16 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_block_get_chunk__obsoleted_version__wr { BOOST_REQUIRE(handshake(electrum::version::v1_4)); - const auto response = get(R"({"id":43,"method":"blockchain.block.get_chunk","params":[0]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":43,"method":"blockchain.block.get_chunk","params":[0]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_block_get_chunk__invalid_index__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - const auto response = get(R"({"id":43,"method":"blockchain.block.get_chunk","params":[42.42]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":43,"method":"blockchain.block.get_chunk","params":[42.42]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_block_get_chunk__above_top__empty) @@ -98,18 +95,16 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_block_get_header__obsoleted_version__w { BOOST_REQUIRE(handshake(electrum::version::v1_4)); - const auto response = get(R"({"id":43,"method":"blockchain.block.get_header","params":[0]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":43,"method":"blockchain.block.get_header","params":[0]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_block_get_header__invalid_height__invalid_argument) { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - const auto response = get(R"({"id":43,"method":"blockchain.block.get_header","params":[42.42]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); + const auto result = get_error(R"({"id":43,"method":"blockchain.block.get_header","params":[42.42]})" "\n"); + BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } BOOST_AUTO_TEST_CASE(electrum__blockchain_block_get_header__above_top__null) diff --git a/test/protocols/electrum/electrum_mempool.cpp b/test/protocols/electrum/electrum_mempool.cpp index 13589731..56b1c5bb 100644 --- a/test/protocols/electrum/electrum_mempool.cpp +++ b/test/protocols/electrum/electrum_mempool.cpp @@ -31,9 +31,8 @@ BOOST_AUTO_TEST_CASE(electrum__mempool_get_fee_histogram__insufficient_version__ { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - const auto response = get(R"({"id":600,"method":"mempool.get_fee_histogram","params":[]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":600,"method":"mempool.get_fee_histogram","params":[]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__mempool_get_fee_histogram__no_params_key__dropped) @@ -56,10 +55,8 @@ BOOST_AUTO_TEST_CASE(electrum__mempool_get_fee_histogram__empty_params__not_impl { BOOST_REQUIRE(handshake(electrum::version::v1_2)); - const auto response = get(R"({"id":603,"method":"mempool.get_fee_histogram","params":[]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), not_implemented.value()); - ///REQUIRE_NO_THROW_TRUE(response.at("result").as_array().empty()); + const auto result = get_error(R"({"id":603,"method":"mempool.get_fee_histogram","params":[]})" "\n"); + BOOST_REQUIRE_EQUAL(result, not_implemented.value()); } // mempool.get_info @@ -68,9 +65,8 @@ BOOST_AUTO_TEST_CASE(electrum__mempool_get_info__insufficient_version__wrong_ver { BOOST_REQUIRE(handshake(electrum::version::v1_4)); - const auto response = get(R"({"id":700,"method":"mempool.get_info","params":[]})" "\n"); - REQUIRE_NO_THROW_TRUE(response.at("error").as_object().at("code").is_int64()); - BOOST_REQUIRE_EQUAL(response.at("error").as_object().at("code").as_int64(), wrong_version.value()); + const auto result = get_error(R"({"id":700,"method":"mempool.get_info","params":[]})" "\n"); + BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } BOOST_AUTO_TEST_CASE(electrum__mempool_get_info__no_params_key__dropped) diff --git a/test/protocols/electrum/electrum_outpoints.cpp b/test/protocols/electrum/electrum_outpoints.cpp index c03ec57e..075f5a67 100644 --- a/test/protocols/electrum/electrum_outpoints.cpp +++ b/test/protocols/electrum/electrum_outpoints.cpp @@ -25,6 +25,8 @@ using namespace system; static const code not_found{ server::error::not_found }; static const code wrong_version{ server::error::wrong_version }; static const code invalid_argument{ server::error::invalid_argument }; +static const std::string found_address{ "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM" }; +static const std::string bogus_hash{ "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" }; // blockchain.utxo.get_address @@ -32,9 +34,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_utxo_get_address__obsoleted_version__w { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - constexpr auto hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"; const auto request = R"({"id":901,"method":"blockchain.utxo.get_address","params":["%1%",0]})" "\n"; - const auto result = get_error((boost::format(request) % hash).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } @@ -50,9 +51,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_utxo_get_address__invalid_index__inval { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":901,"method":"blockchain.utxo.get_address","params":["%1%",-1]})" "\n"; - const auto result = get_error((boost::format(request) % encode_hash(hash)).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } @@ -68,9 +68,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_utxo_get_address__tx_not_found__null) { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":901,"method":"blockchain.utxo.get_address","params":["%1%",0]})" "\n"; - const auto response = get((boost::format(request) % encode_hash(hash)).str()); + const auto response = get((boost::format(request) % bogus_hash).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_null()); } @@ -78,9 +77,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_utxo_get_address__p2pk__null) { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - constexpr auto hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"; const auto request = R"({"id":901,"method":"blockchain.utxo.get_address","params":["%1%",0]})" "\n"; - const auto response = get((boost::format(request) % hash).str()); + const auto response = get((boost::format(request) % bogus_hash).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_null()); } @@ -98,7 +96,7 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_utxo_get_address__p2kh__expected) REQUIRE_NO_THROW_TRUE(response.at("result").is_string()); const auto& result = response.at("result").as_string(); - BOOST_REQUIRE_EQUAL(result,"1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM"); + BOOST_REQUIRE_EQUAL(result, found_address); } BOOST_AUTO_TEST_CASE(electrum__blockchain_utxo_get_address__p2sh__expected) @@ -124,9 +122,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_get_status__insufficient_vers { BOOST_REQUIRE(handshake(electrum::version::v1_6)); - constexpr auto hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"; const auto request = R"({"id":1101,"method":"blockchain.outpoint.get_status","params":["%1%",0]})" "\n"; - const auto result = get_error((boost::format(request) % hash).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } @@ -150,9 +147,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_get_status__invalid_index__in { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":1104,"method":"blockchain.outpoint.get_status","params":["%1%",-1]})" "\n"; - const auto result = get_error((boost::format(request) % encode_hash(hash)).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } @@ -170,9 +166,9 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_get_status__confirmed_unspent { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - const auto hash1 = test::block1.transactions_ptr()->at(0)->hash(false); + const auto hash = test::block1.transactions_ptr()->at(0)->hash(false); const auto request = R"({"id":1106,"method":"blockchain.outpoint.get_status","params":["%1%",0]})" "\n"; - const auto response = get((boost::format(request) % encode_hash(hash1)).str()); + const auto response = get((boost::format(request) % encode_hash(hash)).str()); REQUIRE_NO_THROW_TRUE(response.at("result").is_object()); const auto& result = response.at("result").as_object(); @@ -210,9 +206,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_subscribe__insufficient_versi { BOOST_REQUIRE(handshake(electrum::version::v1_6)); - constexpr auto hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"; const auto request = R"({"id":1101,"method":"blockchain.outpoint.subscribe","params":["%1%",0]})" "\n"; - const auto result = get_error((boost::format(request) % hash).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } @@ -236,9 +231,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_subscribe__invalid_index__inv { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":1104,"method":"blockchain.outpoint.subscribe","params":["%1%",-1]})" "\n"; - const auto result = get_error((boost::format(request) % encode_hash(hash)).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } @@ -246,9 +240,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_subscribe__invalid_hint_encod { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":1104,"method":"blockchain.outpoint.subscribe","params":["%1%",-1,"not_hex"]})" "\n"; - const auto result = get_error((boost::format(request) % encode_hash(hash)).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } @@ -256,9 +249,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_subscribe__invalid_hint__inva { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":1104,"method":"blockchain.outpoint.subscribe","params":["%1%",-1,""]})" "\n"; - const auto result = get_error((boost::format(request) % encode_hash(hash)).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } @@ -266,8 +258,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_subscribe__extra_argument__dr { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; - const auto response = get(R"({"id":1104,"method":"blockchain.outpoint.subscribe","params":["%1%",-1,"00",42]})" "\n"); + const auto request = R"({"id":1104,"method":"blockchain.outpoint.subscribe","params":["%1%",-1,"00",42]})" "\n"; + const auto response = get((boost::format(request) % bogus_hash).str()); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); } @@ -277,9 +269,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_unsubscribe__insufficient_ver { BOOST_REQUIRE(handshake(electrum::version::v1_6)); - constexpr auto hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"; const auto request = R"({"id":1101,"method":"blockchain.outpoint.unsubscribe","params":["%1%",0]})" "\n"; - const auto result = get_error((boost::format(request) % hash).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, wrong_version.value()); } @@ -303,9 +294,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_unsubscribe__invalid_index__i { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; const auto request = R"({"id":1104,"method":"blockchain.outpoint.unsubscribe","params":["%1%",-1]})" "\n"; - const auto result = get_error((boost::format(request) % encode_hash(hash)).str()); + const auto result = get_error((boost::format(request) % bogus_hash).str()); BOOST_REQUIRE_EQUAL(result, invalid_argument.value()); } @@ -313,8 +303,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_outpoint_unsubscribe__extra_argument__ { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; - const auto response = get(R"({"id":1104,"method":"blockchain.outpoint.unsubscribe","params":["%1%",-1,""]})" "\n"); + const auto request = R"({"id":1104,"method":"blockchain.outpoint.unsubscribe","params":["%1%",-1,""]})" "\n"; + const auto response = get((boost::format(request) % bogus_hash).str()); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); } diff --git a/test/protocols/electrum/electrum_subscribe.cpp b/test/protocols/electrum/electrum_subscribe.cpp index 20ff1459..2b91c622 100644 --- a/test/protocols/electrum/electrum_subscribe.cpp +++ b/test/protocols/electrum/electrum_subscribe.cpp @@ -20,12 +20,17 @@ #include "electrum.hpp" using namespace system; +static const code not_found{ server::error::not_found }; static const code wrong_version{ server::error::wrong_version }; static const code invalid_argument{ server::error::invalid_argument }; static const std::string bogus_address{ "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn" }; +static const std::string found_address{ "1BaMPFdqMUQ46BV8iRcwbVfsam57oBLMM" }; static const std::string bogus_scripthash{ "9c2c84a6cf9809e08af19557e28d38257e6fee6981269760637a5f9dfb000b05" }; +static const std::string found_scripthash{ "bad83872c90886be19b98734fd16741611efcd9f5de699c14b712675eec682f5" }; static const chain::script bogus{ chain::script::to_pay_key_hash_pattern({ 0x42 }) }; +static const chain::script found{ chain::script::to_pay_key_hash_pattern({ 0x02 }) }; static const auto bogus_script = encode_base16(bogus.to_data(false)); +static const auto found_script = encode_base16(found.to_data(false)); BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_ten_block_setup_fixture) @@ -60,11 +65,204 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_address_subscribe__extra_argument__dro { BOOST_REQUIRE(handshake(electrum::version::v1_0)); - constexpr hash_digest hash{ 0x42 }; const auto response = get(R"({"id":1104,"method":"blockchain.address.subscribe","params":["%1%",42]})" "\n"); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); } +BOOST_AUTO_TEST_CASE(electrum__blockchain_address_subscribe__bogus_p2pkh__null) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + const auto request = R"({"id":1101,"method":"blockchain.address.subscribe","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % bogus_address).str()); + REQUIRE_NO_THROW_TRUE(response.at("result").is_null()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_address_subscribe__initialization__expected_status) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + // This validates the hash accumulator copy in get_scripthash_history() and incorporates + // confirmed, rooted and unrooted transactions, duplicates, and sort. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + const auto expected_initial = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.address.subscribe","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response.at("result").as_string(), expected_initial); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_address_subscribe__repeat_call__idempotent) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + const auto expected_initial = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.address.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_initial); + + const auto response2 = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response2.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response2.at("result").as_string(), expected_initial); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_address_subscribe__progressive__expected) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + + // Confirming block 10 also makes block 11 to rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + const auto expected_confirm10 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.address.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_confirm10); + + // Confirming block 11 also makes block 12 rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block11.hash()), true)); + const auto expected_confirm11 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":0:" + )); + + const auto response2 = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response2.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response2.at("result").as_string(), expected_confirm11); + + // Confirming block 12 only makes block 12 confirmed. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block12.hash()), true)); + const auto expected_confirm12 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":12:" + )); + + const auto response3 = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response3.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response3.at("result").as_string(), expected_confirm12); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_address_subscribe__progressive_notify__expected) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + + // Confirming block 10 also makes block 11 to rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + const auto expected_confirm10 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.address.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_address).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_confirm10); + + // Confirming block 11 also makes block 12 rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block11.hash()), true)); + const auto expected_confirm11 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":0:" + )); + + // Trigger node chaser event to electrum event subscriber. + notify(node::chase::organized, {}); + + const auto notification1 = receive(); + REQUIRE_NO_THROW_TRUE(notification1.at("method").is_string()); + REQUIRE_NO_THROW_TRUE(notification1.at("params").is_array()); + BOOST_CHECK_EQUAL(notification1.at("method").as_string(), "blockchain.address.subscribe"); + + const auto& params1 = notification1.at("params").as_array(); + BOOST_REQUIRE_EQUAL(params1.size(), 2u); + BOOST_CHECK(params1.at(0).is_string()); + BOOST_CHECK(params1.at(1).is_string()); + BOOST_CHECK_EQUAL(params1.at(0).as_string(), found_scripthash); + BOOST_CHECK_EQUAL(params1.at(1).as_string(), expected_confirm11); + + // Confirming block 12 only makes block 12 confirmed. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block12.hash()), true)); + const auto expected_confirm12 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":12:" + )); + + // Trigger node chaser event to electrum event subscriber. + notify(node::chase::organized, {}); + + const auto notification2 = receive(); + REQUIRE_NO_THROW_TRUE(notification2.at("method").is_string()); + REQUIRE_NO_THROW_TRUE(notification2.at("params").is_array()); + BOOST_CHECK_EQUAL(notification2.at("method").as_string(), "blockchain.address.subscribe"); + + const auto& params2 = notification2.at("params").as_array(); + BOOST_REQUIRE_EQUAL(params2.size(), 2u); + BOOST_CHECK(params2.at(0).is_string()); + BOOST_CHECK(params2.at(1).is_string()); + BOOST_CHECK_EQUAL(params2.at(0).as_string(), found_scripthash); + BOOST_CHECK_EQUAL(params2.at(1).as_string(), expected_confirm12); +} + // blockchain.scripthash.subscribe BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__insufficient_version__wrong_version) @@ -96,11 +294,206 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__extra_argument__ { BOOST_REQUIRE(handshake(electrum::version::v1_1)); - constexpr hash_digest hash{ 0x42 }; - const auto response = get(R"({"id":1104,"method":"blockchain.scripthash.subscribe","params":["%1%",42]})" "\n"); + const auto request = R"({"id":1104,"method":"blockchain.scripthash.subscribe","params":["%1%",42]})" "\n"; + const auto response = get((boost::format(request) % bogus_scripthash).str()); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); } +BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__bogus_scripthash__null) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + const auto request = R"({"id":1101,"method":"blockchain.scripthash.subscribe","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % bogus_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response.at("result").is_null()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__initialization__expected_status) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_1)); + + // This validates the hash accumulator copy in get_scripthash_history() and incorporates + // confirmed, rooted and unrooted transactions, duplicates, and sort. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + const auto expected_initial = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scripthash.subscribe","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response.at("result").as_string(), expected_initial); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__repeat_call__idempotent) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_1)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + const auto expected_initial = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scripthash.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_initial); + + const auto response2 = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response2.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response2.at("result").as_string(), expected_initial); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__progressive__expected) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_1)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + + // Confirming block 10 also makes block 11 to rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + const auto expected_confirm10 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scripthash.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_confirm10); + + // Confirming block 11 also makes block 12 rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block11.hash()), true)); + const auto expected_confirm11 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":0:" + )); + + const auto response2 = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response2.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response2.at("result").as_string(), expected_confirm11); + + // Confirming block 12 only makes block 12 confirmed. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block12.hash()), true)); + const auto expected_confirm12 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":12:" + )); + + const auto response3 = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response3.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response3.at("result").as_string(), expected_confirm12); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_subscribe__progressive_notify__expected) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_1)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + + // Confirming block 10 also makes block 11 to rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + const auto expected_confirm10 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scripthash.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_scripthash).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_confirm10); + + // Confirming block 11 also makes block 12 rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block11.hash()), true)); + const auto expected_confirm11 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":0:" + )); + + // Trigger node chaser event to electrum event subscriber. + notify(node::chase::organized, {}); + + const auto notification1 = receive(); + REQUIRE_NO_THROW_TRUE(notification1.at("method").is_string()); + REQUIRE_NO_THROW_TRUE(notification1.at("params").is_array()); + BOOST_CHECK_EQUAL(notification1.at("method").as_string(), "blockchain.scripthash.subscribe"); + + const auto& params1 = notification1.at("params").as_array(); + BOOST_REQUIRE_EQUAL(params1.size(), 2u); + BOOST_CHECK(params1.at(0).is_string()); + BOOST_CHECK(params1.at(1).is_string()); + BOOST_CHECK_EQUAL(params1.at(0).as_string(), found_scripthash); + BOOST_CHECK_EQUAL(params1.at(1).as_string(), expected_confirm11); + + // Confirming block 12 only makes block 12 confirmed. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block12.hash()), true)); + const auto expected_confirm12 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":12:" + )); + + // Trigger node chaser event to electrum event subscriber. + notify(node::chase::organized, {}); + + const auto notification2 = receive(); + REQUIRE_NO_THROW_TRUE(notification2.at("method").is_string()); + REQUIRE_NO_THROW_TRUE(notification2.at("params").is_array()); + BOOST_CHECK_EQUAL(notification2.at("method").as_string(), "blockchain.scripthash.subscribe"); + + const auto& params2 = notification2.at("params").as_array(); + BOOST_REQUIRE_EQUAL(params2.size(), 2u); + BOOST_CHECK(params2.at(0).is_string()); + BOOST_CHECK(params2.at(1).is_string()); + BOOST_CHECK_EQUAL(params2.at(0).as_string(), found_scripthash); + BOOST_CHECK_EQUAL(params2.at(1).as_string(), expected_confirm12); +} + + // blockchain.scripthash.unsubscribe BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_unsubscribe__insufficient_version__wrong_version) @@ -132,8 +525,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scripthash_unsubscribe__extra_argument { BOOST_REQUIRE(handshake(electrum::version::v1_4_2)); - constexpr hash_digest hash{ 0x42 }; - const auto response = get(R"({"id":1104,"method":"blockchain.scripthash.unsubscribe","params":["%1%",-1]})" "\n"); + const auto request = R"({"id":1104,"method":"blockchain.scripthash.unsubscribe","params":["%1%",-1]})" "\n"; + const auto response = get((boost::format(request) % bogus_scripthash).str()); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); } @@ -168,11 +561,205 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_subscribe__extra_argument { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; - const auto response = get(R"({"id":1104,"method":"blockchain.scriptpubkey.subscribe","params":["%1%",42]})" "\n"); + const auto request = R"({"id":1104,"method":"blockchain.scriptpubkey.subscribe","params":["%1%",42]})" "\n"; + const auto response = get((boost::format(request) % bogus_scripthash).str()); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); } +BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_subscribe__bogus_script__null) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_0)); + + const auto request = R"({"id":1101,"method":"blockchain.scriptpubkey.subscribe","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % bogus_script).str()); + REQUIRE_NO_THROW_TRUE(response.at("result").is_null()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_subscribe__initialization__expected_status) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_7)); + + // This validates the hash accumulator copy in get_scripthash_history() and incorporates + // confirmed, rooted and unrooted transactions, duplicates, and sort. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + const auto expected_initial = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scriptpubkey.subscribe","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response.at("result").as_string(), expected_initial); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_subscribe__repeat_call__idempotent) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_7)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + const auto expected_initial = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scriptpubkey.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_initial); + + const auto response2 = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response2.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response2.at("result").as_string(), expected_initial); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_subscribe__progressive__expected) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_7)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + + // Confirming block 10 also makes block 11 to rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + const auto expected_confirm10 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scriptpubkey.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_confirm10); + + // Confirming block 11 also makes block 12 rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block11.hash()), true)); + const auto expected_confirm11 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":0:" + )); + + const auto response2 = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response2.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response2.at("result").as_string(), expected_confirm11); + + // Confirming block 12 only makes block 12 confirmed. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block12.hash()), true)); + const auto expected_confirm12 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":12:" + )); + + const auto response3 = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response3.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response3.at("result").as_string(), expected_confirm12); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_subscribe__progressive_notify__expected) +{ + BOOST_REQUIRE(handshake(electrum::version::v1_7)); + + // This validates curosr/midstate consistency. + BOOST_REQUIRE(query_.set(test::bogus_block10, database::context{ 0, 10, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block11, database::context{ 0, 11, 0 }, false, false)); + BOOST_REQUIRE(query_.set(test::bogus_block12, database::context{ 0, 12, 0 }, false, false)); + const auto hash10 = test::bogus_block10.transactions_ptr()->at(1)->hash(false); + const auto hash11 = test::bogus_block11.transactions_ptr()->at(0)->hash(false); + const auto hash12 = test::bogus_block12.transactions_ptr()->at(0)->hash(false); + + // Confirming block 10 also makes block 11 to rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block10.hash()), true)); + const auto expected_confirm10 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":0:" + + encode_hash(hash12) + ":-1:" + )); + + const auto request = R"({"id":1101,"method":"blockchain.scriptpubkey.subscribe","params":["%1%"]})" "\n"; + const auto response1 = get((boost::format(request) % found_script).str()); + REQUIRE_NO_THROW_TRUE(response1.at("result").is_string()); + BOOST_REQUIRE_EQUAL(response1.at("result").as_string(), expected_confirm10); + + // Confirming block 11 also makes block 12 rooted. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block11.hash()), true)); + const auto expected_confirm11 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":0:" + )); + + // Trigger node chaser event to electrum event subscriber. + notify(node::chase::organized, {}); + + const auto notification1 = receive(); + REQUIRE_NO_THROW_TRUE(notification1.at("method").is_string()); + REQUIRE_NO_THROW_TRUE(notification1.at("params").is_array()); + BOOST_CHECK_EQUAL(notification1.at("method").as_string(), "blockchain.scriptpubkey.subscribe"); + + const auto& params1 = notification1.at("params").as_array(); + BOOST_REQUIRE_EQUAL(params1.size(), 2u); + BOOST_CHECK(params1.at(0).is_string()); + BOOST_CHECK(params1.at(1).is_string()); + BOOST_CHECK_EQUAL(params1.at(0).as_string(), found_scripthash); + BOOST_CHECK_EQUAL(params1.at(1).as_string(), expected_confirm11); + + // Confirming block 12 only makes block 12 confirmed. + BOOST_REQUIRE(query_.push_confirmed(query_.to_header(test::bogus_block12.hash()), true)); + const auto expected_confirm12 = encode_hash(sha256_hash + ( + encode_hash(hash10) + ":10:" + + encode_hash(hash11) + ":11:" + + encode_hash(hash12) + ":12:" + )); + + // Trigger node chaser event to electrum event subscriber. + notify(node::chase::organized, {}); + + const auto notification2 = receive(); + REQUIRE_NO_THROW_TRUE(notification2.at("method").is_string()); + REQUIRE_NO_THROW_TRUE(notification2.at("params").is_array()); + BOOST_CHECK_EQUAL(notification2.at("method").as_string(), "blockchain.scriptpubkey.subscribe"); + + const auto& params2 = notification2.at("params").as_array(); + BOOST_REQUIRE_EQUAL(params2.size(), 2u); + BOOST_CHECK(params2.at(0).is_string()); + BOOST_CHECK(params2.at(1).is_string()); + BOOST_CHECK_EQUAL(params2.at(0).as_string(), found_scripthash); + BOOST_CHECK_EQUAL(params2.at(1).as_string(), expected_confirm12); +} + // blockchain.scriptpubkey.unsubscribe BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_unsubscribe__insufficient_version__wrong_version) @@ -204,8 +791,8 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_scriptpubkey_unsubscribe__extra_argume { BOOST_REQUIRE(handshake(electrum::version::v1_7)); - constexpr hash_digest hash{ 0x42 }; - const auto response = get(R"({"id":1104,"method":"blockchain.scriptpubkey.unsubscribe","params":["%1%",-1]})" "\n"); + const auto request = R"({"id":1104,"method":"blockchain.scriptpubkey.unsubscribe","params":["%1%",-1]})" "\n"; + const auto response = get((boost::format(request) % bogus_scripthash).str()); REQUIRE_NO_THROW_TRUE(response.at("dropped").as_bool()); }