Skip to content

Commit c7dd9ff

Browse files
committed
Merge bitcoin/bitcoin#22051: Basic Taproot derivation support for descriptors
2667366 tests: check derivation of P2TR (Pieter Wuille) 7cedafc Add tr() descriptor (derivation only, no signing) (Pieter Wuille) 90fcac3 Add TaprootBuilder class (Pieter Wuille) 5f6cc8d Add XOnlyPubKey::CreateTapTweak (Pieter Wuille) 2fbfb1b Make consensus checking of tweaks in pubkey.* Taproot-specific (Pieter Wuille) a4bf840 Separate WitnessV1Taproot variant in CTxDestination (Pieter Wuille) 41839bd Avoid dependence on CTxDestination index order (Pieter Wuille) 31df02a Change Solver() output for WITNESS_V1_TAPROOT (Pieter Wuille) 4b1cc08 Make XOnlyPubKey act like byte container (Pieter Wuille) Pull request description: This is a subset of #21365, to aide review. This adds support `tr(KEY)` or `tr(KEY,SCRIPT)` or `tr(KEY,{{S1,{{S2,S3},...}},...})` descriptors, describing Taproot outputs with specified internal key, and optionally any number of scripts, in nested groups of 2 inside `{`/`}` if there are more than one. While it permits importing `tr(KEY)`, anything beyond that is just laying foundations for more features later. Missing: * Signing support (see #21365) * Support for more interesting scripts inside the tree (only `pk(KEY)` is supported for now). In particular, a multisig policy based on the new `OP_CHECKSIGADD` opcode would be very useful. * Inferring `tr()` descriptors from outputs (given sufficient information). * `getaddressinfo` support. * MuSig support. Standardizing that is still an ongoing effort, and is generally kind of useless without corresponding PSBT support. * Convenient ways of constructing descriptors without spendable internal key (especially ones that arent't trivially recognizable as such). ACKs for top commit: Sjors: utACK 2667366 (based on bitcoin/bitcoin#21365 (comment) review, plus the new functional test) achow101: Code Review ACK 2667366 lsilva01: Tested ACK bitcoin/bitcoin@2667366 meshcollider: utACK 2667366 Tree-SHA512: 61046fef22c561228338cb178422f0b782ef6587ec8208d3ce2bd07afcff29a664b54b35c6b01226eb70b6540b43f6dd245043d09aa6cb6db1381b6042667e75
2 parents 07ededa + 2667366 commit c7dd9ff

16 files changed

+834
-55
lines changed

doc/descriptors.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Output descriptors currently support:
3030
- Pay-to-witness-pubkey-hash scripts (P2WPKH), through the `wpkh` function.
3131
- Pay-to-script-hash scripts (P2SH), through the `sh` function.
3232
- Pay-to-witness-script-hash scripts (P2WSH), through the `wsh` function.
33+
- Pay-to-taproot outputs (P2TR), through the `tr` function.
3334
- Multisig scripts, through the `multi` function.
3435
- Multisig scripts where the public keys are sorted lexicographically, through the `sortedmulti` function.
3536
- Any type of supported address through the `addr` function.
@@ -54,20 +55,22 @@ Output descriptors currently support:
5455
- `pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)` describes a set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`.
5556
- `wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where the first multisig key is the *1/0/`i`* child of the first specified xpub and the second multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default).
5657
- `wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where one multisig key is the *1/0/`i`* child of the first specified xpub and the other multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default). The order of public keys in the resulting witnessScripts is determined by the lexicographic order of the public keys at that index.
58+
- `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,{pk(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),pk(e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)})` describes a P2TR output with the `c6...` x-only pubkey as internal key, and two script paths.
5759

5860
## Reference
5961

6062
Descriptors consist of several types of expressions. The top level expression is either a `SCRIPT`, or `SCRIPT#CHECKSUM` where `CHECKSUM` is an 8-character alphanumeric descriptor checksum.
6163

