diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am index 37eff919..efc84f3e 100644 --- a/builds/gnu/Makefile.am +++ b/builds/gnu/Makefile.am @@ -54,6 +54,7 @@ src_libbitcoin_server_la_SOURCES = \ ${srcdir}/../../src/settings.cpp \ ${srcdir}/../../src/parsers/admin_query.cpp \ ${srcdir}/../../src/parsers/admin_target.cpp \ + ${srcdir}/../../src/parsers/bitcoind_script.cpp \ ${srcdir}/../../src/parsers/bitcoind_target.cpp \ ${srcdir}/../../src/parsers/btcd_filter.cpp \ ${srcdir}/../../src/parsers/electrum_version.cpp \ @@ -145,6 +146,7 @@ include_bitcoin_server_parsersdir = \ include_bitcoin_server_parsers_HEADERS = \ ${srcdir}/../../include/bitcoin/server/parsers/admin_query.hpp \ ${srcdir}/../../include/bitcoin/server/parsers/admin_target.hpp \ + ${srcdir}/../../include/bitcoin/server/parsers/bitcoind_script.hpp \ ${srcdir}/../../include/bitcoin/server/parsers/bitcoind_target.hpp \ ${srcdir}/../../include/bitcoin/server/parsers/btcd_filter.hpp \ ${srcdir}/../../include/bitcoin/server/parsers/electrum_version.hpp \ @@ -210,6 +212,7 @@ test_libbitcoin_server_test_SOURCES = \ ${srcdir}/../../test/main.cpp \ ${srcdir}/../../test/settings.cpp \ ${srcdir}/../../test/test.cpp \ + ${srcdir}/../../test/interfaces/bitcoind_rpc.cpp \ ${srcdir}/../../test/interfaces/btcd.cpp \ ${srcdir}/../../test/mocks/blocks.cpp \ ${srcdir}/../../test/parsers/admin_query.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 6c88926e..3ec778e6 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj @@ -120,6 +120,9 @@ + + $(IntDir)test_interfaces_bitcoind_rpc.obj + @@ -135,7 +138,9 @@ - + + $(IntDir)test_protocols_bitcoind_bitcoind_rpc.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 d60a9e5c..f5ffe0a9 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 @@ -45,6 +45,9 @@ src + + src\interfaces + src\interfaces diff --git a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj index 44738f13..44543583 100644 --- a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj @@ -126,6 +126,7 @@ + @@ -188,6 +189,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters index d98c5445..2a8c9dcd 100644 --- a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters @@ -84,6 +84,9 @@ src\parsers + + src\parsers + src\parsers @@ -266,6 +269,9 @@ include\bitcoin\server\parsers + + include\bitcoin\server\parsers + include\bitcoin\server\parsers diff --git a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj index 68c1ede7..079fab9b 100644 --- a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj +++ b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj @@ -120,6 +120,9 @@ + + $(IntDir)test_interfaces_bitcoind_rpc.obj + @@ -135,7 +138,9 @@ - + + $(IntDir)test_protocols_bitcoind_bitcoind_rpc.obj + diff --git a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters index d60a9e5c..f5ffe0a9 100644 --- a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters +++ b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters @@ -45,6 +45,9 @@ src + + src\interfaces + src\interfaces diff --git a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj index 318a680f..fc067151 100644 --- a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj +++ b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj @@ -126,6 +126,7 @@ + @@ -188,6 +189,7 @@ + diff --git a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters index d98c5445..2a8c9dcd 100644 --- a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters +++ b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters @@ -84,6 +84,9 @@ src\parsers + + src\parsers + src\parsers @@ -266,6 +269,9 @@ include\bitcoin\server\parsers + + include\bitcoin\server\parsers + include\bitcoin\server\parsers diff --git a/include/bitcoin/server.hpp b/include/bitcoin/server.hpp index 44e38fc4..3f5a920a 100644 --- a/include/bitcoin/server.hpp +++ b/include/bitcoin/server.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/server/interfaces/bitcoind_rpc.hpp b/include/bitcoin/server/interfaces/bitcoind_rpc.hpp index cd106210..d5cdb787 100644 --- a/include/bitcoin/server/interfaces/bitcoind_rpc.hpp +++ b/include/bitcoin/server/interfaces/bitcoind_rpc.hpp @@ -38,21 +38,21 @@ struct bitcoind_rpc_methods method<"getblockfilter", string_t, optional<"basic"_t>>{ "blockhash", "filtertype" }, method<"getblockhash", number_t>{ "height" }, method<"getblockheader", string_t, optional>{ "blockhash", "verbose" }, - method<"getblockstats", value_t, optional>{ "hash_or_height", "stats" }, - method<"getchaintxstats", optional<-1.0>, optional<""_t>>{ "nblocks", "blockhash" }, - method<"getchainwork">{}, + method<"getblockstats", value_t, optional>{ unimplemented, "hash_or_height", "stats" }, + method<"getchaintxstats", optional<-1.0>, optional<""_t>>{ unimplemented, "nblocks", "blockhash" }, + method<"getchainwork">{ unimplemented }, method<"gettxout", string_t, number_t, optional>{ "txid", "n", "include_mempool" }, - method<"gettxoutsetinfo">{}, - method<"pruneblockchain", number_t>{ "height" }, - method<"savemempool">{}, - method<"scantxoutset", string_t, optional>{ "action", "scanobjects" }, - method<"verifychain", optional<4.0>, optional<288.0>>{ "checklevel", "nblocks" }, - method<"verifytxoutset", string_t>{ "input_verify_flag" }, + method<"gettxoutsetinfo">{ unimplemented }, + method<"pruneblockchain", number_t>{ unimplemented, "height" }, + method<"savemempool">{ unimplemented }, + method<"scantxoutset", string_t, optional>{ unimplemented, "action", "scanobjects" }, + method<"verifychain", optional<4.0>, optional<288.0>>{ unimplemented, "checklevel", "nblocks" }, + method<"verifytxoutset", string_t>{ unimplemented, "input_verify_flag" }, /////// Control methods. ////method<"getmemoryinfo", optional<"stats"_t>>{ "mode" }, ////method<"getrpcinfo">{}, - ////method<"help", optional<""_t>>{ "command" }, + method<"help", optional<""_t>>{ "command" }, ////method<"logging", optional<"*"_t>>{ "include" }, ////method<"stop">{}, ////method<"uptime">{}, @@ -60,7 +60,7 @@ struct bitcoind_rpc_methods /////// Mining methods. ////method<"getblocktemplate", optional>{ "template_request" }, ////method<"getmininginfo">{}, - ////method<"getnetworkhashps", optional<120_u32>, optional<-1_i32>>{ "nblocks", "height" }, + method<"getnetworkhashps", optional<120_u32>, optional<-1_i32>>{ "nblocks", "height" }, ////method<"prioritisetransaction", string_t, number_t, number_t>{ "txid", "dummy", "priority_delta" }, ////method<"submitblock", string_t, optional<""_t>>{ "block", "parameters" }, @@ -73,8 +73,14 @@ struct bitcoind_rpc_methods method<"getnetworkinfo">{}, /// Rawtransactions methods (implemented). + method<"createrawtransaction", array_t, object_t, optional<0_u32>, optional>{ "inputs", "outputs", "locktime", "replaceable" }, + method<"decoderawtransaction", string_t>{ "hexstring" }, method<"getrawtransaction", string_t, optional<0.0>, optional<""_t>>{ "txid", "verbosity", "blockhash" }, - method<"sendrawtransaction", string_t, optional<0.0>>{ "hexstring", "maxfeerate" } + method<"sendrawtransaction", string_t, optional<0.0>>{ "hexstring", "maxfeerate" }, + + /// Util methods (implemented). + method<"decodescript", string_t>{ "hex" }, + method<"validateaddress", string_t>{ "address" } ////method<"getpeerinfo">{}, ////method<"listbanned">{}, ////method<"ping">{}, @@ -83,8 +89,6 @@ struct bitcoind_rpc_methods /////// Rawtransactions methods. ////method<"combinerawtransaction", array_t>{ "txs" }, - ////method<"createrawtransaction", array_t, object_t, optional<0_u32>, optional>{ "inputs", "outputs", "locktime", "replaceable" }, - ////method<"decoderawtransaction", string_t>{ "hexstring" }, ////method<"fundrawtransaction", string_t, optional>{ "rawtx", "options" }, ////method<"getrawtransaction", string_t, optional<0_u32>, optional<""_t>>{ "txid", "verbose", "blockhash" }, ////method<"sendrawtransaction", string_t, optional<0_u32>>{ "hexstring", "maxfeerate" }, @@ -95,10 +99,8 @@ struct bitcoind_rpc_methods /////// Util methods (node-related). ////method<"createmultisig", number_t, array_t>{ "nrequired", "keys" }, ////method<"decodepsbt", string_t>{ "psbt" }, - ////method<"decodescript", string_t>{ "hex" }, ////method<"estimaterawfee", number_t, optional<"unset"_t>>{ "conf_target", "estimate_mode" }, ////method<"getdescriptorinfo", string_t>{ "descriptor" }, - ////method<"validateaddress", string_t>{ "address" }, /////// Wallet methods (unsupported). ////method<"abandontransaction", string_t>{ "txid" }, @@ -153,6 +155,11 @@ struct bitcoind_rpc_methods template using subscriber = network::unsubscriber; + /// Method names as reported by help. + static constexpr auto name_data = method_names(); + static constexpr std::string_view names{ name_data.data(), + name_data.size() }; + template using at = method_at; @@ -177,13 +184,11 @@ struct bitcoind_rpc_methods ////using get_memory_info = at<17>; ////using get_rpc_info = at<18>; - ////using help = at<19>; ////using logging = at<20>; ////using stop = at<21>; ////using uptime = at<22>; ////using get_block_template = at<23>; ////using get_mining_info = at<24>; - ////using get_network_hash_ps = at<25>; ////using prioritise_transaction = at<26>; ////using submit_block = at<27>; ////using add_node = at<28>; @@ -191,17 +196,21 @@ struct bitcoind_rpc_methods ////using disconnect_node = at<30>; ////using get_added_node_info = at<31>; ////using get_connection_count = at<32>; - using get_network_info = at<17>; - using get_raw_transaction = at<18>; - using send_raw_transaction = at<19>; + using help = at<17>; + using get_network_hash_ps = at<18>; + using get_network_info = at<19>; + using create_raw_transaction = at<20>; + using decode_raw_transaction = at<21>; + using get_raw_transaction = at<22>; + using send_raw_transaction = at<23>; + using decode_script = at<24>; + using validate_address = at<25>; ////using get_peer_info = at<34>; ////using list_banned = at<35>; ////using ping = at<36>; ////using set_ban = at<37>; ////using set_network_active = at<38>; ////using combine_raw_transaction = at<39>; - ////using create_raw_transaction = at<40>; - ////using decode_raw_transaction = at<41>; ////using fund_raw_transaction = at<42>; ////using get_raw_transaction = at<43>; ////using send_raw_transaction = at<44>; @@ -210,10 +219,8 @@ struct bitcoind_rpc_methods ////using test_raw_transaction = at<47>; ////using create_multisig = at<48>; ////using decode_psbt = at<49>; - ////using decode_script = at<50>; ////using estimate_raw_fee = at<51>; ////using get_descriptor_info = at<52>; - ////using validate_address = at<53>; ////using abandon_transaction = at<54>; ////using add_multisig_address = at<55>; ////using backup_wallet = at<56>; diff --git a/include/bitcoin/server/interfaces/btcd.hpp b/include/bitcoin/server/interfaces/btcd.hpp index 6bac48f2..c7ca6256 100644 --- a/include/bitcoin/server/interfaces/btcd.hpp +++ b/include/bitcoin/server/interfaces/btcd.hpp @@ -33,7 +33,6 @@ struct btcd_methods { /// Administrative. method<"authenticate", string_t, string_t>{ "username", "password" }, - method<"help", optional<""_t>>{ "command" }, method<"session">{}, method<"stop">{ unimplemented }, @@ -43,13 +42,6 @@ struct btcd_methods method<"getdifficulty">{}, method<"getinfo">{}, method<"getnettotals">{}, - method<"getnetworkhashps", optional<120_u32>, optional<-1_i32>>{ "blocks", "height" }, - - /// Tools. - method<"createrawtransaction", array_t, object_t, optional<0_u32>>{ "inputs", "outputs", "locktime" }, - method<"decoderawtransaction", string_t>{ "hexstring" }, - method<"decodescript", string_t>{ "hex" }, - method<"validateaddress", string_t>{ "address" }, /// Subscription. method<"notifyblocks">{}, @@ -82,35 +74,28 @@ struct btcd_methods // Derive this from above in c++26 using reflection. using authenticate = at<0>; - using help = at<1>; - using session = at<2>; - using stop = at<3>; - - using get_best_block = at<4>; - using get_current_net = at<5>; - using get_difficulty = at<6>; - using get_info = at<7>; - using get_net_totals = at<8>; - using get_network_hash_ps = at<9>; - - using create_raw_transaction = at<10>; - using decode_raw_transaction = at<11>; - using decode_script = at<12>; - using validate_address = at<13>; - - using notify_blocks = at<14>; - using stop_notify_blocks = at<15>; - using notify_new_transactions = at<16>; - using stop_notify_new_transactions = at<17>; - - using load_tx_filter = at<18>; - using rescan_blocks = at<19>; - - using notify_received = at<20>; - using stop_notify_received = at<21>; - using notify_spent = at<22>; - using stop_notify_spent = at<23>; - using rescan = at<24>; + using session = at<1>; + using stop = at<2>; + + using get_best_block = at<3>; + using get_current_net = at<4>; + using get_difficulty = at<5>; + using get_info = at<6>; + using get_net_totals = at<7>; + + using notify_blocks = at<8>; + using stop_notify_blocks = at<9>; + using notify_new_transactions = at<10>; + using stop_notify_new_transactions = at<11>; + + using load_tx_filter = at<12>; + using rescan_blocks = at<13>; + + using notify_received = at<14>; + using stop_notify_received = at<15>; + using notify_spent = at<16>; + using stop_notify_spent = at<17>; + using rescan = at<18>; }; } // namespace interface diff --git a/include/bitcoin/server/parsers/bitcoind_script.hpp b/include/bitcoin/server/parsers/bitcoind_script.hpp new file mode 100644 index 00000000..85e854ce --- /dev/null +++ b/include/bitcoin/server/parsers/bitcoind_script.hpp @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_SERVER_PARSERS_BITCOIND_SCRIPT_HPP +#define LIBBITCOIN_SERVER_PARSERS_BITCOIND_SCRIPT_HPP + +#include + +namespace libbitcoin { +namespace server { + +// TODO: move into bitcoind namespace (collides with json wrapper). + +/// Parse an address to its output script, prefixes are validated. +BCS_API code output_script(system::chain::script& out, + const std::string& text, uint8_t p2kh, uint8_t p2sh, + const std::string& witness) NOEXCEPT; + +} // namespace server +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/server/parsers/btcd_filter.hpp b/include/bitcoin/server/parsers/btcd_filter.hpp index 35cb115f..5af5f69e 100644 --- a/include/bitcoin/server/parsers/btcd_filter.hpp +++ b/include/bitcoin/server/parsers/btcd_filter.hpp @@ -25,11 +25,6 @@ namespace libbitcoin { namespace server { namespace btcd { -/// Parse an address to its output script, prefixes are validated. -BCS_API code output_script(system::chain::script& out, - const std::string& text, uint8_t p2kh, uint8_t p2sh, - const std::string& witness) NOEXCEPT; - /// Parse loadtxfilter addresses to their output script hashes. BCS_API code filter_keys(system::hashes& out, const network::rpc::value_t& addresses, uint8_t p2kh, uint8_t p2sh, diff --git a/include/bitcoin/server/parsers/parsers.hpp b/include/bitcoin/server/parsers/parsers.hpp index 40ee10ae..40ccfea2 100644 --- a/include/bitcoin/server/parsers/parsers.hpp +++ b/include/bitcoin/server/parsers/parsers.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp index da3eb2a2..e23c92c0 100644 --- a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp +++ b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp @@ -44,7 +44,10 @@ class BCS_API protocol_bitcoind_rpc const network::channel::ptr& channel, const options_t& options) NOEXCEPT : server::protocol_http(session, channel, options), - network::tracker(session->log) + network::tracker(session->log), + p2kh_(session->server_settings().wallet.p2kh_prefix), + p2sh_(session->server_settings().wallet.p2sh_prefix), + witness_(session->server_settings().wallet.witness_prefix) { } @@ -103,6 +106,11 @@ class BCS_API protocol_bitcoind_rpc rpc_interface::verify_chain, double, double) NOEXCEPT; bool handle_verify_tx_out_set(const code& ec, rpc_interface::verify_tx_out_set, const std::string&) NOEXCEPT; + bool handle_help(const code& ec, rpc_interface::help, + const std::string& command) NOEXCEPT; + bool handle_get_network_hash_ps(const code& ec, + rpc_interface::get_network_hash_ps, uint32_t nblocks, + int32_t height) NOEXCEPT; bool handle_get_network_info(const code& ec, rpc_interface::get_network_info) NOEXCEPT; bool handle_get_raw_transaction(const code& ec, @@ -111,6 +119,22 @@ class BCS_API protocol_bitcoind_rpc bool handle_send_raw_transaction(const code& ec, rpc_interface::send_raw_transaction, const std::string& hexstring, double maxfeerate) NOEXCEPT; + bool handle_create_raw_transaction(const code& ec, + rpc_interface::create_raw_transaction, + const network::rpc::array_t& inputs, + const network::rpc::object_t& outputs, uint32_t locktime, + bool replaceable) NOEXCEPT; + bool handle_decode_raw_transaction(const code& ec, + rpc_interface::decode_raw_transaction, + const std::string& hexstring) NOEXCEPT; + bool handle_decode_script(const code& ec, + rpc_interface::decode_script, const std::string& hex) NOEXCEPT; + bool handle_validate_address(const code& ec, + rpc_interface::validate_address, + const std::string& address) NOEXCEPT; + + /// The method names reported by help (btcd prepends its own). + virtual std::string help_names() const NOEXCEPT; /// Serialize an object (chain::header, chain::transaction, ...) to a /// base16 string. @@ -182,6 +206,12 @@ class BCS_API protocol_bitcoind_rpc rpc_dispatcher rpc_dispatcher_{}; network::rpc::version version_{}; network::rpc::id_option id_{}; + +protected: + // These are thread safe. + const uint8_t p2kh_; + const uint8_t p2sh_; + const std::string witness_; }; } // namespace server diff --git a/include/bitcoin/server/protocols/protocol_btcd.hpp b/include/bitcoin/server/protocols/protocol_btcd.hpp index b47c705b..0005cf18 100644 --- a/include/bitcoin/server/protocols/protocol_btcd.hpp +++ b/include/bitcoin/server/protocols/protocol_btcd.hpp @@ -51,9 +51,6 @@ class BCS_API protocol_btcd network::tracker(session->log), options_(options), turbo_(session->database_settings().turbo), - p2kh_(session->server_settings().wallet.p2kh_prefix), - p2sh_(session->server_settings().wallet.p2sh_prefix), - witness_(session->server_settings().wallet.witness_prefix), notification_strand_(channel->service().get_executor()) { } @@ -71,8 +68,6 @@ class BCS_API protocol_btcd /// Handlers (administrative). bool handle_authenticate(const code& ec, btcd_interface::authenticate, const std::string& username, const std::string& password) NOEXCEPT; - bool handle_help(const code& ec, btcd_interface::help, - const std::string& command) NOEXCEPT; bool handle_session(const code& ec, btcd_interface::session) NOEXCEPT; bool handle_stop(const code& ec, btcd_interface::stop) NOEXCEPT; @@ -86,23 +81,6 @@ class BCS_API protocol_btcd bool handle_get_info(const code& ec, btcd_interface::get_info) NOEXCEPT; bool handle_get_net_totals(const code& ec, btcd_interface::get_net_totals) NOEXCEPT; - bool handle_get_network_hash_ps(const code& ec, - btcd_interface::get_network_hash_ps, uint32_t blocks, - int32_t height) NOEXCEPT; - - /// Handlers (tools). - bool handle_create_raw_transaction(const code& ec, - btcd_interface::create_raw_transaction, - const network::rpc::array_t& inputs, - const network::rpc::object_t& outputs, uint32_t locktime) NOEXCEPT; - bool handle_decode_raw_transaction(const code& ec, - btcd_interface::decode_raw_transaction, - const std::string& hexstring) NOEXCEPT; - bool handle_decode_script(const code& ec, - btcd_interface::decode_script, const std::string& hex) NOEXCEPT; - bool handle_validate_address(const code& ec, - btcd_interface::validate_address, - const std::string& address) NOEXCEPT; /// Handlers (subscription). bool handle_notify_blocks(const code& ec, @@ -145,6 +123,9 @@ class BCS_API protocol_btcd bool handle_chase(const code& ec, node::chase event_, node::event_value value) NOEXCEPT; + /// The btcd method names, prepended to the base names. + std::string help_names() const NOEXCEPT override; + /// Sender (server push, no id). void send_notification(const std::string& method, network::rpc::array_t&& params, size_t size_hint) NOEXCEPT; @@ -223,9 +204,6 @@ class BCS_API protocol_btcd // These are thread safe. const options_t& options_; const bool turbo_; - const uint8_t p2kh_; - const uint8_t p2sh_; - const std::string witness_; std::atomic_bool stopping_{}; std::atomic_bool subscribed_blocks_{}; diff --git a/src/parsers/bitcoind_script.cpp b/src/parsers/bitcoind_script.cpp new file mode 100644 index 00000000..16a24453 --- /dev/null +++ b/src/parsers/bitcoind_script.cpp @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +#include + +namespace libbitcoin { +namespace server { + +using namespace system; +using namespace system::chain; + +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +code output_script(script& out, const std::string& text, uint8_t p2kh, + uint8_t p2sh, const std::string& witness) NOEXCEPT +{ + using namespace wallet; + + if (const payment_address payment{ text }; payment) + { + out = payment.output_script(p2kh, p2sh); + return error::success; + } + + // The parse accepts any prefix, so the configured one is a check. + if (const witness_address payment{ text }; + payment && payment.prefix() == witness) + { + out = payment.script(); + return error::success; + } + + return error::invalid_argument; +} + +BC_POP_WARNING() + +} // namespace server +} // namespace libbitcoin diff --git a/src/parsers/btcd_filter.cpp b/src/parsers/btcd_filter.cpp index 0c6040ce..ebfe4a11 100644 --- a/src/parsers/btcd_filter.cpp +++ b/src/parsers/btcd_filter.cpp @@ -20,6 +20,7 @@ #include #include +#include namespace libbitcoin { namespace server { @@ -31,27 +32,6 @@ using namespace network::rpc; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -code output_script(script& out, const std::string& text, uint8_t p2kh, - uint8_t p2sh, const std::string& witness) NOEXCEPT -{ - using namespace wallet; - - if (const payment_address payment{ text }; payment) - { - out = payment.output_script(p2kh, p2sh); - return error::success; - } - - if (const witness_address payment{ text }; - payment && payment.prefix() == witness) - { - out = payment.script(); - return error::success; - } - - return error::invalid_argument; -} - code filter_keys(hashes& out, const value_t& addresses, uint8_t p2kh, uint8_t p2sh, const std::string& witness) NOEXCEPT { diff --git a/src/protocols/bitcoind/protocol_bitcoind_rest.cpp b/src/protocols/bitcoind/protocol_bitcoind_rest.cpp index 73b8e893..8386915f 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_rest.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_rest.cpp @@ -449,8 +449,10 @@ bool protocol_bitcoind_rest::handle_get_block_spent_tx_outputs(const code& ec, return true; } case json: + { send_json(value_from(bitcoind(spent)), two * size); return true; + } } send_not_found(); diff --git a/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp b/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp index c505f987..44f35497 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace libbitcoin { namespace server { @@ -69,9 +70,15 @@ void protocol_bitcoind_rpc::start() NOEXCEPT SUBSCRIBE_BITCOIND(handle_verify_chain, _1, _2, _3, _4); SUBSCRIBE_BITCOIND(handle_verify_tx_out_set, _1, _2, _3); + SUBSCRIBE_BITCOIND(handle_help, _1, _2, _3); + SUBSCRIBE_BITCOIND(handle_get_network_hash_ps, _1, _2, _3, _4); SUBSCRIBE_BITCOIND(handle_get_network_info, _1, _2); + SUBSCRIBE_BITCOIND(handle_create_raw_transaction, _1, _2, _3, _4, _5, _6); + SUBSCRIBE_BITCOIND(handle_decode_raw_transaction, _1, _2, _3); SUBSCRIBE_BITCOIND(handle_get_raw_transaction, _1, _2, _3, _4, _5); SUBSCRIBE_BITCOIND(handle_send_raw_transaction, _1, _2, _3, _4); + SUBSCRIBE_BITCOIND(handle_decode_script, _1, _2, _3); + SUBSCRIBE_BITCOIND(handle_validate_address, _1, _2, _3); network::protocol_http::start(); } @@ -734,6 +741,272 @@ bool protocol_bitcoind_rpc::handle_send_raw_transaction(const code& ec, return true; } +// Lists method names only, no per-command argument usage text. +std::string protocol_bitcoind_rpc::help_names() const NOEXCEPT +{ + return std::string{ rpc_interface::names }; +} + +bool protocol_bitcoind_rpc::handle_help(const code& ec, rpc_interface::help, + const std::string&) NOEXCEPT +{ + if (stopped(ec)) + return false; + + auto names = help_names(); + const auto size = two * names.size(); + send_result(std::move(names), size); + return true; +} + +static std::string to_script_type(chain::script_pattern pattern) NOEXCEPT +{ + using namespace chain; + switch (pattern) + { + case script_pattern::pay_key_hash: + return "pubkeyhash"; + case script_pattern::pay_script_hash: + return "scripthash"; + case script_pattern::pay_multisig: + return "multisig"; + case script_pattern::pay_public_key: + return "pubkey"; + case script_pattern::pay_null_data: + return "nulldata"; + case script_pattern::pay_witness_key_hash: + return "witness_v0_keyhash"; + case script_pattern::pay_witness_script_hash: + return "witness_v0_scripthash"; + case script_pattern::pay_witness_v1_taproot: + return "witness_v1_taproot"; + default: + return "nonstandard"; + } +} + +bool protocol_bitcoind_rpc::handle_get_network_hash_ps(const code& ec, + rpc_interface::get_network_hash_ps, uint32_t, int32_t height) NOEXCEPT +{ + if (stopped(ec)) + return false; + + const auto& query = archive(); + const auto top = query.get_top_confirmed(); + const auto target = is_negative(height) ? top : + std::min(sign_cast(height), top); + + const auto header = query.get_header(query.to_confirmed(target)); + if (!header) + { + send_error(database::error::integrity); + return true; + } + + const auto period = system_settings().block_spacing_seconds; + const auto span = to_floating(power2(32u)); + send_result(header->difficulty() * span / period, 20); + return true; +} + +bool protocol_bitcoind_rpc::handle_create_raw_transaction(const code& ec, + rpc_interface::create_raw_transaction, const array_t& inputs, + const object_t& outputs, uint32_t locktime, bool replaceable) NOEXCEPT +{ + if (stopped(ec)) + return false; + + using namespace chain; + uint32_t vout{}; + hash_digest hash{}; + + // bip125 signals replace-by-fee via a sequence below 0xfffffffe. + constexpr auto bip125_sequence = 0xfffffffd_u32; + const auto sequence = replaceable ? bip125_sequence : max_input_sequence; + + // The transaction owns shared inputs/outputs, so these are populated + // directly, avoiding the intermediate vectors that to_shareds() copies. + const auto ins = std::make_shared(); + ins->reserve(inputs.size()); + + for (const auto& item: inputs) + { + if (!std::holds_alternative(item.value())) + { + send_error(error::invalid_argument); + return true; + } + + const auto& fields = std::get(item.value()); + const auto txid_it = fields.find("txid"); + const auto vout_it = fields.find("vout"); + if (txid_it == fields.end() || vout_it == fields.end() || + !std::holds_alternative(txid_it->second.value()) || + !std::holds_alternative(vout_it->second.value())) + { + send_error(error::invalid_argument); + return true; + } + + if (!decode_hash(hash, std::get(txid_it->second.value())) || + !to_integer(vout, std::get(vout_it->second.value()))) + { + send_error(error::invalid_argument); + return true; + } + + ins->push_back(to_shared(point{ hash, vout }, script{}, + sequence)); + } + + script script{}; + uint64_t satoshi{}; + const auto outs = std::make_shared(); + outs->reserve(outputs.size()); + for (const auto& pair: outputs) + { + if (!std::holds_alternative(pair.second.value())) + { + send_error(error::invalid_argument); + return true; + } + + if (const auto fault = output_script(script, pair.first, p2kh_, + p2sh_, witness_)) + { + send_error(fault); + return true; + } + + const auto btc = std::get(pair.second.value()); + if (!to_integer(satoshi, btc * satoshi_per_bitcoin, false)) + { + send_error(error::invalid_argument); + return true; + } + + outs->push_back(to_shared(satoshi, std::move(script))); + } + + constexpr auto witness = false; + const transaction tx{ 1, ins, outs, locktime }; + send_result(to_text(tx, tx.serialized_size(witness), witness), 400); + return true; +} + +bool protocol_bitcoind_rpc::handle_decode_raw_transaction(const code& ec, + rpc_interface::decode_raw_transaction, + const std::string& hexstring) NOEXCEPT +{ + if (stopped(ec)) + return false; + + data_chunk data{}; + if (!decode_base16(data, hexstring)) + { + send_error(error::invalid_argument); + return true; + } + + constexpr auto witness = true; + const chain::transaction tx{ data, witness }; + if (!tx.is_valid()) + { + send_error(error::invalid_argument); + return true; + } + + send_result(value_from(bitcoind(tx)), two * tx.serialized_size(witness)); + return true; +} + +bool protocol_bitcoind_rpc::handle_decode_script(const code& ec, + rpc_interface::decode_script, const std::string& hex) NOEXCEPT +{ + if (stopped(ec)) + return false; + + data_chunk data{}; + if (!decode_base16(data, hex)) + { + send_error(error::invalid_argument); + return true; + } + + using namespace chain; + constexpr auto prefix = false; + const script script{ data, prefix }; + if (!script.is_valid()) + { + send_error(error::invalid_argument); + return true; + } + + using namespace wallet; + const auto pattern = script.output_pattern(); + + object_t result{}; + result.emplace("asm", script.to_string(flags::all_rules, true)); + result.emplace("type", to_script_type(pattern)); + + if (pattern == script_pattern::pay_key_hash || + pattern == script_pattern::pay_script_hash) + { + const auto pay = payment_address::extract_output(script, p2kh_, p2sh_); + if (pay) + result.emplace("address", pay.encoded()); + } + + const payment_address pay{ script, p2sh_ }; + if (pay) + result.emplace("p2sh", pay.encoded()); + + send_result(std::move(result), 256); + return true; +} + +bool protocol_bitcoind_rpc::handle_validate_address(const code& ec, + rpc_interface::validate_address, const std::string& address) NOEXCEPT +{ + if (stopped(ec)) + return false; + + using namespace wallet; + const payment_address base58(address); + if (base58) + { + send_result(object_t + { + { "isvalid", true }, + { "address", base58.encoded() }, + { "isscript", base58.prefix() == p2sh_ }, + { "iswitness", false } + }, 128); + return true; + } + + const witness_address witness(address); + if (witness && witness.prefix() == witness_) + { + const auto version0_p2sh = (witness.identifier() == + witness_address::program_type::version0_p2sh); + + send_result(object_t + { + { "isvalid", true }, + { "address", witness.encoded() }, + { "isscript", version0_p2sh }, + { "iswitness", true }, + { "witness_version", witness.version() }, + { "witness_program", encode_base16(witness.program()) } + }, 128); + return true; + } + + send_result(object_t{ { "isvalid", false } }, 32); + return true; +} + // Senders. // ---------------------------------------------------------------------------- diff --git a/src/protocols/btcd/protocol_btcd.cpp b/src/protocols/btcd/protocol_btcd.cpp index d5739f7b..ba7e89c1 100644 --- a/src/protocols/btcd/protocol_btcd.cpp +++ b/src/protocols/btcd/protocol_btcd.cpp @@ -53,7 +53,6 @@ void protocol_btcd::start() NOEXCEPT // Administrative methods. SUBSCRIBE_BTCD(handle_authenticate, _1, _2, _3, _4); - SUBSCRIBE_BTCD(handle_help, _1, _2, _3); SUBSCRIBE_BTCD(handle_session, _1, _2); SUBSCRIBE_BTCD(handle_stop, _1, _2); @@ -63,13 +62,6 @@ void protocol_btcd::start() NOEXCEPT SUBSCRIBE_BTCD(handle_get_difficulty, _1, _2); SUBSCRIBE_BTCD(handle_get_info, _1, _2); SUBSCRIBE_BTCD(handle_get_net_totals, _1, _2); - SUBSCRIBE_BTCD(handle_get_network_hash_ps, _1, _2, _3, _4); - - // Tool methods. - SUBSCRIBE_BTCD(handle_create_raw_transaction, _1, _2, _3, _4, _5); - SUBSCRIBE_BTCD(handle_decode_raw_transaction, _1, _2, _3); - SUBSCRIBE_BTCD(handle_decode_script, _1, _2, _3); - SUBSCRIBE_BTCD(handle_validate_address, _1, _2, _3); // Subscription methods. SUBSCRIBE_BTCD(handle_notify_blocks, _1, _2); @@ -231,16 +223,11 @@ bool protocol_btcd::handle_authenticate(const code& ec, return true; } -bool protocol_btcd::handle_help(const code& ec, btcd_interface::help, - const std::string&) NOEXCEPT +// The btcd interface adds its own names to the implemented bitcoind names. +std::string protocol_btcd::help_names() const NOEXCEPT { - if (stopped(ec)) - return false; - - // Lists method names only, no per-command argument usage text. - const auto size = two * btcd_interface::names.size(); - send_result(std::string{ btcd_interface::names }, size); - return true; + return std::string{ btcd_interface::names } + ' ' + + protocol_bitcoind_rpc::help_names(); } bool protocol_btcd::handle_session(const code& ec, diff --git a/src/protocols/btcd/protocol_btcd_utility.cpp b/src/protocols/btcd/protocol_btcd_utility.cpp index db1a2a7e..79449da8 100644 --- a/src/protocols/btcd/protocol_btcd_utility.cpp +++ b/src/protocols/btcd/protocol_btcd_utility.cpp @@ -18,12 +18,8 @@ */ #include -#include -#include -#include #include #include -#include namespace libbitcoin { namespace server { @@ -33,41 +29,11 @@ namespace server { using namespace system; using namespace network; using namespace network::rpc; -using namespace boost::json; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) -// Utilities. -// ---------------------------------------------------------------------------- - -static std::string to_script_type(chain::script_pattern pattern) NOEXCEPT -{ - using namespace chain; - switch (pattern) - { - case script_pattern::pay_key_hash: - return "pubkeyhash"; - case script_pattern::pay_script_hash: - return "scripthash"; - case script_pattern::pay_multisig: - return "multisig"; - case script_pattern::pay_public_key: - return "pubkey"; - case script_pattern::pay_null_data: - return "nulldata"; - case script_pattern::pay_witness_key_hash: - return "witness_v0_keyhash"; - case script_pattern::pay_witness_script_hash: - return "witness_v0_scripthash"; - case script_pattern::pay_witness_v1_taproot: - return "witness_v1_taproot"; - default: - return "nonstandard"; - } -} - // Handlers (getters). // ---------------------------------------------------------------------------- @@ -168,227 +134,6 @@ bool protocol_btcd::handle_get_net_totals(const code& ec, return true; } -bool protocol_btcd::handle_get_network_hash_ps(const code& ec, - btcd_interface::get_network_hash_ps, uint32_t, int32_t height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto target = is_negative(height) ? top : - std::min(sign_cast(height), top); - - const auto header = query.get_header(query.to_confirmed(target)); - if (!header) - { - send_error(database::error::integrity); - return true; - } - - const auto period = system_settings().block_spacing_seconds; - const auto span = to_floating(power2(32u)); - send_result(header->difficulty() * span / period, 20); - return true; -} - -// Handlers (tools). -// ---------------------------------------------------------------------------- - -bool protocol_btcd::handle_create_raw_transaction(const code& ec, - btcd_interface::create_raw_transaction, const array_t& inputs, - const object_t& outputs, uint32_t locktime) NOEXCEPT -{ - if (stopped(ec)) - return false; - - using namespace chain; - uint32_t vout{}; - hash_digest hash{}; - - // The transaction owns shared inputs/outputs, so these are populated - // directly, avoiding the intermediate vectors that to_shareds() copies. - const auto ins = std::make_shared(); - ins->reserve(inputs.size()); - - for (const auto& item: inputs) - { - if (!std::holds_alternative(item.value())) - { - send_error(error::invalid_argument); - return true; - } - - const auto& fields = std::get(item.value()); - const auto txid_it = fields.find("txid"); - const auto vout_it = fields.find("vout"); - if (txid_it == fields.end() || vout_it == fields.end() || - !std::holds_alternative(txid_it->second.value()) || - !std::holds_alternative(vout_it->second.value())) - { - send_error(error::invalid_argument); - return true; - } - - if (!decode_hash(hash, std::get(txid_it->second.value())) || - !to_integer(vout, std::get(vout_it->second.value()))) - { - send_error(error::invalid_argument); - return true; - } - - ins->push_back(to_shared(point{ hash, vout }, script{}, - max_input_sequence)); - } - - script script{}; - uint64_t satoshi{}; - const auto outs = std::make_shared(); - outs->reserve(outputs.size()); - for (const auto& pair: outputs) - { - if (!std::holds_alternative(pair.second.value())) - { - send_error(error::invalid_argument); - return true; - } - - if (const auto fault = btcd::output_script(script, pair.first, p2kh_, - p2sh_, witness_)) - { - send_error(fault); - return true; - } - - const auto btc = std::get(pair.second.value()); - if (!to_integer(satoshi, btc * satoshi_per_bitcoin, false)) - { - send_error(error::invalid_argument); - return true; - } - - outs->push_back(to_shared(satoshi, std::move(script))); - } - - constexpr auto witness = false; - const transaction tx{ 1, ins, outs, locktime }; - send_result(to_text(tx, tx.serialized_size(witness), witness), 400); - return true; -} - -bool protocol_btcd::handle_decode_raw_transaction(const code& ec, - btcd_interface::decode_raw_transaction, - const std::string& hexstring) NOEXCEPT -{ - if (stopped(ec)) - return false; - - data_chunk data{}; - if (!decode_base16(data, hexstring)) - { - send_error(error::invalid_argument); - return true; - } - - constexpr auto witness = true; - const chain::transaction tx{ data, witness }; - if (!tx.is_valid()) - { - send_error(error::invalid_argument); - return true; - } - - send_result(value_from(bitcoind(tx)), two * tx.serialized_size(witness)); - return true; -} - -bool protocol_btcd::handle_decode_script(const code& ec, - btcd_interface::decode_script, const std::string& hex) NOEXCEPT -{ - if (stopped(ec)) - return false; - - data_chunk data{}; - if (!decode_base16(data, hex)) - { - send_error(error::invalid_argument); - return true; - } - - using namespace chain; - constexpr auto prefix = false; - const script script{ data, prefix }; - if (!script.is_valid()) - { - send_error(error::invalid_argument); - return true; - } - - using namespace wallet; - const auto pattern = script.output_pattern(); - - object_t result{}; - result.emplace("asm", script.to_string(flags::all_rules, true)); - result.emplace("type", to_script_type(pattern)); - - if (pattern == script_pattern::pay_key_hash || - pattern == script_pattern::pay_script_hash) - { - const auto pay = payment_address::extract_output(script, p2kh_, p2sh_); - if (pay) - result.emplace("address", pay.encoded()); - } - - const payment_address pay{ script, p2sh_ }; - if (pay) - result.emplace("p2sh", pay.encoded()); - - send_result(std::move(result), 256); - return true; -} - -bool protocol_btcd::handle_validate_address(const code& ec, - btcd_interface::validate_address, const std::string& address) NOEXCEPT -{ - if (stopped(ec)) - return false; - - using namespace wallet; - const payment_address base58(address); - if (base58) - { - send_result(object_t - { - { "isvalid", true }, - { "address", base58.encoded() }, - { "isscript", base58.prefix() == p2sh_ }, - { "iswitness", false } - }, 128); - return true; - } - - const witness_address witness(address); - if (witness && witness.prefix() == witness_) - { - const auto version0_p2sh = (witness.identifier() == - witness_address::program_type::version0_p2sh); - - send_result(object_t - { - { "isvalid", true }, - { "address", witness.encoded() }, - { "isscript", version0_p2sh }, - { "iswitness", true }, - { "witness_version", witness.version() }, - { "witness_program", encode_base16(witness.program()) } - }, 128); - return true; - } - - send_result(object_t{ { "isvalid", false } }, 32); - return true; -} - BC_POP_WARNING() BC_POP_WARNING() BC_POP_WARNING() diff --git a/test/interfaces/bitcoind_rpc.cpp b/test/interfaces/bitcoind_rpc.cpp new file mode 100644 index 00000000..922725f7 --- /dev/null +++ b/test/interfaces/bitcoind_rpc.cpp @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * 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" + +using namespace interface; + +// setup +// ----------------------------------------------------------------------------- + +// Keyed by name, so that assertions are independent of interface order. A +// misspelled name matches neither, failing both served and unserved. +constexpr bool bitcoind_declared(const std::string_view& name, + bool implemented) NOEXCEPT +{ + auto result = false; + std::apply([&](const auto&... items) NOEXCEPT + { + ((result = result || (items.name == name && + items.implemented() == implemented)), ...); + }, bitcoind_rpc_methods::methods); + + return result; +} + +constexpr bool bitcoind_served(const std::string_view& name) NOEXCEPT +{ + return bitcoind_declared(name, true); +} + +constexpr bool bitcoind_unserved(const std::string_view& name) NOEXCEPT +{ + return bitcoind_declared(name, false); +} + +// bitcoind_rpc_methods::implemented +// ----------------------------------------------------------------------------- + +// These are dispatchable but answer not_implemented (see protocol). +static_assert(bitcoind_unserved("getblockstats")); +static_assert(bitcoind_unserved("getchaintxstats")); +static_assert(bitcoind_unserved("getchainwork")); +static_assert(bitcoind_unserved("gettxoutsetinfo")); +static_assert(bitcoind_unserved("pruneblockchain")); +static_assert(bitcoind_unserved("savemempool")); +static_assert(bitcoind_unserved("scantxoutset")); +static_assert(bitcoind_unserved("verifychain")); +static_assert(bitcoind_unserved("verifytxoutset")); + +// Moved from the btcd interface (btcd serves them by derivation). +static_assert(bitcoind_served("help")); +static_assert(bitcoind_served("getnetworkhashps")); +static_assert(bitcoind_served("createrawtransaction")); +static_assert(bitcoind_served("decoderawtransaction")); +static_assert(bitcoind_served("decodescript")); +static_assert(bitcoind_served("validateaddress")); + +// A name absent from the interface satisfies neither. +static_assert(!bitcoind_served("getcurrentnet") && !bitcoind_unserved("getcurrentnet")); + +// bitcoind_rpc_methods::names +// ----------------------------------------------------------------------------- + +// The published list is the served subset, in interface order. +static_assert(bitcoind_rpc_methods::names == + "getbestblockhash getblock getblockchaininfo getblockcount " + "getblockfilter getblockhash getblockheader gettxout " + "help getnetworkhashps getnetworkinfo " + "createrawtransaction decoderawtransaction getrawtransaction " + "sendrawtransaction " + "decodescript validateaddress"); diff --git a/test/interfaces/btcd.cpp b/test/interfaces/btcd.cpp index 55175478..d38cad91 100644 --- a/test/interfaces/btcd.cpp +++ b/test/interfaces/btcd.cpp @@ -71,10 +71,8 @@ static_assert(!btcd_served("getblockcount") && !btcd_unserved("getblockcount")); // The published list is the served subset, in interface order. static_assert(btcd_methods::names == - "authenticate help session " + "authenticate session " "getbestblock getcurrentnet getdifficulty getinfo getnettotals " - "getnetworkhashps " - "createrawtransaction decoderawtransaction decodescript validateaddress " "notifyblocks stopnotifyblocks " "loadtxfilter rescanblocks " "rescan"); diff --git a/test/protocols/bitcoind/bitcoind_rpc.cpp b/test/protocols/bitcoind/bitcoind_rpc.cpp index 88a0b969..a801086c 100644 --- a/test/protocols/bitcoind/bitcoind_rpc.cpp +++ b/test/protocols/bitcoind/bitcoind_rpc.cpp @@ -186,6 +186,58 @@ BOOST_AUTO_TEST_CASE(bitcoind_rpc__sendrawtransaction__malformed__error) BOOST_REQUIRE(has_error(response)); } +// control, mining, rawtransactions, util (moved from btcd) +// ---------------------------------------------------------------------------- + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__help__default__implemented_method_list) +{ + const auto response = rpc("help"); + REQUIRE_NO_THROW_TRUE(response.at("result").is_string()); + BOOST_REQUIRE_NE(as_text(response.at("result")).find("getblockcount"), std::string::npos); + BOOST_REQUIRE_EQUAL(as_text(response.at("result")).find("getblockstats"), std::string::npos); +} + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__getnetworkhashps__default__number) +{ + const auto response = rpc("getnetworkhashps"); + BOOST_REQUIRE(response.at("result").is_double() || response.at("result").is_int64()); +} + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__createrawtransaction__one_in_one_out__hex) +{ + const auto txid = encode_hash(test::block1.transactions_ptr()->front()->hash(false)); + const auto response = rpc("createrawtransaction", "[[{\"txid\":\"" + txid + "\",\"vout\":0}], {\"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\": 0.001}]"); + REQUIRE_NO_THROW_TRUE(response.at("result").is_string()); +} + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__decoderawtransaction__created__round_trips) +{ + const auto txid = encode_hash(test::block1.transactions_ptr()->front()->hash(false)); + const auto created = rpc("createrawtransaction", "[[{\"txid\":\"" + txid + "\",\"vout\":0}], {\"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\": 0.001}]"); + const auto response = rpc("decoderawtransaction", "[\"" + as_text(created.at("result")) + "\"]"); + REQUIRE_NO_THROW_TRUE(response.at("result").is_object()); + BOOST_REQUIRE_EQUAL(response.at("result").at("locktime").as_int64(), 0); +} + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__decodescript__p2kh__pubkeyhash) +{ + const auto response = rpc("decodescript", "[\"76a914000000000000000000000000000000000000000088ac\"]"); + REQUIRE_NO_THROW_TRUE(response.at("result").is_object()); + BOOST_REQUIRE_EQUAL(as_text(response.at("result").at("type")), "pubkeyhash"); +} + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__validateaddress__genesis__valid) +{ + const auto response = rpc("validateaddress", "[\"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\"]"); + REQUIRE_NO_THROW_TRUE(response.at("result").at("isvalid").as_bool()); +} + +BOOST_AUTO_TEST_CASE(bitcoind_rpc__validateaddress__garbage__invalid) +{ + const auto response = rpc("validateaddress", "[\"notanaddress\"]"); + REQUIRE_NO_THROW_TRUE(!response.at("result").at("isvalid").as_bool()); +} + // network // ----------------------------------------------------------------------------