Skip to content

Commit 8d9670c

Browse files
committed
Add rawtr() descriptor for P2TR with unknown tweak
1 parent 5560682 commit 8d9670c

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

doc/descriptors.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Descriptors consist of several types of expressions. The top level expression is
7777
- `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.
7878
- `addr(ADDR)` (top level only): the script which ADDR expands to.
7979
- `raw(HEX)` (top level only): the script whose hex encoding is HEX.
80+
- `rawtr(KEY)` (top level only): P2TR output with the specified key as output key. NOTE: while it's possible to use this to construct wallets, it has several downsides, like being unable to prove no hidden script path exists. Use at your own risk.
8081

8182
`KEY` expressions:
8283
- Optionally, key origin information, consisting of:
@@ -87,7 +88,7 @@ Descriptors consist of several types of expressions. The top level expression is
8788
- Followed by the actual key, which is either:
8889
- 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).
8990
- Inside `wpkh` and `wsh`, only compressed public keys are permitted.
90-
- Inside `tr`, x-only pubkeys are also permitted (64 hex characters).
91+
- Inside `tr` and `rawtr`, x-only pubkeys are also permitted (64 hex characters).
9192
- [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.
9293
- `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)).
9394
- Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps.

src/script/descriptor.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,24 @@ class MiniscriptDescriptor final : public DescriptorImpl
10151015
bool IsSingleType() const final { return true; }
10161016
};
10171017

1018+
/** A parsed rawtr(...) descriptor. */
1019+
class RawTRDescriptor final : public DescriptorImpl
1020+
{
1021+
protected:
1022+
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override
1023+
{
1024+
assert(keys.size() == 1);
1025+
XOnlyPubKey xpk(keys[0]);
1026+
if (!xpk.IsFullyValid()) return {};
1027+
WitnessV1Taproot output{xpk};
1028+
return Vector(GetScriptForDestination(output));
1029+
}
1030+
public:
1031+
RawTRDescriptor(std::unique_ptr<PubkeyProvider> output_key) : DescriptorImpl(Vector(std::move(output_key)), "rawtr") {}
1032+
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; }
1033+
bool IsSingleType() const final { return true; }
1034+
};
1035+
10181036
////////////////////////////////////////////////////////////////////////////
10191037
// Parser //
10201038
////////////////////////////////////////////////////////////////////////////
@@ -1453,6 +1471,16 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
14531471
error = "Can only have tr at top level";
14541472
return nullptr;
14551473
}
1474+
if (ctx == ParseScriptContext::TOP && Func("rawtr", expr)) {
1475+
auto arg = Expr(expr);
1476+
auto output_key = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
1477+
if (!output_key) return nullptr;
1478+
++key_exp_index;
1479+
return std::make_unique<RawTRDescriptor>(std::move(output_key));
1480+
} else if (Func("rawtr", expr)) {
1481+
error = "Can only have rawtr at top level";
1482+
return nullptr;
1483+
}
14561484
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
14571485
std::string str(expr.begin(), expr.end());
14581486
if (!IsHex(str)) {
@@ -1626,6 +1654,13 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
16261654
}
16271655
}
16281656
}
1657+
// If the above doesn't work, construct a rawtr() descriptor with just the encoded x-only pubkey.
1658+
if (pubkey.IsFullyValid()) {
1659+
auto key = InferXOnlyPubkey(pubkey, ParseScriptContext::P2TR, provider);
1660+
if (key) {
1661+
return std::make_unique<RawTRDescriptor>(std::move(key));
1662+
}
1663+
}
16291664
}
16301665

16311666
if (ctx == ParseScriptContext::P2WSH) {

test/functional/data/rpc_decodescript.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{
55
"asm": "1 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
66
"address": "bcrt1pamhwamhwamhwamhwamhwamhwamhwamhwamhwamhwamhwamhwamhqz6nvlh",
7-
"desc": "addr(bcrt1pamhwamhwamhwamhwamhwamhwamhwamhwamhwamhwamhwamhwamhqz6nvlh)#v52jnujz",
7+
"desc": "rawtr(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee)#jk7c6kys",
88
"type": "witness_v1_taproot"
99
}
1010
],

0 commit comments

Comments
 (0)