Alternative Designs

BIP-0119 OP_CHECKTEMPLATEVERIFY has a multitude of benefits for the Bitcoin ecosystem. Are there other paths to these improvements? In short, yes. This page has a survey of the other methods for enabling OP_CHECKTEMPLATEVERIFY like functionality and why such techniques are not suitable substitutes.

OP_CHECKOUTPUTVERIFY

MES16 presents an extension to Bitcoin’s script language enabling covenants, a primitive that allows transactions to restrict howthe value they transfer is used in the future.

OP_COV allows a user to specify a pattern and and output index. The pattern is then evaluated against the output at that index to see if the output is a valid representative of the set described by the pattern.

This can be used to emulate something similar to OP_CHECKTEMPLATEVERIFY by specifying two covenants for outputs 0 and 1 to specify the expanding of a payment tree.

However, there are a few drawbacks:

  1. OP_COV does not have a mechanism to restrict the number of inputs to a single input, meaning the half-spend problem exists
  2. A user needs to specify 2 patterns, as opposed to 1 hash with OP_CHECKTEMPLATEVERIFY
  3. OP_COV is not designed to ensure a stable TXID for the transactions (e.g., locktimes could be malleated) which makes OP_COV based congestion control unsuitable for lightning channel like contracts or CPFP payments.
  4. OP_COV covenant patterns are not “computationally enumerable”. That is, it is not generally possible to enumerate all possible spends of an OP_COV covenant. Whereas with OP_CHECKTEMPLATEVERIFY, the limitations ensure that a spending party (by constructing the key) knows all possible executions.

OP_PUSHTXDATA

A Draft BIP by Johnson Lau proposes OP_PUSHTXDATA.

OP_PUSHTXDATA is a (almost) superset of OP_CHECKTEMPLATEVERIFY in the sense that it is possible to encode an OP_CHECKTEMPLATEVERIFY into an OP_PUSHTXDATA.

To do so, one simply uses OP_PUSHTXDATA to check that the nVersion, nLocktime, vouts, sequences, and vins match the expected values.

However, there are a few drawbacks:

  1. As specified, OP_PUSHTXDATA does not allow the user to get the scriptSig of an input on the stack. This means that without amendment, OP_PUSHTXDATA cannot guarantee TXID stability and is unsuitable for constructing lightning channel like contracts from non-segwit outputs as the stack can be malleated (segwit guarantees scriptsigs are null).
  2. The scripts for OP_PUSHTXDATA to emulate OP_CHECKTEMPLATEVERIFY are rather long in comparison as they require committing to many fields. For example:
1 OP_PUSHTXDATA 1 OP_EQUALVERIFY
2 OP_PUSHTXDATA 2 OP_EQUALVERIFY
5 OP_PUSHTXDATA 2 OP_EQUALVERIFY
6 OP_PUSHTXDATA 0 OP_EQUALVERIFY
0 11 OP_PUSHTXDATA 0 OP_EQUALVERIFY
0 15 OP_PUSHTXDATA <value vout 0> OP_EQUALVERIFY
                   <scriptPubkey vout 0> OP_EQUALVERIFY
1 15 OP_PUSHTXDATA <value vout 1> OP_EQUALVERIFY
                   <scriptPubkey vout 1> OP_EQUALVERIFY 

v.s.

<Template Hash> OP_CHECKTEMPLATEVERIFY 

OP_PUSHTXDATA could ameliorate this by adding an data type to push the Bag Hash from OP_CHECKTEMPLATEVERIFY.

  1. As a superset of functionality, OP_PUSHTXDATA enables a lot of new use cases that we may or may not want to support. OP_CHECKTEMPLATEVERIFY is, by comparison, conservative in what it enables.
  2. OP_CHECKTEMPLATEVERIFY is an OP_NOP upgrade, which maintains compatibility with old scripts, whereas OP_PUSHTXDATA requires new execution semantics. OP_PUSHTXDATA could be made to work with soft-fork friendly VERIFY semantics, but it would be unwieldy.

OP_CAT + OP_CHECKSIGFROMSTACKVERIFY

By enabling OP_CHECKSIGFROMSTACKVERIFY and OP_CAT it would be possible to enable covenants. Russel O’Connor proposes to do this instead of OP_CHECKTEMPLATEVERIFY or ANYPREVOUT.

