Skip to content

script: support BIP-388 wallet policies rendering#114

Open
rxbryan wants to merge 1 commit into
Sjors:masterfrom
rxbryan:bip388-wallet-policy-sjors
Open

script: support BIP-388 wallet policies rendering#114
rxbryan wants to merge 1 commit into
Sjors:masterfrom
rxbryan:bip388-wallet-policy-sjors

Conversation

@rxbryan
Copy link
Copy Markdown

@rxbryan rxbryan commented May 23, 2026

Currently, Bitcoin Core has no support for generating BIP-388 wallet policies from descriptors. Hardware signing devices use these policies to display spending conditions before signing.

This PR adds wallet_policy_template and wallet_policy_keys to the getdescriptorinfo RPC response for BIP-388 policy-compatible multipath <M;N>/* descriptors. Per Sjors's comment on bitcoin#32659, getdescriptorinfo is the recommended place to start.

The bulk of the work is in src/script/descriptor.cpp.

  • Introduces three new virtual methods ToTemplateString, GetDerivationIndex, GetKeyOrigins in PubkeyProvider.
  • Introduces two new virtual method BuildWalletPolicy and BuildWalletPolicyTemplate in DescriptorImpl.
  • A new ParseDescriptorInfo() centralizes descriptor analysis and returns a structured DescriptorInfo, so the Bitcoin Core GUI and external signer tooling can reuse it.

Supported: pkh, wpkh, sh, wsh, tr, multi, sortedmulti, multi_a, sortedmulti_a. tr() may use musig() in the keypath or script leaves.

  • Miniscript support for wsh and tr
  • Key deduplication and disjointness checks for all keys, including MuSig participant and aggregate keys
  • Test coverage for the BIP-388 spec vectors, including negative cases for repeated keys, non-disjoint multipath expressions, and derivation-before-aggregation in musig()

Examples:

./build/bin/bitcoin-cli -regtest getdescriptorinfo   "tr([6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/<0;1>/*,{sortedmulti_a(1,[6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/<2;3>/*,[a5fbe580/86h/1h/0h]tpubDCJjvYf4BeLDy1o2qEJ4cyuqSDvEr3PmygtzYgQRGA6KuV2djdXw2Sq8x9F34KYHM8V5eRdmhXuagaEzbsvroj2k1CKcjcFC3ouvRRkBgZA/<0;1>/*),pk([a5fbe580/86h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth/<0;1>/*)})"
{
  "descriptor": "tr([6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/0/*,{sortedmulti_a(1,[6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/2/*,[a5fbe580/86h/1h/0h]tpubDCJjvYf4BeLDy1o2qEJ4cyuqSDvEr3PmygtzYgQRGA6KuV2djdXw2Sq8x9F34KYHM8V5eRdmhXuagaEzbsvroj2k1CKcjcFC3ouvRRkBgZA/0/*),pk([a5fbe580/86h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth/0/*)})#e7m79gm2",
  "multipath_expansion": [
    "tr([6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/0/*,{sortedmulti_a(1,[6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/2/*,[a5fbe580/86h/1h/0h]tpubDCJjvYf4BeLDy1o2qEJ4cyuqSDvEr3PmygtzYgQRGA6KuV2djdXw2Sq8x9F34KYHM8V5eRdmhXuagaEzbsvroj2k1CKcjcFC3ouvRRkBgZA/0/*),pk([a5fbe580/86h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth/0/*)})#e7m79gm2",
    "tr([6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/1/*,{sortedmulti_a(1,[6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx/3/*,[a5fbe580/86h/1h/0h]tpubDCJjvYf4BeLDy1o2qEJ4cyuqSDvEr3PmygtzYgQRGA6KuV2djdXw2Sq8x9F34KYHM8V5eRdmhXuagaEzbsvroj2k1CKcjcFC3ouvRRkBgZA/1/*),pk([a5fbe580/86h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth/1/*)})#x7w2qh8j"
  ],
  "checksum": "lu9vfk4s",
  "isrange": true,
  "issolvable": true,
  "hasprivatekeys": false,
  "wallet_policy_template": "tr(@0/**,{sortedmulti_a(1,@0/<2;3>/*,@1/**),pk(@2/**)})",
  "wallet_policy_keys": [
    "[6738736c/86h/0h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx",
    "[a5fbe580/86h/1h/0h]tpubDCJjvYf4BeLDy1o2qEJ4cyuqSDvEr3PmygtzYgQRGA6KuV2djdXw2Sq8x9F34KYHM8V5eRdmhXuagaEzbsvroj2k1CKcjcFC3ouvRRkBgZA",
    "[a5fbe580/86h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth"
  ]
}
./build/bin/bitcoin-cli -regtest getdescriptorinfo "tr(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/<0;1>/*,{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth)/<0;1>/*),older(12960)),{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/<0;1>/*),older(12960)),and_v(v:pk(musig([a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/<0;1>/*),older(12960))}})"
{
  "descriptor": "tr(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/0/*,{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth)/0/*),older(12960)),{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/0/*),older(12960)),and_v(v:pk(musig([a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/0/*),older(12960))}})#kh3lv33d",
  "multipath_expansion": [
    "tr(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/0/*,{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth)/0/*),older(12960)),{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/0/*),older(12960)),and_v(v:pk(musig([a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/0/*),older(12960))}})#kh3lv33d",
    "tr(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/1/*,{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth)/1/*),older(12960)),{and_v(v:pk(musig([a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/1/*),older(12960)),and_v(v:pk(musig([a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth,[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys)/1/*),older(12960))}})#vuhahajv"
  ],
  "checksum": "se2qhdlw",
  "isrange": true,
  "issolvable": true,
  "hasprivatekeys": false,
  "wallet_policy_template": "tr(musig(@0,@1,@2)/**,{and_v(v:pk(musig(@0,@1)/**),older(12960)),{and_v(v:pk(musig(@0,@2)/**),older(12960)),and_v(v:pk(musig(@1,@2)/**),older(12960))}})",
  "wallet_policy_keys": [
    "[a5fbe580/84h/1h/0h]tpubDDCzbWt7MLgDapZVYVXfEXCT1GBLELmP7e9ax6t3qVeFP5CmFrJBxZty2F58CoVSsMozrR8dfyHWxSdQJKSno9d44dyKn2ajKbxkPCppYLx",
    "[a5fbe580/44h/1h/0h]tpubDDEhkX7QU6DQ6tYvYERTNxD7FuhfTj3rZqbu1eCxxTrgzRNefT4u3Z5om6WYu2aDudFbdE7GdoCEqLd63b8aVXSzEsh7Fj89Gv1LtkHAfth",
    "[a5fbe580/49h/1h/0h]tpubDCyCF2YKcTy76LBDXFZCM6Cfkmh81KJuFYRswyLFfumpdfH2nDnDsS4iJFUkZCVFYshGFCS9PVtqWZofnRohVnyvfa5qt8NiebkK8v13vys"
  ]
}

We fully support the BIP, and all test vectors pass.

TODO:

  • Expose ParseDescriptorInfo through interfaces::Node

Refs bitcoin#32659

@rxbryan
Copy link
Copy Markdown
Author

rxbryan commented May 23, 2026

This continues work from bitcoin#35287.

- Extend getdescriptorinfo to optionally return BIP-388 wallet policy
  template and keys for compatible multipath <M;N>/* descriptors.
- Move descriptor parsing logic out of getdescriptorinfo into a reusable
  ParseDescriptorInfo() interface returning structured DescriptorInfo.
- Add unit tests validating policy generation against BIP-388 vectors.

refs bitcoin#32659
@rxbryan rxbryan force-pushed the bip388-wallet-policy-sjors branch from ae0a9df to d6fb1b0 Compare May 23, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant