diff --git a/include/bitcoin/server/interfaces/bitcoind_rpc.hpp b/include/bitcoin/server/interfaces/bitcoind_rpc.hpp index 7c83d608..2f688ddf 100644 --- a/include/bitcoin/server/interfaces/bitcoind_rpc.hpp +++ b/include/bitcoin/server/interfaces/bitcoind_rpc.hpp @@ -32,7 +32,7 @@ struct bitcoind_rpc_methods { /// Blockchain methods. method<"getbestblockhash">{}, - method<"getblock", string_t, optional<1.0>>{ "blockhash", "verbosity" }, + method<"getblock", string_t, optional>{ "blockhash", "verbosity" }, method<"getblockchaininfo">{}, method<"getblockcount">{}, method<"getblockfilter", string_t, optional<"basic"_t>>{ "blockhash", "filtertype" }, @@ -73,7 +73,7 @@ struct bitcoind_rpc_methods method<"getnetworkinfo">{}, /// Rawtransactions methods (implemented). - method<"getrawtransaction", string_t, optional<0.0>, optional<""_t>>{ "txid", "verbosity", "blockhash" }, + method<"getrawtransaction", string_t, optional, optional<""_t>>{ "txid", "verbosity", "blockhash" }, method<"sendrawtransaction", string_t, optional<0.0>>{ "hexstring", "maxfeerate" } ////method<"getpeerinfo">{}, ////method<"listbanned">{}, diff --git a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp index 6cbfbb16..7ae6c9e4 100644 --- a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp +++ b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp @@ -66,7 +66,7 @@ class BCS_API protocol_bitcoind_rpc rpc_interface::get_best_block_hash) NOEXCEPT; bool handle_get_block(const code& ec, rpc_interface::get_block, const std::string&, - double verbosity) NOEXCEPT; + const network::rpc::value_t& verbosity) NOEXCEPT; bool handle_get_block_chain_info(const code& ec, rpc_interface::get_block_chain_info) NOEXCEPT; bool handle_get_block_count(const code& ec, @@ -105,7 +105,8 @@ class BCS_API protocol_bitcoind_rpc rpc_interface::get_network_info) NOEXCEPT; bool handle_get_raw_transaction(const code& ec, rpc_interface::get_raw_transaction, const std::string& txid, - double verbose, const std::string& blockhash) NOEXCEPT; + const network::rpc::value_t& verbosity, + const std::string& blockhash) NOEXCEPT; bool handle_send_raw_transaction(const code& ec, rpc_interface::send_raw_transaction, const std::string& hexstring, double maxfeerate) NOEXCEPT; @@ -113,6 +114,8 @@ class BCS_API protocol_bitcoind_rpc /// Json context helpers (shared with rest, defined in *_json.cpp). static uint32_t median_time_past(const node::query& query, const database::header_link& link) NOEXCEPT; + static bool parse_verbosity(double& verbosity, + const network::rpc::value_t& value, double missing) NOEXCEPT; static void inject_block_context(boost::json::object& out, const node::query& query, const database::header_link& link, const system::chain::header& header) NOEXCEPT; diff --git a/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp b/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp index ec1256af..a4e33b7c 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp @@ -184,11 +184,18 @@ bool protocol_bitcoind_rpc::handle_get_best_block_hash(const code& ec, bool protocol_bitcoind_rpc::handle_get_block(const code& ec, rpc_interface::get_block, const std::string& blockhash, - double verbosity) NOEXCEPT + const network::rpc::value_t& verbosity_value) NOEXCEPT { if (stopped(ec)) return false; + double verbosity{}; + if (!parse_verbosity(verbosity, verbosity_value, 1.0)) + { + send_error(error::invalid_argument); + return true; + } + hash_digest hash{}; if (!decode_hash(hash, blockhash)) { @@ -568,11 +575,18 @@ bool protocol_bitcoind_rpc::handle_get_network_info(const code& ec, bool protocol_bitcoind_rpc::handle_get_raw_transaction(const code& ec, rpc_interface::get_raw_transaction, const std::string& txid, - double verbose, const std::string&) NOEXCEPT + const network::rpc::value_t& verbosity_value, const std::string&) NOEXCEPT { if (stopped(ec)) return false; + double verbose{}; + if (!parse_verbosity(verbose, verbosity_value, 0.0)) + { + send_error(error::invalid_argument); + return true; + } + // The blockhash hint is unused: libbitcoin archives all tx (global index). hash_digest hash{}; if (!decode_hash(hash, txid)) diff --git a/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp b/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp index df9085f1..aadafaf1 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include namespace libbitcoin { @@ -32,6 +34,27 @@ uint32_t protocol_bitcoind_rpc::median_time_past(const node::query& query, return query.get_context(ctx, link) ? ctx.median_time_past : 0_u32; } +// bitcoind accepts boolean or number for the getblock/getrawtransaction +// verbosity parameters (both were originally boolean and remain so in +// common clients), so a typed number subscription cannot match them. +bool protocol_bitcoind_rpc::parse_verbosity(double& verbosity, + const network::rpc::value_t& value, double missing) NOEXCEPT +{ + using namespace network::rpc; + const auto& inner = value.value(); + + if (std::holds_alternative(inner)) + verbosity = missing; + else if (std::holds_alternative(inner)) + verbosity = std::get(inner) ? 1.0 : 0.0; + else if (std::holds_alternative(inner)) + verbosity = std::get(inner); + else + return false; + + return true; +} + void protocol_bitcoind_rpc::inject_block_context(boost::json::object& out, const node::query& query, const database::header_link& link, const chain::header& header) NOEXCEPT diff --git a/test/protocols/bitcoind/bitcoind_json.cpp b/test/protocols/bitcoind/bitcoind_json.cpp index 7633f223..145ab605 100644 --- a/test/protocols/bitcoind/bitcoind_json.cpp +++ b/test/protocols/bitcoind/bitcoind_json.cpp @@ -32,6 +32,7 @@ struct json : server::protocol_bitcoind_rpc { using protocol_bitcoind_rpc::median_time_past; + using protocol_bitcoind_rpc::parse_verbosity; using protocol_bitcoind_rpc::inject_block_context; using protocol_bitcoind_rpc::inject_tx_context; using protocol_bitcoind_rpc::header_to_bitcoind; @@ -62,6 +63,50 @@ BOOST_AUTO_TEST_CASE(bitcoind_json__header_to_bitcoind__block1_header__maps_fiel BOOST_AUTO_TEST_SUITE_END() +// parse_verbosity +// ---------------------------------------------------------------------------- + +BOOST_AUTO_TEST_SUITE(bitcoind_parse_verbosity_tests) + +using rpc_value_t = network::rpc::value_t; + +BOOST_AUTO_TEST_CASE(bitcoind_json__parse_verbosity__missing__defaulted) +{ + double verbosity{}; + BOOST_REQUIRE(json::parse_verbosity(verbosity, rpc_value_t{}, 42.0)); + BOOST_REQUIRE_EQUAL(verbosity, 42.0); +} + +BOOST_AUTO_TEST_CASE(bitcoind_json__parse_verbosity__boolean_true__one) +{ + double verbosity{}; + BOOST_REQUIRE(json::parse_verbosity(verbosity, rpc_value_t{ true }, 0.0)); + BOOST_REQUIRE_EQUAL(verbosity, 1.0); +} + +BOOST_AUTO_TEST_CASE(bitcoind_json__parse_verbosity__boolean_false__zero) +{ + double verbosity{}; + BOOST_REQUIRE(json::parse_verbosity(verbosity, rpc_value_t{ false }, 1.0)); + BOOST_REQUIRE_EQUAL(verbosity, 0.0); +} + +BOOST_AUTO_TEST_CASE(bitcoind_json__parse_verbosity__number__passed_through) +{ + double verbosity{}; + BOOST_REQUIRE(json::parse_verbosity(verbosity, rpc_value_t{ 2.0 }, 0.0)); + BOOST_REQUIRE_EQUAL(verbosity, 2.0); +} + +BOOST_AUTO_TEST_CASE(bitcoind_json__parse_verbosity__string__false) +{ + double verbosity{}; + const network::rpc::string_t value{ "1" }; + BOOST_REQUIRE(!json::parse_verbosity(verbosity, rpc_value_t{ value }, 0.0)); +} + +BOOST_AUTO_TEST_SUITE_END() + // ---------------------------------------------------------------------------- struct bitcoind_json_setup_fixture