From 662f4c73d7f9a8f87b96c49d9a0163b674984ee8 Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:01:08 +0000 Subject: [PATCH 1/5] Add support for musig key placeholders --- bip-0388.mediawiki | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/bip-0388.mediawiki b/bip-0388.mediawiki index 36f08d72..0630f0f0 100644 --- a/bip-0388.mediawiki +++ b/bip-0388.mediawiki @@ -1,3 +1,6 @@ +RECENT CHANGES: +* (06 Nov 2024) Added musig key placeholders +
BIP: 388 Layer: Applications @@ -109,7 +112,7 @@ Once the previously registered policy is correctly identified and approved by th While reusing a pubkey in different branches of a miniscript is explicitly forbidden by miniscript (as it has certain negative security implications), it is still reasonable to reuse the same xpub in multiple places, albeit with different final steps of derivation (so that the actual pubkeys that are used in the script are indeed different). In fact, there are many reasonable spending policies with a quadratic size in the number of participants. For example, using Taproot, a 3-of-5 multisignature wallet could use: -* a key path with a 5-of-5 MuSig2 aggregated key +* a key path with a 5-of-5 [[bip-0327.mediawiki|MuSig2]] aggregated key * a script tree with 11 leaves: ** 10 different scripts using a 3-of-3 MuSig2 aggregated key, plus ** a final leaf with a fallback 3-of-5 multisignature using multi_a (in case interactive signing is not available). @@ -133,11 +136,11 @@ A ''wallet descriptor template'' is a SCRIPT expression. SCRIPT expressions: * sh(SCRIPT) (top level only): P2SH embed the argument. * wsh(SCRIPT) (top level or inside sh only): P2WSH embed the argument. -* pkh(KP) (not inside tr): P2PKH output for the given public key. -* wpkh(KP) (top level or inside sh only): P2WPKH output for the given compressed pubkey. -* multi(k,KP_1,KP_2,...,KP_n) (inside sh or wsh only): ''k''-of-''n'' multisig script. -* sortedmulti(k,KP_1,KP_2,...,KP_n) (inside sh or wsh only): ''k''-of-''n'' multisig script with keys sorted lexicographically in the resulting script. -* tr(KP) or tr(KP,TREE) (top level only): P2TR output with the specified key as internal key, and optionally a tree of script paths. +* pkh(KEY) (not inside tr): P2PKH output for the given public key. +* wpkh(KEY) (top level or inside sh only): P2WPKH output for the given compressed pubkey. +* multi(k,KEY_1,KEY_2,...,KEY_n) (inside sh or wsh only): ''k''-of-''n'' multisig script. +* sortedmulti(k,KEY_1,KEY_2,...,KEY_n) (inside sh or wsh only): ''k''-of-''n'' multisig script with keys sorted lexicographically in the resulting script. +* tr(KEY) or tr(KEY,TREE) (top level only): P2TR output with the specified key as internal key, and optionally a tree of script paths. * any valid miniscript template (inside wsh or tr only). See [[bip-0379.md|BIP-379]] for a precise specification of all the valid miniscript SCRIPT expressions. @@ -147,25 +150,38 @@ See [[bip-0379.md|BIP-379]] for a precise specification of all the valid miniscr * An open brace {, a TREE expression, a comma ,, a TREE expression, and a closing brace } -KP expressions (key placeholders) consist of -* a single character @ -* followed by a non-negative decimal number, with no leading zeros (except for @0) +KEY expressions consist of +* a KP expression * ''always'' followed by either: ** the string /**, or ** a string of the form //* , for two distinct decimal numbers NUM representing unhardened derivations, or ** any of the additional, implementation-specific valid derivation path patterns (see [[#optional-derivation-paths|Optional derivation paths]] below). +KP expressions (key placeholders) consist of either: +* a KI (key index) expression, or +* (only inside tr): musig(KI_1,KI_2,...,KI_n) + +A KI (key index) expression consists of: +* a single character @ +* followed by a non-negative decimal number, with no leading zeros (except for @0) + The /** in the placeholder template represents commonly used paths for receive/change addresses, and is equivalent to <0;1>/*. Note that while [[bip-0389.mediawiki|BIP-389]] allows multipath /expressions with an arbitrary number of options, this specification restricts it to exactly 2 choices (with the typical meaning of receive/change addresses). -The placeholder @i for some number ''i'' represents the ''i''-th key in the vector of key information items (which must be of size at least ''i + 1'', or the wallet policy is invalid). +SCRIPT, TREE and KEY expressions map directly to the corresponding concepts defined in [[bip-0380.mediawiki|BIP-380]] for output script descriptors. + +Each KEY expression always correspond to a precise public key in the final bitcoin Script. Therefore, all the derivation steps in the BIP-32 hierarchy are included in a KEY expression. + +Each KP (key placeholder) expressions, on the other hand, maps to the root of all the corresponding public keys for all the possible UTXOs that belong to the account represented in the wallet policy. Therefore, no derivation steps are allowed in a KP expression. + +A KI (key index) @i for some number ''i'' represents the ''i''-th key in the vector of key information items (which must be of size at least ''i + 1'', or the wallet policy is invalid). Note: while descriptor templates for miniscript are not formally defined in this version of the document (pending standardization), it is straightforward to adapt this approach by adding additional SCRIPT expressions. ==== Key information vector ==== -Each element of the key origin information vector is a KEY expression. +Each element of the key origin information vector is a KEY_INFO expression, containing an extended public key, and (optionally) its key origin. * Optionally, key origin information, consisting of: ** An open bracket [ @@ -180,7 +196,7 @@ A wallet policy must have at least one key placeholder and the corresponding key The public keys obtained by deserializing elements of the key information vector must be pairwise distinct'''Why must public keys be distinct?''' Reusing pubkeys could be insecure in the context of wallet policies containing [https://bitcoin.sipa.be/miniscript/ miniscript]. Avoiding repeated public keys altogether avoids the problem at the source.. -If two key placeholders are @i/ /* and @i//* for the same index i, then the sets {M, N} and {P, Q} must be disjoint. +If two KEY are KP/
/* and KP//* for the same key placeholder KP, then the sets {M, N} and {P, Q} must be disjoint. Two musig key placeholders are the same if they have exactly the same set of key indexes (regardless of the order). The key information vector should be ordered so that placeholder @i never appears for the first time before an occurrence of @j for some j < i; for example, the first placeholder is always @0, the next one is @1, etc. @@ -227,6 +243,7 @@ Common single-signature account patterns: Common multisignature schemes: * wsh(multi(2,@0/**,@1/**)) - SegWit 2-of-2 multisignature, keys in order. * sh(sortedmulti(2,@0/**,@1/**,@2/**)) - Legacy 2-of-3 multisignature, sorted keys. +* tr(musig(@0/**,@1/**)) - MuSig2 2-of-2 in the taproot keypath Some miniscript policies in wsh: * wsh(and_v(v:pk(@0/**),or_d(pk(@1/**),older(12960)))) - Trust-minimized second factor, degrading to a single signature after about 90 days. @@ -277,6 +294,10 @@ Taproot wallet policy with sortedmulti_a and a miniscript leaf Keys info: ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", "xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K", "xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM", "xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6"] Descriptor: tr([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*,{sortedmulti_a(1,xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<2;3>/*,xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K/<0;1>/*),or_b(pk(xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM/<0;1>/*),s:pk(xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6/<0;1>/*))})
+Taproot MuSig2 3-of-3 in the key path, with three 2-of-2 MuSig2 recovery paths after 90 days in the script paths + Descriptor template: tr(musig(@0,@1,@2)/**,{and_v(v:pk(musig(@0,@1)/**),older(12960)),{and_v(v:pk(musig(@0,@2)/**),older(12960)),and_v(v:pk(musig(@1,@2)/**),older(12960))}}) + Keys info: ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2"] + Descriptor: tr(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*,{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js)/<0;1>/*),older(12960)),{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960)),and_v(v:pk(musig([b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960))}}) === Invalid policies === @@ -290,6 +311,7 @@ The following descriptor templates are invalid: * sh(multi(1,@0/<0;1>/*,@0/<1;2>/*)): Non-disjoint multipath expressions (@0/1/* appears twice) * sh(multi(1,@0/**,xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/<0;1>/*)): Expression with a non-KP key present * pkh(@0/<0;1;2>/*): Solved cardinality > 2 +* tr(musig(@0/**,@1/**)): Derivation before aggregation is not allowed in wallet policies (despite being allowed in [[bip-0390.mediawiki|BIP-390]]) Remark: some of the examples of invalid descriptor templates may be valid via optional extensions. From e103ddeb1e6a925dd080475f1887b8dc664ce3d1 Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:19:24 +0000 Subject: [PATCH 2/5] Consistency of multisig/multisignature/threshold wording --- bip-0388.mediawiki | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bip-0388.mediawiki b/bip-0388.mediawiki index 0630f0f0..0ee4ac9a 100644 --- a/bip-0388.mediawiki +++ b/bip-0388.mediawiki @@ -83,7 +83,7 @@ We set two fundamental design goals: * Minimize the amount of information that is shown on screen - so that the user can actually validate it. * Minimize the number of times the user has to validate such information. -Designing a secure protocol for the coordination of a descriptor wallet among distant parties is also a challenging problem that is out of scope in this document. See [[bip-0129.mediawiki|BIP-129 (Bitcoin Secure Multisig Setup)]] for an approach designed for multisignature wallets. Regardless of the approach, the ability for the user to carefully verify all the details of the spending policies using the hardware signer's screen is a prerequisite for security in adversarial environments. +Designing a secure protocol for the coordination of a descriptor wallet among distant parties is also a challenging problem that is out of scope in this document. See [[bip-0129.mediawiki|BIP-129 (Bitcoin Secure Multisig Setup)]] for an approach designed for multisig wallets. Regardless of the approach, the ability for the user to carefully verify all the details of the spending policies using the hardware signer's screen is a prerequisite for security in adversarial environments. === Policy registration as a solution === @@ -111,11 +111,11 @@ Once the previously registered policy is correctly identified and approved by th While reusing a pubkey in different branches of a miniscript is explicitly forbidden by miniscript (as it has certain negative security implications), it is still reasonable to reuse the same xpub in multiple places, albeit with different final steps of derivation (so that the actual pubkeys that are used in the script are indeed different). -In fact, there are many reasonable spending policies with a quadratic size in the number of participants. For example, using Taproot, a 3-of-5 multisignature wallet could use: +In fact, there are many reasonable spending policies with a quadratic size in the number of participants. For example, using Taproot, a 3-of-5 threshold wallet could use: * a key path with a 5-of-5 [[bip-0327.mediawiki|MuSig2]] aggregated key * a script tree with 11 leaves: ** 10 different scripts using a 3-of-3 MuSig2 aggregated key, plus -** a final leaf with a fallback 3-of-5 multisignature using multi_a (in case interactive signing is not available). +** a final leaf with a fallback 3-of-5 multisig using multi_a (in case interactive signing is not available). With each xpub being 118 bytes long, the repetition of xpubs makes the descriptor become extremely large. @@ -240,9 +240,9 @@ Common single-signature account patterns: * sh(wpkh(@0/**)) (nested segwit). * tr(@0/**) (taproot single-signature account). -Common multisignature schemes: +Common multisig schemes: * wsh(multi(2,@0/**,@1/**)) - SegWit 2-of-2 multisignature, keys in order. -* sh(sortedmulti(2,@0/**,@1/**,@2/**)) - Legacy 2-of-3 multisignature, sorted keys. +* sh(sortedmulti(2,@0/**,@1/**,@2/**)) - Legacy 2-of-3 multisig, sorted keys. * tr(musig(@0/**,@1/**)) - MuSig2 2-of-2 in the taproot keypath Some miniscript policies in wsh: From a4d9938ed23039538c9f94f5f9e4805e6b196d44 Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:46:16 +0000 Subject: [PATCH 3/5] Explicitly forbid repeated key indexes in musig() --- bip-0388.mediawiki | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bip-0388.mediawiki b/bip-0388.mediawiki index 0ee4ac9a..19091cfb 100644 --- a/bip-0388.mediawiki +++ b/bip-0388.mediawiki @@ -198,6 +198,8 @@ The public keys obtained by deserializing elements of the key information vector If two KEY are KP//* and KP//* for the same key placeholder KP, then the sets {M, N} and {P, Q} must be disjoint. Two musig key placeholders are the same if they have exactly the same set of key indexes (regardless of the order). +Repeated KI expressions are not allowed inside a musig placeholder. + The key information vector should be ordered so that placeholder @i never appears for the first time before an occurrence of @j for some j < i; for example, the first placeholder is always @0, the next one is @1, etc. === Descriptor derivation === From c2026e1de6e8998f07bc2c8d4110dc36b842f716 Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:03:42 +0000 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Jon Atack
--- bip-0388.mediawiki | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bip-0388.mediawiki b/bip-0388.mediawiki index 19091cfb..d2d88c50 100644 --- a/bip-0388.mediawiki +++ b/bip-0388.mediawiki @@ -1,5 +1,5 @@ RECENT CHANGES: -* (06 Nov 2024) Added musig key placeholders +* (06 Nov 2024) Added support for musig key placeholders in descriptor templates BIP: 388 @@ -83,7 +83,7 @@ We set two fundamental design goals: * Minimize the amount of information that is shown on screen - so that the user can actually validate it. * Minimize the number of times the user has to validate such information. -Designing a secure protocol for the coordination of a descriptor wallet among distant parties is also a challenging problem that is out of scope in this document. See [[bip-0129.mediawiki|BIP-129 (Bitcoin Secure Multisig Setup)]] for an approach designed for multisig wallets. Regardless of the approach, the ability for the user to carefully verify all the details of the spending policies using the hardware signer's screen is a prerequisite for security in adversarial environments. +Designing a secure protocol for the coordination of a descriptor wallet among distant parties is also a challenging problem that is out of the scope of this document. See [[bip-0129.mediawiki|BIP-129 (Bitcoin Secure Multisig Setup)]] for an approach designed for multisig wallets. Regardless of the approach, the ability for the user to carefully verify all the details of the spending policies using the hardware signer's screen is a prerequisite for security in adversarial environments. === Policy registration as a solution === @@ -171,9 +171,9 @@ Note that while [[bip-0389.mediawiki|BIP-389]] allows multipath /SCRIPT , TREE and KEY expressions map directly to the corresponding concepts defined in [[bip-0380.mediawiki|BIP-380]] for output script descriptors. -Each KEY expression always correspond to a precise public key in the final bitcoin Script. Therefore, all the derivation steps in the BIP-32 hierarchy are included in a KEY expression. +Each KEY expression always corresponds to a precise public key in the final bitcoin Script. Therefore, all the derivation steps in the BIP-32 hierarchy are included in a KEY expression. -Each KP (key placeholder) expressions, on the other hand, maps to the root of all the corresponding public keys for all the possible UTXOs that belong to the account represented in the wallet policy. Therefore, no derivation steps are allowed in a KP expression. +Each KP (key placeholder) expression, on the other hand, maps to the root of all the corresponding public keys for all the possible UTXOs that belong to the account represented in the wallet policy. Therefore, no derivation steps are allowed in a KP expression. A KI (key index) @i for some number ''i'' represents the ''i''-th key in the vector of key information items (which must be of size at least ''i + 1'', or the wallet policy is invalid). @@ -243,7 +243,7 @@ Common single-signature account patterns: * tr(@0/**) (taproot single-signature account). Common multisig schemes: -* wsh(multi(2,@0/**,@1/**)) - SegWit 2-of-2 multisignature, keys in order. +* wsh(multi(2,@0/**,@1/**)) - SegWit 2-of-2 multisig, keys in order. * sh(sortedmulti(2,@0/**,@1/**,@2/**)) - Legacy 2-of-3 multisig, sorted keys. * tr(musig(@0/**,@1/**)) - MuSig2 2-of-2 in the taproot keypath From 2faf09d395c027d9d5ef1c490d6fe9eb085001dc Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Sat, 9 Nov 2024 09:51:56 +0000 Subject: [PATCH 5/5] Move changelog to standalone section --- bip-0388.mediawiki | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bip-0388.mediawiki b/bip-0388.mediawiki index d2d88c50..a16690ec 100644 --- a/bip-0388.mediawiki +++ b/bip-0388.mediawiki @@ -1,6 +1,3 @@ -RECENT CHANGES: -* (06 Nov 2024) Added support for musig key placeholders in descriptor templates -BIP: 388 Layer: Applications @@ -335,7 +332,14 @@ Wallet policies are implemented in For development and testing purposes, we provide a [[bip-0388/wallet_policies.py|Python 3.7 reference implementation]] of simple classes to handle wallet policies, and the conversion to/from output script descriptors. The reference implementation is for demonstration purposes only and not to be used in production environments. -==Footnotes== +== Change Log == + +* '''1.1.0''' (2024-11): +** Added support for musig key placeholders in descriptor templates. +* '''1.0.0''' (2024-05): +** Initial version. + +== Footnotes ==