doc: Use multipath descriptors in descriptors.md and linked test #34100

pull anuragchvn-blip wants to merge 1 commits into bitcoin:master from anuragchvn-blip:master changing 2 files +17 −23
  1. anuragchvn-blip commented at 12:59 pm on December 18, 2025: none

    Updates documentation and wallet_miniscript_decaying_multisig_descriptor_psbt.py to use single multipath descriptors with <0;1> syntax instead of separate external/internal descriptors.

    Changes

    • doc/descriptors.md: Update examples (lines 70-71) to use /<0;1>/* multipath syntax
    • doc/descriptors.md: Update Basic Multisig Example instructions (line 179) to use single multipath descriptor
    • test/functional/wallet_miniscript_decaying_multisig_descriptor_psbt.py: Refactor to use single multipath descriptor pattern matching wallet_multisig_descriptor_psbt.py

    Implementation

    • _get_xpub() now extracts external descriptor and converts to multipath format
    • create_multisig() imports single descriptor that expands to receive and change addresses
    • Removed fake checksums from documentation examples
    • Added clear comments documenting multipath convention

    Fixes #34086

  2. DrahtBot added the label Docs on Dec 18, 2025
  3. DrahtBot commented at 12:59 pm on December 18, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/34100.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK rkrux

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

  4. in doc/descriptors.md:1 in 912d837326 outdated


    rkrux commented at 2:38 pm on December 18, 2025:

    Nit, feel free to ignore but I think reading /0 instead of just 0 would be more relatable given the context.

     0diff --git a/doc/descriptors.md b/doc/descriptors.md
     1index 6c600af5ba..1b79c99fe6 100644
     2--- a/doc/descriptors.md
     3+++ b/doc/descriptors.md
     4@@ -67,8 +67,8 @@ Output descriptors currently support:
     5 - `wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where one multisig key is the *1/0/`i`* child of the first specified xpub and the other multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default). The order of public keys in the resulting witnessScripts is determined by the lexicographic order of the public keys at that index.
     6 - `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,{pk(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),pk(e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)})` describes a P2TR output with the `c6...` x-only pubkey as internal key, and two script paths.
     7 - `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,sortedmulti_a(2,2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc))` describes a P2TR output with the `c6...` x-only pubkey as internal key, and a single `multi_a` script that needs 2 signatures with 2 specified x-only keys, which will be sorted lexicographically.
     8-- `wsh(sortedmulti(2,[6f53d49c/44h/1h/0h]tpubDDjsCRDQ9YzyaAq9rspCfq8RZFrWoBpYnLxK6sS2hS2yukqSczgcYiur8Scx4Hd5AZatxTuzMtJQJhchufv1FRFanLqUP7JHwusSSpfcEp2/<0;1>/*,[e6807791/44h/1h/0h]tpubDDAfvogaaAxaFJ6c15ht7Tq6ZmiqFYfrSmZsHu7tHXBgnjMZSHAeHSwhvjARNA6Qybon4ksPksjRbPDVp7yXA1KjTjSd5x18KHqbppnXP1s/<0;1>/*,[367c9cfa/44h/1h/0h]tpubDDtPnSgWYk8dDnaDwnof4ehcnjuL5VoUt1eW2MoAed1grPHuXPDnkX1fWMvXfcz3NqFxPbhqNZ3QBdYjLz2hABeM9Z2oqMR1Gt2HHYDoCgh/<0;1>/*))` describes a *2-of-3* multisig with a multipath descriptor specifying both receiving (0) and change (1) address derivation paths.
     9-- `wsh(thresh(4,pk([7258e4f9/44h/1h/0h]tpubDCZrkQoEU3845aFKUu9VQBYWZtrTwxMzcxnBwKFCYXHD6gEXvtFcxddCCLFsEwmxQaG15izcHxj48SXg1QS5FQGMBx5Ak6deXKPAL7wauBU/<0;1>/*),s:pk([c80b1469/44h/1h/0h]tpubDD3UwwHoNUF4F3Vi5PiUVTc3ji1uThuRfFyBexTSHoAcHuWW2z8qEE2YujegcLtgthr3wMp3ZauvNG9eT9xfJyxXCfNty8h6rDBYU8UU1qq/<0;1>/*),s:pk([4e5024fe/44h/1h/0h]tpubDDLrpPymPLSCJyCMLQdmcWxrAWwsqqssm5NdxT2WSdEBPSXNXxwbeKtsHAyXPpLkhUyKovtZgCi47QxVpw9iVkg95UUgeevyAqtJ9dqBqa1/<0;1>/*),s:pk([3b1d1ee9/44h/1h/0h]tpubDCmDTANBWPzf6d8Ap1J5Ku7J1Ay92MpHMrEV7M5muWxCrTBN1g5f1NPcjMEL6dJHxbvEKNZtYCdowaSTN81DAyLsmv6w6xjJHCQNkxrsrfu/<0;1>/*),sln:after(840000),sln:after(1050000),sln:after(1260000)))` describes a Miniscript multisig with spending policy: `thresh(4,pk(key_1),pk(key_2),pk(key_3),pk(key_4),after(t1),after(t2),after(t3))` that starts as 4-of-4 and "decays" to 3-of-4, 2-of-4, and finally 1-of-4 at each future halvening block height. This uses a multipath descriptor specifying both receiving (0) and change (1) address derivation paths.
    10+- `wsh(sortedmulti(2,[6f53d49c/44h/1h/0h]tpubDDjsCRDQ9YzyaAq9rspCfq8RZFrWoBpYnLxK6sS2hS2yukqSczgcYiur8Scx4Hd5AZatxTuzMtJQJhchufv1FRFanLqUP7JHwusSSpfcEp2/<0;1>/*,[e6807791/44h/1h/0h]tpubDDAfvogaaAxaFJ6c15ht7Tq6ZmiqFYfrSmZsHu7tHXBgnjMZSHAeHSwhvjARNA6Qybon4ksPksjRbPDVp7yXA1KjTjSd5x18KHqbppnXP1s/<0;1>/*,[367c9cfa/44h/1h/0h]tpubDDtPnSgWYk8dDnaDwnof4ehcnjuL5VoUt1eW2MoAed1grPHuXPDnkX1fWMvXfcz3NqFxPbhqNZ3QBdYjLz2hABeM9Z2oqMR1Gt2HHYDoCgh/<0;1>/*))` describes a *2-of-3* multisig with a multipath descriptor specifying both receiving (/0) and change (/1) address derivation paths.
    11+- `wsh(thresh(4,pk([7258e4f9/44h/1h/0h]tpubDCZrkQoEU3845aFKUu9VQBYWZtrTwxMzcxnBwKFCYXHD6gEXvtFcxddCCLFsEwmxQaG15izcHxj48SXg1QS5FQGMBx5Ak6deXKPAL7wauBU/<0;1>/*),s:pk([c80b1469/44h/1h/0h]tpubDD3UwwHoNUF4F3Vi5PiUVTc3ji1uThuRfFyBexTSHoAcHuWW2z8qEE2YujegcLtgthr3wMp3ZauvNG9eT9xfJyxXCfNty8h6rDBYU8UU1qq/<0;1>/*),s:pk([4e5024fe/44h/1h/0h]tpubDDLrpPymPLSCJyCMLQdmcWxrAWwsqqssm5NdxT2WSdEBPSXNXxwbeKtsHAyXPpLkhUyKovtZgCi47QxVpw9iVkg95UUgeevyAqtJ9dqBqa1/<0;1>/*),s:pk([3b1d1ee9/44h/1h/0h]tpubDCmDTANBWPzf6d8Ap1J5Ku7J1Ay92MpHMrEV7M5muWxCrTBN1g5f1NPcjMEL6dJHxbvEKNZtYCdowaSTN81DAyLsmv6w6xjJHCQNkxrsrfu/<0;1>/*),sln:after(840000),sln:after(1050000),sln:after(1260000)))` describes a Miniscript multisig with spending policy: `thresh(4,pk(key_1),pk(key_2),pk(key_3),pk(key_4),after(t1),after(t2),after(t3))` that starts as 4-of-4 and "decays" to 3-of-4, 2-of-4, and finally 1-of-4 at each future halvening block height. This uses a multipath descriptor specifying both receiving (/0) and change (/1) address derivation paths.
    12 - `tr(musig(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y)/0/*)` describes a MuSig2 multisig with key derivation. The internal keys are derived at `m/0/*` from the aggregate key computed from the 2 participants.
    13 
    14 ## Reference
    15@@ -177,7 +177,7 @@ The basic steps are:
    16      `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses)
    17   2. Create a watch-only descriptor wallet (blank, private keys disabled). Now the multisig is created by importing a single multipath descriptor:
    18      `wsh(sortedmulti(<M>,XPUB1/<0;1>/*,XPUB2/<0;1>/*,…,XPUBN/<0;1>/*))`
    19-     This single descriptor specifies both receiving (`0`) and change (`1`) addresses. Every participant does this. All key origin information (master key fingerprint and all derivation steps) should be included with xpubs for proper support of hardware devices / external signers
    20+     This single descriptor specifies both receiving (`/0`) and change (`/1`) addresses. Every participant does this. All key origin information (master key fingerprint and all derivation steps) should be included with xpubs for proper support of hardware devices / external signers
    21   3. A receiving address is generated for the multisig. As a check to ensure step 2 was done correctly, every participant
    22      should verify they get the same addresses
    23   4. Funds are sent to the resulting address
    

    rkrux commented at 2:39 pm on December 18, 2025:

    This test fails now, at least in my system:

     02025-12-18T14:35:06.064047Z TestFramework (ERROR): Unexpected exception
     1Traceback (most recent call last):
     2  File "/Users/rkrux/projects/bitcoin/test/functional/test_framework/test_framework.py", line 142, in main
     3    self.run_test()
     4    ~~~~~~~~~~~~~^^
     5  File "/Users/rkrux/projects/bitcoin/./build/test/functional/wallet_miniscript_decaying_multisig_descriptor_psbt.py", line 95, in run_test
     6    psbt = multisig.walletcreatefundedpsbt(inputs=[], outputs={receiver.getnewaddress(): amount}, feeRate=0.00010, locktime=locktime)
     7  File "/Users/rkrux/projects/bitcoin/test/functional/test_framework/coverage.py", line 50, in __call__
     8    return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
     9  File "/Users/rkrux/projects/bitcoin/test/functional/test_framework/authproxy.py", line 156, in __call__
    10    raise JSONRPCException(response['error'], status)
    11test_framework.authproxy.JSONRPCException: Transaction needs a change address, but we can't generate it. Error: No bech32 addresses available. (-4)
    
  5. rkrux commented at 2:40 pm on December 18, 2025: contributor

    Concept ACK 912d837326145c304662ca29a01ccd1d240946e8

    Multi-path convention is indeed convenient and preferred.

  6. anuragchvn-blip commented at 3:56 pm on December 18, 2025: none

    @rkrux Thank you for the review and testing!

    I’ve pushed a fix addressing both issues:

    1. Test failure: Added "range": 1000 parameter to importdescriptors to enable change address generation. The multipath descriptor now properly expands to both receive and change paths.

    2. Documentation clarity: Changed (0) and (1) to (/0) and (/1) notation as suggested - much clearer in context.

    The test should now pass. Could you re-test when you have a chance? Thanks again for catching these!

  7. DrahtBot added the label CI failed on Dec 18, 2025
  8. anuragchvn-blip force-pushed on Dec 19, 2025
  9. rkrux commented at 9:47 am on December 19, 2025: contributor
    No, it still fails with the same error. It’s suggested and preferred that the PR authors run the tests in their system as well. Ref: https://github.com/bitcoin/bitcoin/blob/516ae5ede44a8a6abc59e90f9d89913e254524f4/CONTRIBUTING.md?plain=1#L304-L314
  10. anuragchvn-blip commented at 12:39 pm on December 19, 2025: none

    Fixed the test by matching the pattern from wallet_multisig_descriptor_psbt.py:

    Before:

    0desc = multisig.getdescriptorinfo(...)
    1"desc": desc["descriptor"],
    2"range": 1000,
    

    After:

    0multisig_desc = f"wsh(thresh(...))"
    1checksum = multisig.getdescriptorinfo(multisig_desc)["checksum"]
    2"desc": f"{multisig_desc}#{checksum}",
    

    Removed the explicit "range" parameter. Bitcoin Core automatically expands <0;1>/* multipath descriptors into separate external (0) and internal/change (1) descriptors when imported this way. The previous approach prevented this expansion, causing the “No bech32 addresses available” error.

  11. anuragchvn-blip commented at 12:39 pm on December 19, 2025: none

    Fixed the test by matching the pattern from wallet_multisig_descriptor_psbt.py:

    After:

    Removed the explicit “range” parameter. Bitcoin Core automatically expands <0;1>/* multipath descriptors into separate external (0) and internal/change (1) descriptors when imported this way. The previous approach prevented this expansion, causing the “No bech32 addresses available” error.

  12. anuragchvn-blip closed this on Dec 19, 2025

  13. anuragchvn-blip reopened this on Dec 19, 2025

  14. doc: Use multipath descriptors in descriptors.md and linked test
    Updates documentation and wallet_miniscript_decaying_multisig_descriptor_psbt.py
    to use single multipath descriptors with <0;1> syntax instead of separate
    external/internal descriptors.
    
    Changes:
    - doc/descriptors.md: Update examples to use /<0;1>/* multipath syntax with /0 and /1 notation
    - doc/descriptors.md: Update Basic Multisig Example instructions
    - test: Refactor to use single multipath descriptor pattern, matching wallet_multisig_descriptor_psbt.py
    
    Implementation:
    - _get_xpub() extracts external descriptor and converts to multipath format
    - create_multisig() builds descriptor string, gets checksum, imports descriptor#checksum
    - Multipath descriptor properly expands to external and internal/change descriptors
    
    Fixes #34086
    552bc82b17
  15. anuragchvn-blip force-pushed on Dec 19, 2025
  16. DrahtBot removed the label CI failed on Dec 19, 2025
  17. in doc/descriptors.md:70 in 552bc82b17
    66@@ -67,8 +67,8 @@ Output descriptors currently support:
    67 - `wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where one multisig key is the *1/0/`i`* child of the first specified xpub and the other multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default). The order of public keys in the resulting witnessScripts is determined by the lexicographic order of the public keys at that index.
    68 - `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,{pk(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),pk(e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)})` describes a P2TR output with the `c6...` x-only pubkey as internal key, and two script paths.
    69 - `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,sortedmulti_a(2,2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc))` describes a P2TR output with the `c6...` x-only pubkey as internal key, and a single `multi_a` script that needs 2 signatures with 2 specified x-only keys, which will be sorted lexicographically.
    70-- `wsh(sortedmulti(2,[6f53d49c/44h/1h/0h]tpubDDjsCRDQ9YzyaAq9rspCfq8RZFrWoBpYnLxK6sS2hS2yukqSczgcYiur8Scx4Hd5AZatxTuzMtJQJhchufv1FRFanLqUP7JHwusSSpfcEp2/0/*,[e6807791/44h/1h/0h]tpubDDAfvogaaAxaFJ6c15ht7Tq6ZmiqFYfrSmZsHu7tHXBgnjMZSHAeHSwhvjARNA6Qybon4ksPksjRbPDVp7yXA1KjTjSd5x18KHqbppnXP1s/0/*,[367c9cfa/44h/1h/0h]tpubDDtPnSgWYk8dDnaDwnof4ehcnjuL5VoUt1eW2MoAed1grPHuXPDnkX1fWMvXfcz3NqFxPbhqNZ3QBdYjLz2hABeM9Z2oqMR1Gt2HHYDoCgh/0/*))#av0kxgw0` describes a *2-of-3* multisig. For brevity, the internal "change" descriptor accompanying the above external "receiving" descriptor is not included here, but it typically differs only in the xpub derivation steps, ending in `/1/*` for change addresses.
    71-- `wsh(thresh(4,pk([7258e4f9/44h/1h/0h]tpubDCZrkQoEU3845aFKUu9VQBYWZtrTwxMzcxnBwKFCYXHD6gEXvtFcxddCCLFsEwmxQaG15izcHxj48SXg1QS5FQGMBx5Ak6deXKPAL7wauBU/0/*),s:pk([c80b1469/44h/1h/0h]tpubDD3UwwHoNUF4F3Vi5PiUVTc3ji1uThuRfFyBexTSHoAcHuWW2z8qEE2YujegcLtgthr3wMp3ZauvNG9eT9xfJyxXCfNty8h6rDBYU8UU1qq/0/*),s:pk([4e5024fe/44h/1h/0h]tpubDDLrpPymPLSCJyCMLQdmcWxrAWwsqqssm5NdxT2WSdEBPSXNXxwbeKtsHAyXPpLkhUyKovtZgCi47QxVpw9iVkg95UUgeevyAqtJ9dqBqa1/0/*),s:pk([3b1d1ee9/44h/1h/0h]tpubDCmDTANBWPzf6d8Ap1J5Ku7J1Ay92MpHMrEV7M5muWxCrTBN1g5f1NPcjMEL6dJHxbvEKNZtYCdowaSTN81DAyLsmv6w6xjJHCQNkxrsrfu/0/*),sln:after(840000),sln:after(1050000),sln:after(1260000)))#k28080kv` describes a Miniscript multisig with spending policy: `thresh(4,pk(key_1),pk(key_2),pk(key_3),pk(key_4),after(t1),after(t2),after(t3))` that starts as 4-of-4 and "decays" to 3-of-4, 2-of-4, and finally 1-of-4 at each future halvening block height. For brevity, the internal "change" descriptor accompanying the above external "receiving" descriptor is not included here, but it typically differs only in the xpub derivation steps, ending in `/1/*` for change addresses.
    72+- `wsh(sortedmulti(2,[6f53d49c/44h/1h/0h]tpubDDjsCRDQ9YzyaAq9rspCfq8RZFrWoBpYnLxK6sS2hS2yukqSczgcYiur8Scx4Hd5AZatxTuzMtJQJhchufv1FRFanLqUP7JHwusSSpfcEp2/<0;1>/*,[e6807791/44h/1h/0h]tpubDDAfvogaaAxaFJ6c15ht7Tq6ZmiqFYfrSmZsHu7tHXBgnjMZSHAeHSwhvjARNA6Qybon4ksPksjRbPDVp7yXA1KjTjSd5x18KHqbppnXP1s/<0;1>/*,[367c9cfa/44h/1h/0h]tpubDDtPnSgWYk8dDnaDwnof4ehcnjuL5VoUt1eW2MoAed1grPHuXPDnkX1fWMvXfcz3NqFxPbhqNZ3QBdYjLz2hABeM9Z2oqMR1Gt2HHYDoCgh/<0;1>/*))` describes a *2-of-3* multisig with a multipath descriptor specifying both receiving (/0) and change (/1) address derivation paths.
    


    rkrux commented at 9:58 am on December 23, 2025:

    Super nit if you have to retouch for some other reason:

    To make it congruent with the similar line down below in this file.

    0- both receiving (/0) and change (/1) address derivation paths.
    1+ both receiving (`/0`) and change (`/1`) address derivation paths.
    

    anuragchvn-blip commented at 4:53 am on December 24, 2025:
    Thanks for the review and tACK, much appreciated. The latest commit imports the complete multipath descriptor so both external (/0) and internal (/1) paths are available, which fixes the missing-change-address issue in the test. ​ As far as I can tell this is ready from my side; I don’t have permissions to push/merge to master, so it will need a maintainer to do the actual merge when appropriate. ​@rkrux

    rkrux commented at 2:03 pm on December 24, 2025:
    I believe one or two acks more and then this PR would be ready for merge.

    anuragchvn-blip commented at 3:56 pm on December 24, 2025:
    Thanks for confirming. I’m happy to wait for the remaining ACKs. Please let me know if anything else is needed from my side. @rkrux

    anuragchvn-blip commented at 11:14 am on December 28, 2025:
    @rkrux Its been a while no reviewers are up for reviewing. Please do help me with that
  18. rkrux approved
  19. rkrux commented at 10:05 am on December 23, 2025: contributor

    lgtm tACK 552bc82

    I see in the previous commit https://github.com/bitcoin/bitcoin/commit/912d837326145c304662ca29a01ccd1d240946e8 that failed the test, only the “receive” descriptor was being imported without the “change” descriptor because the “descriptor” property in the response of importdescriptors RPC returns only the first expanded descriptor in case of multipath.

    Thus, the “Transaction needs a change address, but we can’t generate it.” error while creating the transaction seems correct. The latest commit 552bc82b17961b86ae1964e817ba89ee7bfd985f correctly fixes the issue by importing the complete multipath descriptor.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-01-02 15:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me