Output a descriptor in createmultisig and addmultisigaddress

This commit is contained in:
Andrew Chow 2019-03-25 17:00:00 -04:00
parent 3e1bf71064
commit 19a354b11f
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)