6264
`SCRIPT` expressions:
6365
- `sh(SCRIPT)` (top level only): P2SH embed the argument.
64-
- `wsh(SCRIPT)` (not inside another 'wsh'): P2WSH embed the argument.
66+
- `wsh(SCRIPT)` (top level or inside `sh` only): P2WSH embed the argument.
6567
- `pk(KEY)` (anywhere): P2PK output for the given public key.
66-
- `pkh(KEY)` (anywhere): P2PKH output for the given public key (use `addr` if you only know the pubkey hash).
67-
- `wpkh(KEY)` (not inside `wsh`): P2WPKH output for the given compressed pubkey.
68+
- `pkh(KEY)` (not inside `tr`): P2PKH output for the given public key (use `addr` if you only know the pubkey hash).
69+
- `wpkh(KEY)` (top level or inside `sh` only): P2WPKH output for the given compressed pubkey.
6870
- `combo(KEY)` (top level only): an alias for the collection of `pk(KEY)` and `pkh(KEY)`. If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`.
69-
- `multi(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script.
70-
- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script with keys sorted lexicographically in the resulting script.
71+
- `multi(k,KEY_1,KEY_2,...,KEY_n)` (not inside `tr`): k-of-n multisig script.
72+
- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (not inside `tr`): k-of-n multisig script with keys sorted lexicographically in the resulting script.
73+
- `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.
7174
- `addr(ADDR)` (top level only): the script which ADDR expands to.
7275
- `raw(HEX)` (top level only): the script whose hex encoding is HEX.
7376

@@ -80,12 +83,17 @@ Descriptors consist of several types of expressions. The top level expression is
8083
- Followed by the actual key, which is either:
8184
- Hex encoded public keys (either 66 characters starting with `02` or `03` for a compressed pubkey, or 130 characters starting with `04` for an uncompressed pubkey).
8285
- Inside `wpkh` and `wsh`, only compressed public keys are permitted.
86+
- Inside `tr`, x-only pubkeys are also permitted (64 hex characters).
8387
- [WIF](https://en.bitcoin.it/wiki/Wallet_import_format) encoded private keys may be specified instead of the corresponding public key, with the same meaning.
8488
- `xpub` encoded extended public key or `xprv` encoded extended private key (as defined in [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)).
8589
- Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps.
8690
- Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children.
8791
- The usage of hardened derivation steps requires providing the private key.
8892

93+
`TREE` expressions:
94+
- any `SCRIPT` expression
95+
- An open brace `{`, a `TREE` expression, a comma `,`, a `TREE` expression, and a closing brace `}`
96+
8997
(Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead.)
9098

9199
`ADDR` expressions are any type of supported address:

src/key_io.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ class DestinationEncoder
5454
return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
5555
}
5656

57+
std::string operator()(const WitnessV1Taproot& tap) const
58+
{
59+
std::vector<unsigned char> data = {1};
60+
data.reserve(53);
61+
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, tap.begin(), tap.end());
62+
return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
63+
}
64+
5765
std::string operator()(const WitnessUnknown& id) const
5866
{
5967
if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
@@ -135,6 +143,13 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
135143
return CNoDestination();
136144
}
137145

146+
if (version == 1 && data.size() == WITNESS_V1_TAPROOT_SIZE) {
147+
static_assert(WITNESS_V1_TAPROOT_SIZE == WitnessV1Taproot::size());
148+
WitnessV1Taproot tap;
149+
std::copy(data.begin(), data.end(), tap.begin());
150+
return tap;
151+
}
152+
138153
if (version > 16) {
139154
error_str = "Invalid Bech32 address witness version";
140155
return CNoDestination();

src/pubkey.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
180180
std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
181181
}
182182

183+
bool XOnlyPubKey::IsFullyValid() const
184+
{
185+
secp256k1_xonly_pubkey pubkey;
186+
return secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data());
187+
}
188+
183189
bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const
184190
{
185191
assert(sigbytes.size() == 64);
@@ -188,13 +194,45 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
188194
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
189195
}
190196

191-
bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
197+
static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
198+
199+
uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
200+
{
201+
if (merkle_root == nullptr) {
202+
// We have no scripts. The actual tweak does not matter, but follow BIP341 here to
203+
// allow for reproducible tweaking.
204+
return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256();
205+
} else {
206+
return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256();
207+
}
208+
}
209+
210+
bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
211+
{
212+
secp256k1_xonly_pubkey internal_key;
213+
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false;
214+
uint256 tweak = internal.ComputeTapTweakHash(&merkle_root);
215+
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin());
216+
}
217+
218+
std::optional<std::pair<XOnlyPubKey, bool>> XOnlyPubKey::CreateTapTweak(const uint256* merkle_root) const
192219
{
193220
secp256k1_xonly_pubkey base_point;
194-
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
195-
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
221+
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, data())) return std::nullopt;
222+
secp256k1_pubkey out;
223+
uint256 tweak = ComputeTapTweakHash(merkle_root);
224+
if (!secp256k1_xonly_pubkey_tweak_add(secp256k1_context_verify, &out, &base_point, tweak.data())) return std::nullopt;
225+
int parity = -1;
226+
std::pair<XOnlyPubKey, bool> ret;
227+
secp256k1_xonly_pubkey out_xonly;
228+
if (!secp256k1_xonly_pubkey_from_pubkey(secp256k1_context_verify, &out_xonly, &parity, &out)) return std::nullopt;
229+
secp256k1_xonly_pubkey_serialize(secp256k1_context_verify, ret.first.begin(), &out_xonly);
230+
assert(parity == 0 || parity == 1);
231+
ret.second = parity;
232+
return ret;
196233
}
197234

235+
198236
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
199237
if (!IsValid())
200238
return false;

src/pubkey.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <uint256.h>
1414

1515
#include <cstring>
16+
#include <optional>
1617
#include <vector>
1718

1819
const unsigned int BIP32_EXTKEY_SIZE = 74;
@@ -222,19 +223,56 @@ class XOnlyPubKey
222223
uint256 m_keydata;
223224

224225
public:
226+
/** Construct an empty x-only pubkey. */
227+
XOnlyPubKey() = default;
228+
229+
XOnlyPubKey(const XOnlyPubKey&) = default;
230+
XOnlyPubKey& operator=(const XOnlyPubKey&) = default;
231+
232+
/** Determine if this pubkey is fully valid. This is true for approximately 50% of all
233+
* possible 32-byte arrays. If false, VerifySchnorr and CreatePayToContract will always
234+
* fail. */
235+
bool IsFullyValid() const;
236+
225237
/** Construct an x-only pubkey from exactly 32 bytes. */
226238
explicit XOnlyPubKey(Span<const unsigned char> bytes);
227239

240+
/** Construct an x-only pubkey from a normal pubkey. */
241+
explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span<const unsigned char>(pubkey.begin() + 1, pubkey.begin() + 33)) {}
242+
228243
/** Verify a Schnorr signature against this public key.
229244
*
230245
* sigbytes must be exactly 64 bytes.
231246
*/
232247
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
233-
bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
248+
249+
/** Compute the Taproot tweak as specified in BIP341, with *this as internal
250+
* key:
251+
* - if merkle_root == nullptr: H_TapTweak(xonly_pubkey)
252+
* - otherwise: H_TapTweak(xonly_pubkey || *merkle_root)
253+
*
254+
* Note that the behavior of this function with merkle_root != nullptr is
255+
* consensus critical.
256+
*/
257+
uint256 ComputeTapTweakHash(const uint256* merkle_root) const;
258+
259+
/** Verify that this is a Taproot tweaked output point, against a specified internal key,
260+
* Merkle root, and parity. */
261+
bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const;
262+
263+
/** Construct a Taproot tweaked output point with this point as internal key. */
264+
std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const;
234265

235266
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
236267
const unsigned char* data() const { return m_keydata.begin(); }
237-
size_t size() const { return m_keydata.size(); }
268+
static constexpr size_t size() { return decltype(m_keydata)::size(); }
269+
const unsigned char* begin() const { return m_keydata.begin(); }
270+
const unsigned char* end() const { return m_keydata.end(); }
271+
unsigned char* begin() { return m_keydata.begin(); }
272+
unsigned char* end() { return m_keydata.end(); }
273+
bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; }
274+
bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; }
275+
bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; }
238276
};
239277

240278
struct CExtPubKey {

src/rpc/util.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,16 @@ class DescribeAddressVisitor
301301
return obj;
302302
}
303303

304+
UniValue operator()(const WitnessV1Taproot& tap) const
305+
{
306+
UniValue obj(UniValue::VOBJ);
307+
obj.pushKV("isscript", true);
308+
obj.pushKV("iswitness", true);
309+
obj.pushKV("witness_version", 1);
310+
obj.pushKV("witness_program", HexStr(tap));
311+
return obj;
312+
}
313+
304314
UniValue operator()(const WitnessUnknown& id) const
305315
{
306316
UniValue obj(UniValue::VOBJ);

0 commit comments

Comments
 (0)