Essentially what OP_CHECKSIGFROMSTACKVERIFY and OP_CAT lets us do is emulate and OpCode via a script gadget which results in the signature message being on the stack, i.e., like OP_PUSHTXDATA, but all the data gets pushed as one serialized blob. It’s then possible to check that this value matches a pre-supposed template (bits like the prevouts can be passed in through the witness data).

However, there are a few drawbacks:

  1. This form of covenants is possible, but highly complex to understand and implement without higher-order scripting primitives.
  2. Further, with extensions like OP_EC_KEY_TWEAK, it would be possible to enable recursive covenants. That means this strategy might limit future extensions to Bitcoin with unintended “constructive interference” capabilities.
  3. If the functionality provided is roughly equivalent to OP_PUSHTXDATA, it would be easier to just to implement OP_PUSHTXDATA
  4. Validation is more expensive to use the OP_CHECKSIGFROMSTACK gadget compared to OP_CHECKTEMPLATEVERIFY or OP_PUSHTXDATA.
  5. Desire to disable PubKey recovery for signatures would disable the recursive use of this type of covenant, which would make the technique not able to be used for OP_CHECKTEMPLATEVERIFY type use.

OP_CHECKTXOUTSCRIPTHASHVERIFY

OP_CHECKTXOUTSCRIPTHASHVERIFY, proposed on the mailing list is similar to OP_CHECKTEMPLATEVERIFY, but only a single output script is committed to and not the amount of Bitcoin to be paid to that output. The stated design goal is theft-resistant vaults.

As specified, OP_CHECKTXOUTSCRIPTHASHVERIFY is not even sufficient for its own purported use case:

  1. Inability to limit the amount of fee paid, ensuring the amount of value forwarded
  2. Inability to limit the number of outputs created

And is insufficient for OP_CHECKTEMPLATEVERIFY semantics for a myriad of reasons more.

SIGHASH_NOINPUT / ANYPREVOUT

Method 1

With SIGHASH_NOINPUT, it should be theoretically possible for a bare script (non-segwit) to be specified as follows to emulate OP_CHECKTEMPLATEVERIFY:

scriptSig: <random sig || SIGHASH_NOINPUT>
scriptPubkey: <pkR> OP_CODESEPARATOR OP_CHECKSIGVERIFY 

It would then be possible to compute the pkR as being the recovered pubkey from the transaction without signing the inputs, effectively committing to the information in the same manner as OP_CHECKTEMPLATEVERIFY.

In a segwit type transaction, this is not possible (without additional modifications) because the scriptPubkey always commits to the entire pubkey, preventing pubkey recovery techniques.

SIGHASH_NOINPUT is furthermore insufficient to emulate OP_CHECKTEMPLATEVERIFY as the TXID can be malleated by modifying the scriptSig(s) in the transaction, which makes it unsuitable for use in Lightning Channel construction or CPFP transactions.

Method 2

Using SIGHASH_ANYPREVOUTANYSCRIPT, it would be possible to have a segwit script:

scriptPubkey: <sig || SIGHASH_ANYPREVOUTANYSCRIPT> <pk> CHECKSIG 

This way neither the pk nor the txid are a part of the signature hash.

This method is close to how OP_CHECKTEMPLATEVERIFY should work. If the key is a well known specific one, and deterministic nonces are used for the signature, then it also preserves some of the ability to prune interior nodes data for storage as they can be recomputed.

However, this method has drawbacks in terms of verification time as the signatures must either be verified or recomputed, and without sophisticated pruning layers, use more network and storage bandwidth.

This method also fundamentally does not have the extensibility that OP_CHECKTEMPLATEVERIFY permits with new template version hash programs that could be made available in future soft-forks.

Furthermore, SIGHASH_ANYPREVOUTANYSCRIPT has the potential for enabling certain types of recursive covenants.

Lastly, SIGHASH_ANYPREVOUTANYSCRIPT can be used with SIGHASH flags other than SIGHASH_ALL signing the whole transaction. This can lead to surprising behaviors, such as with SIGHASH_SINGLE (which would only enforce a covenant on one of the coins in an output, not all).

Acknowledgements

Thanks to Olaolu Osuntokun and Bob McElrath whose talks helped to inform this survey, and to Jonas Nick who made me aware of Method 2 for SIGHASH_ANYPREVOUTANYSCRIPT.

comments powered by Disqus