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

19a354b11f85a3c6c81ff83bf702bf7a40cf5046 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 19a354b11f85a3c6c81ff83bf702bf7a40cf5046
  MarcoFalke:
    ACK 19a354b11f85a3c6c81ff83bf702bf7a40cf5046
  promag:
    Code review ACK 19a354b11f85a3c6c81ff83bf702bf7a40cf5046.
  meshcollider:
    utACK 19a354b11f85a3c6c81ff83bf702bf7a40cf5046

Tree-SHA512: e813125fbbc358ea8d45b1748de16a29a94efd83175b748fb8fa3b0bfc8e783ed36b6c554d84f5d4ead1ba252a83a3e937b6c3f75da7b8d3b4e55f94d6013771
This commit is contained in:
MarcoFalke 2020-02-09 04:55:29 -08:00 committed by UdjinM6
parent be3b359fa3
commit dceee33ebe
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

@ -255,6 +255,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
{ {
{RPCResult::Type::STR, "address", "The value of the new multisig address."}, {RPCResult::Type::STR, "address", "The value of the new multisig address."},
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."}, {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig."},
} }
}, },
RPCExamples{ RPCExamples{
@ -283,9 +284,13 @@ static UniValue createmultisig(const JSONRPCRequest& request)
CScript inner; CScript inner;
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, keystore, inner); const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, 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)); result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result; return result;
} }

View File

@ -980,6 +980,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
{ {
{RPCResult::Type::STR, "address", "The value of the new multisig address"}, {RPCResult::Type::STR, "address", "The value of the new multisig address"},
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"}, {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig."},
} }
}, },
RPCExamples{ RPCExamples{
@ -1020,9 +1021,13 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, spk_man, inner); CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, 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)); result.pushKV("redeemScript", HexStr(inner));
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,
@ -115,9 +115,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
@ -125,6 +136,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)