diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 9 | ||||
-rw-r--r-- | src/rpc/external_signer.cpp | 76 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 2 | ||||
-rw-r--r-- | src/rpc/net.cpp | 35 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 1 | ||||
-rw-r--r-- | src/rpc/register.h | 5 | ||||
-rw-r--r-- | src/rpc/request.h | 13 | ||||
-rw-r--r-- | src/rpc/server.cpp | 5 | ||||
-rw-r--r-- | src/rpc/util.cpp | 109 | ||||
-rw-r--r-- | src/rpc/util.h | 9 |
10 files changed, 226 insertions, 38 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 053b6913dc..7cf6a413e1 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -14,6 +14,7 @@ #include <core_io.h> #include <hash.h> #include <index/blockfilterindex.h> +#include <node/blockstorage.h> #include <node/coinstats.h> #include <node/context.h> #include <node/utxo_snapshot.h> @@ -1263,10 +1264,8 @@ static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // For BIP9 deployments. - // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden. - // A timeout value of 0 guarantees a softfork will never be activated. - // This is used when merging logic to implement a proposed softfork without a specified deployment schedule. - if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return; + // Deployments that are never active are hidden. + if (consensusParams.vDeployments[id].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return; UniValue bip9(UniValue::VOBJ); const ThresholdState thresholdState = VersionBitsState(active_chain_tip, consensusParams, id, versionbitscache); @@ -1296,6 +1295,7 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniVal statsUV.pushKV("possible", statsStruct.possible); bip9.pushKV("statistics", statsUV); } + bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height); UniValue rv(UniValue::VOBJ); rv.pushKV("type", "bip9"); @@ -1342,6 +1342,7 @@ RPCHelpMan getblockchaininfo() {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, + {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"}, {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)", { {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"}, diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp new file mode 100644 index 0000000000..6ec2b1a07f --- /dev/null +++ b/src/rpc/external_signer.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2018-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <chainparamsbase.h> +#include <external_signer.h> +#include <rpc/server.h> +#include <rpc/util.h> +#include <util/strencodings.h> +#include <rpc/protocol.h> + +#include <string> +#include <vector> + +#ifdef ENABLE_EXTERNAL_SIGNER + +static RPCHelpMan enumeratesigners() +{ + return RPCHelpMan{"enumeratesigners", + "Returns a list of external signers from -signer.", + {}, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "signers", /* optional */ false, "", + { + {RPCResult::Type::STR_HEX, "masterkeyfingerprint", "Master key fingerprint"}, + {RPCResult::Type::STR, "name", "Device name"}, + }, + } + } + }, + RPCExamples{ + HelpExampleCli("enumeratesigners", "") + + HelpExampleRpc("enumeratesigners", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + { + const std::string command = gArgs.GetArg("-signer", ""); + if (command == "") throw JSONRPCError(RPC_MISC_ERROR, "Error: restart bitcoind with -signer=<cmd>"); + const std::string chain = gArgs.GetChainName(); + UniValue signers_res = UniValue::VARR; + try { + std::vector<ExternalSigner> signers; + ExternalSigner::Enumerate(command, signers, chain); + for (const ExternalSigner& signer : signers) { + UniValue signer_res = UniValue::VOBJ; + signer_res.pushKV("fingerprint", signer.m_fingerprint); + signer_res.pushKV("name", signer.m_name); + signers_res.push_back(signer_res); + } + } catch (const std::exception& e) { + throw JSONRPCError(RPC_MISC_ERROR, e.what()); + } + UniValue result(UniValue::VOBJ); + result.pushKV("signers", signers_res); + return result; + } + }; +} + +void RegisterSignerRPCCommands(CRPCTable &t) +{ +// clang-format off +static const CRPCCommand commands[] = +{ // category actor (function) + // --------------------- ------------------------ + { "signer", &enumeratesigners, }, +}; +// clang-format on + for (const auto& c : commands) { + t.appendCommand(c.name, &c); + } +} + +#endif // ENABLE_EXTERNAL_SIGNER diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 1df5c51718..8e98919391 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -628,7 +628,7 @@ static RPCHelpMan echo(const std::string& name) {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""}, {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""}, }, - RPCResult{RPCResult::Type::NONE, "", "Returns whatever was passed in"}, + RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index cd354f46c2..a5159fb792 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -749,9 +749,11 @@ static RPCHelpMan listbanned() { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR, "address", ""}, - {RPCResult::Type::NUM_TIME, "banned_until", ""}, - {RPCResult::Type::NUM_TIME, "ban_created", ""}, + {RPCResult::Type::STR, "address", "The IP/Subnet of the banned node"}, + {RPCResult::Type::NUM_TIME, "ban_created", "The " + UNIX_EPOCH_TIME + " the ban was created"}, + {RPCResult::Type::NUM_TIME, "banned_until", "The " + UNIX_EPOCH_TIME + " the ban expires"}, + {RPCResult::Type::NUM_TIME, "ban_duration", "The ban duration, in seconds"}, + {RPCResult::Type::NUM_TIME, "time_remaining", "The time remaining until the ban expires, in seconds"}, }}, }}, RPCExamples{ @@ -767,6 +769,7 @@ static RPCHelpMan listbanned() banmap_t banMap; node.banman->GetBanned(banMap); + const int64_t current_time{GetTime()}; UniValue bannedAddresses(UniValue::VARR); for (const auto& entry : banMap) @@ -774,8 +777,10 @@ static RPCHelpMan listbanned() const CBanEntry& banEntry = entry.second; UniValue rec(UniValue::VOBJ); rec.pushKV("address", entry.first.ToString()); - rec.pushKV("banned_until", banEntry.nBanUntil); rec.pushKV("ban_created", banEntry.nCreateTime); + rec.pushKV("banned_until", banEntry.nBanUntil); + rec.pushKV("ban_duration", (banEntry.nBanUntil - banEntry.nCreateTime)); + rec.pushKV("time_remaining", (banEntry.nBanUntil - current_time)); bannedAddresses.push_back(rec); } @@ -835,7 +840,7 @@ static RPCHelpMan setnetworkactive() static RPCHelpMan getnodeaddresses() { return RPCHelpMan{"getnodeaddresses", - "\nReturn known addresses which can potentially be used to find new nodes in the network\n", + "\nReturn known addresses, which can potentially be used to find new nodes in the network.\n", { {"count", RPCArg::Type::NUM, /* default */ "1", "The maximum number of addresses to return. Specify 0 to return all known addresses."}, }, @@ -844,10 +849,11 @@ static RPCHelpMan getnodeaddresses() { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"}, - {RPCResult::Type::NUM, "services", "The services offered"}, + {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " when the node was last seen"}, + {RPCResult::Type::NUM, "services", "The services offered by the node"}, {RPCResult::Type::STR, "address", "The address of the node"}, - {RPCResult::Type::NUM, "port", "The port of the node"}, + {RPCResult::Type::NUM, "port", "The port number of the node"}, + {RPCResult::Type::STR, "network", "The network (" + Join(GetNetworkNames(), ", ") + ") the node connected through"}, }}, } }, @@ -862,15 +868,11 @@ static RPCHelpMan getnodeaddresses() throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } - int count = 1; - if (!request.params[0].isNull()) { - count = request.params[0].get_int(); - if (count < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range"); - } - } + const int count{request.params[0].isNull() ? 1 : request.params[0].get_int()}; + if (count < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range"); + // returns a shuffled list of CAddress - std::vector<CAddress> vAddr = node.connman->GetAddresses(count, /* max_pct */ 0); + const std::vector<CAddress> vAddr{node.connman->GetAddresses(count, /* max_pct */ 0)}; UniValue ret(UniValue::VARR); for (const CAddress& addr : vAddr) { @@ -879,6 +881,7 @@ static RPCHelpMan getnodeaddresses() obj.pushKV("services", (uint64_t)addr.nServices); obj.pushKV("address", addr.ToStringIP()); obj.pushKV("port", addr.GetPort()); + obj.pushKV("network", GetNetworkName(addr.GetNetClass())); ret.push_back(obj); } return ret; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 19e9c75e32..ecdd00dcca 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -10,6 +10,7 @@ #include <index/txindex.h> #include <key_io.h> #include <merkleblock.h> +#include <node/blockstorage.h> #include <node/coin.h> #include <node/context.h> #include <node/psbt.h> diff --git a/src/rpc/register.h b/src/rpc/register.h index 374a1e3db8..6724203ffe 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -19,6 +19,8 @@ void RegisterMiscRPCCommands(CRPCTable &tableRPC); void RegisterMiningRPCCommands(CRPCTable &tableRPC); /** Register raw transaction RPC commands */ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); +/** Register raw transaction RPC commands */ +void RegisterSignerRPCCommands(CRPCTable &tableRPC); static inline void RegisterAllCoreRPCCommands(CRPCTable &t) { @@ -27,6 +29,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t) RegisterMiscRPCCommands(t); RegisterMiningRPCCommands(t); RegisterRawTransactionRPCCommands(t); +#ifdef ENABLE_EXTERNAL_SIGNER + RegisterSignerRPCCommands(t); +#endif // ENABLE_EXTERNAL_SIGNER } #endif // BITCOIN_RPC_REGISTER_H diff --git a/src/rpc/request.h b/src/rpc/request.h index e1569673f6..bb091efea8 100644 --- a/src/rpc/request.h +++ b/src/rpc/request.h @@ -35,18 +35,7 @@ public: std::string URI; std::string authUser; std::string peerAddr; - const std::any& context; - - explicit JSONRPCRequest(const std::any& context) : id(NullUniValue), params(NullUniValue), context(context) {} - - //! Initializes request information from another request object and the - //! given context. The implementation should be updated if any members are - //! added or removed above. - JSONRPCRequest(const JSONRPCRequest& other, const std::any& context) - : id(other.id), strMethod(other.strMethod), params(other.params), mode(other.mode), URI(other.URI), - authUser(other.authUser), peerAddr(other.peerAddr), context(context) - { - } + std::any context; void parse(const UniValue& valRequest); }; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 2f05c8842f..5d816ba5bb 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -137,8 +137,9 @@ static RPCHelpMan help() { {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"}, }, - RPCResult{ - RPCResult::Type::STR, "", "The help text" + { + RPCResult{RPCResult::Type::STR, "", "The help text"}, + RPCResult{RPCResult::Type::ANY, "", ""}, }, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 44e58cb75f..ba3105ca01 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -113,17 +113,80 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey) return ParseHexV(find_value(o, strKey), strKey); } +namespace { + +/** + * Quote an argument for shell. + * + * @note This is intended for help, not for security-sensitive purposes. + */ +std::string ShellQuote(const std::string& s) +{ + std::string result; + result.reserve(s.size() * 2); + for (const char ch: s) { + if (ch == '\'') { + result += "'\''"; + } else { + result += ch; + } + } + return "'" + result + "'"; +} + +/** + * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing. + * + * @note This is intended for help, not for security-sensitive purposes. + */ +std::string ShellQuoteIfNeeded(const std::string& s) +{ + for (const char ch: s) { + if (ch == ' ' || ch == '\'' || ch == '"') { + return ShellQuote(s); + } + } + + return s; +} + +} + std::string HelpExampleCli(const std::string& methodname, const std::string& args) { return "> bitcoin-cli " + methodname + " " + args + "\n"; } +std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args) +{ + std::string result = "> bitcoin-cli -named " + methodname; + for (const auto& argpair: args) { + const auto& value = argpair.second.isStr() + ? argpair.second.get_str() + : argpair.second.write(); + result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value); + } + result += "\n"; + return result; +} + std::string HelpExampleRpc(const std::string& methodname, const std::string& args) { return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", " "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; } +std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args) +{ + UniValue params(UniValue::VOBJ); + for (const auto& param: args) { + params.pushKV(param.first, param.second); + } + + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", " + "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; +} + // Converts a hex string to a public key if possible CPubKey HexToPubKey(const std::string& hex_in) { @@ -442,6 +505,7 @@ std::string RPCResults::ToDescriptionString() const { std::string result; for (const auto& r : m_results) { + if (r.m_type == RPCResult::Type::ANY) continue; // for testing only if (r.m_cond.empty()) { result += "\nResult:\n"; } else { @@ -459,7 +523,7 @@ std::string RPCExamples::ToDescriptionString() const return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples; } -UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) +UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const { if (request.mode == JSONRPCRequest::GET_ARGS) { return GetArgMap(); @@ -471,7 +535,9 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) { throw std::runtime_error(ToString()); } - return m_fun(*this, request); + const UniValue ret = m_fun(*this, request); + CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [ret](const RPCResult& res) { return res.MatchesType(ret); })); + return ret; } bool RPCHelpMan::IsValidNumArgs(size_t num_args) const @@ -677,6 +743,9 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const sections.PushSection({indent + "..." + maybe_separator, m_description}); return; } + case Type::ANY: { + CHECK_NONFATAL(false); // Only for testing + } case Type::NONE: { sections.PushSection({indent + "null" + maybe_separator, Description("json null")}); return; @@ -742,6 +811,42 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const CHECK_NONFATAL(false); } +bool RPCResult::MatchesType(const UniValue& result) const +{ + switch (m_type) { + case Type::ELISION: { + return false; + } + case Type::ANY: { + return true; + } + case Type::NONE: { + return UniValue::VNULL == result.getType(); + } + case Type::STR: + case Type::STR_HEX: { + return UniValue::VSTR == result.getType(); + } + case Type::NUM: + case Type::STR_AMOUNT: + case Type::NUM_TIME: { + return UniValue::VNUM == result.getType(); + } + case Type::BOOL: { + return UniValue::VBOOL == result.getType(); + } + case Type::ARR_FIXED: + case Type::ARR: { + return UniValue::VARR == result.getType(); + } + case Type::OBJ_DYN: + case Type::OBJ: { + return UniValue::VOBJ == result.getType(); + } + } // no default case, so the compiler can warn about missing cases + CHECK_NONFATAL(false); +} + std::string RPCArg::ToStringObj(const bool oneline) const { std::string res; diff --git a/src/rpc/util.h b/src/rpc/util.h index 94c2d2d626..6e783e19fb 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -78,8 +78,12 @@ extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strNa extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey); extern CAmount AmountFromValue(const UniValue& value); + +using RPCArgList = std::vector<std::pair<std::string, UniValue>>; extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); +extern std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args); extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); +extern std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args); CPubKey HexToPubKey(const std::string& hex_in); CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in); @@ -223,6 +227,7 @@ struct RPCResult { NUM, BOOL, NONE, + ANY, //!< Special type to disable type checks (for testing only) STR_AMOUNT, //!< Special string to represent a floating point amount STR_HEX, //!< Special string with only hex chars OBJ_DYN, //!< Special dictionary with keys that are not literals @@ -295,6 +300,8 @@ struct RPCResult { std::string ToStringObj() const; /** Return the description string, including the result type. */ std::string ToDescriptionString() const; + /** Check whether the result JSON type matches. */ + bool MatchesType(const UniValue& result) const; }; struct RPCResults { @@ -333,7 +340,7 @@ public: using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>; RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun); - UniValue HandleRequest(const JSONRPCRequest& request); + UniValue HandleRequest(const JSONRPCRequest& request) const; std::string ToString() const; /** Return the named args that need to be converted from string to another JSON type */ UniValue GetArgMap() const; |