diff --git a/doc/descriptors.md b/doc/descriptors.md index e31665b12..181ff77e5 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 56bd33b0e..f360cb752 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -83,6 +83,7 @@ static UniValue createmultisig(const JSONRPCRequest& request) "{\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" + " \"descriptor\":\"descriptor\" (string) The descriptor for this multisig\n" "}\n" }, RPCExamples{ @@ -119,9 +120,13 @@ static UniValue createmultisig(const JSONRPCRequest& request) CScript inner; const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, 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.begin(), inner.end())); + result.pushKV("descriptor", descriptor->ToString()); return result; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 00c4a7427..dc9124e1a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -974,6 +974,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) "{\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" + " \"descriptor\":\"descriptor\" (string) The descriptor for this multisig\n" "}\n" }, RPCExamples{ @@ -1018,9 +1019,13 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, 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.begin(), inner.end())); + result.pushKV("descriptor", descriptor->ToString()); return result; } diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index aa7f12848..a98371617 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, @@ -116,9 +116,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 @@ -126,6 +137,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 29482ce01..46b405749 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)