BIP: ?
Layer: Applications
Title: unspendable() Descriptor Key Expression
Author: Andrew Toth
Kewde
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
==Abstract==
This document specifies a unspendable() key expression for output script descriptors. The unspendable() expression takes multiple public keys as input 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==
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: unspendable()/NUM/.../*.
===unspendable()/NUM/.../*===
The unspendable expression can only be used as the first argument of a BIP386 tr(KEY, TREE) expression. All other KEY expressions in the descriptor must be xpub encoded extended public keys with exactly 2 unhardened derivation steps. The derivation steps may include /* or a BIP389 multipath expression, but still must have only unhardened steps. Additionally, BIP390 musig expressions are forbidden.
The unspendable expression resolves to an extended public key, which is then further derived. As there is no aggregate 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 KEY 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 c.
The unspendable BIP32 extended key is constructed by using the NUMS point H, suggested in BIP341, as the public key, and the chaincode c is computed as follows:
* The public keys are collected from all extended public keys in all KEY expressions.
* All duplicate public keys are removed from the collection and the public keys are sorted lexicographically.
* Let ''P0 ... Pn'' be the sorted and deduplicated public keys. Using the notation from BIP340, ''c = hashBIP0???/chaincode(bytes(P0) || ... || bytes(Pn))''.
==Test Vectors==
Valid descriptors containing the unspendable 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:
* tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0))
* tr(unspendable()/0, pk(xpub661MyMwAqRbcFXsZHGwUFzya6zhjaLUoKt2jKZTsWEoHAPjUERUbW215Fy6DGNLZdNDyMo8WJLgouGNRypxvDFc3MgW8TvRJdpbzsxuyfvr/0/0))
* tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<0;1>/*))
The following has two identical public keys which are deduplicated:
* tr(unspendable()/0, {pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0),pk(xpub661MyMwAqRbcFXsZHGwUFzya6zhjaLUoKt2jKZTsWEoHAPjUERUbW215Fy6DGNLZdNDyMo8WJLgouGNRypxvDFc3MgW8TvRJdpbzsxuyfvr/0/0)})
The following has two identical public keys which are deduplicated, and then the remaining two public keys are sorted:xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
* tr(unspendable()/0, {pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0),{pk(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw),pk(xpub661MyMwAqRbcFXsZHGwUFzya6zhjaLUoKt2jKZTsWEoHAPjUERUbW215Fy6DGNLZdNDyMo8WJLgouGNRypxvDFc3MgW8TvRJdpbzsxuyfvr/0/0)}})
Invalid descriptors:
No TREE expression:
* tr(unspendable()/0)
No derivation path for the unspendable key:
* tr(unspendable(), pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0))
Hardened derivation path for the unspendable key:
* tr(unspendable()/0'/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0))
Hardened derivation path in a KEY expression:
* tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0'/0))
Greater than two derivation paths in a KEY expression:
* tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/0/0))
Less than two derivation paths in a KEY expression:
* tr(unspendable()/0, pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0))
A KEY expression is not an xpub:
* tr(unspendable()/0, pk(0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600))
==Rationale==
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 KEY expression and replacing the unspendable expression as the first argument of the tr() expression.
==Acknowledgements==
Thanks to Salvatore Ingala, Pieter Wuille, Antoine Poinsot, Andrew Kozlik and all others who
participated in discussions on this topic.