Merge #18032: rpc: Output a descriptor in createmultisig and addmultisigaddress

19a354b11f Output a descriptor in createmultisig and addmultisigaddress (Andrew Chow)

Pull request description:

  Give a descriptor from `createmultisig` and `addmultisigaddress`.

  Extracted from #16528 with `addmultisgaddress` and tests added.

ACKs for top commit:
  Sjors:
    tACK 19a354b11f
  MarcoFalke:
    ACK 19a354b11f
  promag:
    Code review ACK 19a354b11f.
  meshcollider:
    utACK 19a354b11f

Tree-SHA512: e813125fbbc358ea8d45b1748de16a29a94efd83175b748fb8fa3b0bfc8e783ed36b6c554d84f5d4ead1ba252a83a3e937b6c3f75da7b8d3b4e55f94d6013771
This commit is contained in:
MarcoFalke 2020-02-09 04:55:29 -08:00
commit 75fb37ce68
No known key found for this signature in database
GPG key ID: CE2B75697E69A548
5 changed files with 33 additions and 1 deletions

View file

@ -17,6 +17,7 @@ Supporting RPCs are:
(`regtest` only, since v0.19). (`regtest` only, since v0.19).
- `utxoupdatepsbt` takes as input descriptors to add information to the psbt - `utxoupdatepsbt` takes as input descriptors to add information to the psbt
(since v0.19). (since v0.19).
- `createmultisig` and `addmultisigaddress` return descriptors as well (since v0.20)
This document describes the language. For the specifics on usage, see the RPC This document describes the language. For the specifics on usage, see the RPC
documentation for the functions mentioned above. documentation for the functions mentioned above.

View file

@ -83,6 +83,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"{\n" "{\n"
" \"address\":\"multisigaddress\", (string) The value of the new multisig address.\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" " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
" \"descriptor\":\"descriptor\" (string) The descriptor for this multisig\n"
"}\n" "}\n"
}, },
RPCExamples{ RPCExamples{
@ -119,9 +120,13 @@ static UniValue createmultisig(const JSONRPCRequest& request)
CScript inner; CScript inner;
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner); const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
// Make the descriptor
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest)); result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
result.pushKV("descriptor", descriptor->ToString());
return result; return result;
} }

View file

@ -974,6 +974,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"{\n" "{\n"
" \"address\":\"multisigaddress\", (string) The value of the new multisig address.\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" " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
" \"descriptor\":\"descriptor\" (string) The descriptor for this multisig\n"
"}\n" "}\n"
}, },
RPCExamples{ RPCExamples{
@ -1018,9 +1019,13 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner); CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner);
pwallet->SetAddressBook(dest, label, "send"); pwallet->SetAddressBook(dest, label, "send");
// Make the descriptor
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest)); result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
result.pushKV("descriptor", descriptor->ToString());
return result; return result;
} }

View file

@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test multisig RPCs""" """Test multisig RPCs"""
from test_framework.descriptors import descsum_create from test_framework.descriptors import descsum_create, drop_origins
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import ( from test_framework.util import (
assert_raises_rpc_error, assert_raises_rpc_error,
@ -116,9 +116,20 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
def do_multisig(self): def do_multisig(self):
node0, node1, node2 = self.nodes node0, node1, node2 = self.nodes
# Construct the expected descriptor
desc = 'multi({},{})'.format(self.nsigs, ','.join(self.pub))
if self.output_type == 'legacy':
desc = 'sh({})'.format(desc)
elif self.output_type == 'p2sh-segwit':
desc = 'sh(wsh({}))'.format(desc)
elif self.output_type == 'bech32':
desc = 'wsh({})'.format(desc)
desc = descsum_create(desc)
msig = node2.createmultisig(self.nsigs, self.pub, self.output_type) msig = node2.createmultisig(self.nsigs, self.pub, self.output_type)
madd = msig["address"] madd = msig["address"]
mredeem = msig["redeemScript"] mredeem = msig["redeemScript"]
assert_equal(desc, msig['descriptor'])
if self.output_type == 'bech32': if self.output_type == 'bech32':
assert madd[0:4] == "bcrt" # actually a bech32 address assert madd[0:4] == "bcrt" # actually a bech32 address
@ -126,6 +137,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
msigw = node1.addmultisigaddress(self.nsigs, self.pub, None, self.output_type) msigw = node1.addmultisigaddress(self.nsigs, self.pub, None, self.output_type)
maddw = msigw["address"] maddw = msigw["address"]
mredeemw = msigw["redeemScript"] mredeemw = msigw["redeemScript"]
assert_equal(desc, drop_origins(msigw['descriptor']))
# addmultisigiaddress and createmultisig work the same # addmultisigiaddress and createmultisig work the same
assert maddw == madd assert maddw == madd
assert mredeemw == mredeem assert mredeemw == mredeem

View file

@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utility functions related to output descriptors""" """Utility functions related to output descriptors"""
import re
INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ " INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd] GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd]
@ -53,3 +55,10 @@ def descsum_check(s, require=True):
return False return False
symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]] symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
return descsum_polymod(symbols) == 1 return descsum_polymod(symbols) == 1
def drop_origins(s):
'''Drop the key origins from a descriptor'''
desc = re.sub(r'\[.+?\]', '', s)
if '#' in s:
desc = desc[:desc.index('#')]
return descsum_create(desc)