<p>The answer is twofold.</p>
<p>First, the Bitcoin Core descriptors are in the process of being extended with <a href="https://bitcoin.sipa.be/miniscript/" rel="nofollow noreferrer">Miniscript</a>. In the upcoming version (24.0), Miniscript will only be supported in Segwit version 0 outputs (P2WSH).<br />
Once Miniscript support for Tapscript is added, you will be able to use a lot more Script functionalities in the script paths of a Taproot descriptor.</p>
<p>With regard to arbitrary Scripts, that is including those that are not representable (and therefore not solvable) using Miniscript. There is an ongoing discussion about supporting &quot;<a href="https://github.com/bitcoin/bitcoin/issues/24114" rel="nofollow noreferrer">partial descriptors</a>&quot;, an otherwise solvable descriptor containing a spending path for which we have no solvability information.<br />
A partial Taproot descriptor would allow you to have a <code>rawnode(HEX)</code> in the tree. This is what you need if you want to keep the solvability through the key path, because otherwise you could just use <a href="https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md#reference" rel="nofollow noreferrer"><code>rawtr()</code></a>.</p>
<blockquote>
<p>an OP_RETURN type commitment in a taproot script path, or at least I couldn't figure out a way to do otherwise. I could think of using the pk() expression with a hash for example, but I guess it's bad to do that.</p>
</blockquote>
<p>Unrelated to your main question, but i don't think it's necessarily bad to use another form of commitment. If you were thinking this because of OP_RETURN outputs pruning from the UTxO set, note this is meaningless in this context.</p>
<p>You could use less expensive methods for committing the data. Such as <a href="https://bitcoinops.org/en/topics/pay-to-contract-outputs/" rel="nofollow noreferrer">tweaking a public key</a>. And that would remove the need for partial descriptors.</p>

CC BY-SA 4.0
2022-08-18T12:57:23.757Z
Thanks for the explanations, to sum it up 1) supporting arbitrary script is out of scope for descriptors and is to be taken care by miniscript 2) descriptors shouldn't allow for spending conditions that we don't fully understand, even if it might be the case in the future.
If I were to do a taproot script with an arbitrary path, I should first write it (how?) and then represent it with `rawtr()`, is that correct?
Sosthène
CC BY-SA 4.0
2022-08-18T13:54:14.847Z
For 1), no. Miniscript is about safely supporting more scripts in a general manner, not *any* script. Miniscript is a subset of Bitcoin Script. Regarding 2) It was indeed the thinking of their initial designer. But see the discussion i linked above. We (contributors in this area, including the original designer) are starting to think partial descriptors should be supported. You are correct that as of version `24.0` you would have to use a `rawtr()` descriptor to track Taproot outputs with an `OP_RETURN` in one of the Script path branches.
Antoine Poinsot
CC BY-SA 4.0
2022-08-18T14:21:52.637Z
Ugh, when I wrote "arbitrary script" what I meant was indeed "arbitrary script within what's supported by miniscript", thanks for correcting me. As for 2), is there some tooling that could help writing this kind of script? 
Just asking out of curiosity, at this point I'm basically convinced this is a bad idea anyway.
Sosthène
CC BY-SA 4.0
2022-08-18T14:27:56.910Z
Just noticed your last edit, what I meant was that hacking the descriptors by pushing a `pk()` with the hash you want to commit in place of an `OP_RETURN` looks ugly and is probably bad. I'm also thinking about pubkey tweaking but a bit cautious because not sure what libs implemented it besides btcd and lnp/bp and I don't think I can use any of it on this project.
Sosthène
CC BY-SA 4.0
2022-08-18T14:56:46.803Z
I'm curious what you are trying to achieve now. :) Re the tooling, for Bitcoin Core i'm not aware of say an RPC call that'd allow you to do that. But finding a library that allows you to create a Taproot scriptPubKey from a set of Script leaves shouldn't be too hard? For what it's worth this https://github.com/darosior/python-bip380 will probably feature what you want once i finally implement Taproot descriptors.
Antoine Poinsot
CC BY-SA 4.0
2022-08-18T12:01:31.723Z
2022-08-18T14:12:00.103Z
2022-08-18T14:12:00.103Z
Antoine Poinsot
<p>It's not clear to me why one would want to commit to an <code>OP_RETURN</code> inside of a P2TR output construction, let alone inside of a descriptor. I feel like there might be a misunderstanding here.</p>
<p>Using the <code>OP_RETURN</code> op-code makes an output script <em>unspendable</em> (and in most cases non-standard). Putting <code>OP_RETURN</code> as the first instruction in a scriptPubKey indicates a <em>NULL DATA</em> output, which permits writing a small amount of data to the blockchain. Because NULL DATA outputs are unspendable, they do not get inserted in the UTXO set, but since they're written out as part of the transaction in the blockchain, they're still published.</p>
<p>A popular application of NULL DATA outputs is the <em>publishing of a commitment</em>, e.g. the hash of a document to prove that it existed at the time, the witness commitment in segwit blocks, or a Merkle root of a whole tree of commitments as with OpenTimeStamps.</p>
<p>Putting an <code>OP_RETURN</code> into a leafscript of a Taptree does not serve the purpose of publishing the data, since only the leafscript used for spending the UTXO in the input will ever written to the blockchain.
If the data should be published per the transaction, it should be a dedicated output.</p>
<p>If the creation of a commitment suffices, it would be enough to tweak the inner public key with the hash of the data. That way, the owner could at a later time publish the data, and attest to the hash by signing with the inner public key. The act of publishing would then be separate from the act of committing, but the commitment would still proof that the data existed at the time of the transaction output creation.</p>

