Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcwallet.cpp
Show All 17 Lines | |||||
#include <rpc/rawtransaction.h> | #include <rpc/rawtransaction.h> | ||||
#include <rpc/server.h> | #include <rpc/server.h> | ||||
#include <rpc/util.h> | #include <rpc/util.h> | ||||
#include <timedata.h> | #include <timedata.h> | ||||
#include <util.h> | #include <util.h> | ||||
#include <utilmoneystr.h> | #include <utilmoneystr.h> | ||||
#include <validation.h> | #include <validation.h> | ||||
#include <wallet/coincontrol.h> | #include <wallet/coincontrol.h> | ||||
#include <wallet/rpcwallet.h> | |||||
#include <wallet/wallet.h> | #include <wallet/wallet.h> | ||||
#include <wallet/walletdb.h> | #include <wallet/walletdb.h> | ||||
#include <wallet/walletutil.h> | #include <wallet/walletutil.h> | ||||
// Input src/init.h (not wallet/init.h) for StartShutdown | // Input src/init.h (not wallet/init.h) for StartShutdown | ||||
#include <init.h> | #include <init.h> | ||||
#include <univalue.h> | #include <univalue.h> | ||||
▲ Show 20 Lines • Show All 3,921 Lines • ▼ Show 20 Lines | if (!stopBlock) { | ||||
"Rescan failed. Potentially corrupted data files."); | "Rescan failed. Potentially corrupted data files."); | ||||
} | } | ||||
UniValue response(UniValue::VOBJ); | UniValue response(UniValue::VOBJ); | ||||
response.pushKV("start_height", pindexStart->nHeight); | response.pushKV("start_height", pindexStart->nHeight); | ||||
response.pushKV("stop_height", stopBlock->nHeight); | response.pushKV("stop_height", stopBlock->nHeight); | ||||
return response; | return response; | ||||
} | } | ||||
class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> { | |||||
public: | |||||
CWallet *const pwallet; | |||||
void ProcessSubScript(const CScript &subscript, UniValue &obj, | |||||
bool include_addresses = false) const { | |||||
// Always present: script type and redeemscript | |||||
txnouttype which_type; | |||||
std::vector<std::vector<uint8_t>> solutions_data; | |||||
Solver(subscript, which_type, solutions_data); | |||||
obj.pushKV("script", GetTxnOutputType(which_type)); | |||||
obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); | |||||
CTxDestination embedded; | |||||
UniValue a(UniValue::VARR); | |||||
if (ExtractDestination(subscript, embedded)) { | |||||
// Only when the script corresponds to an address. | |||||
UniValue subobj(UniValue::VOBJ); | |||||
UniValue detail = DescribeAddress(embedded); | |||||
subobj.pushKVs(detail); | |||||
UniValue wallet_detail = boost::apply_visitor(*this, embedded); | |||||
subobj.pushKVs(wallet_detail); | |||||
subobj.pushKV("address", EncodeDestination(embedded)); | |||||
subobj.pushKV("scriptPubKey", | |||||
HexStr(subscript.begin(), subscript.end())); | |||||
// Always report the pubkey at the top level, so that | |||||
// `getnewaddress()['pubkey']` always works. | |||||
if (subobj.exists("pubkey")) { | |||||
obj.pushKV("pubkey", subobj["pubkey"]); | |||||
} | |||||
obj.pushKV("embedded", std::move(subobj)); | |||||
if (include_addresses) { | |||||
a.push_back(EncodeDestination(embedded)); | |||||
} | |||||
} else if (which_type == TX_MULTISIG) { | |||||
// Also report some information on multisig scripts (which do not | |||||
// have a corresponding address). | |||||
// TODO: abstract out the common functionality between this logic | |||||
// and ExtractDestinations. | |||||
obj.pushKV("sigsrequired", solutions_data[0][0]); | |||||
UniValue pubkeys(UniValue::VARR); | |||||
for (size_t i = 1; i < solutions_data.size() - 1; ++i) { | |||||
CPubKey key(solutions_data[i].begin(), solutions_data[i].end()); | |||||
if (include_addresses) { | |||||
a.push_back(EncodeDestination(key.GetID())); | |||||
} | |||||
pubkeys.push_back(HexStr(key.begin(), key.end())); | |||||
} | |||||
obj.pushKV("pubkeys", std::move(pubkeys)); | |||||
} | |||||
// The "addresses" field is confusing because it refers to public keys | |||||
// using their P2PKH address. For that reason, only add the 'addresses' | |||||
// field when needed for backward compatibility. New applications can | |||||
// use the 'pubkeys' field for inspecting multisig participants. | |||||
if (include_addresses) { | |||||
obj.pushKV("addresses", std::move(a)); | |||||
} | |||||
} | |||||
explicit DescribeWalletAddressVisitor(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; | |||||
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { | |||||
obj.pushKV("pubkey", HexStr(vchPubKey)); | |||||
obj.pushKV("iscompressed", vchPubKey.IsCompressed()); | |||||
} | |||||
return obj; | |||||
} | |||||
UniValue operator()(const CScriptID &scriptID) const { | |||||
UniValue obj(UniValue::VOBJ); | |||||
CScript subscript; | |||||
if (pwallet && pwallet->GetCScript(scriptID, subscript)) { | |||||
ProcessSubScript(subscript, obj, true); | |||||
} | |||||
return obj; | |||||
} | |||||
}; | |||||
UniValue DescribeWalletAddress(CWallet *pwallet, const CTxDestination &dest) { | |||||
UniValue ret(UniValue::VOBJ); | |||||
UniValue detail = DescribeAddress(dest); | |||||
ret.pushKVs(detail); | |||||
ret.pushKVs( | |||||
boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest)); | |||||
return ret; | |||||
} | |||||
UniValue getaddressinfo(const Config &config, const JSONRPCRequest &request) { | |||||
CWallet *const pwallet = GetWalletForJSONRPCRequest(request); | |||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { | |||||
return NullUniValue; | |||||
} | |||||
if (request.fHelp || request.params.size() != 1) { | |||||
throw std::runtime_error( | |||||
"getaddressinfo \"address\"\n" | |||||
"\nReturn information about the given bitcoin address. Some " | |||||
"information requires the address\n" | |||||
"to be in the wallet.\n" | |||||
"\nArguments:\n" | |||||
"1. \"address\" (string, required) The bitcoin " | |||||
"address to get the information of.\n" | |||||
"\nResult:\n" | |||||
"{\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. Only if \"isscript\" is true and the redeemscript is " | |||||
"known. Possible types: nonstandard, pubkey, pubkeyhash, " | |||||
"scripthash, multisig, nulldata, witness_v0_keyhash, " | |||||
"witness_v0_scripthash, witness_unknown\n" | |||||
" \"hex\" : \"hex\", (string, optional) The " | |||||
"redeemscript for the p2sh address\n" | |||||
" \"pubkeys\" (string, optional) Array of " | |||||
"pubkeys associated with the known redeemscript (only if " | |||||
"\"script\" is \"multisig\")\n" | |||||
" [\n" | |||||
" \"pubkey\"\n" | |||||
" ,...\n" | |||||
" ]\n" | |||||
" \"sigsrequired\" : xxxxx (numeric, optional) Number of " | |||||
"signatures required to spend multisig output (only if \"script\" " | |||||
"is \"multisig\")\n" | |||||
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex " | |||||
"value of the raw public key, for single-key addresses (possibly " | |||||
"embedded in P2SH or P2WSH)\n" | |||||
" \"embedded\" : {...}, (object, optional) Information " | |||||
"about the address embedded in P2SH or P2WSH, if relevant and " | |||||
"known. It includes all getaddressinfo output fields for the " | |||||
"embedded address, excluding metadata (\"timestamp\", " | |||||
"\"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet " | |||||
"(\"ismine\", \"iswatchonly\", \"account\").\n" | |||||
" \"iscompressed\" : true|false, (boolean) If the address is " | |||||
"compressed\n" | |||||
" \"account\" : \"account\" (string) 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("getaddressinfo", | |||||
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + | |||||
HelpExampleRpc("getaddressinfo", | |||||
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")); | |||||
} | |||||
LOCK(pwallet->cs_wallet); | |||||
UniValue ret(UniValue::VOBJ); | |||||
CTxDestination dest = | |||||
DecodeDestination(request.params[0].get_str(), config.GetChainParams()); | |||||
// Make sure the destination is valid | |||||
if (!IsValidDestination(dest)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |||||
} | |||||
std::string currentAddress = EncodeDestination(dest); | |||||
ret.pushKV("address", currentAddress); | |||||
CScript scriptPubKey = GetScriptForDestination(dest); | |||||
ret.pushKV("scriptPubKey", | |||||
HexStr(scriptPubKey.begin(), scriptPubKey.end())); | |||||
isminetype mine = IsMine(*pwallet, dest); | |||||
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); | |||||
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); | |||||
UniValue detail = DescribeWalletAddress(pwallet, dest); | |||||
ret.pushKVs(detail); | |||||
if (pwallet->mapAddressBook.count(dest)) { | |||||
ret.pushKV("account", pwallet->mapAddressBook[dest].name); | |||||
} | |||||
const CKeyMetadata *meta = nullptr; | |||||
CKeyID key_id = GetKeyForDestination(*pwallet, dest); | |||||
if (!key_id.IsNull()) { | |||||
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.pushKV("timestamp", meta->nCreateTime); | |||||
if (!meta->hdKeypath.empty()) { | |||||
ret.pushKV("hdkeypath", meta->hdKeypath); | |||||
ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex()); | |||||
} | |||||
} | |||||
return ret; | |||||
} | |||||
// clang-format off | // clang-format off | ||||
static const ContextFreeRPCCommand commands[] = { | static const ContextFreeRPCCommand commands[] = { | ||||
// category name actor (function) argNames | // category name actor (function) argNames | ||||
// ------------------- ------------------------ ---------------------- ---------- | // ------------------- ------------------------ ---------------------- ---------- | ||||
{ "rawtransactions", "fundrawtransaction", fundrawtransaction, {"hexstring","options"} }, | { "rawtransactions", "fundrawtransaction", fundrawtransaction, {"hexstring","options"} }, | ||||
{ "hidden", "resendwallettransactions", resendwallettransactions, {} }, | { "hidden", "resendwallettransactions", resendwallettransactions, {} }, | ||||
{ "wallet", "abandontransaction", abandontransaction, {"txid"} }, | { "wallet", "abandontransaction", abandontransaction, {"txid"} }, | ||||
{ "wallet", "addmultisigaddress", addmultisigaddress, {"nrequired","keys","label|account"} }, | { "wallet", "addmultisigaddress", addmultisigaddress, {"nrequired","keys","label|account"} }, | ||||
{ "wallet", "backupwallet", backupwallet, {"destination"} }, | { "wallet", "backupwallet", backupwallet, {"destination"} }, | ||||
{ "wallet", "encryptwallet", encryptwallet, {"passphrase"} }, | { "wallet", "encryptwallet", encryptwallet, {"passphrase"} }, | ||||
{ "wallet", "getaccountaddress", getlabeladdress, {"account"} }, | { "wallet", "getaccountaddress", getlabeladdress, {"account"} }, | ||||
{ "wallet", "getlabeladdress", getlabeladdress, {"label"} }, | { "wallet", "getlabeladdress", getlabeladdress, {"label"} }, | ||||
{ "wallet", "getaccount", getaccount, {"address"} }, | { "wallet", "getaccount", getaccount, {"address"} }, | ||||
{ "wallet", "getaddressesbyaccount", getaddressesbyaccount, {"account"} }, | { "wallet", "getaddressesbyaccount", getaddressesbyaccount, {"account"} }, | ||||
{ "wallet", "getaddressinfo", getaddressinfo, {"address"} }, | |||||
{ "wallet", "getbalance", getbalance, {"account","minconf","include_watchonly"} }, | { "wallet", "getbalance", getbalance, {"account","minconf","include_watchonly"} }, | ||||
{ "wallet", "getnewaddress", getnewaddress, {"label|account", "address_type"} }, | { "wallet", "getnewaddress", getnewaddress, {"label|account", "address_type"} }, | ||||
{ "wallet", "getrawchangeaddress", getrawchangeaddress, {"address_type"} }, | { "wallet", "getrawchangeaddress", getrawchangeaddress, {"address_type"} }, | ||||
{ "wallet", "getreceivedbylabel", getreceivedbylabel, {"label","minconf"} }, | { "wallet", "getreceivedbylabel", getreceivedbylabel, {"label","minconf"} }, | ||||
{ "wallet", "getreceivedbyaccount", getreceivedbylabel, {"account","minconf"} }, | { "wallet", "getreceivedbyaccount", getreceivedbylabel, {"account","minconf"} }, | ||||
{ "wallet", "getreceivedbyaddress", getreceivedbyaddress, {"address","minconf"} }, | { "wallet", "getreceivedbyaddress", getreceivedbyaddress, {"address","minconf"} }, | ||||
{ "wallet", "gettransaction", gettransaction, {"txid","include_watchonly"} }, | { "wallet", "gettransaction", gettransaction, {"txid","include_watchonly"} }, | ||||
{ "wallet", "getunconfirmedbalance", getunconfirmedbalance, {} }, | { "wallet", "getunconfirmedbalance", getunconfirmedbalance, {} }, | ||||
Show All 35 Lines |