Merge bitcoin/bitcoin#33286: doc: update multisig tutorial to use multipath descriptors
de7c3587cddoc: Update add checksum instructions in tutorial (Ben Westgate)2a46e94a16doc: Update multisig-tutorial.md to use multipath descriptors (Ben Westgate) Pull request description: ### Summary Update `doc/multisig-tutorial.md` to use multipath descriptor format instead of separate external/internal descriptors. The tutorial now: - extracts a single `xpub_n` per participant - constructs a multipath `wsh(sortedmulti(...))` descriptor with `<0;1>` change index semantics - uses `getdescriptorinfo` to compute descriptor checksum - explains that `importdescriptors` expands the multipath descriptor into internal and external descriptors - update `/test/functional/wallet_multisig_descriptor_psbt.py` functional test / documentation to use multi-path descriptors --- ### Motivation A single multipath descriptor is the most convenient pattern for multisig; our documentation should use it. --- ### What changed - replaced extraction of `external_xpub_n` and `internal_xpub_n` with extraction of a single `xpub_n` - removed instructions to create and import separate external/internal descriptors - added instructions to build a multipath `wsh(sortedmulti(...))` descriptor and derive checksum with `getdescriptorinfo` - checksum field is parsed and appended as the multipath descriptor is not the canonical "desc" output - clarified that `importdescriptors` automatically expands multipath descriptors into internal and external forms - similar changes to the functional test: wallet_multisig_descriptor_psbt. --- ### Testing I have run the updated shell snippets and confirmed the multipath descriptor produces the same `listdescriptors` output after importing as the two descriptor method in bitcoin:master. --- ### Related issues / PRs This tutorial change references the multipath descriptor consolidation (see commit / PR referenced in the change). The commit message points to bitcoin#22838 as the upstream change that enables this behavior. --- ### Release note (for changelog) Documentation: update multisig tutorial and multisig functional test to use multipath descriptors ACKs for top commit: Sjors: utACKde7c3587cdkannapoix: Core review ACK:de7c3587cdachow101: ACKde7c3587cdrkrux: crACKde7c3587cdTree-SHA512: ca7275d8ad04922b3fa8d2a3084ff96aa2104265f1fc2f749814dd16776351ab692d67e7e643d08052f7492e3eaa1a9a4dfe3470163e57939a49f782d3df511a
This commit is contained in:
2 files changed
+51
-63
No files matched your search
@@ -25,12 +25,13 @@ class WalletMultisigDescriptorPSBTTest(BitcoinTestFramework):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
@staticmethod
|
||||
def _get_xpub(wallet, internal):
|
||||
def _get_xpub(wallet):
|
||||
"""Extract the wallet's xpubs using `listdescriptors` and pick the one from the `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses)."""
|
||||
pkh_descriptor = next(filter(lambda d: d["desc"].startswith("pkh(") and d["internal"] == internal, wallet.listdescriptors()["descriptors"]))
|
||||
pkh_descriptor = next(filter(lambda d: d["desc"].startswith("pkh(") and not d["internal"], wallet.listdescriptors()["descriptors"]))
|
||||
# Keep all key origin information (master key fingerprint and all derivation steps) for proper support of hardware devices
|
||||
# See section 'Key origin identification' in 'doc/descriptors.md' for more details...
|
||||
return pkh_descriptor["desc"].split("pkh(")[1].split(")")[0]
|
||||
# Replace the change index with the multipath convention
|
||||
return pkh_descriptor["desc"].split("pkh(")[1].split(")")[0].replace("/0/*", "/<0;1>/*")
|
||||
|
||||
@staticmethod
|
||||
def _check_psbt(psbt, to, value, multisig):
|
||||
@@ -44,26 +45,19 @@ class WalletMultisigDescriptorPSBTTest(BitcoinTestFramework):
|
||||
amount += vout["value"]
|
||||
assert_approx(amount, float(value), vspan=0.001)
|
||||
|
||||
def participants_create_multisigs(self, external_xpubs, internal_xpubs):
|
||||
def participants_create_multisigs(self, xpubs):
|
||||
"""The multisig is created by importing the following descriptors. The resulting wallet is watch-only and every participant can do this."""
|
||||
for i, node in enumerate(self.nodes):
|
||||
node.createwallet(wallet_name=f"{self.name}_{i}", blank=True, disable_private_keys=True)
|
||||
multisig = node.get_wallet_rpc(f"{self.name}_{i}")
|
||||
external = multisig.getdescriptorinfo(f"wsh(sortedmulti({self.M},{','.join(external_xpubs)}))")
|
||||
internal = multisig.getdescriptorinfo(f"wsh(sortedmulti({self.M},{','.join(internal_xpubs)}))")
|
||||
multisig_desc = f"wsh(sortedmulti({self.M},{','.join(xpubs)}))"
|
||||
checksum = multisig.getdescriptorinfo(multisig_desc)["checksum"]
|
||||
result = multisig.importdescriptors([
|
||||
{ # receiving addresses (internal: False)
|
||||
"desc": external["descriptor"],
|
||||
{ # Multipath descriptor expands to receive and change
|
||||
"desc": f"{multisig_desc}#{checksum}",
|
||||
"active": True,
|
||||
"internal": False,
|
||||
"timestamp": "now",
|
||||
},
|
||||
{ # change addresses (internal: True)
|
||||
"desc": internal["descriptor"],
|
||||
"active": True,
|
||||
"internal": True,
|
||||
"timestamp": "now",
|
||||
},
|
||||
}
|
||||
])
|
||||
assert all(r["success"] for r in result)
|
||||
yield multisig
|
||||
@@ -84,10 +78,10 @@ class WalletMultisigDescriptorPSBTTest(BitcoinTestFramework):
|
||||
}
|
||||
|
||||
self.log.info("Generate and exchange xpubs...")
|
||||
external_xpubs, internal_xpubs = [[self._get_xpub(signer, internal) for signer in participants["signers"]] for internal in [False, True]]
|
||||
xpubs = [self._get_xpub(signer) for signer in participants["signers"]]
|
||||
|
||||
self.log.info("Every participant imports the following descriptors to create the watch-only multisig...")
|
||||
participants["multisigs"] = list(self.participants_create_multisigs(external_xpubs, internal_xpubs))
|
||||
participants["multisigs"] = list(self.participants_create_multisigs(xpubs))
|
||||
|
||||
self.log.info("Check that every participant's multisig generates the same addresses...")
|
||||
for _ in range(10): # we check that the first 10 generated addresses are the same for all participant's multisigs
|
||||
|
||||
Reference in new issue
Block a user