CC BY-SA 4.0
2022-08-31T15:47:43.000Z
Note that for OpenTimestamps, publishing is never needed, and IIRC OpenTimestamps also supports committing the Merkle root of its tree using sign-to-contract (making an otherwise normal transaction signature commit to it).
Pieter Wuille
CC BY-SA 4.0
2022-09-13T14:51:07.057Z
Thanks for the answer. I got your point on the distinction between publishing and committing. Interestingly a project like RGB, whose business is commitment and not publishing, do use OP_RETURN inside a taproot leafscript (they call it "tapret" I think). I don't know the rationale for that instead of directly tweaking the internal pubkey as you propose. 

As to why I was asking how to put it inside a descriptor, it was because I couldn't think of another way to create the corresponding scriptpubkey using Bitcoin Core RPC. Wether it was a good idea or not is another question :)
Sosthène
CC BY-SA 4.0
2022-09-13T14:52:12.283Z
Thinking about it a reason to do an OP_RETURN commitment inside a leafscript instead of directly tweaking the internal pubkey might be to commit other scripts in the same scriptpubkey?
Sosthène
CC BY-SA 4.0
2022-09-13T15:17:37.810Z
Right, but you could just hash your data and use the hash as a leaf. You don't really need an OP_RETURN, do you?
Murch
CC BY-SA 4.0
2022-09-19T14:50:19.100Z
I'm just saying that afaiu some people are doing that, and that they might have a reason for that.
Sosthène
CC BY-SA 4.0
2022-08-31T14:14:05.143Z
2022-08-31T14:14:05.143Z
Murch
<p>I'm reading the <a href="https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md" rel="nofollow noreferrer">Bitcoin Core's descriptor documentation</a> and I noticed that the <code>raw()</code> <code>SCRIPT</code> expression can only be used at descriptor top-level. Indeed when called with this descriptor <code>getdescriptorinfo</code> returns <code>-5</code> error with the message <code>Can only have raw() at top level</code>:</p>
<p><code>tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,raw(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))</code></p>
<p>I assume that <code>raw()</code> is the only way you add an <code>OP_RETURN</code> type commitment in a taproot script path, or at least I couldn't figure out a way to do otherwise. I could think of using the <code>pk()</code> expression with a hash for example, but I guess it's bad to do that.</p>
<p>Why isn't it allowed to commit <code>OP_RETURN</code> type commitments, let alone other arbitrary script, inside a taproot address? Is it a descriptor restriction or a more fundamental limitation?</p>
<p>I read <a href="https://bitcoin.stackexchange.com/questions/106586/output-descriptor-for-an-arbitrary-script">this</a> but I don't think it really answer my point. If descriptors support arbitrary script only at top-level, why then?</p>

