Browse Source

Extend validateaddress information for P2SH-embedded witness

This adds new fields 'pubkeys' and 'embedded' to the RPC's output, and improves the
documentation for previously added 'witness_version' and 'witness_program' fields.
0.16
Pieter Wuille 7 years ago
parent
commit
3eaa003c88
  1. 82
      src/rpc/misc.cpp
  2. 4
      src/script/standard.h

82
src/rpc/misc.cpp

@ -40,6 +40,46 @@ public:
explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {} explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_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<unsigned char>> 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 = boost::apply_visitor(*this, embedded);
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 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
// inspecting multisig participants.
if (include_addresses) obj.pushKV("addresses", std::move(a));
}
UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const CKeyID &keyID) const { UniValue operator()(const CKeyID &keyID) const {
@ -60,19 +100,7 @@ public:
obj.push_back(Pair("isscript", true)); obj.push_back(Pair("isscript", true));
obj.push_back(Pair("iswitness", false)); obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetCScript(scriptID, subscript)) { if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
std::vector<CTxDestination> addresses; ProcessSubScript(subscript, obj, true);
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; return obj;
} }
@ -103,7 +131,7 @@ public:
uint160 hash; uint160 hash;
hasher.Write(id.begin(), 32).Finalize(hash.begin()); hasher.Write(id.begin(), 32).Finalize(hash.begin());
if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); ProcessSubScript(subscript, obj);
} }
return obj; return obj;
} }
@ -135,16 +163,25 @@ UniValue validateaddress(const JSONRPCRequest& request)
" \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\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" " \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n" " \"isscript\" : true|false, (boolean, optional) If the address is P2SH or P2WSH. Not included for unknown witness types.\n"
" \"script\" : \"type\" (string, optional) The output script type. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash\n" " \"iswitness\" : true|false, (boolean) If the address is P2WPKH, P2WSH, or an unknown witness version\n"
" \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n" " \"witness_version\" : version (number, optional) For all witness output types, gives the version number.\n"
" \"addresses\" (string, optional) Array of addresses associated with the known redeemscript\n" " \"witness_program\" : \"hex\" (string, optional) For all witness output types, gives the script or key hash present in the address.\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 or P2WSH address\n"
" \"addresses\" (string, optional) Array of addresses associated with the known redeemscript (only if \"iswitness\" is false). This field is superseded by the \"pubkeys\" field and the address inside \"embedded\".\n"
" [\n" " [\n"
" \"address\"\n" " \"address\"\n"
" ,...\n" " ,...\n"
" ]\n" " ]\n"
" \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output\n" " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n"
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\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 validateaddress output fields for the embedded address, excluding \"isvalid\", metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n"
" \"iscompressed\" : true|false, (boolean) If the address is compressed\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" " \"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" " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
@ -188,8 +225,9 @@ UniValue validateaddress(const JSONRPCRequest& request)
} }
if (pwallet) { if (pwallet) {
const CKeyMetadata* meta = nullptr; const CKeyMetadata* meta = nullptr;
if (const CKeyID* key_id = boost::get<CKeyID>(&dest)) { CKeyID key_id = GetKeyForDestination(*pwallet, dest);
auto it = pwallet->mapKeyMetadata.find(*key_id); if (!key_id.IsNull()) {
auto it = pwallet->mapKeyMetadata.find(key_id);
if (it != pwallet->mapKeyMetadata.end()) { if (it != pwallet->mapKeyMetadata.end()) {
meta = &it->second; meta = &it->second;
} }

4
src/script/standard.h

@ -155,6 +155,10 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* addressRet is populated with a single value and nRequiredRet is set to 1. * addressRet is populated with a single value and nRequiredRet is set to 1.
* Returns true if successful. Currently does not extract address from * Returns true if successful. Currently does not extract address from
* pay-to-witness scripts. * pay-to-witness scripts.
*
* Note: this function confuses destinations (a subset of CScripts that are
* encodable as an address) with key identifiers (of keys involved in a
* CScript), and its use should be phased out.
*/ */
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);

Loading…
Cancel
Save