653 lines
27 KiB
C++
653 lines
27 KiB
C++
// Copyright (c) 2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2017 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 <base58.h>
|
|
#include <chain.h>
|
|
#include <clientversion.h>
|
|
#include <core_io.h>
|
|
#include <crypto/ripemd160.h>
|
|
#include <init.h>
|
|
#include <validation.h>
|
|
#include <httpserver.h>
|
|
#include <net.h>
|
|
#include <netbase.h>
|
|
#include <rpc/blockchain.h>
|
|
#include <rpc/server.h>
|
|
#include <timedata.h>
|
|
#include <util.h>
|
|
#include <utilstrencodings.h>
|
|
#ifdef ENABLE_WALLET
|
|
#include <wallet/rpcwallet.h>
|
|
#include <wallet/wallet.h>
|
|
#include <wallet/walletdb.h>
|
|
#endif
|
|
#include <warnings.h>
|
|
|
|
#include <stdint.h>
|
|
#ifdef HAVE_MALLOC_INFO
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#include <univalue.h>
|
|
|
|
#ifdef ENABLE_WALLET
|
|
class DescribeAddressVisitor : public boost::static_visitor<UniValue>
|
|
{
|
|
public:
|
|
CWallet * const pwallet;
|
|
|
|
explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {}
|
|
|
|
UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
|
|
|
|
UniValue operator()(const CKeyID &keyID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
CPubKey vchPubKey;
|
|
obj.push_back(Pair("isscript", false));
|
|
obj.push_back(Pair("iswitness", false));
|
|
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
|
|
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
|
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const CScriptID &scriptID) const {
|
|
UniValue obj(UniValue::VOBJ);
|
|
CScript subscript;
|
|
obj.push_back(Pair("isscript", true));
|
|
obj.push_back(Pair("iswitness", false));
|
|
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
|
|
std::vector<CTxDestination> addresses;
|
|
txnouttype whichType;
|
|
int nRequired;
|
|
ExtractDestinations(subscript, whichType, addresses, nRequired);
|
|
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
|
|
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
|
UniValue a(UniValue::VARR);
|
|
for (const CTxDestination& addr : addresses) {
|
|
a.push_back(EncodeDestination(addr));
|
|
}
|
|
obj.push_back(Pair("addresses", a));
|
|
if (whichType == TX_MULTISIG)
|
|
obj.push_back(Pair("sigsrequired", nRequired));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const WitnessV0KeyHash& id) const
|
|
{
|
|
UniValue obj(UniValue::VOBJ);
|
|
CPubKey pubkey;
|
|
obj.push_back(Pair("isscript", false));
|
|
obj.push_back(Pair("iswitness", true));
|
|
obj.push_back(Pair("witness_version", 0));
|
|
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
|
|
if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
|
|
obj.push_back(Pair("pubkey", HexStr(pubkey)));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const WitnessV0ScriptHash& id) const
|
|
{
|
|
UniValue obj(UniValue::VOBJ);
|
|
CScript subscript;
|
|
obj.push_back(Pair("isscript", true));
|
|
obj.push_back(Pair("iswitness", true));
|
|
obj.push_back(Pair("witness_version", 0));
|
|
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
|
|
CRIPEMD160 hasher;
|
|
uint160 hash;
|
|
hasher.Write(id.begin(), 32).Finalize(hash.begin());
|
|
if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
|
|
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
UniValue operator()(const WitnessUnknown& id) const
|
|
{
|
|
UniValue obj(UniValue::VOBJ);
|
|
CScript subscript;
|
|
obj.push_back(Pair("iswitness", true));
|
|
obj.push_back(Pair("witness_version", (int)id.version));
|
|
obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length)));
|
|
return obj;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
UniValue validateaddress(const JSONRPCRequest& request)
|
|
{
|
|
if (request.fHelp || request.params.size() != 1)
|
|
throw std::runtime_error(
|
|
"validateaddress \"address\"\n"
|
|
"\nReturn information about the given bitcoin address.\n"
|
|
"\nArguments:\n"
|
|
"1. \"address\" (string, required) The bitcoin address to validate\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
|
|
" \"address\" : \"address\", (string) The bitcoin address validated\n"
|
|
" \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
|
|
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
|
|
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
|
|
" \"isscript\" : true|false, (boolean) If the key is a script\n"
|
|
" \"script\" : \"type\" (string, optional) The output script type. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash\n"
|
|
" \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n"
|
|
" \"addresses\" (string, optional) Array of addresses associated with the known redeemscript\n"
|
|
" [\n"
|
|
" \"address\"\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
" \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output\n"
|
|
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
|
|
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
|
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
|
|
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
|
|
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
|
|
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
|
+ HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
|
);
|
|
|
|
#ifdef ENABLE_WALLET
|
|
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
|
|
|
|
LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
|
|
#else
|
|
LOCK(cs_main);
|
|
#endif
|
|
|
|
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
|
bool isValid = IsValidDestination(dest);
|
|
|
|
UniValue ret(UniValue::VOBJ);
|
|
ret.push_back(Pair("isvalid", isValid));
|
|
if (isValid)
|
|
{
|
|
std::string currentAddress = EncodeDestination(dest);
|
|
ret.push_back(Pair("address", currentAddress));
|
|
|
|
CScript scriptPubKey = GetScriptForDestination(dest);
|
|
ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
|
|
|
#ifdef ENABLE_WALLET
|
|
isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO;
|
|
ret.push_back(Pair("ismine", bool(mine & ISMINE_SPENDABLE)));
|
|
ret.push_back(Pair("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)));
|
|
UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest);
|
|
ret.pushKVs(detail);
|
|
if (pwallet && pwallet->mapAddressBook.count(dest)) {
|
|
ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name));
|
|
}
|
|
if (pwallet) {
|
|
const CKeyMetadata* meta = nullptr;
|
|
if (const CKeyID* key_id = boost::get<CKeyID>(&dest)) {
|
|
auto it = pwallet->mapKeyMetadata.find(*key_id);
|
|
if (it != pwallet->mapKeyMetadata.end()) {
|
|
meta = &it->second;
|
|
}
|
|
}
|
|
if (!meta) {
|
|
auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
|
|
if (it != pwallet->m_script_metadata.end()) {
|
|
meta = &it->second;
|
|
}
|
|
}
|
|
if (meta) {
|
|
ret.push_back(Pair("timestamp", meta->nCreateTime));
|
|
if (!meta->hdKeypath.empty()) {
|
|
ret.push_back(Pair("hdkeypath", meta->hdKeypath));
|
|
ret.push_back(Pair("hdmasterkeyid", meta->hdMasterKeyID.GetHex()));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around
|
|
class CWallet;
|
|
|
|
/**
|
|
* Used by addmultisigaddress / createmultisig:
|
|
*/
|
|
CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& params)
|
|
{
|
|
int nRequired = params[0].get_int();
|
|
const UniValue& keys = params[1].get_array();
|
|
|
|
// Gather public keys
|
|
if (nRequired < 1)
|
|
throw std::runtime_error("a multisignature address must require at least one key to redeem");
|
|
if ((int)keys.size() < nRequired)
|
|
throw std::runtime_error(
|
|
strprintf("not enough keys supplied "
|
|
"(got %u keys, but need at least %d to redeem)", keys.size(), nRequired));
|
|
if (keys.size() > 16)
|
|
throw std::runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
|
|
std::vector<CPubKey> pubkeys;
|
|
pubkeys.resize(keys.size());
|
|
for (unsigned int i = 0; i < keys.size(); i++)
|
|
{
|
|
const std::string& ks = keys[i].get_str();
|
|
#ifdef ENABLE_WALLET
|
|
// Case 1: Bitcoin address and we have full public key:
|
|
CTxDestination dest = DecodeDestination(ks);
|
|
if (pwallet && IsValidDestination(dest)) {
|
|
const CKeyID *keyID = boost::get<CKeyID>(&dest);
|
|
if (!keyID) {
|
|
throw std::runtime_error(strprintf("%s does not refer to a key", ks));
|
|
}
|
|
CPubKey vchPubKey;
|
|
if (!pwallet->GetPubKey(*keyID, vchPubKey)) {
|
|
throw std::runtime_error(strprintf("no full public key for address %s", ks));
|
|
}
|
|
if (!vchPubKey.IsFullyValid())
|
|
throw std::runtime_error(" Invalid public key: "+ks);
|
|
pubkeys[i] = vchPubKey;
|
|
}
|
|
|
|
// Case 2: hex public key
|
|
else
|
|
#endif
|
|
if (IsHex(ks))
|
|
{
|
|
CPubKey vchPubKey(ParseHex(ks));
|
|
if (!vchPubKey.IsFullyValid())
|
|
throw std::runtime_error(" Invalid public key: "+ks);
|
|
pubkeys[i] = vchPubKey;
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error(" Invalid public key: "+ks);
|
|
}
|
|
}
|
|
CScript result = GetScriptForMultisig(nRequired, pubkeys);
|
|
|
|
if (result.size() > MAX_SCRIPT_ELEMENT_SIZE)
|
|
throw std::runtime_error(
|
|
strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE));
|
|
|
|
return result;
|
|
}
|
|
|
|
UniValue createmultisig(const JSONRPCRequest& request)
|
|
{
|
|
#ifdef ENABLE_WALLET
|
|
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
|
|
#else
|
|
CWallet * const pwallet = nullptr;
|
|
#endif
|
|
|
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
|
|
{
|
|
std::string msg = "createmultisig nrequired [\"key\",...]\n"
|
|
"\nCreates a multi-signature address with n signature of m keys required.\n"
|
|
"It returns a json object with the address and redeemScript.\n"
|
|
|
|
"\nArguments:\n"
|
|
"1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
|
|
"2. \"keys\" (string, required) A json array of keys which are bitcoin addresses or hex-encoded public keys\n"
|
|
" [\n"
|
|
" \"key\" (string) bitcoin address or hex-encoded public key\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
|
|
" \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
|
|
"}\n"
|
|
|
|
"\nExamples:\n"
|
|
"\nCreate a multisig address from 2 addresses\n"
|
|
+ HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
|
|
"\nAs a json rpc call\n"
|
|
+ HelpExampleRpc("createmultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
|
|
;
|
|
throw std::runtime_error(msg);
|
|
}
|
|
|
|
// Construct using pay-to-script-hash:
|
|
CScript inner = _createmultisig_redeemScript(pwallet, request.params);
|
|
CScriptID innerID(inner);
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("address", EncodeDestination(innerID)));
|
|
result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
|
|
|
|
return result;
|
|
}
|
|
|
|
UniValue verifymessage(const JSONRPCRequest& request)
|
|
{
|
|
if (request.fHelp || request.params.size() != 3)
|
|
throw std::runtime_error(
|
|
"verifymessage \"address\" \"signature\" \"message\"\n"
|
|
"\nVerify a signed message\n"
|
|
"\nArguments:\n"
|
|
"1. \"address\" (string, required) The bitcoin address to use for the signature.\n"
|
|
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
|
|
"3. \"message\" (string, required) The message that was signed.\n"
|
|
"\nResult:\n"
|
|
"true|false (boolean) If the signature is verified or not.\n"
|
|
"\nExamples:\n"
|
|
"\nUnlock the wallet for 30 seconds\n"
|
|
+ HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
std::string strAddress = request.params[0].get_str();
|
|
std::string strSign = request.params[1].get_str();
|
|
std::string strMessage = request.params[2].get_str();
|
|
|
|
CTxDestination destination = DecodeDestination(strAddress);
|
|
if (!IsValidDestination(destination)) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
|
}
|
|
|
|
const CKeyID *keyID = boost::get<CKeyID>(&destination);
|
|
if (!keyID) {
|
|
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
|
|
}
|
|
|
|
bool fInvalid = false;
|
|
std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
|
|
if (fInvalid)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
ss << strMessageMagic;
|
|
ss << strMessage;
|
|
|
|
CPubKey pubkey;
|
|
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
|
|
return false;
|
|
|
|
return (pubkey.GetID() == *keyID);
|
|
}
|
|
|
|
UniValue signmessagewithprivkey(const JSONRPCRequest& request)
|
|
{
|
|
if (request.fHelp || request.params.size() != 2)
|
|
throw std::runtime_error(
|
|
"signmessagewithprivkey \"privkey\" \"message\"\n"
|
|
"\nSign a message with the private key of an address\n"
|
|
"\nArguments:\n"
|
|
"1. \"privkey\" (string, required) The private key to sign the message with.\n"
|
|
"2. \"message\" (string, required) The message to create a signature of.\n"
|
|
"\nResult:\n"
|
|
"\"signature\" (string) The signature of the message encoded in base 64\n"
|
|
"\nExamples:\n"
|
|
"\nCreate the signature\n"
|
|
+ HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
|
|
"\nVerify the signature\n"
|
|
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
|
|
"\nAs json rpc\n"
|
|
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
|
|
);
|
|
|
|
std::string strPrivkey = request.params[0].get_str();
|
|
std::string strMessage = request.params[1].get_str();
|
|
|
|
CBitcoinSecret vchSecret;
|
|
bool fGood = vchSecret.SetString(strPrivkey);
|
|
if (!fGood)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
|
|
CKey key = vchSecret.GetKey();
|
|
if (!key.IsValid())
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
ss << strMessageMagic;
|
|
ss << strMessage;
|
|
|
|
std::vector<unsigned char> vchSig;
|
|
if (!key.SignCompact(ss.GetHash(), vchSig))
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
|
|
|
|
return EncodeBase64(vchSig.data(), vchSig.size());
|
|
}
|
|
|
|
UniValue setmocktime(const JSONRPCRequest& request)
|
|
{
|
|
if (request.fHelp || request.params.size() != 1)
|
|
throw std::runtime_error(
|
|
"setmocktime timestamp\n"
|
|
"\nSet the local time to given timestamp (-regtest only)\n"
|
|
"\nArguments:\n"
|
|
"1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n"
|
|
" Pass 0 to go back to using the system time."
|
|
);
|
|
|
|
if (!Params().MineBlocksOnDemand())
|
|
throw std::runtime_error("setmocktime for regression testing (-regtest mode) only");
|
|
|
|
// For now, don't change mocktime if we're in the middle of validation, as
|
|
// this could have an effect on mempool time-based eviction, as well as
|
|
// IsCurrentForFeeEstimation() and IsInitialBlockDownload().
|
|
// TODO: figure out the right way to synchronize around mocktime, and
|
|
// ensure all call sites of GetTime() are accessing this safely.
|
|
LOCK(cs_main);
|
|
|
|
RPCTypeCheck(request.params, {UniValue::VNUM});
|
|
SetMockTime(request.params[0].get_int64());
|
|
|
|
return NullUniValue;
|
|
}
|
|
|
|
static UniValue RPCLockedMemoryInfo()
|
|
{
|
|
LockedPool::Stats stats = LockedPoolManager::Instance().stats();
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("used", uint64_t(stats.used)));
|
|
obj.push_back(Pair("free", uint64_t(stats.free)));
|
|
obj.push_back(Pair("total", uint64_t(stats.total)));
|
|
obj.push_back(Pair("locked", uint64_t(stats.locked)));
|
|
obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used)));
|
|
obj.push_back(Pair("chunks_free", uint64_t(stats.chunks_free)));
|
|
return obj;
|
|
}
|
|
|
|
#ifdef HAVE_MALLOC_INFO
|
|
static std::string RPCMallocInfo()
|
|
{
|
|
char *ptr = nullptr;
|
|
size_t size = 0;
|
|
FILE *f = open_memstream(&ptr, &size);
|
|
if (f) {
|
|
malloc_info(0, f);
|
|
fclose(f);
|
|
if (ptr) {
|
|
std::string rv(ptr, size);
|
|
free(ptr);
|
|
return rv;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
#endif
|
|
|
|
UniValue getmemoryinfo(const JSONRPCRequest& request)
|
|
{
|
|
/* Please, avoid using the word "pool" here in the RPC interface or help,
|
|
* as users will undoubtedly confuse it with the other "memory pool"
|
|
*/
|
|
if (request.fHelp || request.params.size() > 1)
|
|
throw std::runtime_error(
|
|
"getmemoryinfo (\"mode\")\n"
|
|
"Returns an object containing information about memory usage.\n"
|
|
"Arguments:\n"
|
|
"1. \"mode\" determines what kind of information is returned. This argument is optional, the default mode is \"stats\".\n"
|
|
" - \"stats\" returns general statistics about memory usage in the daemon.\n"
|
|
" - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+).\n"
|
|
"\nResult (mode \"stats\"):\n"
|
|
"{\n"
|
|
" \"locked\": { (json object) Information about locked memory manager\n"
|
|
" \"used\": xxxxx, (numeric) Number of bytes used\n"
|
|
" \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n"
|
|
" \"total\": xxxxxxx, (numeric) Total number of bytes managed\n"
|
|
" \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
|
|
" \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
|
|
" \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\nResult (mode \"mallocinfo\"):\n"
|
|
"\"<malloc version=\"1\">...\"\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getmemoryinfo", "")
|
|
+ HelpExampleRpc("getmemoryinfo", "")
|
|
);
|
|
|
|
std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
|
|
if (mode == "stats") {
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("locked", RPCLockedMemoryInfo()));
|
|
return obj;
|
|
} else if (mode == "mallocinfo") {
|
|
#ifdef HAVE_MALLOC_INFO
|
|
return RPCMallocInfo();
|
|
#else
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo is only available when compiled with glibc 2.10+");
|
|
#endif
|
|
} else {
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
|
|
}
|
|
}
|
|
|
|
uint32_t getCategoryMask(UniValue cats) {
|
|
cats = cats.get_array();
|
|
uint32_t mask = 0;
|
|
for (unsigned int i = 0; i < cats.size(); ++i) {
|
|
uint32_t flag = 0;
|
|
std::string cat = cats[i].get_str();
|
|
if (!GetLogCategory(&flag, &cat)) {
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
|
|
}
|
|
if (flag == BCLog::NONE) {
|
|
return 0;
|
|
}
|
|
mask |= flag;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
UniValue logging(const JSONRPCRequest& request)
|
|
{
|
|
if (request.fHelp || request.params.size() > 2) {
|
|
throw std::runtime_error(
|
|
"logging ( <include> <exclude> )\n"
|
|
"Gets and sets the logging configuration.\n"
|
|
"When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
|
|
"When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
|
|
"The arguments are evaluated in order \"include\", \"exclude\".\n"
|
|
"If an item is both included and excluded, it will thus end up being excluded.\n"
|
|
"The valid logging categories are: " + ListLogCategories() + "\n"
|
|
"In addition, the following are available as category names with special meanings:\n"
|
|
" - \"all\", \"1\" : represent all logging categories.\n"
|
|
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
|
|
"\nArguments:\n"
|
|
"1. \"include\" (array of strings, optional) A json array of categories to add debug logging\n"
|
|
" [\n"
|
|
" \"category\" (string) the valid logging category\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
"2. \"exclude\" (array of strings, optional) A json array of categories to remove debug logging\n"
|
|
" [\n"
|
|
" \"category\" (string) the valid logging category\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
"\nResult:\n"
|
|
"{ (json object where keys are the logging categories, and values indicates its status\n"
|
|
" \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n"
|
|
" ...\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
|
|
+ HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
|
|
);
|
|
}
|
|
|
|
uint32_t originalLogCategories = logCategories;
|
|
if (request.params[0].isArray()) {
|
|
logCategories |= getCategoryMask(request.params[0]);
|
|
}
|
|
|
|
if (request.params[1].isArray()) {
|
|
logCategories &= ~getCategoryMask(request.params[1]);
|
|
}
|
|
|
|
// Update libevent logging if BCLog::LIBEVENT has changed.
|
|
// If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
|
|
// in which case we should clear the BCLog::LIBEVENT flag.
|
|
// Throw an error if the user has explicitly asked to change only the libevent
|
|
// flag and it failed.
|
|
uint32_t changedLogCategories = originalLogCategories ^ logCategories;
|
|
if (changedLogCategories & BCLog::LIBEVENT) {
|
|
if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
|
|
logCategories &= ~BCLog::LIBEVENT;
|
|
if (changedLogCategories == BCLog::LIBEVENT) {
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
|
|
}
|
|
}
|
|
}
|
|
|
|
UniValue result(UniValue::VOBJ);
|
|
std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
|
|
for (const auto& logCatActive : vLogCatActive) {
|
|
result.pushKV(logCatActive.category, logCatActive.active);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
UniValue echo(const JSONRPCRequest& request)
|
|
{
|
|
if (request.fHelp)
|
|
throw std::runtime_error(
|
|
"echo|echojson \"message\" ...\n"
|
|
"\nSimply echo back the input arguments. This command is for testing.\n"
|
|
"\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in"
|
|
"bitcoin-cli and the GUI. There is no server-side difference."
|
|
);
|
|
|
|
return request.params;
|
|
}
|
|
|
|
static const CRPCCommand commands[] =
|
|
{ // category name actor (function) argNames
|
|
// --------------------- ------------------------ ----------------------- ----------
|
|
{ "control", "getmemoryinfo", &getmemoryinfo, {"mode"} },
|
|
{ "control", "logging", &logging, {"include", "exclude"}},
|
|
{ "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */
|
|
{ "util", "createmultisig", &createmultisig, {"nrequired","keys"} },
|
|
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
|
|
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
|
|
|
|
/* Not shown in help */
|
|
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}},
|
|
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
|
|
{ "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
|
|
};
|
|
|
|
void RegisterMiscRPCCommands(CRPCTable &t)
|
|
{
|
|
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
|
|
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
|
|
}
|