<taproot><op-return><output-script-descriptors><miniscript>
Why isn't it possible to add an OP_RETURN (or some arbitrary script) inside a taproot script path with a descriptor?
Sosthène
# Why isn't it possible to add an OP_RETURN (or some arbitrary script) inside a taproot script path with a descriptor?

(Asked by: username_0 on 2022-08-18)

I'm reading the [Bitcoin Core's descriptor documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) and I noticed that the `raw()` `SCRIPT` expression can only be used at descriptor top-level. Indeed when called with this descriptor `getdescriptorinfo` returns `-5` error with the message `Can only have raw() at top level`:

`tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,raw(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))`

I assume that `raw()` is the only way you add an `OP_RETURN` type commitment in a taproot script path, or at least I couldn't figure out a way to do otherwise. I could think of using the `pk()` expression with a hash for example, but I guess it's bad to do that.

Why isn't it allowed to commit `OP_RETURN` type commitments, let alone other arbitrary script, inside a taproot address? Is it a descriptor restriction or a more fundamental limitation?

I read [this](https://bitcoin.stackexchange.com/questions/106586/output-descriptor-for-an-arbitrary-script) but I don't think it really answer my point. If descriptors support arbitrary script only at top-level, why then?

**Tags:** taproot, op-return, output-script-descriptors, miniscript


---

## 2 answers

### Answer by username_1 on 2022-08-18. Score: 3 | Accepted answer

The answer is twofold.

First, the Bitcoin Core descriptors are in the process of being extended with [Miniscript](https://bitcoin.sipa.be/miniscript/). In the upcoming version (24.0), Miniscript will only be supported in Segwit version 0 outputs (P2WSH).  
Once Miniscript support for Tapscript is added, you will be able to use a lot more Script functionalities in the script paths of a Taproot descriptor.

With regard to arbitrary Scripts, that is including those that are not representable (and therefore not solvable) using Miniscript. There is an ongoing discussion about supporting "[partial descriptors](https://github.com/bitcoin/bitcoin/issues/24114)", an otherwise solvable descriptor containing a spending path for which we have no solvability information.  
A partial Taproot descriptor would allow you to have a `rawnode(HEX)` in the tree. This is what you need if you want to keep the solvability through the key path, because otherwise you could just use [`rawtr()`](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md#reference).

> an OP\_RETURN type commitment in a taproot script path, or at least I couldn't figure out a way to do otherwise. I could think of using the pk() expression with a hash for example, but I guess it's bad to do that.

Unrelated to your main question, but i don't think it's necessarily bad to use another form of commitment. If you were thinking this because of OP\_RETURN outputs pruning from the UTxO set, note this is meaningless in this context.

You could use less expensive methods for committing the data. Such as [tweaking a public key](https://bitcoinops.org/en/topics/pay-to-contract-outputs/). And that would remove the need for partial descriptors.

> **Comments:**
> **username_0**: Thanks for the explanations, to sum it up 1) supporting arbitrary script is out of scope for descriptors and is to be taken care by miniscript 2) descriptors shouldn't allow for spending conditions that we don't fully understand, even if it might be the case in the future. If I were to do a taproot script with an arbitrary path, I should first write it (how?) and then represent it with `rawtr()`, is that correct?  
> **username_1**: For 1), no. Miniscript is about safely supporting more scripts in a general manner, not *any* script. Miniscript is a subset of Bitcoin Script. Regarding 2) It was indeed the thinking of their initial designer. But see the discussion i linked above. We (contributors in this area, including the original designer) are starting to think partial descriptors should be supported. You are correct that as of version `24.0` you would have to use a `rawtr()` descriptor to track Taproot outputs with an `OP_RETURN` in one of the Script path branches.  
> **username_0**: Ugh, when I wrote "arbitrary script" what I meant was indeed "arbitrary script within what's supported by miniscript", thanks for correcting me. As for 2), is there some tooling that could help writing this kind of script?  Just asking out of curiosity, at this point I'm basically convinced this is a bad idea anyway.  
> **username_0**: Just noticed your last edit, what I meant was that hacking the descriptors by pushing a `pk()` with the hash you want to commit in place of an `OP_RETURN` looks ugly and is probably bad. I'm also thinking about pubkey tweaking but a bit cautious because not sure what libs implemented it besides btcd and lnp/bp and I don't think I can use any of it on this project.  
> **username_1**: I'm curious what you are trying to achieve now. :) Re the tooling, for Bitcoin Core i'm not aware of say an RPC call that'd allow you to do that. But finding a library that allows you to create a Taproot scriptPubKey from a set of Script leaves shouldn't be too hard? For what it's worth this https://github.com/darosior/python-bip380 will probably feature what you want once i finally implement Taproot descriptors.  

