mirror of
https://github.com/bitcoin/bips.git
synced 2025-03-04 03:03:53 +01:00
111 lines
8 KiB
Text
111 lines
8 KiB
Text
<pre>
|
|
BIP: ?
|
|
Layer: Applications
|
|
Title: unspendable() Descriptor Key Expression
|
|
Author: Andrew Toth <andrewstoth@gmail.com>
|
|
Kewde <kewde@particl.io>
|
|
Comments-Summary: No comments yet.
|
|
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-?
|
|
Status: Draft
|
|
Type: Standards Track
|
|
Created: 2024-12-12
|
|
License: BSD-2-Clause
|
|
</pre>
|
|
|
|
==Abstract==
|
|
|
|
This document specifies a <tt>unspendable()</tt> key expression for output script descriptors. The <tt>unspendable()</tt> expression operates on the root <tt>TREE</tt> expression and produces an unspendable public key that can be independently verified by anyone with knowledge of all the constituent public keys.
|
|
|
|
==Copyright==
|
|
|
|
This BIP is licensed under the BSD 2-clause license.
|
|
|
|
==Motivation==
|
|
|
|
When creating a multi-party Taproot transaction spending only from the script path, it is useful to be able to prove to all cosigners that they keypath is unspendable. Otherwise a malicious participant could use an internal key which they have the private key for and spend the transaction out from the rest of the participants.
|
|
|
|
This document introduces a mechanism to compute a NUMS (Nothing Up My Sleeve) point for use in the Taproot key path that:
|
|
* Allows active participants involved in constructing the output script to independently verify the unspendable key.
|
|
* Prevents passive observers from recognizing that the key path is unspendable.
|
|
* Enables signers with limited information, such as hardware wallets, to verify unspendability without requiring user interaction.
|
|
|
|
==Specification==
|
|
|
|
A new key expression is defined: <tt>unspendable()/NUM/.../*</tt>.
|
|
|
|
===<tt>unspendable()/NUM/.../*</tt>===
|
|
|
|
The <tt>unspendable</tt> expression can only be used as the first argument of a BIP386 <tt>tr(KEY, TREE)</tt> expression. All other <tt>KEY</tt> expressions in the descriptor must be <tt>xpub</tt> encoded extended public keys with exactly 2 unhardened derivation steps. The derivation steps may include <tt>/*</tt> or a BIP389 multipath expression, but still must have only unhardened steps. BIP390 <tt>musig(KEY, KEY, ..., KEY)</tt> expressions are allowed, but the variant with derivation after the expression <tt>musig(KEY, KEY, ..., KEY)/NUM/.../*</tt> is forbidden.
|
|
|
|
The <tt>unspendable</tt> expression resolves to an extended public key, which is then further derived. As there is no private key for an unspendable key, only unhardened derivation is allowed.
|
|
|
|
The extended public key is computed by first collecting the public key from all the extended public keys in all the <tt>KEY</tt> expressions. The collection of public keys then has all duplicates removed and the remaining public keys are sorted lexicographically.
|
|
The vector of keys is processed in the following sequence: deduplication, compression, sorting, concatenation, and finally, SHA256 hashing to generate a chaincode <tt>c</tt>.
|
|
The unspendable BIP32 extended key is constructed by using the NUMS point <tt>H</tt>, suggested in BIP341, as the public key, and the chaincode <tt>c</tt> is computed as follows:
|
|
* The public keys are collected from all extended public keys in all <tt>KEY</tt> expressions.
|
|
* All duplicate public keys are removed from the collection and the public keys are sorted lexicographically.
|
|
* Let ''P<sub>0</sub> ... P<sub>n</sub>'' be the sorted and deduplicated public keys. Using the notation from BIP340, ''c = hash<sub>BIP0???/chaincode</sub>(bytes(P<sub>0</sub>) || ... || bytes(P<sub>n</sub>))''.
|
|
|
|
==Test Vectors==
|
|
|
|
Valid descriptors containing the <tt>unspendable</tt> expression followed by the chaincode of the unspendable extended public key they expand to and then the scripts they produce.
|
|
Todo: These will be filled in when the BIP number is assigned for the tagged hash.
|
|
|
|
The following produce identical extended public keys and scripts:
|
|
* <tt>tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0))</tt>
|
|
* <tt>tr(unspendable()/0, pk(xpub661MyMwAqRbcFXsZHGwUFzya6zhjaLUoKt2jKZTsWEoHAPjUERUbW215Fy6DGNLZdNDyMo8WJLgouGNRypxvDFc3MgW8TvRJdpbzsxuyfvr/0/0))</tt>
|
|
* <tt>tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<0;1>/*))</tt>
|
|
|
|
The following has two identical public keys which are deduplicated:
|
|
* <tt>tr(unspendable()/0, {pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0),pk(xpub661MyMwAqRbcFXsZHGwUFzya6zhjaLUoKt2jKZTsWEoHAPjUERUbW215Fy6DGNLZdNDyMo8WJLgouGNRypxvDFc3MgW8TvRJdpbzsxuyfvr/0/0)})</tt>
|
|
|
|
The following has two identical public keys which are deduplicated, and then the remaining two public keys are sorted:xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
|
|
* <tt>tr(unspendable()/0, {pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0),{pk(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw),pk(xpub661MyMwAqRbcFXsZHGwUFzya6zhjaLUoKt2jKZTsWEoHAPjUERUbW215Fy6DGNLZdNDyMo8WJLgouGNRypxvDFc3MgW8TvRJdpbzsxuyfvr/0/0)}})</tt>
|
|
|
|
Invalid descriptors:
|
|
|
|
No <tt>TREE</tt> expression:
|
|
* <tt>tr(unspendable()/0)</tt>
|
|
|
|
No derivation path for the unspendable key:
|
|
* <tt>tr(unspendable(), pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0))</tt>
|
|
|
|
Hardened derivation path for the unspendable key:
|
|
* <tt>tr(unspendable()/0'/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0))</tt>
|
|
|
|
Hardened derivation path in a <tt>KEY</tt> expression:
|
|
* <tt>tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0'/0))</tt>
|
|
|
|
Greater than two derivation paths in a <tt>KEY</tt> expression:
|
|
* <tt>tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0/0))</tt>
|
|
|
|
Less than two derivation paths in a <tt>KEY</tt> expression:
|
|
* <tt>tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0))</tt>
|
|
|
|
A <tt>KEY</tt> expression is not an <tt>xpub</tt>:
|
|
* <tt>tr(unspendable()/0, pk(0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600))</tt>
|
|
|
|
A <tt>musig</tt> expression with derivation paths is used:
|
|
* <tt>tr(unspendable()/0, musig(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0)/0/0)</tt>
|
|
|
|
==Rationale==
|
|
|
|
The restrictions on <tt>KEY</tt> expressions is necessary to not allow multiple <tt>TREE</tt> expressions which would all produce the same merkle root to produce different internal keys.
|
|
* Using different lengths of derivation paths would allow a descriptor using a child xpub to generate a different key, while the merkle root would be identical.
|
|
* Not sorting the public keys would allow <tt>sortedmulti</tt> expressions to generate different keys depending on the order specified in the descriptor, while the merkle roots would be identical.
|
|
|
|
This proposal ensures:
|
|
* Compatibility with existing Taproot functionality by leveraging NUMS points.
|
|
* Verifiability of unspendable constructions by participants, without exposing this property to outside observers.
|
|
* Security and simplicity for signers with limited information (e.g., hardware wallets).
|
|
|
|
==Backwards Compatibility==
|
|
|
|
This is backwards compatible with BIP386 by computing the unspendable key as a BIP380 <tt>KEY</tt> expression and replacing the <tt>unspendable</tt> expression as the first argument of the <tt>tr()</tt> expression.
|
|
|
|
This is backwards compatible with BIP388, since the public keys are deduplicated. The key information vector will contain all the necessary public keys.
|
|
|
|
==Acknowledgements==
|
|
|
|
Thanks to Salvatore Ingala, Pieter Wuille, Antoine Poinsot, Andrew Kozlik and all others who
|
|
participated in discussions on this topic.
|