BIP draft: OP_CHECKCONTRACTVERIFY #1793

pull bigspider wants to merge 11 commits into bitcoin:master from Merkleize:ccv changing 5 files +409 −0
  1. bigspider commented at 5:58 am on March 17, 2025: contributor

    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:

    • sigops budget (benchmarks needed)
    • activation logic
    • policy considerations (if any)

    I recommend delving bitcoin for high level discussions about alternative implementations, applications, etc.

  2. BIP draft for OP_CHECKCONTRACTVERIFY 4240fa8fb1
  3. bigspider force-pushed on Mar 17, 2025
  4. jonatack added the label New BIP on Mar 17, 2025
  5. in bip-ccv.mediawiki:252 in 4240fa8fb1 outdated
    247+  if target_script != P2TR(final_key):
    248+    return fail()
    249+
    250+  # Amount checks
    251+
    252+  if flags == 0:
    


    instagibbs commented at 1:02 pm on March 17, 2025:
    0  if flags == CCV_FLAG_CHECK_OUTPUT:
    

    bigspider commented at 6:09 pm on March 22, 2025:
    Done in b2199950, thanks!
  6. in bip-ccv.mediawiki:122 in 4240fa8fb1 outdated
    117+* <code><index></code> is a minimally encoded -1, or a minimally encoded non-negative integer.
    118+* <code><data></code> is a buffer of arbitrary length.
    119+
    120+In short, the semantics of the opcode with respect to the Script can be summarized as follows:
    121+
    122+  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).
    


    jirijakes commented at 10:03 am on March 18, 2025:
    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.

    Sjors commented at 4:17 pm on March 18, 2025:
    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.

    bigspider commented at 6:22 pm on March 22, 2025:
    b2199950 I added the blockquote, and rephrased a bit - saying P2TR UTXO instead of the ambiguous ‘output’. Does this make it clearer?
  7. in bip-ccv.mediawiki:146 in 4240fa8fb1 outdated
    141+* 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.
    142+* If the <code><pk></code> is 0, it is replaced with 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.
    143+* If the <code><index></code> is -1, it is replaced with the index of the current input.
    144+* If the <code><data></code> is the empty buffer, then there is no data tweak for the input/output being checked.
    145+
    146+Any other value of the parameters (except the <code><flags></code> as specified above) is invalid, and makes the opcode fail validation immediately.
    


    Sjors commented at 4:21 pm on March 18, 2025:
    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?

    bigspider commented at 6:24 pm on March 22, 2025:
    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 b2199950 I tried to make it more explicit by explicitly listing all the parameters.
  8. Sjors commented at 5:25 pm on March 18, 2025: member

    Some initial questions that come to mind:

    1. Do you really need inputs to check other inputs? What does that enable? Otherwise my sense is that evaluating each input independently of others makes life easier (but you’ll find out when implementing).

    2. Can you make it so the output ordering doesn’t matter?

    3. You bring up “vector commitments” with no further comment, but it would be good to at least briefly explain what they’re good for.

    And see inline.


    update:

    1. maybe it’s not too bad, because you’re only checking the scriptPubKey other inputs are spending
  9. in bip-ccv.mediawiki:95 in 4240fa8fb1 outdated
    90+::[[File:bip-ccv/amount_example_4.png|framed|center|alt=split and aggregate amount logic|600px]]
    91+::'''Figure 4:''' Similar to the previous example, but a second input <code>B</code> also checks the same output <code>X</code> with the <i>default</i> semantic, aggregating its input with the residual amount of the first input.
    92+
    93+-----
    94+
    95+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.
    


    Sjors commented at 5:25 pm on March 18, 2025:
    It would be good to elaborate with an example.

    bigspider commented at 9:29 pm on April 2, 2025:
    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 9bccf545930f9d4b8211f11d5bd93eac27b75695.
  10. in bip-ccv.mediawiki:33 in 4240fa8fb1 outdated
    28+=== Motivation ===
    29+
    30+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:
    31+
    32+* 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.
    33+* <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.
    


    Sjors commented at 5:28 pm on March 18, 2025:
    Having a vault example with OP_CCV would be useful, doesn’t have to be perfectly identical to OP_VAULT.


    bigspider commented at 4:23 pm on April 2, 2025:

    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.


    Sjors commented at 5:00 pm on April 2, 2025:
    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.
  11. in bip-ccv.mediawiki:34 in 4240fa8fb1 outdated
    29+
    30+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:
    31+
    32+* 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.
    33+* <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.
    34+* <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.
    


    Sjors commented at 5:28 pm on March 18, 2025:
    Ditto: having an example that (roughly) replicates OP_CTV would be useful

    bigspider commented at 5:53 pm on April 2, 2025:

    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.

  12. in bip-ccv.mediawiki:37 in 4240fa8fb1 outdated
    32+* 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.
    33+* <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.
    34+* <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.
    35+* Sidechains and rollups could be implemented via a UTXO encumbered with a recursive covenant, updating the sidechain state root every time it is spent.
    36+
    37+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.
    


    Sjors commented at 5:30 pm on March 18, 2025:
    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.
  13. in bip-ccv.mediawiki:49 in 4240fa8fb1 outdated
    44+
    45+If the UTXO carries a commitment to a 32-byte hash (the ''data''), the naked key is tweaked with a hash of the data. The resulting key is the taproot internal key per BIP-341.
    46+
    47+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:
    48+* the committed data does not make the UTXO any larger;
    49+* 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;
    


    Sjors commented at 5:34 pm on March 18, 2025:
    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.

    Sjors commented at 5:40 pm on March 18, 2025:

    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”.


    bigspider commented at 6:32 pm on March 22, 2025:

    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 b2199950 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:

    0      naked_key ==(taptweak)==> taproot output key         (where naked_key == internal key)
    

    Instead, if you have embedded data:

    0      naked_key ==(data tweak)==> internal_key ==(taptweak)==> taproot output key
    
  14. Address some comments from review b219995050
  15. Fix formatting inside the blockquote aa291c23fb
  16. in bip-ccv.mediawiki:295 in 4240fa8fb1 outdated
    290+TODO
    291+
    292+== Examples ==
    293+
    294+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.
    295+In these examples, <code><></code> (empty buffer) and <code>0</code> both refer to an empty stack element.
    


    Sjors commented at 5:38 pm on March 18, 2025:
    Can you frame these examples in the context of an actual application?

    bigspider commented at 9:32 pm on April 2, 2025:
    In 9bccf545930f9d4b8211f11d5bd93eac27b75695 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).
  17. bigspider commented at 6:57 pm on March 22, 2025: contributor
    1. Do you really need inputs to check other inputs? What does that enable? Otherwise my sense is that evaluating each input independently of others makes life easier (but you’ll find out when implementing).

    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.

    1. Can you make it so the output ordering doesn’t matter?

    You can leave flexibility as to where outputs must be by either

    • using -1 for the output index (meaning, the corresponding output must be in the same position as the input index); this would for example allow batching multiple 1-input-1-output CCV-encumbered spends (where each input must “produce” its own output, without aggregation) in the same transaction
    • passing the <index> parameter in the witness of the transaction. E.g. in the vault example, the revault index and the trigger index of the trigger_and_revault clause are arbitrary in this way.
    1. You bring up “vector commitments” with no further comment, but it would be good to at least briefly explain what they’re good for.

    I’ll add a footnote, thanks!

  18. in bip-ccv.mediawiki:137 in aa291c23fb outdated
    132+</source>
    133+
    134+In the following, the ''current input'' is the input whose Script is being executed.
    135+
    136+The following value of the <code><flags></code> are defined:
    137+* <code>CCV_FLAG_CHECK_INPUT = -1</code>: Check an input's script; no amount check.
    


    Sjors commented at 7:36 pm on March 24, 2025:

    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?


    bigspider commented at 11:01 pm on March 25, 2025:

    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.


    Sjors commented at 8:12 am on March 26, 2025:

    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:

    0CCV_FLAG_INPUT = 0; // default behavior is to check output
    1CCV_FLAG_IGNORE_AMOUNT = 1; // default is to preserve, can't be combined with CCV_FLAG_INPUT or CCV_FLAG_DEDUCT_AMOUNT
    2CCV_FLAG_DEDUCT_AMOUNT = 2; // default is to preserve, can't be combined with CCV_FLAG_INPUT or CCV_FLAG_IGNORE_AMOUNT
    3CCV_FLAG_CURRENT_INPUT_TAPTREE = 4; // Merkle root of the current input's tapscript tree. Omit taptree from witness)
    4CCV_FLAG_CURRENT_INPUT_INTERNAL_KEY = 8; // taproot internal key of the current input. Omit pk from witness. Mutually exclusive with CCV_FLAG_NUMS_KEY
    5CCV_FLAG_NUMS_KEY = 16; // use NUMS point. Mutually exclusive with CCV_FLAG_CURRENT_INPUT_INTERNAL_KEY.
    6...
    

    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.

  19. in bip-ccv.mediawiki:138 in aa291c23fb outdated
    133+
    134+In the following, the ''current input'' is the input whose Script is being executed.
    135+
    136+The following value of the <code><flags></code> are defined:
    137+* <code>CCV_FLAG_CHECK_INPUT = -1</code>: Check an input's script; no amount check.
    138+* <code>CCV_FLAG_CHECK_OUTPUT = 0</code>: Check an output's script; preserve the (possibly residual) amount.
    


    Sjors commented at 7:39 pm on March 24, 2025:

    When this flag is absent, you don’t check the output?

    And CCV_FLAG_CHECK_INPUT can be used in any combination with CCV_FLAG_CHECK_INPUT, including not checking either?


    bigspider commented at 11:02 pm on March 25, 2025:
    As per this comment, the defined flags are all mutually exclusive.
  20. in bip-ccv.mediawiki:145 in aa291c23fb outdated
    140+* <code>CCV_FLAG_DEDUCT_OUTPUT_AMOUNT = 2</code>: Check an output's script; deduct the output amount from the input's residual amount.
    141+
    142+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>.
    143+
    144+The following values of the other parameters have special meanings:
    145+* 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.
    


    Sjors commented at 7:47 pm on March 24, 2025:
    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.

    bigspider commented at 11:15 pm on March 25, 2025:
    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.
  21. in bip-ccv.mediawiki:211 in aa291c23fb outdated
    206+The following code is executed every time the <code>OP_CHECKCONTRACTVERIFY</code> opcode is encountered during the evalutation of a taproot Script spend. <code>this_input_index</code>, <code>this_input_internal_key</code> and <code>this_input_taptree</code> are the index, taproot internal key and taproot Merkle root of the current input.
    207+
    208+<code>BIP341_NUMS_KEY</code> is the x-only provably unspendable key <code>50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0</code> defined in [[bip-341.mediawiki|BIP-341]].
    209+
    210+<source lang="python">
    211+  if flags < CCV_FLAG_CHECK_INPUT or flags > CCV_FLAG_DEDUCT_OUTPUT_AMOUNT:
    


    Sjors commented at 7:59 pm on March 24, 2025:

    I assume you mean to check that no unknown flags are in use? This seems wrong though, because CCV_FLAG_DEDUCT_OUTPUT_AMOUNT | CCV_FLAG_IGNORE_OUTPUT_AMOUNT > CCV_FLAG_DEDUCT_OUTPUT_AMOUNT

    Probably more clear to have something like ALL_FLAGS = ... | CCV_FLAG_DEDUCT_OUTPUT_AMOUNT | CCV_FLAG_IGNORE_OUTPUT_AMOUNT | ... and check flags && !ALL_FLAGS == 0


    bigspider commented at 11:04 pm on March 25, 2025:
    Since flags are mutually exclusive and the defined values are -1, 0, 1, 2, this is just checking if you’re out of the range (then, SUCCESS as an upgrade hook to add future flags).

    Sjors commented at 8:29 am on March 26, 2025:

    See above comment, I think calling it “mode” would make this code clear:

    0if mode < CCV_FLAG_CHECK_INPUT or mode > CCV_FLAG_DEDUCT_OUTPUT_AMOUNT:
    

    bigspider commented at 4:48 pm on March 29, 2025:
    Changed to mode instead of flags in 4ed71843e51742b6a60f445c272b73a1e90dc10f.
  22. Sjors commented at 8:17 pm on March 24, 2025: member

    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 tr(cold,{trigger_leaf}), where trigger_leaf could be something like ccv(-1,{withdraw_leaf,recover_leaf},0,...), and recover_leaf is tr(cold), etc.

    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?

  23. Sjors commented at 2:37 pm on March 25, 2025: member

    Do you really need inputs to check other inputs? What does that enable?

    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,

    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”.

  24. bigspider commented at 11:52 pm on March 25, 2025: contributor

    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? @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.

    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”.

    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 OP_CAT and OP_TXHASH both enable fine-grained introspection of other inputs, and would therefore raise the same concern - if one thinks that is a valid concern; yet the BIPs don’t discuss this.

    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).

  25. Sjors commented at 8:42 am on March 26, 2025: member

    @sanket1729 has given several great talks on generalizing miniscript for covenant use cases, you can look for those if you’re interested.

    Found one, which I’ll watch: https://www.youtube.com/watch?v=xNAn9LTzk2g

    My general take is that descriptors are the wrong tool for this purpose:

    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

    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.

    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

    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.

  26. Rename flags -> mode 4ed71843e5
  27. Add footnote about vector commitments d10971b0bc
  28. bigspider force-pushed on Mar 29, 2025
  29. in bip-ccv.mediawiki:16 in d10971b0bc outdated
    11+  Post-History: 2022-11-08: https://gnusha.org/pi/bitcoindev/CAMhCMoH9uZPeAE_2tWH6rf0RndqV+ypjbNzazpFwFnLUpPsZ7g@mail.gmail.com
    12+                2023-07-30: https://gnusha.org/pi/bitcoindev/CAMhCMoFYF+9NL1sqKfn=ma3C_mfQv7mj2fqbqO5WXVwd6vyhLw@mail.gmail.com
    13+</pre>
    14+
    15+
    16+== Introduction ==
    


    murchandamus commented at 9:25 pm on April 1, 2025:
    Nit: Missing “Abstract” section.

    bigspider commented at 9:33 pm on April 2, 2025:
    9bccf545930f9d4b8211f11d5bd93eac27b75695: I renamed the Introduction to Abstract.
  30. murchandamus commented at 9:27 pm on April 1, 2025: contributor
    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.
  31. Address some comments from PR review; delete unused 'References' section 9bccf54593
  32. in bip-ccv.mediawiki:18 in 9bccf54593 outdated
    13+</pre>
    14+
    15+
    16+== Abstract ==
    17+
    18+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>).
    


    murchandamus commented at 8:46 pm on April 4, 2025:

    “proposes opcode that adds support for opcode” sounds weird. How about:

    0This BIP proposes to add consensus support for a new tapscript opcode that enables a new type of output restrictions: <code>OP_CHECKCONTRACTVERIFY</code> (<code>OP_CCV</code>).
    
  33. in bip-ccv.mediawiki:20 in 9bccf54593 outdated
    15+
    16+== Abstract ==
    17+
    18+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>).
    19+
    20+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.
    


    murchandamus commented at 8:55 pm on April 4, 2025:

    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.?

    0This 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.

  34. in bip-ccv.mediawiki:22 in 9bccf54593 outdated
    17+
    18+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>).
    19+
    20+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.
    21+
    22+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).
    


    murchandamus commented at 8:59 pm on April 4, 2025:
    0In 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).
    

    murchandamus commented at 9:00 pm on April 4, 2025:
    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.
  35. in bip-ccv.mediawiki:34 in 9bccf54593 outdated
    29+
    30+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:
    31+
    32+* 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.
    33+* <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.
    34+* <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.
    


    murchandamus commented at 9:03 pm on April 4, 2025:
    0* <code>OP_CHECKTEMPLATEVERIFY</code><ref>[[bip-119.mediawiki|BIP-114]]</ref> is a long-proposed opcode to constrain a transaction to a ''template'' with a fixed set of outputs.
    

    “The transaction” begs the question “which transaction”, but I think you simply mean “a transaction”.

  36. in bip-ccv.mediawiki:43 in 9bccf54593 outdated
    38+
    39+Rather than providing a construct optimized for a specific application, this BIP aims to provide a fundamental building block that is widely applicable, and common to many constructions.
    40+
    41+== Design ==
    42+
    43+<code>OP_CHECKCONTRACTVERIFY</code> is an implementation of a new primitive that could be called ''state-carrying UTXOs''. It allows to embed a commitment to a piece of data in a UTXO, and to validate it during the execution of the Script, and ''carry'' a (possibly dyamically computed) piece of data to the new UTXOs that are produced.
    


    murchandamus commented at 9:05 pm on April 4, 2025:
    re: Capitalized “Script” see above.
  37. in bip-ccv.mediawiki:45 in 9bccf54593 outdated
    40+
    41+== Design ==
    42+
    43+<code>OP_CHECKCONTRACTVERIFY</code> is an implementation of a new primitive that could be called ''state-carrying UTXOs''. It allows to embed a commitment to a piece of data in a UTXO, and to validate it during the execution of the Script, and ''carry'' a (possibly dyamically computed) piece of data to the new UTXOs that are produced.
    44+
    45+We consider the ''program'' of a P2TR UTXO to be composed of an x-only public key (that we call the ''naked key''), and a Merkle tree of Scripts. If there is no data committed in the UTXO, then the naked key is the internal key as defined in BIP-341.
    


    murchandamus commented at 9:06 pm on April 4, 2025:
    It seems to me that this is the definition for “program” that I was missing above. I would suggest either moving the definition to the first use, or sidestepping the use of the term above.
  38. in bip-ccv.mediawiki:51 in 9bccf54593 outdated
    46+
    47+If the UTXO carries a commitment to a 32-byte hash (the ''data''), the naked key is tweaked with a hash of the data. The resulting key is the taproot internal key per BIP-341.
    48+
    49+This allows to embed a commitment to the data that can be validated during the Script execution, while staying fully compatible with taproot. Notably:
    50+* the committed data does not make the UTXO any larger;
    51+* 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 (or at least their hash)<ref>For example, in a multi-party contract, the naked key could be aggregate key using [[bip-0327.mediawiki|MuSig2]]; the taproot keypath would therefore allow a ''cooperative'' spend, without executing any Script at all. Like for all taproot transactions, this is indeed the cheapest way of spending the UTXO - albeit not always possible in practice.</ref>;
    


    murchandamus commented at 9:09 pm on April 4, 2025:
    0* 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 (or at least the data’s hash)<ref>For example, in a multi-party contract, the naked key could be aggregate key using [[bip-0327.mediawiki|MuSig2]]; the taproot keypath would therefore allow a ''cooperative'' spend, without executing any Script at all. Like for all taproot transactions, this is indeed the cheapest way of spending the UTXO - albeit not always possible in practice.</ref>;
    
  39. in bip-ccv.mediawiki:52 in 9bccf54593 outdated
    47+If the UTXO carries a commitment to a 32-byte hash (the ''data''), the naked key is tweaked with a hash of the data. The resulting key is the taproot internal key per BIP-341.
    48+
    49+This allows to embed a commitment to the data that can be validated during the Script execution, while staying fully compatible with taproot. Notably:
    50+* the committed data does not make the UTXO any larger;
    51+* 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 (or at least their hash)<ref>For example, in a multi-party contract, the naked key could be aggregate key using [[bip-0327.mediawiki|MuSig2]]; the taproot keypath would therefore allow a ''cooperative'' spend, without executing any Script at all. Like for all taproot transactions, this is indeed the cheapest way of spending the UTXO - albeit not always possible in practice.</ref>;
    52+* if multiple Scripts are in different leaves of the taptree, only the ones that actually need to access the data have to pay a cost for it, in terms of additional witness bytes.
    


    murchandamus commented at 9:10 pm on April 4, 2025:

    Is it possible that “Script” always refers to a leaf script?

    0* if multiple Scripts are in different leaves of the taptree, only the ones that actually need to access the data have to pay a cost for it, in terms of additional witness bytes.
    
  40. in bip-ccv.mediawiki:58 in 9bccf54593 outdated
    53+
    54+<code>OP_CHECKCONTRACTVERIFY</code> can be applied to introspect the program and data of one of the inputs of the transaction (typically, the current input, in order to access its committed data), or one of the outputs of the transaction (in order to define its program, and possibly its committed data).
    55+
    56+=== Output amounts ===
    57+
    58+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.
    


    murchandamus commented at 9:12 pm on April 4, 2025:
    What is “the current UTXO”? Is this referring to an input that spends an OP_CHECKCONTRACTVERIFY-encumbered TXO?
  41. in bip-ccv.mediawiki:60 in 9bccf54593 outdated
    55+
    56+=== Output amounts ===
    57+
    58+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.
    59+
    60+There are three supported modes for the opcode when checking an output, depending on the value of the <code>mode</code> parameter:
    


    murchandamus commented at 9:13 pm on April 4, 2025:
    Where does the opcode that is being checked stand? Is it in the output itself, or are we talking about a previous encumbrance?
  42. in bip-ccv.mediawiki:61 in 9bccf54593 outdated
    56+=== Output amounts ===
    57+
    58+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.
    59+
    60+There are three supported modes for the opcode when checking an output, depending on the value of the <code>mode</code> parameter:
    61+* ''default'': the residual amount of the current input must be preserved in the output (aggregate across the inputs that specify the output);
    


    murchandamus commented at 9:14 pm on April 4, 2025:

    Does current input refer to the input at the same index as this output or what makes it “current”? What happens when there are multiple inputs spending TXOs that were encumbered with OP_CHECKCONTRACTVERIFY?

    0* ''default'': the residual amount of the current input must be preserved in the output (aggregate across the inputs that specify the output);
    
  43. in bip-ccv.mediawiki:65 in 9bccf54593 outdated
    60+There are three supported modes for the opcode when checking an output, depending on the value of the <code>mode</code> parameter:
    61+* ''default'': the residual amount of the current input must be preserved in the output (aggregate across the inputs that specify the output);
    62+* ''ignore'': the output amount is ignored.
    63+* ''deduct'': the amount of the checked output is subtracted from the amount of the current input (the residual amount is then available for further checks);
    64+
    65+The ''default'' logic covers the common case where an input wishes to send the full amount to a specific output.
    


    murchandamus commented at 9:17 pm on April 4, 2025:

    This seems to assign a strange sense of agency to the input. Do I understand that you mean something like:

    0The ''default'' logic covers the common case where a UTXO’s full amount is required to be sent to a specific output.
    
  44. in bip-ccv.mediawiki:67 in 9bccf54593 outdated
    62+* ''ignore'': the output amount is ignored.
    63+* ''deduct'': the amount of the checked output is subtracted from the amount of the current input (the residual amount is then available for further checks);
    64+
    65+The ''default'' logic covers the common case where an input wishes to send the full amount to a specific output.
    66+
    67+The ''deduct'' logic allows to assign portions of the input amount to one or more outputs; the residual amount, checked with a final check using the ''default'' logic, can be used to enforce that the total amount is preserved.
    


    murchandamus commented at 9:18 pm on April 4, 2025:
    If there are multiple UTXOs, what happens with the change? If there is only one UTXO, how do you spend the entire amount without bringing more funds to pay for fees?

    bigspider commented at 10:17 pm on April 12, 2025:

    What is ‘change’? The amount has to be entirely assigned to the outputs. For applications like vaults, you can do use the deduct logic with an output that has the same script as the input; most contracts are non-recursive, so they always ‘move forward’, and there is no meaningful definition of change.

    Fees have to be paid exogenously, with a separate input (or by spending an output, if that applies). I think this is necessary in any kind of covenant, as the fee is by definition an amount that is not encumbered by the covenant.


    murchandamus commented at 7:11 pm on April 15, 2025:
    I may have not realized then yet that the fees are always paid exogenously.
  45. in bip-ccv.mediawiki:73 in 9bccf54593 outdated
    68+
    69+The following figures illustrate some common examples of supported use cases for the amount logic. This list is not exhaustive, as there are many more possible combinations.
    70+
    71+'''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.
    72+
    73+'''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.
    


    murchandamus commented at 9:26 pm on April 4, 2025:
    I must have failed to pick up on some part of the logic above, but how are contracts tied to specific outputs? What happens if you have two UTXOs tied to the same contract that requires an equal split of the funds between A and B? Let’s say 50% is deducted to A and then the rest is defaulted to B. Would you need two outputs to A and one to B to achieve this?
  46. in bip-ccv.mediawiki:97 in 9bccf54593 outdated
    92+::[[File:bip-ccv/amount_example_4.png|framed|center|alt=split and aggregate amount logic|600px]]
    93+::'''Figure 4:''' Similar to the previous example, but a second input <code>B</code> also checks the same output <code>X</code> with the <i>default</i> semantic, aggregating its input with the residual amount of the first input.
    94+
    95+-----
    96+
    97+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 should be constrained either with a signature, or with a future introspection opcodes that allow fixing the amount. In lack of that, amounts would be malleable.
    


    murchandamus commented at 9:26 pm on April 4, 2025:
    0Note 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 should be constrained either with a signature, or with future introspection opcodes that allow fixing the amount. In lack of that, amounts would be malleable.
    
  47. in bip-ccv.mediawiki:101 in 9bccf54593 outdated
     96+
     97+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 should be constrained either with a signature, or with a future introspection opcodes that allow fixing the amount. In lack of that, amounts would be malleable.
     98+
     99+== Specification ==
    100+
    101+The tapscript opcode <code>OP_SUCCESS187</code> (<code>0xbb</code>) is constrained with new rules to implement <code>OP_CHECKCONTRACTVERIFY</code>.
    


    murchandamus commented at 9:28 pm on April 4, 2025:
    Note that BIP 345: OP_VAULT also proposes to use OP_SUCCESS187.
  48. in bip-ccv.mediawiki:112 in 9bccf54593 outdated
    107+<mode>
    108+<taptree>
    109+<pk>
    110+<index>
    111+<data>
    112+</source>
    


    murchandamus commented at 9:32 pm on April 4, 2025:

    AFAIA, the content of the stack is usually written in one line bottom to top:

    0<source>
    1<data> <index> <pk> <taptree> <mode> 
    2</source>
    
  49. in bip-ccv.mediawiki:134 in 9bccf54593 outdated
    129+
    130+<source lang="python">
    131+  data_tweak = sha256(pk || data)
    132+</source>
    133+
    134+In the following, the ''current input'' is the input whose Script is being executed.
    


    murchandamus commented at 9:34 pm on April 4, 2025:
    “Current input” was used above without an introduction of the term. Please introduce the term on first use.
  50. in bip-ccv.mediawiki:146 in 9bccf54593 outdated
    141+
    142+Any other value of the <code><mode></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><mode></code>. As the mode would always be hard-coded via a push in the Script, the risk of mistakes seems negligible.</ref>.
    143+
    144+The following values of the other parameters have special meanings:
    145+* 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.
    146+* If the <code><pk></code> is 0, it is replaced with 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.
    


    murchandamus commented at 9:36 pm on April 4, 2025:
    0* 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.
    
  51. in bip-ccv.mediawiki:154 in 9bccf54593 outdated
    149+
    150+Any other value of the <code><taptree></code>, <code><pk></code>, <code><index></code> or <code><data></code> parameters is invalid, and makes the opcode immediately fail validation.
    151+
    152+=== Script support for <code>OP_CHECKCONTRACTVERIFY</code> ===
    153+
    154+The specification is divided in three parts:
    


    murchandamus commented at 9:39 pm on April 4, 2025:
    0The specification is divided into three parts:
    
  52. murchandamus commented at 9:41 pm on April 4, 2025: contributor
    I only got up to the start of the section “Script support for OP_CHECKCONTRACTVERIFY” this time.
  53. Apply suggestions from code review
    Co-authored-by: Mark "Murch" Erhardt <murch@murch.one>
    d246a61249
  54. Do not capitalize 'Script' when referring to individual scripts eb56ec5e60
  55. Typo 808fa44a6e
  56. Rephrase paragraph in abstract describing the output restriction 1e12795205
  57. Define 'current input' at first use; clarify order of arguments on the stack 2655b8b88f

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-04-19 01:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me