-
Notifications
You must be signed in to change notification settings - Fork 5.6k
BIP 443: OP_CHECKCONTRACTVERIFY #1793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
bip-ccv.mediawiki
Outdated
|
||
In short, the semantics of the opcode with respect to the Script can be summarized as follows: | ||
|
||
Verify that the input/output with the given <code>index</code> is a P2TR output where the public key is obtained from <code><pk></code>, tweaked with the hash of <data> (if non-empty), then taptweaked with <code><taptree</code> (if present). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This renders as a code block without wrapping ⇒ have to scroll to the right to read it. Plain text (or blockquote, if you want to emphasize) might be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The phrasing "input/output" followed by "is a P2TR output" is also confusing. Maybe just have a separate sentence to explain the input handling. And in particular whether inputs can refer to other inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b219995
I added the blockquote, and rephrased a bit - saying P2TR UTXO instead of the ambiguous 'output'. Does this make it clearer?
bip-ccv.mediawiki
Outdated
* If the <code><index></code> is -1, it is replaced with the index of the current input. | ||
* If the <code><data></code> is the empty buffer, then there is no data tweak for the input/output being checked. | ||
|
||
Any other value of the parameters (except the <code><flags></code> as specified above) is invalid, and makes the opcode fail validation immediately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you mean that first flags are checked. If they're different the transaction is valid. And only after that the other params are checked? In other words, params can be changed / added / removed in later soft forks by introducing a new flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, undefined flags (which is more an extension of the opcode) are left for possible future extensions, while that seems dangerous/footgun-prone for the other parameters (which could be passed via the witness)
In b219995 I tried to make it more explicit by explicitly listing all the parameters.
Some initial questions that come to mind:
And see inline. update:
|
bip-ccv.mediawiki
Outdated
|
||
----- | ||
|
||
Note that the ''deduct'' semantic does not allow to check the exact amount of its output. Therefore, in contracts using a scheme similar to figure 3 or 4 above, amounts be constrained with a signature, or with future introspection opcodes that allow fixing the amount. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to elaborate with an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't add an example (perhaps if I add a section explaining the vault construction, it could also serve for that purpose); but I elaborated slightly more on the fact that the amount is malleable without further checks in 9bccf54.
bip-ccv.mediawiki
Outdated
The ability to constrain the future of coins beyond what is possible with presigned transactions is at the core of numerous attempts to improve bitcoin. Some of the proposed applications include: | ||
|
||
* UTXO sharing schemes like Ark, CoinPools, Timeout Trees, etc. use various types of output restrictions in order to enable multiple parties to share the control of a UTXO, while ensuring that each participant controls their own balance. | ||
* <code>OP_VAULT</code><ref>[[bip-0345.mediawiki|BIP-345]]</ref> is a proposed opcode to implement a 2-step withdrawal process, enabling on-chain reactive security. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a vault example with OP_CCV would be useful, doesn't have to be perfectly identical to OP_VAULT
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was a bit on the fence whether to put the code of the vault implementation in the BIP or in the bitcoin-core implementation, and ultimately I went for the latter, as functional tests are a lot more readable than dangling python scripts, or bare bitcoin Script fragments.
In general, I'm a bit concerned about focusing on the applications in the BIP, as that's extremely open-ended. I tried to focus on the programming tool that CCV introduces. It will be mostly devs reading it, after all.
I feel the CCV-only vault is in a sweet spot as it's probably the smallest interesting construction that uses all the three modes (all except the IGNORE_AMOUNT one)
Advise on the approach is welcome, though - please let me know if you would like a different approach, or some things to be covered more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it a very useful example. Perhaps it's still useful to have a brief overview in the BIP itself, e.g. with the state transitions and perhaps even the taptrees and witness at each state.
bip-ccv.mediawiki
Outdated
|
||
* UTXO sharing schemes like Ark, CoinPools, Timeout Trees, etc. use various types of output restrictions in order to enable multiple parties to share the control of a UTXO, while ensuring that each participant controls their own balance. | ||
* <code>OP_VAULT</code><ref>[[bip-0345.mediawiki|BIP-345]]</ref> is a proposed opcode to implement a 2-step withdrawal process, enabling on-chain reactive security. | ||
* <code>OP_CHECKTEMPLATEVERIFY</code><ref>[[bip-119.mediawiki|BIP-114]]</ref> is a long-proposed opcode to constrain the transaction to a ''template'' with a fixed set of outputs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto: having an example that (roughly) replicates OP_CTV
would be useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CCV alone doesn't replicate CTV, as it misses the 'exact amounts'. It could do it with OP_AMOUNT
(although, of course, CTV is extremely efficient when you just want to commit to all the outputs - plus it has txid stability, if that's needed).
In this paragraph, my intention is to just set the context and hint that 'additional output restrictions are generally useful' - not compare proposals.
bip-ccv.mediawiki
Outdated
* <code>OP_CHECKTEMPLATEVERIFY</code><ref>[[bip-119.mediawiki|BIP-114]]</ref> is a long-proposed opcode to constrain the transaction to a ''template'' with a fixed set of outputs. | ||
* Sidechains and rollups could be implemented via a UTXO encumbered with a recursive covenant, updating the sidechain state root every time it is spent. | ||
|
||
Constructions like BitVM<ref>https://bitvm.org/</ref> try to side-step the lack of a primitive allowing UTXOs to carry state with a clever use of Lamport Signatures, and optimistic execution of smart contracts. This comes with an extremely high cost in term of complexity, interactivity, and (potentially) in block size occupation, for some of the possible execution paths. Moreover, the design of fully trustless bridges remains elusive. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of scope for a BIP, but maybe someone who works on BitVM can demonstrate the equivalent with OP_CCV
? Presumably it doesn't completely replace it, but makes certain aspects more compact. Do they need additional op codes? Etc.
bip-ccv.mediawiki
Outdated
|
||
This allows to embed a commitment to the data that can be validated during the Script execution, with an ad-hoc opcode called <code>OP_CHECKCONTRACTVERIFY</code>, while staying fully compatible with taproot. Notably: | ||
* the committed data does not make the UTXO any larger; | ||
* the keypath spend is still available to any party that possesses the private key of the naked key, as long as they have knowledge of the embedded data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by what you mean with "data", as opposed to a "program". Does this "party" have to know the pre-image or just the hash? And why is that helpful. Hopefully the future examples can illustrate this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation uses the term "double tweak". Perhaps that's a helpful term to explain the concept better?
And IIUC maybe use the term "single tweak" instead of "naked".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by what you mean with "data", as opposed to a "program". Does this "party" have to know the pre-image or just the hash? And why is that helpful. Hopefully the future examples can illustrate this.
In b219995 I added an additional introductory sentence in the paragraph, and also a footnote to explain how this could be used (which is anyway identical to taproot's keypath vs script spending, anyway; the point I'm trying to make is that this feature is not lost when using Scripts with CCV, despite tampering with the internal key).
And IIUC maybe use the term "single tweak" instead of "naked".
What I call the 'naked key' is the key before any tweak. So if you have no embedded data:
naked_key ==(taptweak)==> taproot output key (where naked_key == internal key)
Instead, if you have embedded data:
naked_key ==(data tweak)==> internal_key ==(taptweak)==> taproot output key
bip-ccv.mediawiki
Outdated
== Examples == | ||
|
||
This section documents some common Script fragments that use <code>OP_CHECKCONTRACTVERIFY</code> for various common choices of the parameters. Depending on the use case, some of the parameters might be passed via the witness stack. | ||
In these examples, <code><></code> (empty buffer) and <code>0</code> both refer to an empty stack element. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you frame these examples in the context of an actual application?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 9bccf54 I added one- or two-sentence explainers after each example (and deleted the example with just the deduct mode, as the one right after already has a deduct check followed by a default one - which is the correct way of using it anyway).
I don't think it would make it easier; in fact, apart from the amount semantic (amounts are not checked when you CCV-check and input), the opcode's behavior is currently very symmetric. So in a way, I think the current formulation is simpler by not restricting to just the current input (while staying more general). In everything I worked on so far (even in combinations with other opcodes, e.g. see pymatt), checking just the current input is indeed sufficient. However, I think it's likely that it would be useful in some more advanced constructions. For example, you could create a separate sentinel UTXO, and have some spending condition (possibly for many UTXOs at the same time) be only available if the sentinel UTXO is present in the transaction. Once the sentinel is spent, those spending conditions become unavailable for all the remaining. That seems similar in spirit to the connector outputs used in Ark, but without presigned transactions, so I suspect it's useful.
You can leave flexibility as to where outputs must be by either
I'll add a footnote, thanks! |
bip-ccv.mediawiki
Outdated
In the following, the ''current input'' is the input whose Script is being executed. | ||
|
||
The following value of the <code><flags></code> are defined: | ||
* <code>CCV_FLAG_CHECK_INPUT = -1</code>: Check an input's script; no amount check. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most flags in Bitcoin Core are usigned integers, which seems easier to read. But I guess this is because the script interpreter uses (variable size) signed integers? On the bright side, it means you can expand the number of bit flags easily...
When this flag is absent, I assume you don't check the input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The flag can't be 'absent', as it's the value of the flags
parameter. Note that it's not a bitmap: -1 means "check input", 0, 1 and 2 are the three defferent amount behaviors for checking the output (default, deduct and ignore).
Perhaps the confusion comes from calling it flags
- let me know if you have suggestions for a better name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least to me flags imply a bitmap.
Since they're all mututally exclusive, I would probably call it "mode".
And to prevent confusion, maybe rename the last two: CCV_MODE_CHECK_OUTPUT_IGNORE_AMOUNT
and CCV_MODE_CHECK_OUTPUT_DEDUCT_AMOUNT
Terminology aside, why not use a bitmap?
A bitmap based scheme could do something like:
CCV_FLAG_INPUT = 0; // default behavior is to check output
CCV_FLAG_IGNORE_AMOUNT = 1; // default is to preserve, can't be combined with CCV_FLAG_INPUT or CCV_FLAG_DEDUCT_AMOUNT
CCV_FLAG_DEDUCT_AMOUNT = 2; // default is to preserve, can't be combined with CCV_FLAG_INPUT or CCV_FLAG_IGNORE_AMOUNT
CCV_FLAG_CURRENT_INPUT_TAPTREE = 4; // Merkle root of the current input's tapscript tree. Omit taptree from witness)
CCV_FLAG_CURRENT_INPUT_INTERNAL_KEY = 8; // taproot internal key of the current input. Omit pk from witness. Mutually exclusive with CCV_FLAG_NUMS_KEY
CCV_FLAG_NUMS_KEY = 16; // use NUMS point. Mutually exclusive with CCV_FLAG_CURRENT_INPUT_INTERNAL_KEY.
...
I tend to agree it's not pretty. Too many mutually exclusive flags, and often times you really need 3 modalities, e.g. NUMS, current input key or custom key.
bip-ccv.mediawiki
Outdated
Any other value of the <code><flags></code> makes the opcode succeed validation immediately for the current input<ref>This allows to soft-fork future behavior by introducing new values for the <code><flags></code>. As the flags would always be hard-coded via a push in the Script, the risk of mistakes seems negligible.</ref>. | ||
|
||
The following values of the other parameters have special meanings: | ||
* If the <code><taptree></code> is -1, it is replaced with the Merkle root of the current input's tapscript tree. If the taptree is the empty buffer, then the taptweak is skipped. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered using flags for these special cases and then having fewer stack elements? That seems both more space efficient and probably makes scripts easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the flags and the special parameters are encoded as 1-byte push opcodes, so I don't think you can really save much by aggregating them in the flags (e.g. using extra bits that are currently in the OP_SUCCESS
behavior).
Moreover, I think it would be very inconvenient (both in implementation and in Script readability) if the number of stack arguments is different for different use cases of CCV.
Do you have any thoughts on what this could look like in terms of descriptors? Either in the general case of anything that can be expressed in miniscript, or more narrowly for something like a vault? E.g. fresh vault deposit addresses could be generated from something like Since programs can take arbitrary data, e.g. a withdrawal address for a vault, I could imagine that when a wallet signs (or detects an unauthorized) a trigger transaction it generates a descriptor for that, so that it knows how to spend it (and when). For the first part the wallet software doesn't even need to know what a vault is. For the second part it probably does need to "understand" it in order to know what descriptors to generate, and in order to prompt the user for the right action. But how generalisable is that? |
Just saw the mailinglist thread about this from 2023: https://gnusha.org/pi/bitcoindev/CALZpt+F251k7gSpogwFYHxFtGxc_tZjB4UU4SVEr=WvrsyMVMQ@mail.gmail.com/ The BIP should probably address the pros and cons of "cross-input inspection". |
@sanket1729 has given several great talks on generalizing miniscript for covenant use cases, you can look for those if you're interested. My general take is that descriptors are the wrong tool for this purpose: a spend from UTXO X to UTXO Y where f(Y) = X needs to somehow encode the relation between X and Y as a predicate. While you could do that (every program is a predicate, and every program expressible in Script is a predicate that can be expressed in a tree structure like miniscript...) it quickly becomes unmanageable. For CCV, what works well (kinda by design) is to think in terms of states and state transitions. You can check these docs and the code examples in the pymatt repo if you're interested in more details in how I'm framing it - the PR in bitcoin-core strips most of those useful abstractions for the sake of conciseness, but that's certainly not how one would write those contracts in practice. The framework has some nice properties: once you define the contracts, all you need to know is the contract definition, and the initial parameters. Everything else can be deterministically derived from that, and the blockchain. You can scan for the initial UTXOs matching that contract, and given a transaction spending those UTXOs, you can deterministically deduce what are the next states, parameters (and data, if any) of the new UTXOs that are produced. That's even when someone else in the contract made transactions, as it's easy to 'decode' the witness to understand what clause (tapleaf) was used, and with what arguments.
I personally think speculation on all the possible ways people might use (and abuse) the opcode is out of scope, and it quickly gets unmanageable - because there is an infinite number of ways of (per-)using the opcode. For example, both IMHO that kind of discussion would be more fitting for a BIP describing a proposal to activate a certain package of opcodes as a soft-fork. OP_CCV is not meant to be activated as a stand-alone opcode (that would be kinda silly for a number of reasons). Such a BIP could in fact focus on the capabilities, ignoring the exact details of the opcodes (that are not interesting in that context). |
Found one, which I'll watch: https://www.youtube.com/watch?v=xNAn9LTzk2g
I'll look into that. With previous soft forks like SegWit and Taproot it took many years after activation for enough tooling to be developed to fully take advantage. I'm trying to get a sense of that in this case. It seems fine keep the actual Bitcoin Core implementation simpler.
That makes sense. My impression so far is that there exists some sort of covenant threshold beyond which we open the pandora's box, and it doesn't really matter how we cross the threshold. Perhaps there could be a BIP that discusses several combinations of op codes, explain which combinations cross the threshold, and then explain why that's fine, or not. If it's fine to cross the threshold, I tend to think we should go for the most powerful tool(s) with simplest and safest implementation. If it's not fine, we would be limited to extremely restricted op codes that give us desired functionality without crossing the threshold. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The formatting looks mostly good, got one nit. This seems to have some action already, so I will take a look at the content when the open feedback has been processed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only got up to the start of the section "Script support for OP_CHECKCONTRACTVERIFY
" this time.
bip-ccv.mediawiki
Outdated
|
||
This BIP proposes a new tapscript opcode that adds consensus support for an opcode that enables a new type of output restrictions: <code>OP_CHECKCONTRACTVERIFY</code> (<code>OP_CCV</code>). | ||
|
||
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the Script, allowing introspection to the committed data. Moreover, a Script can constrain the program (internal public key and taptree) and the data of one or more outputs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that the programming language is also called "Script", I find the capitalization here confusing. Also, did you mean to be more specific? Do you mean an input script, output script, leaf script, etc.?
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the Script, allowing introspection to the committed data. Moreover, a Script can constrain the program (internal public key and taptree) and the data of one or more outputs. | |
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the script, allowing introspection to the committed data. Moreover, a script can constrain the program (internal public key and taptree) and the data of one or more outputs. |
Also, in the final sentence, what do you mean with "the program"? How would an opcode that appears in a leaf script be able to affect the "internal public key"? The content of those parentheses throws up more questions than it answers, perhaps this could be clarified by defining what "the program" refers to in this context, or by using a different term for "the program" and dropping the parentheses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bip-ccv.mediawiki
Outdated
|
||
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the Script, allowing introspection to the committed data. Moreover, a Script can constrain the program (internal public key and taptree) and the data of one or more outputs. | ||
|
||
In conjunction with an opcode for ''vector commitments''<ref>''Vector commitments'' are cryptographic primitives that allow to commit to a vector of values via single short value. Hashing and concatenation trivially allow to commit to an entire vector, and later reveal all of its elements. Merkle trees are among the simplest efficient vector commitments, allowing to reveal individual elements with logarithmically-sized proofs.</ref>, this allows to create and compose arbitrary state machines that define the possible future outcomes of a UTXO. The validity of a state transition depends on the conditions that can be expressed in the program (Scripts in the taptree). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In conjunction with an opcode for ''vector commitments''<ref>''Vector commitments'' are cryptographic primitives that allow to commit to a vector of values via single short value. Hashing and concatenation trivially allow to commit to an entire vector, and later reveal all of its elements. Merkle trees are among the simplest efficient vector commitments, allowing to reveal individual elements with logarithmically-sized proofs.</ref>, this allows to create and compose arbitrary state machines that define the possible future outcomes of a UTXO. The validity of a state transition depends on the conditions that can be expressed in the program (Scripts in the taptree). | |
In conjunction with an opcode for ''vector commitments''<ref>''Vector commitments'' are cryptographic primitives that allow to commit to a vector of values via a single short value. Hashing and concatenation trivially allow to commit to an entire vector, and later reveal all of its elements. Merkle trees are among the simplest efficient vector commitments, allowing to reveal individual elements with logarithmically-sized proofs.</ref>, this allows to create and compose arbitrary state machines that define the possible future outcomes of a UTXO. The validity of a state transition depends on the conditions that can be expressed in the program (scripts in the taptree). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be easier to provide detailed suggestions if the lines were broken to a shorter length, e.g., 120 characters, instead of whole paragraphs in one line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was suggesting that the "MediaWiki source code" of your document should be line broken. This would not affect the formatting of the rendered document, but just the presentation of diffs between changes to this pull request and the amount of text that suggestions need to work with.
I’ve explained my suggestion in more detail on another PR before
bip-ccv.mediawiki
Outdated
|
||
== Specification == | ||
|
||
The tapscript opcode <code>OP_SUCCESS187</code> (<code>0xbb</code>) is constrained with new rules to implement <code>OP_CHECKCONTRACTVERIFY</code>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that BIP 345: OP_VAULT also proposes to use OP_SUCCESS187.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionality-wise, OP_CCV is strictly more general than OP_VAULT/OP_VAULT_RECOVER*, so I don't think there is possibility of deployment conflict.
[*] this statement assumes that an opcode suitable for vector commitments (like OP_CAT or OP_PAIRCOMMIT) is deployed together with OP_CCV; there might be dirty tricks to make it true (inefficiently) with CCV alone, but that's not an interesting thing to do.
Similar ugly tricks would allow to inefficiently simulate CCV's functionality with OP_VAULT - so if one foregoes efficiency considerations, they are equivalent.
bip-ccv.mediawiki
Outdated
<source> | ||
<mode> | ||
<taptree> | ||
<pk> | ||
<index> | ||
<data> | ||
</source> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIA, the content of the stack is usually written in one line bottom to top:
<source> | |
<mode> | |
<taptree> | |
<pk> | |
<index> | |
<data> | |
</source> | |
<source> | |
<data> <index> <pk> <taptree> <mode> | |
</source> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 2655b8b (I was incorrectly saying 'top to bottom' when it was already 'bottom to top'. <data>
is the top (as it's the most convenient place for something that is usually either forwarded through the witness, or computed).
bip-ccv.mediawiki
Outdated
data_tweak = sha256(pk || data) | ||
</source> | ||
|
||
In the following, the ''current input'' is the input whose Script is being executed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Current input" was used above without an introduction of the term. Please introduce the term on first use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 2655b8b.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed most of the comments from @murchandamus (sorry for the delay, I didn't realize the review comments were left as 'Pending').
bip-ccv.mediawiki
Outdated
|
||
When checking the Script of one or more output with <code>OP_CHECKCONTRACTVERIFY</code>, it is usually necessary to also check that the amount of the current UTXO is correctly distributed among the outputs in the expected way. Therefore, the opcode already includes an amount semantic that covers the common use cases. | ||
|
||
There are three supported modes for the opcode when checking an output, depending on the value of the <code>mode</code> parameter: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CCV can check the Script of either an input or an output of the transaction. Amount checking is only relevant when the mode is one that refers to an output (0, 1 or 2). Here 'checking an output' refers to the checks that are done in the interpreter when a CCV opcode is executed in one of the modes referring to an output.
bip-ccv.mediawiki
Outdated
'''Remark:''' validation fails if the amount of an output is checked with both the ''default'' and the ''deduct'' logic in the same transaction, or multiple times with the ''deduct'' logic. This prevents duplicate or inconsistent counting of the same amounts. | ||
|
||
'''Remark:''' it is allowed to check for multiple inputs to check the same output with the ''default'' logic. This allows multiple inputs to aggregate (in full or in part) their amounts to the same output. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It make more sense with CCV to consider each UTXO as an instance of a contract; so any logic to make multiple UTXOs behave as if they belong to the same logical 'wallet' needs to be explicitly programmed, and each UTXO/input explicitly specifies what must happen to its amount.
Note that CCV cannot enforce a specific proportion of the amount, but only that the amount of the inputs is accounted for in the output. So you cannot force an equal split without additional introspection opcodes (e.g. combine with a future OP_AMOUNT
opcode, or OP_CTV/TXHASH
, etc.).
Without additional opcodes, not sure if the deduct semantic is useful outside of vault/recovery use cases. Together with OP_AMOUNT, it could for example be used to withdraw a certain amount from a shared UTXO, leaving the rest in a new shared UTXO.
bip-ccv.mediawiki
Outdated
|
||
== Specification == | ||
|
||
The tapscript opcode <code>OP_SUCCESS187</code> (<code>0xbb</code>) is constrained with new rules to implement <code>OP_CHECKCONTRACTVERIFY</code>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionality-wise, OP_CCV is strictly more general than OP_VAULT/OP_VAULT_RECOVER*, so I don't think there is possibility of deployment conflict.
[*] this statement assumes that an opcode suitable for vector commitments (like OP_CAT or OP_PAIRCOMMIT) is deployed together with OP_CCV; there might be dirty tricks to make it true (inefficiently) with CCV alone, but that's not an interesting thing to do.
Similar ugly tricks would allow to inefficiently simulate CCV's functionality with OP_VAULT - so if one foregoes efficiency considerations, they are equivalent.
bip-ccv.mediawiki
Outdated
|
||
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the Script, allowing introspection to the committed data. Moreover, a Script can constrain the program (internal public key and taptree) and the data of one or more outputs. | ||
|
||
In conjunction with an opcode for ''vector commitments''<ref>''Vector commitments'' are cryptographic primitives that allow to commit to a vector of values via single short value. Hashing and concatenation trivially allow to commit to an entire vector, and later reveal all of its elements. Merkle trees are among the simplest efficient vector commitments, allowing to reveal individual elements with logarithmically-sized proofs.</ref>, this allows to create and compose arbitrary state machines that define the possible future outcomes of a UTXO. The validity of a state transition depends on the conditions that can be expressed in the program (Scripts in the taptree). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bip-ccv.mediawiki
Outdated
|
||
This BIP proposes a new tapscript opcode that adds consensus support for an opcode that enables a new type of output restrictions: <code>OP_CHECKCONTRACTVERIFY</code> (<code>OP_CCV</code>). | ||
|
||
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the Script, allowing introspection to the committed data. Moreover, a Script can constrain the program (internal public key and taptree) and the data of one or more outputs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bip-ccv.mediawiki
Outdated
<source> | ||
<mode> | ||
<taptree> | ||
<pk> | ||
<index> | ||
<data> | ||
</source> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 2655b8b (I was incorrectly saying 'top to bottom' when it was already 'bottom to top'. <data>
is the top (as it's the most convenient place for something that is usually either forwarded through the witness, or computed).
bip-ccv.mediawiki
Outdated
data_tweak = sha256(pk || data) | ||
</source> | ||
|
||
In the following, the ''current input'' is the input whose Script is being executed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 2655b8b.
bip-ccv.mediawiki
Outdated
The following values of the other parameters have special meanings: | ||
* If the <code><taptree></code> is -1, it is replaced with the Merkle root of the current input's tapscript tree. If the taptree is the empty buffer, then the taptweak is skipped. | ||
* If the <code><pk></code> is 0, it is replaced with the NUMS x-only pubkey <code>0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0</code> defined in BIP-0340. If the <code><pk></code> is -1, it is replaced with the taproot internal key of the current input. | ||
* If the <code><index></code> is -1, it is replaced with the index of the current input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the usage of -1
as default arguments for several reasons
- It opens up malleability vectors. IIUC this proposal correctly, we have 3 different usages of
-1
on the witness stack. This means we have 8 different combinations of potential valid values on the witness stack. I've pasted a table below of the different malleability combinations - Its not composable with other index based opcodes. Other index based opcodes would need to adopt your
-1
semantic to be composable withOP_CCV
.
Combination # | Taptree | PK | IDX |
---|---|---|---|
1 | taptree | pk | idx |
2 | taptree | pk | -1 |
3 | taptree | -1 | idx |
4 | taptree | -1 | -1 |
5 | -1 | pk | idx |
6 | -1 | pk | -1 |
7 | -1 | -1 | idx |
8 | -1 | -1 | -1 |
If you remove the -1
semantic you would get rid of malleability potential and make OP_CCV more composable with future index based opcodes (OP_INOUT_AMOUNT
, and various elements project opcodes)
This has the tradeoff of increasing witness size, but I think the tradeoff is worth it for the benefit of composability. These values can be useful for other opcodes other than OP_CCV
during script validation! :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- It opens up malleability vectors. IIUC this proposal correctly, we have 3 different usages of
-1
on the witness stack. This means we have 8 different combinations of potential valid values on the witness stack. I've pasted a table below of the different malleability combinations
In the vast majority of usages, all those arguments would not be passed via the witness stack. The only case where I used an index passed via the witness stack, so far, was for index
argument in vaults, in order to make them more ergonomic (in that you have more freedom in how to compose your transactions).
If a script takes those arguments from the witness, then it's up to the script designer to deal with malleability, possibly by adding additional constraints (like a CHECKSIG
) in order to avoid it.
Remark: I don't it would ever make sense for pk
or taptree
to directly come from the witness; in some case, it could come from a computation that depends on the witness arguments - for example, as the result of opening a commitment, which implicitly deals with malleability,
- Its not composable with other index based opcodes. Other index based opcodes would need to adopt your
-1
semantic to be composable withOP_CCV
.
Do you have a concrete example of lack of composability?
If you remove the
-1
semantic you would get rid of malleability potential and make OP_CCV more composable with future index based opcodes (OP_INOUT_AMOUNT
, and various elements project opcodes)
This has the tradeoff of increasing witness size, but I think the tradeoff is worth it for the benefit of composability. These values can be useful for other opcodes other thanOP_CCV
during script validation! :-)
The -1
semantic is extremely common and dramatically optimizes the script sizes.
The CCV_MODE_CHECK_OUTPUT_IGNORE_AMOUNT
mode is there precisely to future-proof the opcode and make it composable with any other amount introspection - even if I don't currently have a use case for it.
It's unclear to me how removing a feature could make the opcode more composable. If you don't need the feature for your script, you can simply not use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll reply on delving or the mailing list as this probably isn't the best place for posting long form results and to start the conversation about potentially use design changes. Apologies for starting the conversation here and not a more appropriate venue. I should have something ready in the next few days.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a full review, more of a quick check of what has happened here lately and a skim of the changes based on my prior review.
I see a few todos in the document, could you describe in a couple sentences how you see the status of this document?
bip-ccv.mediawiki
Outdated
|
||
This opcode enables users to create UTXOs that carry a dynamic commitment to a piece of data. The commitment can be validated during the execution of the Script, allowing introspection to the committed data. Moreover, a Script can constrain the program (internal public key and taptree) and the data of one or more outputs. | ||
|
||
In conjunction with an opcode for ''vector commitments''<ref>''Vector commitments'' are cryptographic primitives that allow to commit to a vector of values via single short value. Hashing and concatenation trivially allow to commit to an entire vector, and later reveal all of its elements. Merkle trees are among the simplest efficient vector commitments, allowing to reveal individual elements with logarithmically-sized proofs.</ref>, this allows to create and compose arbitrary state machines that define the possible future outcomes of a UTXO. The validity of a state transition depends on the conditions that can be expressed in the program (Scripts in the taptree). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was suggesting that the "MediaWiki source code" of your document should be line broken. This would not affect the formatting of the rendered document, but just the presentation of diffs between changes to this pull request and the amount of text that suggestions need to work with.
I’ve explained my suggestion in more detail on another PR before
Let’s refer to this proposal as BIP 443. Could you please rename the file, add an entry to the table, and update the number and created date in the preamble? |
Updated header per number assignment, and split the long lines in bec994f; however, that breaks for lists, so I partially undid it c950a1e. @murchandamus would it be a good time to squash all commits? I think there are no unaddressed review comments at this time. |
Regarding the TODOs for the sigops budget and policy changed: they both deserve more thought at this stage - so I think it's worth keeping the TODO as a reminder.
|
Sure that sounds reasonable |
Squashed all commits and rebased. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed, this will still need to be amended in a few details to be ready for Proposed, but looks ready to be merged as Draft.
Can you expand the BIP to mention that fees have to be paid exogenously?#1793 (comment) That said, a mode similar to In particular there's a privacy advantage when you don't have to use coins from some other wallet to initiate and/or cancel a vault withdrawal. The trade-off there is that an attacker can (threaten to) burn your wallet, but they can't take it. |
@bigspider would you like to follow up on @Sjors' feedback? |
I can add a comment about it if you think it's useful. However, I believe this is not specific to CCV: for any covenant that constrains an output Script, it would be highly unusual to not constrain amounts, as sending 0 to an output is equivalent to not having a covenant at all. CTV and VAULT reached the same conclusion.
If the attacker is a miner, they can take the money. I'm not sure it would be a good idea to enshrine in the opcode a semantic that is inherently vulnerable to miner's theft. Besides, I don't see a way of defining a semantic that is not ambiguous, once you take other inputs into account: it wouldn't be unequivocally determined whose amount is 'going' to fees, and whose is 'going' to the corresponding output. |
Or colludes with a miner. One could even imagine a malevolent accelerator that pays out its users half the fee for any transaction wasn't already in their mempool. I do think it's good to clarify this, since not everyone reading this BIP will have read CTV and VAULT. |
Hi all,
This is a draft for the formal specifications of the
OP_CHECKCONTRACTVERIFY
(CCV
) opcode.CCV
enables to build Script-based state machines that span across multiple transactions, by providing an ergonomic tool to commit to - and introspect - the Script and possibly some data that is committed inside inputs or outputs.Related to this PR:
Not covered in this draft:
I recommend delving bitcoin for high level discussions about alternative implementations, applications, etc.