---

### Answer by username_2 on 2022-08-31. Score: 4

It's not clear to me why one would want to commit to an `OP_RETURN` inside of a P2TR output construction, let alone inside of a descriptor. I feel like there might be a misunderstanding here.

Using the `OP_RETURN` op-code makes an output script *unspendable* (and in most cases non-standard). Putting `OP_RETURN` as the first instruction in a scriptPubKey indicates a *NULL DATA* output, which permits writing a small amount of data to the blockchain. Because NULL DATA outputs are unspendable, they do not get inserted in the UTXO set, but since they're written out as part of the transaction in the blockchain, they're still published.

A popular application of NULL DATA outputs is the *publishing of a commitment*, e.g. the hash of a document to prove that it existed at the time, the witness commitment in segwit blocks, or a Merkle root of a whole tree of commitments as with OpenTimeStamps.

Putting an `OP_RETURN` into a leafscript of a Taptree does not serve the purpose of publishing the data, since only the leafscript used for spending the UTXO in the input will ever written to the blockchain.
If the data should be published per the transaction, it should be a dedicated output.

If the creation of a commitment suffices, it would be enough to tweak the inner public key with the hash of the data. That way, the owner could at a later time publish the data, and attest to the hash by signing with the inner public key. The act of publishing would then be separate from the act of committing, but the commitment would still proof that the data existed at the time of the transaction output creation.

> **Comments:**
> **username_3**: Note that for OpenTimestamps, publishing is never needed, and IIRC OpenTimestamps also supports committing the Merkle root of its tree using sign-to-contract (making an otherwise normal transaction signature commit to it).  
> **username_0**: Thanks for the answer. I got your point on the distinction between publishing and committing. Interestingly a project like RGB, whose business is commitment and not publishing, do use OP_RETURN inside a taproot leafscript (they call it "tapret" I think). I don't know the rationale for that instead of directly tweaking the internal pubkey as you propose.   As to why I was asking how to put it inside a descriptor, it was because I couldn't think of another way to create the corresponding scriptpubkey using Bitcoin Core RPC. Wether it was a good idea or not is another question :)  
> **username_0**: Thinking about it a reason to do an OP_RETURN commitment inside a leafscript instead of directly tweaking the internal pubkey might be to commit other scripts in the same scriptpubkey?  
> **username_2**: Right, but you could just hash your data and use the hash as a leaf. You don't really need an OP_RETURN, do you?  
> **username_0**: I'm just saying that afaiu some people are doing that, and that they might have a reason for that.  

---

