wallet: Be able to receive and spend inputs involving MuSig2 aggregate keys #29675

pull achow101 wants to merge 23 commits into bitcoin:master from achow101:musig2 changing 24 files +1689 −61
  1. achow101 commented at 10:32 pm on March 18, 2024: member

    This PR implements MuSig2 descriptors (BIP 390), derivation (BIP 328), and PSBT fields (BIP 373) so that the wallet can receive and spend from taproot addresses that have keys involving a MuSig2 aggregate key.

    The libsecp musig module is enabled so that it can be used for all of the MuSig2 cryptography.

    Secnonces are handled in a separate class which holds the libsecp secnonce object in a secure_unique_ptr. Since secnonces must not be used, this class has no serialization and will only live in memory. A restart of the software will require a restart of the MuSig2 signing process.

  2. DrahtBot commented at 10:32 pm on March 18, 2024: 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/29675.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK jonatack, fjahr, theStack

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #bitcoin-core/gui/872 (Menu action to export a watchonly wallet by achow101)
    • #32724 (Musig2 tests by w0xlt)
    • #32489 (wallet: Add exportwatchonlywallet RPC to export a watchonly version of a wallet by achow101)
    • #32471 (wallet/rpc: fix listdescriptors RPC fails to return descriptors with private key information when wallet contains descriptors missing any key by Eunovo)
    • #32332 (refactor: Update XOnlyPubKey::GetKeyIDs() to return a pair of pubkeys by w0xlt)
    • #31244 (descriptors: MuSig2 by achow101)
    • #30243 (descriptors: taproot partial descriptors by Eunovo)
    • #21283 (Implement BIP 370 PSBTv2 by achow101)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • in musig.h: “secp25k1_musig_secnonce” → “secp256k1_musig_secnonce” [correct the library name for clarity]

    drahtbot_id_4_m

  3. DrahtBot added the label Wallet on Mar 18, 2024
  4. DrahtBot added the label CI failed on Mar 19, 2024
  5. DrahtBot commented at 2:45 am on March 19, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/22808312237

  6. in test/functional/wallet_musig.py:125 in a1e4c323db outdated
    144+        for deriv_path in dec_psbt["inputs"][0]["taproot_bip32_derivs"]:
    145+            if deriv_path["pubkey"] in part_pks:
    146+                part_pks.remove(deriv_path["pubkey"])
    147+        assert_equal(len(part_pks), 0)
    148+
    149+        nonce_psbts = []
    


    Sjors commented at 10:49 am on March 19, 2024:
    a1e4c323dbff9fa5095cf216d7cd528f10a1feeb: I assume this where the first nonce collection round starts, maybe say so in a comment?
  7. in test/functional/wallet_musig.py:149 in a1e4c323db outdated
    155+        comb_nonce_psbt = self.nodes[0].combinepsbt(nonce_psbts)
    156+
    157+        dec_psbt = self.nodes[0].decodepsbt(comb_nonce_psbt)
    158+        assert_equal(len(dec_psbt["inputs"][0]["musig2_pubnonces"]), exp_key_leaf)
    159+
    160+        psig_psbts = []
    


    Sjors commented at 10:51 am on March 19, 2024:
    a1e4c323dbff9fa5095cf216d7cd528f10a1feeb: and that this is where round 2 happens (maybe link to the BIP at the top of the test and briefly summarise the steps)
  8. in test/functional/wallet_musig.py:155 in a1e4c323db outdated
    161+        for wallet in wallets:
    162+            proc = wallet.walletprocesspsbt(comb_nonce_psbt)
    163+            assert_equal(proc["complete"], False)
    164+            psig_psbts.append(proc["psbt"])
    165+
    166+        comb_psig_psbt = self.nodes[0].combinepsbt(psig_psbts)
    


    Sjors commented at 10:54 am on March 19, 2024:
    a1e4c323dbff9fa5095cf216d7cd528f10a1feeb: because all wallets live on the same node, it’s useful to point out here that anyone, including non-participants can combine the partial signatures. Which is why the non-wallet combinepsbt and finalizepsbt RPC’s are used.
  9. Sjors commented at 2:22 pm on March 19, 2024: member

    Very cool stuff! Will review more later.

    This pulls in (an older version of) the musig module in libsecp

    What do you mean by “older”? Just that the PR to libsecp needs another rebase?

    An open question is whether the approach for handling the secnonces is ideal and safe. Since nonces must not be reused, this PR holds them exclusively in memory, so a restart of the software will require a restart of the MuSig2 signing process.

    It sounds safe, but not ideal, which might make it unsafe. Every Bitcoin Core instance involved would need to keep running, with the wallet loaded (and decrypted?) throughout the two rounds. For an airgapped setup with keys in multiple locations, the node in each location would have to be left running unattended (assuming one person running between them).

    My understanding is that Ledger (cc @bigspider) creates a nonce, stores it, and then deletes it from storage as soon as it’s loaded (before signing). We could similarly store the nonce in our wallet and then delete the field at the start of the new round. For safety we could disable backups and dump RPC’s while a round is in progress (e.g. with a NO_BACKUP flag).

    That only prevents accidental replay, not a replay attack, but it seems that anyone who is able to replay a node, already has access to its private keys (from the time a wallet was decrypted), so can’t do additional harm?


    Implementation questions.

    I tried making a 2 party tr(musig(A,B)) in a blank wallet. Initially I obtained two private keys and their public keys from another legacy wallet. I gave the new Alice wallet her private key and Bob’s public key, i.e. tr(musig(a,B)/0/*) but this failed with Ranged musig() requires all participants to be xpubs. Why though? Given that bip-musig2-derivation defines a virtual root xpub, and providers a fake chaincode, this restriction seems unneeded? (though it’s not blocker either, with descriptor wallets it’s easy to get an xpub - after #29130 anyway)

    Once I had two wallets, I could see they generated the same receive address, nice! I then imported the same xpub/xpriv pair for the change address 1/*. I sent some (signet) coins to it, which arrived and confirmed.

    Sadly after the GUI rugged me :-) Trying to send any amount elsewhere resulted in “Signing transaction failed” followed by “Transaction creation failed!”. Whereas I was hoping to get a PSBT this way.

    Using the send RPC I do get a PSBT (from Alice). I had the musig2_participant_pubkeys set, but no musig2_pubnonces. That required calling walletprocesspsbt which seems an unnecessary extra step (but such fine tuning can wait). On Bob’s side the GUI complained with “Could not sign any more inputs”, but it did add a nonce.

    At this point all the nonces were commited, so Bob could have added his partial signature. But at the stage the GUI crashes when trying to sign: [libsecp256k1] illegal argument: secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4) == 0.

    After a restart Bob’s walletprocesspsbt command didn’t fail. Which seems wrong: at this point the nonce should be gone, which he should complain about.

    Starting with a fresh transaction, sing only the RPC I got the same crash, i.e.:

    1. Alice: send
    2. Alice: processpsbt
    3. Bob: processpsbt
    4. Bob: processpsbt: crash

    Perhaps relevant: Bob’s wallet is encrypted, though it was unlocked throughout steps 3 and 4.


    0 % test/functional/wallet_musig.py 
    12024-03-19T14:23:33.113000Z TestFramework (INFO): PRNG seed is: 6470719924404054174
    22024-03-19T14:23:33.115000Z TestFramework (INFO): Initializing test directory /var/folders/h6/qrb4j9vn6530kp7j4ymj934h0000gn/T/bitcoin_func_test_66knao3l
    32024-03-19T14:23:35.070000Z TestFramework (INFO): Testing rawtr(musig(keys/*))
    42024-03-19T14:23:35.192000Z TestFramework (ERROR): Unexpected exception caught during testing
    

    (didn’t check if it’s the same crash)

  10. bigspider commented at 2:36 pm on March 19, 2024: none

    My understanding is that Ledger (cc @bigspider) creates a nonce, stores it, and then deletes it from storage as soon as it’s loaded (before signing). We could similarly store the nonce in our wallet and then delete the field at the start of the new round. For safety we could disable backups and dump RPC’s while a round is in progress (e.g. with a NO_BACKUP flag).

    Not yet implemented, but that’s the plan: store nonces in flash memory (persistent memory) after generation; remove them from flash memory before signing starts (therefore, they’re gone even if there is a later failure, and signing must restart from nonce generation).

    Note that there is no backup possibility for the persistent memory.

  11. achow101 commented at 3:52 pm on March 19, 2024: member

    What do you mean by “older”? Just that the PR to libsecp needs another rebase?

    I pulled in a commit that is probably outdated at this point. There may have been API changes since.

    We could similarly store the nonce in our wallet and then delete the field at the start of the new round. For safety we could disable backups and dump RPC’s while a round is in progress (e.g. with a NO_BACKUP flag).

    Disabling backups with a flag would not help as an oft suggested method for backing up a wallet is by copying the wallet file. There’s nothing that we can do about that, so to be safe, I don’t think we can store the nonces in the wallet file.

    I tried making a 2 party tr(musig(A,B)) in a blank wallet. Initially I obtained two private keys and their public keys from another legacy wallet. I gave the new Alice wallet her private key and Bob’s public key, i.e. tr(musig(a,B)/0/*) but this failed with Ranged musig() requires all participants to be xpubs. Why though? Given that bip-musig2-derivation defines a virtual root xpub, and providers a fake chaincode, this restriction seems unneeded? (though it’s not blocker either, with descriptor wallets it’s easy to get an xpub - after #29130 anyway)

    It’s specified in bip-musig2-descriptors that the musig must only contain xpubs if the aggregate will be derived from. I believe the rationale for this is that xpubs are intended to have derivation done on them whereas normal keys are not, and so there may be particular handling of such keys to deal with possibilities of derivation doing something unexpected, and so if we do anything with derivation, we should only use keys that are intended for derivation to avoid any confusion. I think @sipa was the one who made this suggestion.

    Sadly after the GUI rugged me :-) Trying to send any amount elsewhere resulted in “Signing transaction failed” followed by “Transaction creation failed!”. Whereas I was hoping to get a PSBT this way.

    The GUI may be expecting that at least one signature is produced, but we can’t do that with musig without at least one round with the cosigners. I have it implemented such that ProduceSignature does not report the tx as being signed until there is actually a signature, so even the partial sigs generation will not return “signed”.

    After a restart Bob’s walletprocesspsbt command didn’t fail. Which seems wrong: at this point the nonce should be gone, which he should complain about.

    Currently it just ignores if there is already a nonce for a participant’s key. It doesn’t replace the nonce, but it also doesn’t validate whether that key belongs to the wallet or whether the nonce exists in the wallet.

    At this point all the nonces were commited, so Bob could have added his partial signature. But at the stage the GUI crashes when trying to sign: [libsecp256k1] illegal argument: secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4) == 0. … Starting with a fresh transaction, sing only the RPC I got the same crash, i.e.:

    1. Alice: `send`
    
    2. Alice: `processpsbt`
    
    3. Bob: `processpsbt`
    
    4. Bob: `processpsbt`: crash
    

    Perhaps relevant: Bob’s wallet is encrypted, though it was unlocked throughout steps 3 and 4.

    0 % test/functional/wallet_musig.py 
    12024-03-19T14:23:33.113000Z TestFramework (INFO): PRNG seed is: 6470719924404054174
    22024-03-19T14:23:33.115000Z TestFramework (INFO): Initializing test directory /var/folders/h6/qrb4j9vn6530kp7j4ymj934h0000gn/T/bitcoin_func_test_66knao3l
    32024-03-19T14:23:35.070000Z TestFramework (INFO): Testing rawtr(musig(keys/*))
    42024-03-19T14:23:35.192000Z TestFramework (ERROR): Unexpected exception caught during testing
    

    (didn’t check if it’s the same crash)

    Huh, works fine for me.

  12. Sjors commented at 4:37 pm on March 19, 2024: member

    Huh, works fine for me.

    This was on Intel macOS 14.4 with a clean checkout and ./configure --disable-bench --disable-tests --enable-wallet --disable-fuzz-binary --disable-zmq --with-gui.

    On Ubuntu 23.10 with gcc 13.2.0 the test do pass, odd.

    (if this still happens after CI passes, I’ll dig a bit deeper, for now I’ll just test on Ubuntu)

    I don’t think we can store the nonces in the wallet file.

    Storing them in some other file might be fine too. As long as we delete it upon read, don’t sign anything if deletion fails and maybe also commit to some unique property of the PSBT.

    Currently it just ignores if there is already a nonce for a participant’s key.

    I guess we need to distinguish here between a nonce for our own key and one for other participants. We have no idea if some other node crashed. But it does seem reasonable to fail if we see a nonce for ourselves. Whether we previously crashed or if someone is trying a replay attack doesn’t really matter. Though it’s unusual for processpsbt to fail when called twice normally, here it seems justifiable.


    Update: successfully completed the MuSig2 signing on Ubuntu!

  13. achow101 force-pushed on Mar 19, 2024
  14. achow101 force-pushed on Mar 20, 2024
  15. achow101 force-pushed on Mar 25, 2024
  16. achow101 force-pushed on Mar 25, 2024
  17. achow101 force-pushed on Mar 25, 2024
  18. achow101 force-pushed on Mar 26, 2024
  19. DrahtBot added the label Needs rebase on Mar 29, 2024
  20. achow101 force-pushed on Apr 1, 2024
  21. DrahtBot removed the label Needs rebase on Apr 1, 2024
  22. Sjors commented at 10:36 am on April 2, 2024: member
    Only 3 red CI machines to go :-)
  23. achow101 force-pushed on Apr 2, 2024
  24. achow101 commented at 5:10 pm on April 2, 2024: member

    Only 3 red CI machines to go :-)

    Only the tidy job is an actual failure from this PR. MSan is an issue with libsecp that needs to be fixed in https://github.com/bitcoin-core/secp256k1/pull/1479. The ASan failure affects all PRs currently, see #29788

  25. Sjors commented at 8:05 am on April 3, 2024: member
    The test passes for me now on macOS.
  26. DrahtBot added the label Needs rebase on Apr 6, 2024
  27. achow101 force-pushed on Apr 16, 2024
  28. bitcoin deleted a comment on May 23, 2024
  29. bigspider commented at 11:43 am on June 6, 2024: none

    Hi all, an early alpha of the Ledger Bitcoin Testnet app with MuSig2 support is available for testing. (NB: the app is called Bitcoin Test Musig and not Bitcoin Test). It should be compatible with the latest draft of the specs.

    Instructions and an easy end-2-end script for MuSig signing to play with it is available here for anyone interested in trying it out:

    https://github.com/bigspider/moosig

    It works for both keypath and script path spending (but it was only tested on very simple policies, so far).

    ADDENDUM: MuSig2 support will not be available on Nano S. Sorry, not enough RAM to make it fit.

  30. achow101 force-pushed on Oct 12, 2024
  31. achow101 commented at 0:18 am on October 12, 2024: member

    Rebased and updated the libsecp subtree to its master

    Still need to work on the location of musig specific functions as currently it requires linking secp256k1 directly for a bunch of targets.

  32. DrahtBot removed the label Needs rebase on Oct 12, 2024
  33. achow101 force-pushed on Oct 24, 2024
  34. achow101 force-pushed on Oct 25, 2024
  35. achow101 force-pushed on Oct 25, 2024
  36. achow101 force-pushed on Oct 25, 2024
  37. achow101 force-pushed on Oct 25, 2024
  38. achow101 force-pushed on Oct 29, 2024
  39. DrahtBot removed the label CI failed on Oct 29, 2024
  40. achow101 force-pushed on Nov 1, 2024
  41. DrahtBot added the label CI failed on Nov 1, 2024
  42. achow101 force-pushed on Nov 4, 2024
  43. DrahtBot removed the label CI failed on Nov 4, 2024
  44. in src/psbt.h:55 in 6d8213dda3 outdated
    49@@ -50,6 +50,9 @@ static constexpr uint8_t PSBT_IN_TAP_LEAF_SCRIPT = 0x15;
    50 static constexpr uint8_t PSBT_IN_TAP_BIP32_DERIVATION = 0x16;
    51 static constexpr uint8_t PSBT_IN_TAP_INTERNAL_KEY = 0x17;
    52 static constexpr uint8_t PSBT_IN_TAP_MERKLE_ROOT = 0x18;
    53+static constexpr uint8_t PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x19;
    54+static constexpr uint8_t PSBT_IN_MUSIG2_PUB_NONCE = 0x1a;
    55+static constexpr uint8_t PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1b;
    


    bigspider commented at 6:09 pm on November 5, 2024:
    These three constants are 0x1a , 0x1b, 0x1c in the final version of BIP-373.

    achow101 commented at 8:11 pm on November 5, 2024:
    Good catch! Fixed.
  45. achow101 force-pushed on Nov 5, 2024
  46. fanquake referenced this in commit 80cb630bd9 on Nov 6, 2024
  47. bigspider commented at 11:37 am on November 6, 2024: none

    EDIT: this is now resolved.

    The current implementation seems to be using the aggregate pubkey (before the tweaks) inside the key of the PSBT_IN_MUSIG2_PUB_NONCE (and I’d assume PSBT_IN_MUSIG2_PARTIAL_SIGNATURE, but I didn’t reach there, yet); instead, BIP-373 says that it must be the key found in the script and not the aggregate public key that it was derived from, if it was derived from an aggregate key. Therefore, I interpreted it as the taproot pubkey for a keypath spend, and the exact key that appears in the tapleaf for a scriptspend.

    Using the aggregate key pre-tweaks could be problematic if the same aggregate key appears multiple times, for example in something like: tr(NUMS/<0;1>/*,or_d(pk(musig(A,B)/<0;1>/*),pk(musig(A,B)/<2;3>/*))) Here, the two musigs have the same participants, and same aggregate key pre-tweaks (and they are in the same leaf, so even the tapleaf_hash won’t help); only the tweaks allow the disambiguation.


    Here’s what I pulled from the test I’m working on:

    Descriptor: tr(musig([f5acc2fd/44'/1'/0']tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT,tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6)/<0;1>/*)

    (where core has the private key for the second tpub).

    Descriptors (with tprivs) imported in core:

    0[{'desc': "tr(musig([f5acc2fd/44'/1'/0']tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT,tprv8ZgxMBicQKsPehMjviGVbsCXGVnN5PMniVWAfyiNXHt5rANKD8wW3ASWAVnoD3FPLGH3v7RGJ4FffNHhZQbUGN1cRDKQ1CosyX2MQtFsGht)/0/*)#j27a8m7j", 'active': True, 'internal': False, 'timestamp': 'now'}, {'desc': "tr(musig([f5acc2fd/44'/1'/0']tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT,tprv8ZgxMBicQKsPehMjviGVbsCXGVnN5PMniVWAfyiNXHt5rANKD8wW3ASWAVnoD3FPLGH3v7RGJ4FffNHhZQbUGN1cRDKQ1CosyX2MQtFsGht)/1/*)#r7mu6ww2", 'active': True, 'internal': True, 'timestamp': 'now'}]
    

    Unsigned psbt:

    0cHNidP8BAH0CAAAAAdL2NNlBE2JSvqSfT7rD55qtlQo0vpBj+7qay5jlRK8aAQAAAAD9////AkBCDwAAAAAAFgAU2bY05Ye8D/o31ppWq8ycsL5td1WxU4kAAAAAACJRIG8BY+C8txooPxDbrDXmFNCHPn5OWgStjAvWJPmkG/CSAAAAAAABASuAlpgAAAAAACJRIMlJh9KeiTnXKN2Ieci1qmwhtFxhowdR45TK++8OWX8UIRaZg4sXm+qcswzNRehImrkH8m8OkH3oq9iqbJO4E9721AUAG4RQpCEW29VNEFgkvwKv+7zMiMVCtBJrM43iZkRMWEATVlTy31YNACbnYhIAAAAAAwAAACEW6Ex/S3Zi+u2fXrLYEtm3vPDgvyozsXrEXji4RZZpwyERAPWswv0sAACAAQAAgAAAAIABFyDb1U0QWCS/Aq/7vMyIxUK0EmszjeJmRExYQBNWVPLfViIaA0gHk0hckD8RuKzi2BqJ9MDsOxlEr5i2BIQm01cD+YmKQgOZg4sXm+qcswzNRehImrkH8m8OkH3oq9iqbJO4E9721APoTH9LdmL67Z9estgS2be88OC/KjOxesReOLhFlmnDIQAAAQUg9zq5DQINlksdoy1F6n8H4aGsIgVYVEHwMprZjOBWEKUhB5mDixeb6pyzDM1F6EiauQfybw6Qfeir2Kpsk7gT3vbUBQAbhFCkIQfoTH9LdmL67Z9estgS2be88OC/KjOxesReOLhFlmnDIREA9azC/SwAAIABAACAAAAAgCEH9zq5DQINlksdoy1F6n8H4aGsIgVYVEHwMprZjOBWEKUNACbnYhIBAAAAAAAAACIIA0gHk0hckD8RuKzi2BqJ9MDsOxlEr5i2BIQm01cD+YmKQgOZg4sXm+qcswzNRehImrkH8m8OkH3oq9iqbJO4E9721APoTH9LdmL67Z9estgS2be88OC/KjOxesReOLhFlmnDIQA=
    

    PSBT processed by core:

    0cHNidP8BAH0CAAAAAdL2NNlBE2JSvqSfT7rD55qtlQo0vpBj+7qay5jlRK8aAQAAAAD9////AkBCDwAAAAAAFgAU2bY05Ye8D/o31ppWq8ycsL5td1WxU4kAAAAAACJRIG8BY+C8txooPxDbrDXmFNCHPn5OWgStjAvWJPmkG/CSAAAAAAABASuAlpgAAAAAACJRIMlJh9KeiTnXKN2Ieci1qmwhtFxhowdR45TK++8OWX8UIRaZg4sXm+qcswzNRehImrkH8m8OkH3oq9iqbJO4E9721AUAG4RQpCEW29VNEFgkvwKv+7zMiMVCtBJrM43iZkRMWEATVlTy31YNACbnYhIAAAAAAwAAACEW6Ex/S3Zi+u2fXrLYEtm3vPDgvyozsXrEXji4RZZpwyERAPWswv0sAACAAQAAgAAAAIABFyDb1U0QWCS/Aq/7vMyIxUK0EmszjeJmRExYQBNWVPLfViIaA0gHk0hckD8RuKzi2BqJ9MDsOxlEr5i2BIQm01cD+YmKQgOZg4sXm+qcswzNRehImrkH8m8OkH3oq9iqbJO4E9721APoTH9LdmL67Z9estgS2be88OC/KjOxesReOLhFlmnDIUMbA5mDixeb6pyzDM1F6EiauQfybw6Qfeir2Kpsk7gT3vbUA0gHk0hckD8RuKzi2BqJ9MDsOxlEr5i2BIQm01cD+YmKQgPQuidM6rVFptyqaKqAOFl7PD7UfSkbp1rMhATpFUiEXwN8MICPo5paODBnrSm6350HF7EjM5LWgYTPXWGmFhFW1QAAAQUg9zq5DQINlksdoy1F6n8H4aGsIgVYVEHwMprZjOBWEKUhB5mDixeb6pyzDM1F6EiauQfybw6Qfeir2Kpsk7gT3vbUBQAbhFCkIQfoTH9LdmL67Z9estgS2be88OC/KjOxesReOLhFlmnDIREA9azC/SwAAIABAACAAAAAgCEH9zq5DQINlksdoy1F6n8H4aGsIgVYVEHwMprZjOBWEKUNACbnYhIBAAAAAAAAACIIA0gHk0hckD8RuKzi2BqJ9MDsOxlEr5i2BIQm01cD+YmKQgOZg4sXm+qcswzNRehImrkH8m8OkH3oq9iqbJO4E9721APoTH9LdmL67Z9estgS2be88OC/KjOxesReOLhFlmnDIQA=
    

    PSBT processed by core, decoded:

      0{
      1  "tx": {
      2    "txid": "71ac326e339863accc5ccb85d071ac41a7162fc1406c322c23648c0fe5839b92",
      3    "hash": "71ac326e339863accc5ccb85d071ac41a7162fc1406c322c23648c0fe5839b92",
      4    "version": 2,
      5    "size": 125,
      6    "vsize": 125,
      7    "weight": 500,
      8    "locktime": 0,
      9    "vin": [
     10      {
     11        "txid": "1aaf44e598cb9abafb6390be340a95ad9ae7c3ba4f9fa4be52621341d934f6d2",
     12        "vout": 1,
     13        "scriptSig": {
     14          "asm": "",
     15          "hex": ""
     16        },
     17        "sequence": 4294967293
     18      }
     19    ],
     20    "vout": [
     21      {
     22        "value": 0.01000000,
     23        "n": 0,
     24        "scriptPubKey": {
     25          "asm": "0 d9b634e587bc0ffa37d69a56abcc9cb0be6d7755",
     26          "desc": "addr(bcrt1qmxmrfev8hs8l5d7knft2hnyukzlx6a64ystah7)#4sr0u9wg",
     27          "hex": "0014d9b634e587bc0ffa37d69a56abcc9cb0be6d7755",
     28          "address": "bcrt1qmxmrfev8hs8l5d7knft2hnyukzlx6a64ystah7",
     29          "type": "witness_v0_keyhash"
     30        }
     31      },
     32      {
     33        "value": 0.08999857,
     34        "n": 1,
     35        "scriptPubKey": {
     36          "asm": "1 6f0163e0bcb71a283f10dbac35e614d0873e7e4e5a04ad8c0bd624f9a41bf092",
     37          "desc": "rawtr(6f0163e0bcb71a283f10dbac35e614d0873e7e4e5a04ad8c0bd624f9a41bf092)#f0mm5uaz",
     38          "hex": "51206f0163e0bcb71a283f10dbac35e614d0873e7e4e5a04ad8c0bd624f9a41bf092",
     39          "address": "bcrt1pduqk8c9ukudzs0csmwkrtes56zrnuljwtgz2mrqt6cj0nfqm7zfqgd563z",
     40          "type": "witness_v1_taproot"
     41        }
     42      }
     43    ]
     44  },
     45  "global_xpubs": [
     46  ],
     47  "psbt_version": 0,
     48  "proprietary": [
     49  ],
     50  "unknown": {
     51  },
     52  "inputs": [
     53    {
     54      "witness_utxo": {
     55        "amount": 0.10000000,
     56        "scriptPubKey": {
     57          "asm": "1 c94987d29e8939d728dd8879c8b5aa6c21b45c61a30751e394cafbef0e597f14",
     58          "desc": "rawtr(c94987d29e8939d728dd8879c8b5aa6c21b45c61a30751e394cafbef0e597f14)#qt9mflrv",
     59          "hex": "5120c94987d29e8939d728dd8879c8b5aa6c21b45c61a30751e394cafbef0e597f14",
     60          "address": "bcrt1pe9yc05573yuaw2xa3puu3dd2dssmghrp5vr4rcu5eta77rje0u2qamzrjq",
     61          "type": "witness_v1_taproot"
     62        }
     63      },
     64      "taproot_bip32_derivs": [
     65        {
     66          "pubkey": "99838b179bea9cb30ccd45e8489ab907f26f0e907de8abd8aa6c93b813def6d4",
     67          "master_fingerprint": "1b8450a4",
     68          "path": "m",
     69          "leaf_hashes": [
     70          ]
     71        },
     72        {
     73          "pubkey": "dbd54d105824bf02affbbccc88c542b4126b338de266444c5840135654f2df56",
     74          "master_fingerprint": "26e76212",
     75          "path": "m/0/3",
     76          "leaf_hashes": [
     77          ]
     78        },
     79        {
     80          "pubkey": "e84c7f4b7662faed9f5eb2d812d9b7bcf0e0bf2a33b17ac45e38b8459669c321",
     81          "master_fingerprint": "f5acc2fd",
     82          "path": "m/44h/1h/0h",
     83          "leaf_hashes": [
     84          ]
     85        }
     86      ],
     87      "taproot_internal_key": "dbd54d105824bf02affbbccc88c542b4126b338de266444c5840135654f2df56",
     88      "musig2_participant_pubkeys": [
     89        {
     90          "aggregate_pubkey": "03480793485c903f11b8ace2d81a89f4c0ec3b1944af98b6048426d35703f9898a",
     91          "participant_pubkeys": [
     92            "0399838b179bea9cb30ccd45e8489ab907f26f0e907de8abd8aa6c93b813def6d4",
     93            "03e84c7f4b7662faed9f5eb2d812d9b7bcf0e0bf2a33b17ac45e38b8459669c321"
     94          ]
     95        }
     96      ],
     97      "musig2_pubnonces": [
     98        {
     99          "participant_pubkey": "0399838b179bea9cb30ccd45e8489ab907f26f0e907de8abd8aa6c93b813def6d4",
    100          "aggregate_pubkey": "03480793485c903f11b8ace2d81a89f4c0ec3b1944af98b6048426d35703f9898a",
    101          "pubnonce": "03d0ba274ceab545a6dcaa68aa8038597b3c3ed47d291ba75acc8404e91548845f037c30808fa39a5a383067ad29badf9d0717b1233392d68184cf5d61a6161156d5"
    102        }
    103      ]
    104    }
    105  ],
    106  "outputs": [
    107    {
    108    },
    109    {
    110      "taproot_internal_key": "f73ab90d020d964b1da32d45ea7f07e1a1ac2205585441f0329ad98ce05610a5",
    111      "taproot_bip32_derivs": [
    112        {
    113          "pubkey": "99838b179bea9cb30ccd45e8489ab907f26f0e907de8abd8aa6c93b813def6d4",
    114          "master_fingerprint": "1b8450a4",
    115          "path": "m",
    116          "leaf_hashes": [
    117          ]
    118        },
    119        {
    120          "pubkey": "e84c7f4b7662faed9f5eb2d812d9b7bcf0e0bf2a33b17ac45e38b8459669c321",
    121          "master_fingerprint": "f5acc2fd",
    122          "path": "m/44h/1h/0h",
    123          "leaf_hashes": [
    124          ]
    125        },
    126        {
    127          "pubkey": "f73ab90d020d964b1da32d45ea7f07e1a1ac2205585441f0329ad98ce05610a5",
    128          "master_fingerprint": "26e76212",
    129          "path": "m/1/0",
    130          "leaf_hashes": [
    131          ]
    132        }
    133      ],
    134      "musig2_participant_pubkeys": [
    135        {
    136          "aggregate_pubkey": "03480793485c903f11b8ace2d81a89f4c0ec3b1944af98b6048426d35703f9898a",
    137          "participant_pubkeys": [
    138            "0399838b179bea9cb30ccd45e8489ab907f26f0e907de8abd8aa6c93b813def6d4",
    139            "03e84c7f4b7662faed9f5eb2d812d9b7bcf0e0bf2a33b17ac45e38b8459669c321"
    140          ]
    141        }
    142      ]
    143    }
    144  ],
    145  "fee": 0.00000143
    146}
    

    The aggregate_pubkey added by core is 03480793485c903f11b8ace2d81a89f4c0ec3b1944af98b6048426d35703f9898a, but I think it should be 02c94987d29e8939d728dd8879c8b5aa6c21b45c61a30751e394cafbef0e597f14 (after the BIP32-tweaks + the taptweak), matching the pubkey in the Script, if my understanding of BIP-373 is correct.

    As a consequence, the musig2_pubnonces should probably use the name aggregate_pubkey_tweaked or something else that avoids confusion with the untweaked aggregate_pubkey that appears in musig2_participant_pubkeys.

  48. achow101 force-pushed on Nov 6, 2024
  49. achow101 commented at 6:01 pm on November 6, 2024: member

    The current implementation seems to be using the aggregate pubkey (before the tweaks) inside the key of the PSBT_IN_MUSIG2_PUB_NONCE

    Indeed, fixed.

    Therefore, I interpreted it as the taproot pubkey for a keypath spend

    I’ve interpreted (and implemented) it as also allowing the taproot internal key, not just the output key. I think that is actually what I meant when writing the BIP, but it’s been a while.

  50. achow101 marked this as ready for review on Nov 6, 2024
  51. achow101 force-pushed on Nov 6, 2024
  52. achow101 force-pushed on Nov 6, 2024
  53. DrahtBot added the label CI failed on Nov 6, 2024
  54. DrahtBot commented at 8:09 pm on November 6, 2024: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/32617937935

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  55. DrahtBot removed the label CI failed on Nov 6, 2024
  56. bigspider commented at 9:53 pm on November 6, 2024: none

    I’ve interpreted (and implemented) it as also allowing the taproot internal key, not just the output key. I think that is actually what I meant when writing the BIP, but it’s been a while.

    I think it’s important that the (aggregate) plain key used in the key of PSBT_IN_MUSIG2_PUB_NONCE/PSBT_IN_MUSIG2_PARTIAL_SIGNATURE is unambiguous and clearly specified, or implementations will essentially have to try both in order to find in the PSBT all the pubnonces/musig_partial_signatures for a certain key.

    Using the same public key that can be used to verify the final signature (therefore, the tweaked taproot pubkey for keypath spends, or the pubkey as it appears in tapleaves for script spends) seems the most natural choice to me.

    I don’t have an opinion on PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS - I’m not using it, since it’s redundant in the context of signing based on BIP-388 wallet policies.

  57. achow101 force-pushed on Nov 6, 2024
  58. achow101 commented at 10:46 pm on November 6, 2024: member

    Using the same public key that can be used to verify the final signature (therefore, the tweaked taproot pubkey for keypath spends, or the pubkey as it appears in tapleaves for script spends) seems the most natural choice to me.

    That’s a good point, I don’t feel too strongly about this, it was just a bit more annoying to figure out how. I’ve updated the PR to do that.

    Also opened https://github.com/bitcoin/bips/pull/1695 to clarify in the BIP.

  59. bigspider commented at 1:34 pm on November 7, 2024: none

    Looking good!

    I can confirm that I was able to complete two e2e tests on regtest (commit 09a6091711eef299de7cfbfd340a112706422c81), using Ledger’s MuSig2 implementation for a cosigner and bitcoin-core for the other one.

    The descriptors had the form:

    tr(musig(ledger_key, bitcoin_core_key)/<0;1>/*)

    and:

    tr(nums_key/<0;1>/*, musig(ledger_key, bitcoin_core_key)/<0;1>/*)

    In both cases, the Ledger device was just the MuSig2 cosigner, while bitcoin-core was both the other musig cosigner and the combiner/finalizer/extractor.

    I will report more once I have more extensive tests.

  60. bigspider commented at 4:44 pm on November 7, 2024: none

    Attempting some fancier setups, I’m trying to do a “decaying MuSig” that starts as a 3-of-3 in the keypath, with 3 timelocked 2-of-2 in the scriptpaths. Not managing to work with the descriptor in core:

    0$ ./bitcoin-cli -regtest getdescriptorinfo "tr(musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6,tpubD6NzVbkrYhZ4WSLhjy9cMaGTEW7No4ALTRikqWo5xFsiTTkRfxA8eRyj2GTkFbkKU6HeW5z8LqrcgHbcVoVg2QZ8JECSHv4PpQ5vUdKJbkR)/1/*,{and_v(v:pk(musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6)/1/*),older(12960)),{and_v(v:pk(musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4WSLhjy9cMaGTEW7No4ALTRikqWo5xFsiTTkRfxA8eRyj2GTkFbkKU6HeW5z8LqrcgHbcVoVg2QZ8JECSHv4PpQ5vUdKJbkR)/1/*),older(12960)),and_v(v:pk(musig(tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6,tpubD6NzVbkrYhZ4WSLhjy9cMaGTEW7No4ALTRikqWo5xFsiTTkRfxA8eRyj2GTkFbkKU6HeW5z8LqrcgHbcVoVg2QZ8JECSHv4PpQ5vUdKJbkR)/1/*),older(12960))}})"
    1error code: -5
    2error message:
    3'and_v(v:pk(musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6)/1/*),older(12960))' is not a valid descriptor function
    

    Same descriptor formatted:

     0tr(
     1  musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6,tpubD6NzVbkrYhZ4WSLhjy9cMaGTEW7No4ALTRikqWo5xFsiTTkRfxA8eRyj2GTkFbkKU6HeW5z8LqrcgHbcVoVg2QZ8JECSHv4PpQ5vUdKJbkR)/1/*,
     2  {
     3    and_v(
     4      v:pk(musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6)/1/*),
     5      older(12960)
     6    ),
     7    {
     8      and_v(
     9        v:pk(musig(tpubDD863BuWFdsaCg6f1SGdwLxp9mDcm3YRm3HxxbppBrizxvU1MqhQ1WpMwhz4vrZHNT7NFbXQ35CquVG9xaLsWaUWfSMZamDESisvtKZ7veF,tpubD6NzVbkrYhZ4WSLhjy9cMaGTEW7No4ALTRikqWo5xFsiTTkRfxA8eRyj2GTkFbkKU6HeW5z8LqrcgHbcVoVg2QZ8JECSHv4PpQ5vUdKJbkR)/1/*),
    10        older(12960)
    11      ),
    12      and_v(
    13        v:pk(musig(tpubD6NzVbkrYhZ4YAPXpMw61GrdqXJJEiYhHo6wxVkfwZgUged5qXm6Df4NLf8ZTFXxW1UhxDKGeKdAVxZtmodC8KfR7SqmW6LGQfDGfnFLmQ6,tpubD6NzVbkrYhZ4WSLhjy9cMaGTEW7No4ALTRikqWo5xFsiTTkRfxA8eRyj2GTkFbkKU6HeW5z8LqrcgHbcVoVg2QZ8JECSHv4PpQ5vUdKJbkR)/1/*),
    14        older(12960)
    15      )
    16    }
    17  }
    18)
    

    Been staring at it for a while, it seems valid to me. Am I missing something? Sorry if it’s obvious

  61. achow101 commented at 4:51 pm on November 7, 2024: member
    musig() is not being parsed in Miniscript expressions yet.
  62. bigspider commented at 4:58 pm on November 7, 2024: none

    musig() is not being parsed in Miniscript expressions yet.

    Ah, ok, I’ll keep an eye for updates. Thanks!

  63. achow101 force-pushed on Nov 7, 2024
  64. achow101 marked this as a draft on Nov 7, 2024
  65. achow101 commented at 6:23 pm on November 7, 2024: member
    Several earlier commits have been split out into separate PRs. See the tracking issue #31246 for the breakdown.
  66. achow101 force-pushed on Nov 7, 2024
  67. achow101 force-pushed on Nov 7, 2024
  68. NicolasDorier commented at 4:12 am on November 8, 2024: contributor
    Given BIP373 doesn’t have test vectors, it would be very useful that either this PR or the BIP include some hard coded PSBT examples to ensure every implementations are on the same page.
  69. jonatack commented at 12:54 pm on November 26, 2024: member

    Concept ACK

    Given BIP373 doesn’t have test vectors, it would be very useful that either this PR or the BIP include some hard coded PSBT examples to ensure every implementations are on the same page.

    Good point (the BIP373 test vector section currently states “TBD” and seems worth completing even if the implementation here also has tests).

  70. Sjors commented at 3:24 pm on November 26, 2024: member

    It might be useful if someone expands doc/multisig-tutorial.md to add a MuSig2 section. That doesn’t have to go in this PR, but it will make testing and review easier. The functional test added in this PR can be used for inspiration.

    It can take advantage of the new <0;1> syntax and the new gethdkeys RPC.

    I was able to generate a simple 2-of-2 tr(musig(A,B)/<0;1>/*) watch-only wallet on testnet4 and receive to it. Keys A and B were extracted from regular wallets by taking the pkh() account level xpub, including its origin.

    However when trying to send walletprocesspsbt (with the wallet that has private keys) does not add any fields.

  71. Sjors commented at 9:49 am on December 3, 2024: member

    I also tried @bigspider’s MooSig demo which worked. I then crafted a multisig between A and the device: tr(musig(A,L)/<0;1>/*). I managed to register the policy (after several mistakes, it’s very tedious to do this manually).

    I wanted to try using HWI to display the address, but I would have to modify it to work with the test app. I just yolo funded it.

    I then created a withdrawal PSBT and pasted it in the Moosig script, modifying it to only add its public nonce. I also hardcoded the registered hmac. The device recognized the account being spent from. I can see that musig2_pubnonces was added to the PSBT.

    I then ran it through walletprocesspsbt in wallet A. The resulting PSBT was longer, but it didn’t add its nonce to musig2_pubnonces.

  72. Sjors commented at 10:02 am on December 3, 2024: member

    Looking at the functional tests in 3649c2eb2053a0c166c68beb310c9c64ddc5b273 it seems the way this is designed to work is by swapping out the active tr([m/86'/1'/0']xpriv/<0;1>)/* descriptor for tr(musig([m/86'/1'/0']xpriv,other,other)/<0;1>)/*.

    I guess that’s fine for the purpose of getting MuSig2 functionality in for experimental use, but it seems a bit unsafe and confusing for general use.

    The test could make the intention a bit more clear by starting with blank wallets, only adding a tr() descriptor and mentioning in a comment that we just want its keys.


    So instead of creating a watch-only wallet, I created a blank wallet. I imported the same tr(musig(A,L)/<0;1>/*) descriptor, but using the xpriv instead of xpub for A. It found the deposited coin. This time I started the withdrawal from the Core, so the Moosig could do its two calls to device in quick succession.

    Even though I had “enable PSBT controls” selected, the GUI did not give me a chance to create a PSBT and immediately complained “Signing transaction failed” after I clicked “Send”.

    The send RPC did work though it didn’t add a nonce. I had to use walletprocesspsbt for that.

    I fed the result to Moosig generate_public_nonces and generate_partial_signatures. I fed the result to walletprocesspsbt et voila! Money back.

  73. bigspider commented at 4:03 pm on December 3, 2024: none

    Thanks @Sjors for testing the app!

    I just updated it with a new version (Bitcoin Test Musig v2.4.0-rc):

    • Some bug fixes for more complex policies. Testing is still not extensive, but it should work for all combinations of musig and miniscript (with the due limits to the policy size; currently, at most 5 keys for MuSig2).
    • If a psbt is sent where only MuSig2 round 1 is executed (no signatures returned), the app will return pubnonces with no user interaction. Note that if there are other internal keys in the wallet policy for whom the PSBT_IN_TAP_BIP32_DERIVATION is present, then the device will sign for those, and in that case confirmation is of course still required.

    Also a quick note that I made a PR to add support for musig in BIP-388 wallet policies - comments welcome.

  74. DrahtBot added the label CI failed on Dec 17, 2024
  75. hugohn commented at 12:42 pm on December 19, 2024: contributor

    Hey guys, we’ve successfully integrated this into Nunchuk, so you should be able to test this out with actual UI/UX very soon.

    MuSig2 key path spend: https://mempool.space/tx/69c75aa798e03dbe782c9a11eed316440fa2a4cb9c4645af2f5d8d566c04207b?mode=details

    MuSig2 script path spend: https://mempool.space/tx/73f63684994477924105966e646427f7fc802352d9ba9d1baebf05b1f3dc3fab?mode=details

    Sample descriptors: tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/**,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/**),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/**,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/**)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/**,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/**))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/**,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/**)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/**,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/**))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/**,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/**))})

  76. DrahtBot removed the label CI failed on Dec 22, 2024
  77. fjahr commented at 1:00 pm on December 30, 2024: contributor
    Concept ACK
  78. starius commented at 2:18 pm on January 4, 2025: contributor

    @hugohn Great work!

    Does /** in descriptors mean a combination of /0/* and /1/*? I.e. a receive and a change descriptor.

  79. hugohn commented at 4:35 pm on January 4, 2025: contributor
    @starius Kind of. We build on top of the descriptor template defined in BIP 129 (BSMS). The above snippet is part of a larger BSMS wallet configuration file, which includes derivation path restrictions.
  80. starius commented at 10:41 pm on January 4, 2025: contributor

    @hugohn I built bitcoin core using this PR rebased on master. I tried the descriptor from your message, replacing /**/ with /0/* and /1/*. It works!

     0getdescriptorinfo "tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))})"
     1{
     2  "descriptor": "tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))})#eywenfaf",
     3  "checksum": "eywenfaf",
     4  "isrange": true,
     5  "issolvable": true,
     6  "hasprivatekeys": false
     7}
     8
     9deriveaddresses "tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/0/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/0/*))})#eywenfaf" 0
    10[
    11  "bc1psdmetx4vudkwte82duvxdn6np9z64njp9d9qd55ffeh5x3jey0gqmr25nu"
    12]
    13
    14
    15
    16getdescriptorinfo "tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))})"
    17{
    18  "descriptor": "tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))})#7q32hxqs",
    19  "checksum": "7q32hxqs",
    20  "isrange": true,
    21  "issolvable": true,
    22  "hasprivatekeys": false
    23}
    24
    25
    26deriveaddresses "tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/1/*,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/1/*))})#7q32hxqs" 0
    27[
    28  "bc1pfx72zmfwx34tcay26l3g4sm4unypjnqqmrnxt9hdjg5t4xy03ufshc6e8k"
    29]
    

    This is how I generated addressed bc1psdmetx4vudkwte82duvxdn6np9z64njp9d9qd55ffeh5x3jey0gqmr25nu and bc1pfx72zmfwx34tcay26l3g4sm4unypjnqqmrnxt9hdjg5t4xy03ufshc6e8k used in the transactions that you posted.

    That is a really cool stuff! Many thanks!

  81. bigspider commented at 10:50 pm on January 4, 2025: none

    @starius Kind of. We build on top of the descriptor template defined in BIP 129 (BSMS). The above snippet is part of a larger BSMS wallet configuration file, which includes derivation path restrictions. @hugohn: FYI BIP-388 generalizes descriptor templates to arbitrary wallets, including with miniscript and musig; it should be entirely compatible with the special cases defined in BIP-129 for multisig.

  82. starius commented at 4:28 am on January 5, 2025: contributor

    I attempted to test this on Signet with a 2-of-2 MuSig2 Taproot address (without script leaves).

    Succeeded using walletprocesspsbt, but failed when using GUI “Load PSBT from keyboard” option.

    Setup:

    • Node 1: Watch-only, connected to the network.
    • Node 2: Offline, holds the first private key.
    • Node 3: Offline, holds the second private key.

    Steps to Reproduce:

    1. Imported descriptors for each node:
     0node 2 (first private key):
     1importdescriptors '[{
     2  "desc": "tr(musig(tprv8ZgxMBicQKsPdRg438LnQj6Fpx1vR6uSwJ3Nda2cZ3oqLqQwT2eae4DcnPNWLc6n8WbXceFuUPGL2QuPCb1DRp9UBsmxbAk8BqDuUZZWWLw/86h/1h/0h/0/*,[370c1c18/86h/1h/0h]tpubDCgnZAFGxVZ5AVLcjCGkLX7sNb85itosYgy25KSPHpM7hCDCwBBn3b9tdHvD9x9DnCrGedGu7gBkRjiAaUFuZJpGUu6s3YerC5KvSCA9NqB/0/*))#l68aglxt",
     3  "active": true,
     4  "internal": false,
     5  "range": 1000,
     6  "timestamp": "now"
     7}, {
     8  "desc": "tr(musig(tprv8ZgxMBicQKsPdRg438LnQj6Fpx1vR6uSwJ3Nda2cZ3oqLqQwT2eae4DcnPNWLc6n8WbXceFuUPGL2QuPCb1DRp9UBsmxbAk8BqDuUZZWWLw/86h/1h/0h/1/*,[370c1c18/86h/1h/0h]tpubDCgnZAFGxVZ5AVLcjCGkLX7sNb85itosYgy25KSPHpM7hCDCwBBn3b9tdHvD9x9DnCrGedGu7gBkRjiAaUFuZJpGUu6s3YerC5KvSCA9NqB/1/*))#umlr3wjn",
     9  "active": true,
    10  "internal": true,
    11  "range": 1000,
    12  "timestamp": "now"
    13}]'
    14
    15node 3 (second private key):
    16importdescriptors '[{
    17  "desc": "tr(musig([a59b4dab/86h/1h/0h]tpubDCwHkWFDhGWHMRaR7U9awzmVYnD4PsmM5kn8E5qMQFptGLLddvmZnhEgqMJ4NP1Bg8UwVcRy6M1rHVCpjiAp7WT2NYBVz8fppCfot7aBtwC/0/*,tprv8ZgxMBicQKsPet5Q2PMpAdv2NeA1siXwywqxRMFvQcuYQCMf39uc9BAvBMNpNxyNqDLPcWv1NrxGsFwvhUN7FStVZj5u78j3x4wGMsNFfgf/86h/1h/0h/0/*))#lhmv3sth",
    18  "active": true,
    19  "internal": false,
    20  "range": 1000,
    21  "timestamp": "now"
    22}, {
    23  "desc": "tr(musig([a59b4dab/86h/1h/0h]tpubDCwHkWFDhGWHMRaR7U9awzmVYnD4PsmM5kn8E5qMQFptGLLddvmZnhEgqMJ4NP1Bg8UwVcRy6M1rHVCpjiAp7WT2NYBVz8fppCfot7aBtwC/1/*,tprv8ZgxMBicQKsPet5Q2PMpAdv2NeA1siXwywqxRMFvQcuYQCMf39uc9BAvBMNpNxyNqDLPcWv1NrxGsFwvhUN7FStVZj5u78j3x4wGMsNFfgf/86h/1h/0h/1/*))#rmh783wt",
    24  "active": true,
    25  "internal": true,
    26  "range": 1000,
    27  "timestamp": "now"
    28}]'
    29
    30node 1 (watch only):
    31importdescriptors '[{
    32  "desc": "tr(musig([a59b4dab/86h/1h/0h]tpubDCwHkWFDhGWHMRaR7U9awzmVYnD4PsmM5kn8E5qMQFptGLLddvmZnhEgqMJ4NP1Bg8UwVcRy6M1rHVCpjiAp7WT2NYBVz8fppCfot7aBtwC/0/*,[370c1c18/86h/1h/0h]tpubDCgnZAFGxVZ5AVLcjCGkLX7sNb85itosYgy25KSPHpM7hCDCwBBn3b9tdHvD9x9DnCrGedGu7gBkRjiAaUFuZJpGUu6s3YerC5KvSCA9NqB/0/*))#fmwctz5x",
    33  "active": true,
    34  "internal": false,
    35  "range": 1000,
    36  "timestamp": "now"
    37}, {
    38  "desc": "tr(musig([a59b4dab/86h/1h/0h]tpubDCwHkWFDhGWHMRaR7U9awzmVYnD4PsmM5kn8E5qMQFptGLLddvmZnhEgqMJ4NP1Bg8UwVcRy6M1rHVCpjiAp7WT2NYBVz8fppCfot7aBtwC/1/*,[370c1c18/86h/1h/0h]tpubDCgnZAFGxVZ5AVLcjCGkLX7sNb85itosYgy25KSPHpM7hCDCwBBn3b9tdHvD9x9DnCrGedGu7gBkRjiAaUFuZJpGUu6s3YerC5KvSCA9NqB/1/*))#qnx2rsgm",
    39  "active": true,
    40  "internal": true,
    41  "range": 1000,
    42  "timestamp": "now"
    43}]'
    
    1. Generated the same address across all nodes:
    0getnewaddress first bech32m
    1tb1pjtz0kg2xz263m6gg6tdgemratx8yucec475jrmq986ae9x8c20dqwux9lm
    
    1. I funded the address with 10k signet sats.

    2. Then I tried to spend the funds. I created an unsigned transaction on node 1 using Send GUI.

    0cHNidP8BAFMCAAAAAbCurhHBbTaWjUb3AJm5tiL+UuZEBKL57olqD4NhTes9AQAAAAD9////ARwlAAAAAAAAF6kULPYzwL1RJx2z4JbkdKLDC7WTsXWHTYADAAABASsQJwAAAAAAACJRIJLE+yFGErUd6QjS2ozsfVmOTmM4r6kh7AU+u5KY+FPaIRbIQKO0UExrrtpBwA4TAlMFYcEXqcWaXTJrp+ZvBuNbrxkANwwcGFYAAIABAACAAAAAgAAAAAAAAAAAIRbP5CTNgLooVS8JstkMC+dZTu09WKpyDXreYc7rJbloExkApZtNq1YAAIABAACAAAAAgAAAAAAAAAAAIRbdYILLif9F1ydNUxjrADeL+Zq3omPmImN4D35ckFJkBQUA7CwezwEXIN1ggsuJ/0XXJ01TGOsAN4v5mreiY+YiY3gPflyQUmQFIhoC3WCCy4n/RdcnTVMY6wA3i/mat6Jj5iJjeA9+XJBSZAVCAshAo7RQTGuu2kHADhMCUwVhwRepxZpdMmun5m8G41uvA8/kJM2AuihVLwmy2QwL51lO7T1YqnINet5hzusluWgTAAA=
    

    I copied it to node 2, loaded PSBT there, signed, then copied to node 3, loaded PSBT there, signed, then copied to node 2, loaded PSBT there, signed, then copied to node 3, loaded PSBT there, signed.

    1. Resulting PSBT:
    0cHNidP8BAFMCAAAAAbCurhHBbTaWjUb3AJm5tiL+UuZEBKL57olqD4NhTes9AQAAAAD9////ARwlAAAAAAAAF6kULPYzwL1RJx2z4JbkdKLDC7WTsXWHTYADAAABASsQJwAAAAAAACJRIJLE+yFGErUd6QjS2ozsfVmOTmM4r6kh7AU+u5KY+FPaARNAfM5w41qiYZnRTghSR1I5mkMl3Em7oqR8NJZjOofQDPV1LOcK3bs8Ibfs+W+pKyLIq2CqiC1uaQYHfC8xgQ1CWyEWyECjtFBMa67aQcAOEwJTBWHBF6nFml0ya6fmbwbjW68ZADcMHBhWAACAAQAAgAAAAIAAAAAAAAAAACEWz+QkzYC6KFUvCbLZDAvnWU7tPViqcg163mHO6yW5aBMZAKWbTatWAACAAQAAgAAAAIAAAAAAAAAAACEW3WCCy4n/RdcnTVMY6wA3i/mat6Jj5iJjeA9+XJBSZAUFAOwsHs8BFyDdYILLif9F1ydNUxjrADeL+Zq3omPmImN4D35ckFJkBSIaAt1ggsuJ/0XXJ01TGOsAN4v5mreiY+YiY3gPflyQUmQFQgLIQKO0UExrrtpBwA4TAlMFYcEXqcWaXTJrp+ZvBuNbrwPP5CTNgLooVS8JstkMC+dZTu09WKpyDXreYc7rJbloE0MbAshAo7RQTGuu2kHADhMCUwVhwRepxZpdMmun5m8G41uvApLE+yFGErUd6QjS2ozsfVmOTmM4r6kh7AU+u5KY+FPaQgNDOrym8PtabR9c5PgAgqhpNiY8OkwD7sX7qVZh8Tg/UAP0lrxYDDIFzWAkcHcUiH++b96c4MMrEpuI/ng9E0qEYkMbA8/kJM2AuihVLwmy2QwL51lO7T1YqnINet5hzusluWgTApLE+yFGErUd6QjS2ozsfVmOTmM4r6kh7AU+u5KY+FPaQgIOALVOKGFjNXOJc6BswdpoDlwdEjuB0MsUost132owmAJs9dBiyOnSBpsWZpuqBd0GROS/aqh3MBGFqQUa/LrM20McAshAo7RQTGuu2kHADhMCUwVhwRepxZpdMmun5m8G41uvApLE+yFGErUd6QjS2ozsfVmOTmM4r6kh7AU+u5KY+FPaIGsO4eRmAJAXw2HtTCZcgNtugDuJS8Xkk+2WncNVRyZAQxwDz+QkzYC6KFUvCbLZDAvnWU7tPViqcg163mHO6yW5aBMCksT7IUYStR3pCNLajOx9WY5OYzivqSHsBT67kpj4U9ogTcmLQbWqMoPInXc7RgmBw5TdMyaZ4Cq9Euidd8LGVsEAAA==
    

    Decoded version:

      0{
      1  "tx": {
      2    "txid": "e7b6cc769c03ff4c4ade13aa8d912e0a5eaa5aa5a7c73df5a297f2acc6c5aeba",
      3    "hash": "e7b6cc769c03ff4c4ade13aa8d912e0a5eaa5aa5a7c73df5a297f2acc6c5aeba",
      4    "version": 2,
      5    "size": 83,
      6    "vsize": 83,
      7    "weight": 332,
      8    "locktime": 229453,
      9    "vin": [
     10      {
     11        "txid": "3deb4d61830f6a89eef9a20444e652fe22b6b99900f7468d96366dc111aeaeb0",
     12        "vout": 1,
     13        "scriptSig": {
     14          "asm": "",
     15          "hex": ""
     16        },
     17        "sequence": 4294967293
     18      }
     19    ],
     20    "vout": [
     21      {
     22        "value": 0.00009500,
     23        "n": 0,
     24        "scriptPubKey": {
     25          "asm": "OP_HASH160 2cf633c0bd51271db3e096e474a2c30bb593b175 OP_EQUAL",
     26          "desc": "addr(2MwLxf9gM6RHdkyhm5hqJ4zwBj66YkkqkVU)#96wqgez4",
     27          "hex": "a9142cf633c0bd51271db3e096e474a2c30bb593b17587",
     28          "address": "2MwLxf9gM6RHdkyhm5hqJ4zwBj66YkkqkVU",
     29          "type": "scripthash"
     30        }
     31      }
     32    ]
     33  },
     34  "global_xpubs": [
     35  ],
     36  "psbt_version": 0,
     37  "proprietary": [
     38  ],
     39  "unknown": {
     40  },
     41  "inputs": [
     42    {
     43      "witness_utxo": {
     44        "amount": 0.00010000,
     45        "scriptPubKey": {
     46          "asm": "1 92c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da",
     47          "desc": "rawtr(92c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da)#3uy4ake4",
     48          "hex": "512092c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da",
     49          "address": "tb1pjtz0kg2xz263m6gg6tdgemratx8yucec475jrmq986ae9x8c20dqwux9lm",
     50          "type": "witness_v1_taproot"
     51        }
     52      },
     53      "taproot_key_path_sig": "7cce70e35aa26199d14e08524752399a4325dc49bba2a47c3496633a87d00cf5752ce70addbb3c21b7ecf96fa92b22c8ab60aa882d6e6906077c2f31810d425b",
     54      "taproot_bip32_derivs": [
     55        {
     56          "pubkey": "c840a3b4504c6baeda41c00e1302530561c117a9c59a5d326ba7e66f06e35baf",
     57          "master_fingerprint": "370c1c18",
     58          "path": "m/86h/1h/0h/0/0",
     59          "leaf_hashes": [
     60          ]
     61        },
     62        {
     63          "pubkey": "cfe424cd80ba28552f09b2d90c0be7594eed3d58aa720d7ade61ceeb25b96813",
     64          "master_fingerprint": "a59b4dab",
     65          "path": "m/86h/1h/0h/0/0",
     66          "leaf_hashes": [
     67          ]
     68        },
     69        {
     70          "pubkey": "dd6082cb89ff45d7274d5318eb00378bf99ab7a263e62263780f7e5c90526405",
     71          "master_fingerprint": "ec2c1ecf",
     72          "path": "m",
     73          "leaf_hashes": [
     74          ]
     75        }
     76      ],
     77      "taproot_internal_key": "dd6082cb89ff45d7274d5318eb00378bf99ab7a263e62263780f7e5c90526405",
     78      "musig2_participant_pubkeys": [
     79        {
     80          "aggregate_pubkey": "02dd6082cb89ff45d7274d5318eb00378bf99ab7a263e62263780f7e5c90526405",
     81          "participant_pubkeys": [
     82            "02c840a3b4504c6baeda41c00e1302530561c117a9c59a5d326ba7e66f06e35baf",
     83            "03cfe424cd80ba28552f09b2d90c0be7594eed3d58aa720d7ade61ceeb25b96813"
     84          ]
     85        }
     86      ],
     87      "musig2_pubnonces": [
     88        {
     89          "participant_pubkey": "02c840a3b4504c6baeda41c00e1302530561c117a9c59a5d326ba7e66f06e35baf",
     90          "aggregate_pubkey": "0292c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da",
     91          "pubnonce": "03433abca6f0fb5a6d1f5ce4f80082a86936263c3a4c03eec5fba95661f1383f5003f496bc580c3205cd6024707714887fbe6fde9ce0c32b129b88fe783d134a8462"
     92        },
     93        {
     94          "participant_pubkey": "03cfe424cd80ba28552f09b2d90c0be7594eed3d58aa720d7ade61ceeb25b96813",
     95          "aggregate_pubkey": "0292c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da",
     96          "pubnonce": "020e00b54e28616335738973a06cc1da680e5c1d123b81d0cb14a2cb75df6a3098026cf5d062c8e9d2069b16669baa05dd0644e4bf6aa877301185a9051afcbaccdb"
     97        }
     98      ],
     99      "musig2_partial_sigs": [
    100        {
    101          "participant_pubkey": "02c840a3b4504c6baeda41c00e1302530561c117a9c59a5d326ba7e66f06e35baf",
    102          "aggregate_pubkey": "0292c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da",
    103          "partial_sig": "6b0ee1e466009017c361ed4c265c80db6e803b894bc5e493ed969dc355472640"
    104        },
    105        {
    106          "participant_pubkey": "03cfe424cd80ba28552f09b2d90c0be7594eed3d58aa720d7ade61ceeb25b96813",
    107          "aggregate_pubkey": "0292c4fb214612b51de908d2da8cec7d598e4e6338afa921ec053ebb9298f853da",
    108          "partial_sig": "4dc98b41b5aa3283c89d773b460981c394dd332699e02abd12e89d77c2c656c1"
    109        }
    110      ]
    111    }
    112  ],
    113  "outputs": [
    114    {
    115    }
    116  ],
    117  "fee": 0.00000500
    118}
    

    Issue:
    Despite including a taproot_key_path_sig, the transaction remains incomplete. Why?

    When I use walletprocesspsbt instead of GUI for nonce exchange and signing (4 times), the process completes and I get a hex encoded final transaction.

    Any insights or clarifications on this would be appreciated!

  83. bigspider commented at 6:44 pm on January 5, 2025: none

    Sample descriptors: tr(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/**,[7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/**),{{{pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/**,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/**)),pk(musig([15d62cdf/87'/0'/0']xpub6CpM1svHYyNMTVdmDh5syFXCJHKctJNajbyLEdA8pAgAeg1jotmg9G1aVkND5Rzf37uhwhs8o2Lvq22iRpWwcbNGCrYxAozQfYQYi1eduES/**,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/**))},{pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/**,[07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/**)),pk(musig([7f15646b/87'/0'/0']xpub6ChFTmSdBrhN3D16Rna7hJVQe4w56Gx83U4uNhT3oJaEXiPv7LKnY2gXi3FbbusCb145c3SMEUsSLMRdkxa82MNKqkatnK5b77BXPc3aK8h/**,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/**))}},pk(musig([07895d1c/87'/0'/0']xpub6DF4oz8Ws6Qcd87qKeKFJCMMvcY3X8vQkS5uQE6P5GxCjNE6XfCeak8xc7VUWjUnH4W1N9rmyjVrUHS5S5odkipXkH8G3VGqVoqoRJzL3UZ/**,[17f48baa/87'/0'/0']xpub6DQGEWSeUwmDE9HHzV3Biwj6VWxJj3VkGjefC7zqRJWM1xTU1s5dozA7DNty3ZniaejgLZBPVhsmrR88cpAeW8E3yieJHhfkPVDmAtuhkym/**))}) @hugohn: Update: I notice now that you are using the derive-then-aggregate pattern (musig(xpub1/**,xpub2/**,...)), instead of the aggregate-then-derive one: (musig(xpub1,xpub2,...)/**); therefore, contrarily to what I claimed above, it is not compatible with BIP-388, which only supports the latter.

    Descriptors, as currently specified, support both, which I think it’s unfortunate.

    I recommend using the aggregate-then-derive pattern as it is going to be a lot more efficient in hardware signing devices - and the only one compatible with BIP-388.

  84. hugohn commented at 2:22 am on January 6, 2025: contributor

    Thanks @bigspider , we’ll take a look.

    Our main concern would be compatibility with other wallets. Do you know what wallets support BIP-388 right now (besides Ledger)? Does Sparrow support it?

  85. bigspider commented at 8:22 am on January 6, 2025: none

    Thanks @bigspider , we’ll take a look.

    Our main concern would be compatibility with other wallets. Do you know what wallets support BIP-388 right now (besides Ledger)? Does Sparrow support it?

    BIP-388 is the base for the miniscript implementation used currently in Ledger, BitBox and Jade. Sparrow only supports the standard multisig types, and I suppose it works with most devices.

    The musig part of BIP-388 is currently only implemented in Ledger in a test application (details here), and should reach production this quarter.

  86. Sjors commented at 9:25 am on January 6, 2025: member
    Maybe we should have a wallet or importdescriptors flag that restricts imported descriptors to BIP-388? Whether to make that the default would be a separate debate.
  87. achow101 force-pushed on Jan 6, 2025
  88. achow101 commented at 9:19 pm on January 6, 2025: member

    Succeeded using walletprocesspsbt, but failed when using GUI “Load PSBT from keyboard” option.

    Thanks for testing! This revealed an issue with sighash type handling in the aggregation code. I’ve pushed a fix for it, as well as a functional test which could replicate the issue with walletprocesspsbt.

    This fix does require changing how we handle non-default sighash types - namely we now will add PSBT_IN_SIGHASH to an input if we are trying to sign it with something other than SIGHASH_DEFAULT (note that SIGHASH_DEFAULT == SIGHASH_ALL for non-taproot, so the normal non-taproot won’t have this field added, unless SIGHASH_ALL was explicitly specified on the command line).

  89. starius commented at 3:08 am on January 7, 2025: contributor

    @achow101 I tried the updated version (2da3f0e659d3e89da0cdf525f8ce370bb35365a1). I tested GUI flow - it works the same (doesn’t produce a transaction). I also re-tested walletprocesspsbt flow - now it is also broken:

    0walletprocesspsbt "cHNidP8BAH4CAAAAAfkoG4WU8+OG7ihR9ax1V+NQK6C9ZIEbsNH8qfB/A90YAAAAAAD9////AlcDAAAAAAAAF6kUp6q1daWOXVcRwue0FRtYgEAxvTCHKCMAAAAAAAAiUSBug0N9qhtsEJizov7RUbZtdDokLCvlo5+zNl+ocwJn636BAwAAAQErECcAAAAAAAAiUSBZP7q1V48G5XegaVSU+plRyc0hddLNxEwKjgaxGnEVXwEDBAEAAAAhFkNN5PloHAjb0E0osBGFERJSFPdYILJBOGBJ2JaPanvHBQBF3NXJIRZv7RyVBRBNtuBaMSje0M/TrzPCwWIAwsFJNQSMJyagTBkANwwcGFYAAIABAACAAAAAgAAAAAACAAAAIRbk+7os0tinqFncH2A0ptli9Ee0Bworam/Red00sAzNTxkApZtNq1YAAIABAACAAAAAgAAAAAACAAAAARcgQ03k+WgcCNvQTSiwEYURElIU91ggskE4YEnYlo9qe8ciGgNDTeT5aBwI29BNKLARhRESUhT3WCCyQThgSdiWj2p7x0IC5Pu6LNLYp6hZ3B9gNKbZYvRHtAcKK2pv0XndNLAMzU8Db+0clQUQTbbgWjEo3tDP068zwsFiAMLBSTUEjCcmoEwAAAEFIM2w2fouNow30T2TaXy1RedtUv3HPUlj94AmquLBD4vjIQciy89SfFIJjEsrmkBUGjRAF5uKUVaoO7YceX7/+NFBjRkANwwcGFYAAIABAACAAAAAgAEAAAACAAAAIQe3pe9vP18BEUSz/NdpLmQETB90YgqM4GKWJYx6VawM7hkApZtNq1YAAIABAACAAAAAgAEAAAACAAAAIQfNsNn6LjaMN9E9k2l8tUXnbVL9xz1JY/eAJqriwQ+L4wUAqsEXDyIIA82w2fouNow30T2TaXy1RedtUv3HPUlj94AmquLBD4vjQgIiy89SfFIJjEsrmkBUGjRAF5uKUVaoO7YceX7/+NFBjQK3pe9vP18BEUSz/NdpLmQETB90YgqM4GKWJYx6VawM7gA=" true
    1
    2Specified sighash value does not match value stored in PSBT (code -22)
    
  90. achow101 commented at 4:52 am on January 7, 2025: member

    @achow101 I tried the updated version (2da3f0e). I tested GUI flow - it works the same (doesn’t produce a transaction). I also re-tested walletprocesspsbt flow - now it is also broken:

    Ah, the sighash stuff needs a bit more work, and it also has impacts outside of MuSig support.

    For the GUI workflow, if you apply https://github.com/bitcoin-core/gui/pull/850 on top of 3649c2e, it should “work”. However that is not a complete fix as it doesn’t work with other sighash types

  91. achow101 commented at 2:10 am on January 9, 2025: member
    I’ve pushed several commits to fix the sighash issues. These are also opened in their own PR #31622
  92. Sjors commented at 10:52 am on January 16, 2025: member
    Can you update the PR description to have a list of pre-requisite PRs?
  93. fanquake referenced this in commit f9032a4abb on Jan 16, 2025
  94. achow101 commented at 8:16 pm on January 16, 2025: member

    Can you update the PR description to have a list of pre-requisite PRs?

    There’s a tracking issue with everything listed #31246

  95. achow101 force-pushed on Jan 22, 2025
  96. DrahtBot added the label Needs rebase on Feb 4, 2025
  97. achow101 force-pushed on Feb 10, 2025
  98. DrahtBot removed the label Needs rebase on Feb 12, 2025
  99. DrahtBot added the label Needs rebase on Mar 20, 2025
  100. achow101 force-pushed on Apr 10, 2025
  101. DrahtBot removed the label Needs rebase on Apr 11, 2025
  102. DrahtBot added the label CI failed on Apr 11, 2025
  103. DrahtBot commented at 1:23 am on April 11, 2025: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/40360903127

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  104. achow101 force-pushed on Apr 14, 2025
  105. DrahtBot removed the label CI failed on Apr 14, 2025
  106. glozow referenced this in commit c7b592fbd7 on Apr 18, 2025
  107. DrahtBot added the label Needs rebase on Apr 18, 2025
  108. achow101 force-pushed on Apr 18, 2025
  109. DrahtBot removed the label Needs rebase on Apr 19, 2025
  110. glozow referenced this in commit 3e78ac6811 on Apr 21, 2025
  111. DrahtBot added the label Needs rebase on Apr 21, 2025
  112. achow101 force-pushed on Apr 21, 2025
  113. DrahtBot removed the label Needs rebase on Apr 21, 2025
  114. DrahtBot added the label CI failed on May 1, 2025
  115. achow101 force-pushed on May 1, 2025
  116. DrahtBot removed the label CI failed on May 1, 2025
  117. DrahtBot added the label Needs rebase on May 7, 2025
  118. achow101 force-pushed on May 7, 2025
  119. DrahtBot removed the label Needs rebase on May 7, 2025
  120. DrahtBot added the label Needs rebase on May 14, 2025
  121. achow101 force-pushed on May 14, 2025
  122. DrahtBot removed the label Needs rebase on May 14, 2025
  123. achow101 force-pushed on May 21, 2025
  124. Sjors commented at 1:47 pm on June 2, 2025: member
    It seems that if a (miniscript, not yet spendable) script path is present in the descriptor, then walletprocesspsbt won’t provide a pubnonce. (nvm, mistake in my testing flow) @bigspider what version of the Ledger test app is required? And can it just sign (or provide the nonce) the PSBT (via HWI) and register the policy on first use? Or do you have to first explicitly register the policy like Moosig does?
  125. bigspider commented at 5:54 pm on June 2, 2025: none

    @bigspider what version of the Ledger test app is required? And can it just sign (or provide the nonce) the PSBT (via HWI) and register the policy on first use? Or do you have to first explicitly register the policy like Moosig does? @Sjors, musig2 is supported from version 2.4.0 of the Bitcoin app, which at this time is the latest version. Note that the firmware OS must be up to date, or you would only find older versions of the app.

    Registration is necessary (and not implemented in HWI).

  126. Sjors commented at 6:01 pm on June 2, 2025: member

    @bigspider Ledger Live says my LedgerX device is up to date. The mainnet Bitcoin app version is 2.4.0, but the testnet is at 2.3.0.

    Or do I need the Bitcoin Test Legacy app? That one has a version 2.4.7, but it throws “ClaNotSupported” if I try it with async-hwi device list command.

  127. bigspider commented at 7:48 pm on June 2, 2025: none

    @bigspider Ledger Live says my LedgerX device is up to date. The mainnet Bitcoin app version is 2.4.0, but the testnet is at 2.3.0.

    Or do I need the Bitcoin Test Legacy app? That one has a version 2.4.7, but it throws “ClaNotSupported” if I try it with async-hwi device list command.

    No, “Bitcoin Test” version 2.4.0 is the correct app; maybe something went wrong and its deployment is missing - I’ll check tomorrow. What is the firmware version on your LNX?

  128. Sjors commented at 5:06 am on June 3, 2025: member
    @bigspider OS (firmware) version 2.4.2. I tried removing and reinstalling Bitcoin Test, but that didn’t bump the version. Will await your update. So you’re testing on mainnet then? :-)
  129. bigspider commented at 7:48 am on June 3, 2025: none

    @bigspider OS (firmware) version 2.4.2. I tried removing and reinstalling Bitcoin Test, but that didn’t bump the version. Will await your update. So you’re testing on mainnet then? :-) @Sjors thanks for pointing that out, you should now be able to find Bicoin Test version 2.4.0 in the store!

  130. Sjors commented at 8:18 am on June 3, 2025: member

    @bigspider got it! Registration seems to work and device recognized which of the keys is “ours”. Though after approval async-hwi threw Error: Device("ClientError(\n \"Failed to parse descriptor\",\n)") and did not return an HMAC.

    IMG_9612 groot IMG_9613 groot

  131. bigspider commented at 8:30 am on June 3, 2025: none

    @Sjors async-hwi probably doesn’t support musig() in descriptors. Rust libraries are generally waiting for upstream support in rust-bitcoin.

    The python package ledger-bitcoin is currently the only client library that is expected to work with musig2 (but it doesn’t have a CLI).

  132. Sjors commented at 9:01 am on June 3, 2025: member

    I see. So instead I registered the policy by tweaking Moosig. That worked and returned an HMAC.

    I then hardcode that HMAC into moosig.py along with the PSBT generated by Bitcoin Core and try to make it request a pubnonce. For this I removed the HotMusig2Cosigner.

    The device recognizes the policy, destination address, amount and fees which I then approve. But then it fails again:

     0🐮 Requesting pubnonces (Round 1)
     1Traceback (most recent call last):
     2  File "/Users/sjors/dev/moosig/moosig.py", line 104, in <module>
     3    main(client)
     4  File "/Users/sjors/dev/moosig/moosig.py", line 74, in main
     5    signer.generate_public_nonces(psbt)
     6  File "/Users/sjors/dev/moosig/utils/musig2.py", line 812, in generate_public_nonces
     7    res = self.client.sign_psbt(psbt, self.wallet_policy, self.wallet_hmac)
     8  File "/Users/sjors/dev/moosig/venv/lib/python3.10/site-packages/ledger_bitcoin/client.py", line 330, in sign_psbt
     9    raise DeviceException(error_code=sw, ins=BitcoinInsType.SIGN_PSBT)
    10ledger_bitcoin.exception.errors.IncorrectDataError: ('0x6a80', 'Error in <BitcoinInsType.SIGN_PSBT: 4> command', '')
    

    I tried both a PSBT generated with send, as well as one that went through walletprocesspsbt which added the public nonce from Bitcoin Core’s side.

    Unfortunately that error is a bit cryptic.

  133. bigspider commented at 9:24 am on June 3, 2025: none

    If you’re only requesting pubnonces to the device, this should happen silently (without on-screen interaction). So if you’re validating the transaction in what is supposed to be round 1, it ain’t round 1 :)

    If you have multiple spending paths, it might be doing round 1 for the musig2 key, and signing normally for the other spending path(s).

    For reference, here’s the code in our e2e tests with core, if that helps.

    Also note that as MuSig2 is a stateful process, if you try round 2 and it fails for any reason, you’ll need to start over from round 1.

  134. Sjors commented at 10:20 am on June 3, 2025: member

    It fails at this step, in the generate_public_nonces function:

    0  print("\n🐮 Requesting pubnonce (Round 1)")
    1    signer_1.generate_public_nonces(psbt)
    

    Just in case, I restarted the signing session by having Bitcoin Core make a fresh musig2_pubnonces for its key.

    Are your e2e tests running against the latest version of this branch?

    and signing normally for the other spending path(s).

    The other paths don’t have enough confirmations yet, but I guess that doesn’t mattter?

    Update: I get the same error with the following policy: tr(musig(@0,@1)/**) if I pass it a PSBT that already has the Bitcoin Core public nonce. It also prompts to confirm the send, so it’s clearly doing round 2 if the public nonce is present.

    But when I pass a PSBT without the Bitcoin Core public nonce, it goes ahead and does round 1 without prompt. I then take the PSBT to Bitcoin Core, has it add its nonce and sign (two calls to walletprocesspsbt) and then give that back to the Ledger. Then it happily signs and the transaction can be broadcast.


    Do PRINTF( statements get propagated to the caller? In that case it would aid in debugging to add these to more failure modes. Otherwise it’s hard to figure out which of the 37 SW_INCORRECT_DATA checks triggered the error.

    https://github.com/LedgerHQ/app-bitcoin-new/blob/2.4.0/src/handler/sign_psbt.c

    I guess it doesn’t, because if I deliberately pass an invalid HMAC, I don’t see the message generated by PRINTF("Incorrect hmac\n");. I guess I need to run an emulator for that?

    I’m able to run the emulator using the Visual Studio extension, but I have no idea how to connect to it from the host machine.


    So I think there’s two things the device doesn’t like:

    1. If there’s a script path it can spend
    2. If it’s not the first to provide a public nonce

    Though it’s possible I’m still doing something wrong on my end.

  135. bigspider commented at 2:31 pm on June 3, 2025: none

    Are your e2e tests running against the latest version of this branch?

    The container is rebuilt every time a PR is merged, and the musig2 test never failed over the last 6+ months since they were added.

    I rebuilt the container today and confirmed it works on the tagged commit for 2.4.0 release (this is the CI run).

    But when I pass a PSBT without the Bitcoin Core public nonce, it goes ahead and does round 1 without prompt. I then take the PSBT to Bitcoin Core, has it add its nonce and sign (two calls to walletprocesspsbt) and then give that back to the Ledger. Then it happily signs and the transaction can be broadcast.

    Yes, the app expects that there are either no nonces, or all nonces.

    I guess it doesn’t, because if I deliberately pass an invalid HMAC, I don’t see the message generated by PRINTF("Incorrect hmac\n");. I guess I need to run an emulator for that?

    PRINTFs are only present in the debug builds for the emulator.

    I’m able to run the emulator using the Visual Studio extension, but I have no idea how to connect to it from the host machine.

    I recommend to only use the VSCode extension to build it, but then install and run Speculos locally, so you don’t have to deal with containers. Other people work entirely inside the containers, but I personally don’t like Docker that much.

    So I think there’s two things the device doesn’t like:

    1. If there’s a script path it can spend

    The expected behavior is that the app will sign (or run the first round) for all the internal key placeholders (where a key placeholder is internal if it’s a key controlled by the signing device, or a MuSig with a key controlled by the device), as long as the BIP-32 derivations are present for that internal key. That’s because the API is sign(psbt, wallet_policy), so there’s otherwise no way of determining which key you want the device to sign for. Before MuSig2, signing for unnecessary paths was only a waste of time, but now that’s necessary in order to be able to keep the first round silent.

    Prior to version 2.4.0 the app might have signed for some spending paths even with incomplete or BIP32_DERIVATION.

    1. If it’s not the first to provide a public nonce

    The workflow I’m expecting is that the coordinator would give the same PSBT in parallel to all the signers, which execute round 1, then all nonces are added to the PSBT, and eah signer is invoked again and produces the partial musig signatures. If only some nonces are present, then the device assumes Round 1 already happened, but it will fail to run Round 2.

  136. Sjors commented at 2:52 pm on June 3, 2025: member

    If only some nonces are present, then the device assumes Round 1 already happened, but it will fail to run Round 2.

    Ok, that explains issue (2).

    But not why it failed with (1), because the policy variant with older fallback triggers the error without a public nonce from Bitcoin Core for the key path.

    The workflow I’m expecting is that the coordinator would give the same PSBT in parallel to all the signers, which execute round 1, then all nonces are added to the PSBT, and eah signer is invoked again and produces the partial musig signatures.

    That’s suboptimal though. In a two-of-two, if you need to physically go to a second location, you’ll want to:

    1. site A: get nonce 1
    2. site B: get nonce 2
    3. site B: get partial signature 2
    4. site A: get partial signature 2 and broadcast

    Where ideally step (2) and (3) are a single action. Though you can still achieve this by running combinepsbt in site B after step (2).

    Are your e2e tests running against the latest version of this branch?

    The container is rebuilt every time a PR is merged

    Right, but that just tests Bitcoin Core master, not this PR?

    I guess if I want to debug this further, I’d have to modify the e2e test to use this branch and recreate the multisig setup I described in the test. Then if a test fails it will log the specific error in the console (if I add a few more PRINTFs).

    but then install and run Speculos locally, so you don’t have to deal with containers

    I couldn’t figure out how to do that on an M4 mac, but it might be easier on my Ubuntu machine. I’m not a Docker fan either.


    On a different note, thinking about MuSig2 session management a bit more, the BIP says:

    To avoid accidental reuse of secnonce, an implementation may securely erase the secnonce argument by overwriting it with 64 zero bytes after it has been read by Sign. A secnonce consisting of only zero bytes is invalid for Sign and will cause it to fail.

    So if the user makes multiple calls to walletprocesspsbt, and the psbt contains our public nonce, then the first call would return a PSBT with a partial signature. If the call is repeated, i.e. without the partial signature that we produced earlier, it should fail (not silently).

    Currently it just quietly removes musig2_partial_sigs, which may have contributed to my confusion.

     0diff --git a/test/functional/wallet_musig.py b/test/functional/wallet_musig.py
     1index b7f3cc9d96..9c8e0a8fd3 100755
     2--- a/test/functional/wallet_musig.py
     3+++ b/test/functional/wallet_musig.py
     4@@ -8,7 +8,7 @@ import re
     5 from test_framework.descriptors import descsum_create
     6 from test_framework.key import H_POINT
     7 from test_framework.test_framework import BitcoinTestFramework
     8-from test_framework.util import assert_equal
     9+from test_framework.util import assert_equal, assert_raises_rpc_error
    10
    11 PRIVKEY_RE = re.compile(r"^tr\((.+?)/.+\)#.{8}$")
    12 PUBKEY_RE = re.compile(r"^tr\((\[.+?\].+?)/.+\)#.{8}$")
    13@@ -150,6 +150,9 @@ class WalletMuSigTest(BitcoinTestFramework):
    14         for wallet in wallets:
    15             proc = wallet.walletprocesspsbt(psbt=comb_nonce_psbt, sighashtype=sighash_type)
    16             assert_equal(proc["complete"], False)
    17+            # Never sign twice:
    18+            assert_raises_rpc_error(-1, "sec nonce lost in tragic boating accident", wallet.walletprocesspsbt, psbt=comb_nonce_psbt, sighashtype=sighash_type)
    19+
    20             psig_psbts.append(proc["psbt"])
    
  137. bigspider commented at 3:20 pm on June 3, 2025: none

    But not why it failed with (1), because the policy variant with older fallback triggers the error without a public nonce from Bitcoin Core for the key path.

    Please feel free to open an issue and I’ll investigate.

    The workflow I’m expecting is that the coordinator would give the same PSBT in parallel to all the signers, which execute round 1, then all nonces are added to the PSBT, and eah signer is invoked again and produces the partial musig signatures.

    That’s suboptimal though. In a two-of-two, if you need to physically go to a second location, you’ll want to:

    1. site A: get nonce 1
    2. site B: get nonce 2
    3. site B: get partial signature 2
    4. site A: get partial signature 2 and broadcast

    Where ideally step (2) and (3) are a single action. Though you can still achieve this by running combinepsbt in site B after step (2).

    Sure, but while the Ledger app is using PSBT in its protocol, it is not designed to be a full implementation of the PSBT standard, as it’s running in a very constrained setting where it doesn’t even have full access to the PSBT without communicating with the client. Verifying if ‘my nonce is present’ is a substantial amount of work, while checking if ‘some nonces’ are present is trivial by iterating once through the PSBT keys.

    It is much easier to handle this on the client side (‘if only some but not all nonces are present, delete them before calling the device"), rather than implementing such complex logic on the device.

    Right, but that just tests Bitcoin Core master, not this PR?

    I’m currently using Bitcoin Core compiled from this PR, since MuSig2 is part of the CI; I’ll switch back to master once this PR is merged.

  138. Sjors commented at 3:31 pm on June 3, 2025: member

    But not why it failed with (1), because the policy variant with older fallback triggers the error without a public nonce from Bitcoin Core for the key path.

    Please feel free to open an issue and I’ll investigate.

    I’ll test it once more and will open an issue if needed.

    Verifying if ‘my nonce is present’ is a substantial amount of work, while checking if ‘some nonces’ are present is trivial by iterating once through the PSBT keys.

    I see. It would be good to document these caveats somewhere. They’re easy to work around, but hard to debug if you run into them.

  139. in src/musig.h:42 in 5e90a3c747 outdated
    39@@ -40,8 +40,7 @@ std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkey
    40 class MuSig2SecNonce
    41 {
    42 private:
    43-    //! The actual secnonce itself
    44-    secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;
    45+    std::unique_ptr<MuSig2SecNonceImpl> m_impl;
    


    theStack commented at 1:17 pm on June 5, 2025:

    in commit 5e90a3c747ac2d44f9f52a3eeffbbe391f837bd8: seems like this change should be part of the earlier commit 01f639835783aa10d4c4b63da477c0300bc8495f, which currently doesn’t compile

    0...
    1In file included from /home/thestack/bitcoin_prrev/pr29675/src/musig.cpp:5:
    2/home/thestack/bitcoin_prrev/pr29675/src/musig.h:44:5: error: no template named 'secure_unique_ptr'
    3    secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;
    4
    5/home/thestack/bitcoin_prrev/pr29675/src/musig.cpp:74:36: error: member initializer 'm_impl' does not name a non-static data member or base class
    6MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {}
    7...
    

    (not sure why the test-each-commit CI job doesn’t detect this though?)


    Sjors commented at 1:36 pm on June 5, 2025:
    @theStack it only runs the most recent N (6?) commits.

    achow101 commented at 8:55 pm on June 5, 2025:
    Fixed
  140. theStack commented at 1:18 pm on June 5, 2025: contributor
    Concept ACK
  141. achow101 force-pushed on Jun 5, 2025
  142. achow101 force-pushed on Jun 11, 2025
  143. DrahtBot added the label CI failed on Jun 16, 2025
  144. achow101 force-pushed on Jun 17, 2025
  145. achow101 force-pushed on Jun 17, 2025
  146. DrahtBot removed the label CI failed on Jun 17, 2025
  147. achow101 force-pushed on Jun 18, 2025
  148. XOnlyPubKey: Add GetCPubKeys
    We need to retrieve the even and odd compressed pubkeys for xonly
    pubkeys, so add a function to do that. Also reuse it in GetKeyIDs.
    8a409b050e
  149. script/parsing: Allow Const to not skip the found constant
    When parsing a descriptor, it is useful to be able to check whether a
    string begins with a substring without consuming that substring as
    another function such as Func() will be used later which requires that
    substring to be present at the beginning.
    
    Specifically, for MuSig2, this modified Const will be used to determine
    whether a an expression begins with "musig(" before a subsequent
    Func("musig", ...) is used.
    4bf9e64e0c
  150. util/string: Allow Split to include the separator
    When splitting a string, sometimes the separator needs to be included.
    Split will now optionally include the separator at the end of the left
    side of the splits, i.e. it appears at the end of the splits, except
    for the last one.
    
    Specifically, for musig() descriptors, Split is used to separate a
    musig() from any derivation path that follows it by splitting on the
    closing parentheses. Since that parentheses is needed for Func() and
    Expr(), Split() needs to preserve the end parentheses instead of
    discarding it.
    fff26f8409
  151. descriptors: Add PubkeyProvider::IsBIP32() 177f8f57ba
  152. build: Enable secp256k1 musig module 40e0875973
  153. sign: Add GetMuSig2ParticipantPubkeys to SigningProvider 721f0a10a2
  154. Add MuSig2 Keyagg Cache helper functions
    secp256k1 provides us secp256k1_musig_keyagg_cache objects which we are
    used as part of session info and to get the aggregate pubkey. These
    helper functions help us convert to/from the secp256k1 C objects into
    the Bitcoin Core C++ objects.
    a196216dc8
  155. descriptor: Add MuSigPubkeyProvider 3572c5095f
  156. descriptor: Parse musig() key expressions c36f45389f
  157. tests: Test musig() parsing f6f1cc28e3
  158. doc: Add musig() example 92ecc41cfb
  159. sign: Refactor Schnorr sighash computation out of CreateSchnorrSig
    There will be other functions within MutableTransactionSignatureCreator
    that need to compute the same sighash, so make it a separate member
    function.
    8da82482e7
  160. pubkey: Return tweaks from BIP32 derivation ec4ecfd3e1
  161. sign: Include taproot output key's KeyOriginInfo in sigdata 13b534a7a4
  162. Add MuSig2SecNonce class for secure allocation of musig nonces 8ca3968377
  163. signingprovider: Add musig2 secnonces
    Adds GetMuSig2SecNonces which returns secp256k1_musig_secnonce*, and
    DeleteMuSig2Session which removes the MuSig2 secnonce from wherever it
    was retrieved. FlatSigningProvider stores it as a pointer to a map of
    session id to secnonce so that deletion will actually delete from the
    object that actually owns the secnonces.
    
    The session id is just a unique identifier for the caller to determine
    what secnonces have been created.
    fe5d88ef16
  164. sign: Add CreateMuSig2AggregateSig aba5cd81d0
  165. sign: Add CreateMuSig2Nonce 2e44c1a3d4
  166. sign: Add CreateMuSig2PartialSig 19ebc0c0fc
  167. sign: Create MuSig2 signatures for known MuSig2 aggregate keys
    When creating Taproot signatures, if the key being signed for is known
    to be a MuSig2 aggregate key, do the MuSig2 signing algorithms.
    
    First try to create the aggregate signature. This will fail if there are
    not enough partial signatures or public nonces. If it does fail, try to
    create a partial signature with all participant keys. This will fail for
    those keys that we do not have the private keys for, and if there are
    not enough public nonces. Lastly, if the partial signatures could be
    created, add our own public nonces for the private keys that we know, if
    they do not yet exist.
    8caaac6de4
  168. wallet: Keep secnonces in DescriptorScriptPubKeyMan aea1d92a47
  169. psbt: MuSig2 data in Fill/FromSignatureData 091de9b570
  170. test: Test MuSig2 in the wallet 54d1e74046
  171. achow101 force-pushed on Jun 19, 2025
  172. DrahtBot added the label CI failed on Jun 19, 2025
  173. DrahtBot commented at 0:16 am on June 19, 2025: contributor

    🚧 At least one of the CI tasks failed. Task tidy: https://github.com/bitcoin/bitcoin/runs/44377387322 LLM reason (✨ experimental): The CI failure is caused by a clang-tidy error due to the use of a variable ‘match’ that is copy-constructed from a const reference but not used, which is treated as an error.

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  174. DrahtBot removed the label CI failed on Jun 19, 2025

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: 2025-06-28 09:13 UTC

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