Skip to content

Commit

Permalink
Add descriptors to listunspent and getaddressinfo + tests
Browse files Browse the repository at this point in the history
Summary: This is a partial backport of Core [[bitcoin/bitcoin#14477 | PR14477]] : bitcoin/bitcoin@16203d5

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D6136
  • Loading branch information
sipa authored and deadalnix committed May 19, 2020
1 parent 8a2bca9 commit 3192309
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
18 changes: 18 additions & 0 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <shutdown.h>
#include <timedata.h>
#include <util/error.h>
Expand Down Expand Up @@ -3353,6 +3354,8 @@ static UniValue listunspent(const Config &config,
"private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to "
"spend this output, ignoring the lack of keys\n"
" \"desc\" : xxx, (string, only when solvable) "
"A descriptor for spending this output\n"
" \"safe\" : xxx (bool) Whether this output is "
"considered safe to spend. Unconfirmed transactions\n"
" from outside keys are "
Expand Down Expand Up @@ -3504,6 +3507,10 @@ static UniValue listunspent(const Config &config,
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
auto descriptor = InferDescriptor(scriptPubKey, *pwallet);
entry.pushKV("desc", descriptor->ToString());
}
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
Expand Down Expand Up @@ -4177,6 +4184,12 @@ UniValue getaddressinfo(const Config &config, const JSONRPCRequest &request) {
"yours or not\n"
" \"iswatchonly\" : true|false, (boolean) If the address is "
"watchonly\n"
" \"solvable\" : true|false, (boolean) Whether we know "
"how to spend coins sent to this address, ignoring the "
"possible lack of private keys\n"
" \"desc\" : \"desc\", (string, optional) A "
"descriptor for spending coins sent to this address (only when "
"solvable)\n"
" \"isscript\" : true|false, (boolean) If the key is a "
"script\n"
" \"ischange\" : true|false, (boolean) If the address "
Expand Down Expand Up @@ -4260,6 +4273,11 @@ UniValue getaddressinfo(const Config &config, const JSONRPCRequest &request) {

isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
bool solvable = IsSolvable(*pwallet, scriptPubKey);
ret.pushKV("solvable", solvable);
if (solvable) {
ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString());
}
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
Expand Down
56 changes: 56 additions & 0 deletions test/functional/wallet_address_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ def test_address(self, node, address, multisig, typ):
self.log.info(address)
info = self.nodes[node].getaddressinfo(address)
assert(self.nodes[node].validateaddress(address)['isvalid'])
assert_equal(info.get('solvable'), True)

if not multisig and typ == 'legacy':
# P2PKH
assert(not info['isscript'])
Expand All @@ -88,6 +90,41 @@ def test_address(self, node, address, multisig, typ):
# Unknown type
assert(False)

def test_desc(self, node, address, multisig, typ, utxo):
"""Run sanity checks on a descriptor reported by getaddressinfo."""
info = self.nodes[node].getaddressinfo(address)
assert('desc' in info)

assert_equal(info['desc'], utxo['desc'])
assert(self.nodes[node].validateaddress(address)['isvalid'])

# Use a ridiculously roundabout way to find the key origin info through
# the PSBT logic. However, this does test consistency between the PSBT reported
# fingerprints/paths and the descriptor logic.
psbt = self.nodes[node].createpsbt(
[{'txid': utxo['txid'], 'vout':utxo['vout']}], [{address: 0.00010000}])
psbt = self.nodes[node].walletprocesspsbt(
psbt, False, "ALL|FORKID", True)
decode = self.nodes[node].decodepsbt(psbt['psbt'])
key_descs = {}
for deriv in decode['inputs'][0]['bip32_derivs']:
assert_equal(len(deriv['master_fingerprint']), 8)
assert_equal(deriv['path'][0], 'm')
key_descs[deriv['pubkey']] = '[' + deriv['master_fingerprint'] + \
deriv['path'][1:] + ']' + deriv['pubkey']

if not multisig and typ == 'legacy':
# P2PKH
assert_equal(info['desc'],
"pkh({})".format(key_descs[info['pubkey']]))
elif typ == 'legacy':
# P2SH-multisig
assert_equal(info['desc'], "sh(multi(2,{},{}))".format(
key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]]))
else:
# Unknown type
assert(False)

def test_change_output_type(
self, node_sender, destinations, expected_type):
txid = self.nodes[node_sender].sendmany(
Expand Down Expand Up @@ -152,6 +189,7 @@ def run_test(self):
101).quantize(
Decimal("0.00000001"))
sends = {}
addresses = {}

self.log.debug("Prepare sends")
for n, to_node in enumerate(range(from_node, from_node + 4)):
Expand All @@ -175,6 +213,7 @@ def run_test(self):

# Output entry
sends[address] = to_send * 10 * (1 + n)
addresses[to_node] = (address, typ)

self.log.debug("Sending: {}".format(sends))
self.nodes[from_node].sendmany("", sends)
Expand All @@ -192,6 +231,23 @@ def run_test(self):
self.nodes[4].generate(1)
sync_blocks(self.nodes)

# Verify that the receiving wallet contains a UTXO with the
# expected address, and expected descriptor
for n, to_node in enumerate(range(from_node, from_node + 4)):
to_node %= 4
found = False
for utxo in self.nodes[to_node].listunspent():
if utxo['address'] == addresses[to_node][0]:
found = True
self.test_desc(
to_node,
addresses[to_node][0],
multisig,
addresses[to_node][1],
utxo)
break
assert found

new_balances = self.get_balances()
self.log.debug("Check new balances: {}".format(new_balances))
# We don't know what fee was set, so we can only check bounds on
Expand Down

0 comments on commit 3192309

Please sign in to comment.