From dceee33ebe036ec9ad345b55a4466f218b18dbe2 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 9 Feb 2020 04:55:29 -0800 Subject: [PATCH] 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 --- doc/descriptors.md | 1 + src/rpc/misc.cpp | 5 +++++ src/wallet/rpcwallet.cpp | 5 +++++ test/functional/rpc_createmultisig.py | 14 +++++++++++++- test/functional/test_framework/descriptors.py | 9 +++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/doc/descriptors.md b/doc/descriptors.md index 5758140cda..f8040e823d 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -17,6 +17,7 @@ Supporting RPCs are: (`regtest` only, since v0.19). - `utxoupdatepsbt` takes as input descriptors to add information to the psbt (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 documentation for the functions mentioned above. diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index fa92165710..8776aa4908 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -255,6 +255,7 @@ static UniValue createmultisig(const JSONRPCRequest& request) { {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, "descriptor", "The descriptor for this multisig."}, } }, RPCExamples{ @@ -283,9 +284,13 @@ static UniValue createmultisig(const JSONRPCRequest& request) CScript inner; const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, keystore, inner); + // Make the descriptor + std::unique_ptr descriptor = InferDescriptor(GetScriptForDestination(dest), keystore); + UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(dest)); result.pushKV("redeemScript", HexStr(inner)); + result.pushKV("descriptor", descriptor->ToString()); return result; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4bd5a4cc74..6c68ce3e48 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -980,6 +980,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) { {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, "descriptor", "The descriptor for this multisig."}, } }, RPCExamples{ @@ -1020,9 +1021,13 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, spk_man, inner); pwallet->SetAddressBook(dest, label, "send"); + // Make the descriptor + std::unique_ptr descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man); + UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(dest)); result.pushKV("redeemScript", HexStr(inner)); + result.pushKV("descriptor", descriptor->ToString()); return result; } diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index 2a64a29967..3f1558cbbb 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """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.util import ( assert_raises_rpc_error, @@ -115,9 +115,20 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): def do_multisig(self): 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) madd = msig["address"] mredeem = msig["redeemScript"] + assert_equal(desc, msig['descriptor']) if self.output_type == 'bech32': 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) maddw = msigw["address"] mredeemw = msigw["redeemScript"] + assert_equal(desc, drop_origins(msigw['descriptor'])) # addmultisigiaddress and createmultisig work the same assert maddw == madd assert mredeemw == mredeem diff --git a/test/functional/test_framework/descriptors.py b/test/functional/test_framework/descriptors.py index 29482ce01e..46b405749b 100644 --- a/test/functional/test_framework/descriptors.py +++ b/test/functional/test_framework/descriptors.py @@ -4,6 +4,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Utility functions related to output descriptors""" +import re + INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ " CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd] @@ -53,3 +55,10 @@ def descsum_check(s, require=True): return False symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]] 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)