Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2012-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <core_io.h>6#include <interfaces/chain.h>7#include <node/context.h>8#include <rpc/blockchain.h>9#include <rpc/client.h>10#include <rpc/server.h>11#include <rpc/util.h>12#include <test/util/common.h>13#include <test/util/setup_common.h>14#include <test/util/time.h>15#include <univalue.h>16#include <util/time.h>18#include <any>19#include <string_view>21#include <boost/test/unit_test.hpp>23using util::SplitString;25static UniValue JSON(std::string_view json)26{27UniValue value;28BOOST_CHECK(value.read(json));29return value;30}32class HasJSON33{34public:35explicit HasJSON(std::string json) : m_json(std::move(json)) {}36bool operator()(const UniValue& value) const37{38std::string json{value.write()};39BOOST_CHECK_EQUAL(json, m_json);40return json == m_json;41};43private:44const std::string m_json;45};47class RPCTestingSetup : public TestingSetup48{49public:50UniValue TransformParams(const UniValue& params, std::vector<std::pair<std::string, bool>> arg_names) const;51UniValue CallRPC(std::string args);52};54UniValue RPCTestingSetup::TransformParams(const UniValue& params, std::vector<std::pair<std::string, bool>> arg_names) const55{56UniValue transformed_params;57CRPCTable table;58CRPCCommand command{"category", "method", [&](const JSONRPCRequest& request, UniValue&, bool) -> bool { transformed_params = request.params; return true; }, arg_names, /*unique_id=*/0};59table.appendCommand("method", &command);60JSONRPCRequest request;61request.strMethod = "method";62request.params = params;63if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();64table.execute(request);65return transformed_params;66}68UniValue RPCTestingSetup::CallRPC(std::string args)69{70std::vector<std::string> vArgs{SplitString(args, ' ')};71std::string strMethod = vArgs[0];72vArgs.erase(vArgs.begin());73JSONRPCRequest request;74request.context = &m_node;75request.strMethod = strMethod;76request.params = RPCConvertValues(strMethod, vArgs);77if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();78try {79UniValue result = tableRPC.execute(request);80return result;81}82catch (const UniValue& objError) {83throw std::runtime_error(objError.find_value("message").get_str());84}85}88BOOST_FIXTURE_TEST_SUITE(rpc_tests, RPCTestingSetup)90BOOST_AUTO_TEST_CASE(rpc_namedparams)91{92const std::vector<std::pair<std::string, bool>> arg_names{{"arg1", false}, {"arg2", false}, {"arg3", false}, {"arg4", false}, {"arg5", false}};94// Make sure named arguments are transformed into positional arguments in correct places separated by nulls95BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg2": 2, "arg4": 4})"), arg_names).write(), "[null,2,null,4]");97// Make sure named argument specified multiple times raises an exception98BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"arg2": 2, "arg2": 4})"), arg_names), UniValue,99HasJSON(R"({"code":-8,"message":"Parameter arg2 specified multiple times"})"));101// Make sure named and positional arguments can be combined.102BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg5": 5, "args": [1, 2], "arg4": 4})"), arg_names).write(), "[1,2,null,4,5]");104// Make sure a unknown named argument raises an exception105BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"arg2": 2, "unknown": 6})"), arg_names), UniValue,106HasJSON(R"({"code":-8,"message":"Unknown named parameter unknown"})"));108// Make sure an overlap between a named argument and positional argument raises an exception109BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"args": [1,2,3], "arg4": 4, "arg2": 2})"), arg_names), UniValue,110HasJSON(R"({"code":-8,"message":"Parameter arg2 specified twice both as positional and named argument"})"));112// Make sure extra positional arguments can be passed through to the method implementation, as long as they don't overlap with named arguments.113BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"args": [1,2,3,4,5,6,7,8,9,10]})"), arg_names).write(), "[1,2,3,4,5,6,7,8,9,10]");114BOOST_CHECK_EQUAL(TransformParams(JSON(R"([1,2,3,4,5,6,7,8,9,10])"), arg_names).write(), "[1,2,3,4,5,6,7,8,9,10]");115}117BOOST_AUTO_TEST_CASE(rpc_namedonlyparams)118{119const std::vector<std::pair<std::string, bool>> arg_names{{"arg1", false}, {"arg2", false}, {"opt1", true}, {"opt2", true}, {"options", false}};121// Make sure optional parameters are really optional.122BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg1": 1, "arg2": 2})"), arg_names).write(), "[1,2]");124// Make sure named-only parameters are passed as options.125BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg1": 1, "arg2": 2, "opt1": 10, "opt2": 20})"), arg_names).write(), R"([1,2,{"opt1":10,"opt2":20}])");127// Make sure options can be passed directly.128BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg1": 1, "arg2": 2, "options":{"opt1": 10, "opt2": 20}})"), arg_names).write(), R"([1,2,{"opt1":10,"opt2":20}])");130// Make sure options and named parameters conflict.131BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"arg1": 1, "arg2": 2, "opt1": 10, "options":{"opt1": 10}})"), arg_names), UniValue,132HasJSON(R"({"code":-8,"message":"Parameter options conflicts with parameter opt1"})"));134// Make sure options object specified through args array conflicts.135BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"args": [1, 2, {"opt1": 10}], "opt2": 20})"), arg_names), UniValue,136HasJSON(R"({"code":-8,"message":"Parameter options specified twice both as positional and named argument"})"));137}139BOOST_AUTO_TEST_CASE(rpc_remove_command_cleans_up_empty_entry)140{141CRPCTable table;142RpcMethodFnType method{143[]() -> RPCMethod {144return RPCMethod{145"method",146"Test RPC method.\n",147{},148RPCResult{RPCResult::Type::STR, "", ""},149RPCExamples{""},150[](const RPCMethod&, const JSONRPCRequest&) -> UniValue { return "ok"; },151};152}153};154CRPCCommand command{"test", method};156table.appendCommand(command.name, &command);157BOOST_CHECK(table.removeCommand(command.name, &command));159bool found{false};160for (const auto& name : table.listCommands()) {161if (name == command.name) {162found = true;163break;164}165}166BOOST_CHECK(!found);168UniValue doc{table.buildOpenRPCDoc()};169BOOST_CHECK(doc.isObject());170}172BOOST_AUTO_TEST_CASE(rpc_rawparams)173{174// Test raw transaction API argument handling175UniValue r;177BOOST_CHECK_THROW(CallRPC("getrawtransaction"), std::runtime_error);178BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), std::runtime_error);179BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), std::runtime_error);181BOOST_CHECK_THROW(CallRPC("createrawtransaction"), std::runtime_error);182BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), std::runtime_error);183BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), std::runtime_error);184BOOST_CHECK_THROW(CallRPC("createrawtransaction {} {}"), std::runtime_error);185BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [] {}"));186BOOST_CHECK_THROW(CallRPC("createrawtransaction [] {} extra"), std::runtime_error);188BOOST_CHECK_THROW(CallRPC("decoderawtransaction"), std::runtime_error);189BOOST_CHECK_THROW(CallRPC("decoderawtransaction null"), std::runtime_error);190BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), std::runtime_error);191std::string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";192BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx));193BOOST_CHECK_EQUAL(r.get_obj().find_value("size").getInt<int>(), 193);194BOOST_CHECK_EQUAL(r.get_obj().find_value("version").getInt<int>(), 1);195BOOST_CHECK_EQUAL(r.get_obj().find_value("locktime").getInt<int>(), 0);196BOOST_CHECK_THROW(CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error);197BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false"));198BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error);200// Only check failure cases for sendrawtransaction, there's no network to send to...201BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), std::runtime_error);202BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), std::runtime_error);203BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), std::runtime_error);204BOOST_CHECK_THROW(CallRPC(std::string("sendrawtransaction ")+rawtx+" extra"), std::runtime_error);205}207BOOST_AUTO_TEST_CASE(rpc_togglenetwork)208{209UniValue r;211r = CallRPC("getnetworkinfo");212bool netState = r.get_obj().find_value("networkactive").get_bool();213BOOST_CHECK_EQUAL(netState, true);215BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive false"));216r = CallRPC("getnetworkinfo");217int numConnection = r.get_obj().find_value("connections").getInt<int>();218BOOST_CHECK_EQUAL(numConnection, 0);220netState = r.get_obj().find_value("networkactive").get_bool();221BOOST_CHECK_EQUAL(netState, false);223BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive true"));224r = CallRPC("getnetworkinfo");225netState = r.get_obj().find_value("networkactive").get_bool();226BOOST_CHECK_EQUAL(netState, true);227}229BOOST_AUTO_TEST_CASE(rpc_rawsign)230{231UniValue r;232// input is a 1-of-2 multisig (so is output):233std::string prevout =234"[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\","235"\"vout\":1,\"scriptPubKey\":\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\","236"\"redeemScript\":\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ecefca5b94d6df834e77e108f68e66f126044c052ae\"}]";237r = CallRPC(std::string("createrawtransaction ")+prevout+" "+238"{\"3HqAe9LtNBjnsfM4CyYaWTnvCaUYT7v4oZ\":11}");239std::string notsigned = r.get_str();240std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";241std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";242r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout);243BOOST_CHECK(r.get_obj().find_value("complete").get_bool() == false);244r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout);245BOOST_CHECK(r.get_obj().find_value("complete").get_bool() == true);246}248BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)249{250BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\"}"));
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.