Hey all

The idea of TXHASH has been around for a while, but AFAIK it was never formalized. After conversations with Russell, I worked on a specification and have been gathering some feedback in the last several weeks.

I think the draft is in a state where it's ready for wider feedback and at the same time I'm curious about the sentiment in the community about this idea.

The full BIP text can be found in the attachment as well as at the following link:
https://github.com/bitcoin/bips/pull/1500

I will summarize here in this writing.

*What does the BIP specify?*

 * The concept of a TxFieldSelector, a serialized data structure for
   selecting data inside a transaction.
     o The following global fields are available:
         + version
         + locktime
         + number of inputs
         + number of outputs
         + current input index
         + current input control block
     o For each input, the following fields are available:
         + previous outpoint
         + sequence
         + scriptSig
         + scriptPubkey of spending UTXO
         + value of spending UTXO
         + taproot annex
     o For each output, the following fields are available:
         + scriptPubkey
         + value
     o There is support for selecting inputs and outputs as follows:
         + all in/outputs
         + a single in/output at the same index as the input being executed
         + any number of leading in/outputs up to 2^14 - 1 (16,383)
         + up to 64 individually selected in/outputs (up to 2^16 or 65,536)
     o The empty byte string is supported and functions as a default
       value which commits to everything except the previous outpoints
       and the scriptPubkeys of the spending UTXOs.

 * An opcode OP_TXHASH, enabled only in tapscript, that takes a
   serialized TxFieldSelector from the stack and pushes on the stack a
   hash committing to all the data selected.

 * An opcode OP_CHECKTXHASHVERIFY, enabled in all script contexts, that
   expects a single item on the stack, interpreted as a 32-byte hash
   value concatenated with (at the end) a serialized TxFieldSelector.
   Execution fails is the hash value in the data push doesn't equal the
   calculated hash value based on the TxFieldSelector.

 * A consideration for resource usage trying to address concerns around
   quadratic hashing. A potential caching strategy is outlined so that
   users can't trigger excessive hashing.
     o Individual selection is limited to 64 items.
     o Selecting "all" in/outputs can mostly use the same caches as
       sighash calculations.
     o For prefix hashing, intermediate SHA256 contexts can be stored
       every N items so that at most N-1 items have to be hashed when
       called repeatedly.
     o In non-tapscript contexts, at least 32 witness bytes are
       required and because (given the lack of OP_CAT) subsequent calls
       can only re-enforce the same TxFieldSelector, no additional
       limitations are put in place.
     o In tapscript, because OP_TXHASH doesn't require 32 witness bytes
       and because of a potential addition of operations like OP_CAT,
       the validation budget is decreased by 10 for every OP_TXHASH or
       OP_CHECKTXHASHVERIFY operation.


*What does this achieve?*

 * Since the default TxFieldSelector is functionally equivalent to
   OP_CHECKTEMPLATEVERIFY, with no extra bytes required, this proposal
   is a strict upgrade of BIP-119.

 * The flexibility of selecting transaction fields and in/output
   (ranges), makes this construction way more useful
     o when designing protocols where users want to be able to add fees
       to their transactions without breaking a transaction chain;
     o when designing protocols where users construct transactions
       together, each providing some of their own in- and outputs and
       wanting to enforce conditions only on these in/outputs.

 * OP_TXHASH, together with OP_CHECKSIGFROMSTACK (and maybe OP_CAT*)
   could be used as a replacement for almost arbitrarily complex
   sighash constructions, like SIGHASH_ANYPREVOUT.

 * Apart from being able to enforce specific fields in the transaction
   to have a pre-specified value, equality can also be enforced, which
   can f.e. replace the desire for opcodes like OP_IN_OUT_VALUE.*

 * The same TxFieldSelector construction would function equally well
   with a hypothetical OP_TX opcode that directly pushes the selected
   fields on the stack to enable direct introspection.


*What are still open questions?*

 * Does the proposal sufficiently address concerns around resource
   usage and quadratic hashing?

 * *: Miraculously, once we came up with all possible fields that we
   might consider interesting, we filled exactly 16 spots. There is
   however one thing that I would have liked to be optionally available
   and I am unsure of which side to take in the proposal. This is
   including the TxFieldSelector as part of the hash. Doing so no
   longer makes the hash only represent the value being hashed, but
   also the field selector that was used; this would no longer make it
   possible to proof equality of fields. If a txhash as specified here
   would ever be used as a signature hash, it would definitely have to
   be included, but this could be done after the fact if OP_CAT was
   available. For signature hashes, the hash should ideally be somehow
   tagged, so we might need OP_CAT, or OP_CATSHA256 or something anyway.

     * A solution could be to take an additional bit from each of the
       two "in/output selector" bytes, and assign to this bit "commit
       to total number of in/outputs" (instead of having 2 bits for
       this in the first byte).
         o This would free up 2 bits in the first byte, one of which
           could be used for including the TxFieldSelector in the hash
           and the other one could be left free (OP_SUCCESS) to
           potentially revisit later-on.
         o This would limit the number of selectable leading in/outputs
           to 8,191 and the number of individually selectable
           in/outputs to 32, both of which seem reasonable or maybe
           even more desirable from a resource usage perspective.

 * General feedback of how people feel towards a proposal like this,
   which could either be implemented in a softfork as is, like BIP-119
   or be combined in a single softfork with OP_CHECKSIGFROMSTACK and
   perhaps OP_CAT, OP_TWEAKADD and/or a hypothetical OP_TX.


This work is just an attempt to make some of the ideas that have been floating around into a concrete proposal. If there is community interest, I would be willing to spend time to adequately formalize this BIP and to work on an implementation for Bitcoin Core.


Looking forward to your thoughts

Steven









  BIP: tbd
  Layer: Consensus (soft fork)
  Title: OP_TXHASH and OP_CHECKTXHASHVERIFY
  Author: Steven Roose 
  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-tbd
  Status: Draft
  Type: Standards Track
  Created: 2023-09-03
  License: BSD-3-Clause
==Abstract== This BIP proposes two new opcodes, OP_CHECKTXHASHVERIFY, to be activated as a change to the semantics of OP_NOP4 in legacy script, segwit and tapscript; and OP_TXHASH, to be activated as a change to the semantics of OP_SUCCESS189 in tapscript only. These new opcodes allow for non-interactive enforcement of certain properties of transactions spending a certain UTXO. Together with something like OP_CHECKSIGFROMSTACK (and maybe OP_CAT) it would also enable the emulation of arbitrarily complex sighash types. More on the use cases in the Motivation section of this BIP. ==Summary== OP_CHECKTXHASHVERIFY uses opcode OP_NOP4 (0xb3) as a soft fork upgrade. OP_CHECKTXHASHVERIFY does the following: * There is at least one element on the stack, fail otherwise. * The element on the stack is at least 32 bytes long, fail otherwise. * The first 32 bytes are interpreted as the TxHash and the remaining suffix bytes specify the TxFieldSelector. * If ValidateTxFieldSelector fails for the provided TxFieldSelector, fail. * The actual TxHash of the transaction at the current input index, calculated using the given TxFieldSelector must be equal to the first 32 bytes of the element on the stack, fail otherwise. OP_TXHASH uses tapscript opcode OP_SUCCESS189 (0xbd) as a soft fork upgrade. OP_TXHASH does the following: * There is at least one element on the stack, fail otherwise. * The element is interpreted as the TxFieldSelector and is popped off the stack. * If ValidateTxFieldSelector fails for the provided TxFieldSelector, fail. * The 32-byte TxHash of the transaction at the current input index, calculated using the given TxFieldSelector is pushed onto the stack. The TxFieldSelector has the following semantics. We will give a brief conceptual summary, followed by a reference implementation of the CalculateTxHash function. * If the TxFieldSelector is zero bytes long, it is set equal to 0xff|0xf6|0x3f|0x3f, the de-facto default value which means everything except the prevouts and the prevout scriptPubkeys. * The first byte of the TxFieldSelector has its 8 bits assigned as follows, from lowest to highest: 1. version 2. locktime 3. nb_inputs 4. nb_outputs 5. current input index 6. current input control block (only in case of tapscript spend) 7. inputs 8. outputs * If either "inputs" or "outputs" is set to 1, expect another byte with its 8 bits assigning the following variables, from lowest to highest: * Specifying which fields of the inputs will be selected: 1. prevouts 2. sequences 3. scriptSigs 4. prevout scriptPubkeys 5. prevout values 6. taproot annexes (only in case of tapscript spend) * Specifying which fields of the outputs will be selected: 7. scriptPubkeys 8. values For both inputs and then outputs, do the following: * If the "in/outputs" field is set to 1, another additional byte is expected: * There are two exceptional values: - 0x00 means "select only the in/output of the current input index". - 0x3f means "select all in/outputs". //TODO(stevenroose) make this 0xff? * The highest bit is the "specification mode": - Set to 0 it means "prefix mode". - Set to 1 it means "individual mode". * The second highest bit is used to indicate the "index size", i.e. the number of bytes will be used to represent in/output indices. * In "prefix mode", - With "index size" set to 0, the remaining lowest 6 bits of the first byte will be interpreted as the number of leading in/outputs to select. - With "index size" set to 1, the remaining lowest 6 bits of the first byte together with the 8 bits of the next byte will be interpreted as the number of leading in/outputs to select. * In "individual mode", the remaining lowest 6 bits of the first byte will be interpreted as `n`, the number of individual in/outputs to select. - With "index size" set to 0, interpret the following `n` individual bytes as the indices of an individual in/outputs to select. - With "index size" set to 1, interpret the next `n` pairs of two bytes as the indices of individual in/outputs to select. The function ValidateTxFieldSelector has the following semantics, it effectively checks that the TxFieldSelector value is valid according to above rules: * If there are 0 bytes, it becomes the default of 0xff|0xf6|0xff|0xff; succeed. * If the first byte is exactly 0x00, the Script execution succeeds immediately. //TODO(stevenroose) is this valuable? it's the only "exception case" that could potentially be hooked for some future upgrade * If in the first byte, "inputs" and "outputs" is 0, no additional bytes can be present, otherwise fail. * If the "inputs" bit is set to 1, one of the lowest 6 bits in the second byte must be set to 1, otherwise fail. * If the "outputs" bit is set to 1, one of the highest 2 bits in the second byte must be set to 1, otherwise fail. * If in the first byte, "inputs" or "outputs" is 1, do the following, once for each: * Expect an additional byte. * If this byte is either 0x00 or 0x3f, no additional bytes are expected for this section. * Interpret the top 2 bits of this byte as per above, setting the "specification" mode and "index size". * In "prefix mode": * with "index size" 0, no additional bytes are expected, otherwise fail; * with "index size" 1, one additional byte are expected, otherwise fail. * In "individual mode", interpret the lowest 6 bits of this byte as integer n, so that * with "index size" 0, n additional bytes are expected, otherwise fail; * with "index size" 1, two times n additional bytes are expected, otherwise fail. * If any additional bytes are present after this, fail. The recommended standardness rules additionally: * Reject non-empty pushes starting with the 0 byte preceding OP_TXHASH as SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS. ===Resource limits=== * For legacy scripts and segwit, we don't add any extra resource limitations, with the argumentation that OP_CHECKTXHASHVERIFY already requires the user to provide at least 32 bytes of extra transaction size, either in the input scriptSig, or the witness. Additional more complex hashes require additional witness bytes. Given that OP_CAT is not available in this context, if a malicious user tries to increase the number of TransactionHashes being calculated by using opcodes like OP_DUP, the TxFieldSelector for all these calculations is identical, so the calculation can be cached within the same transaction. * For tapscript, primarily motivated by the cheaper opcode OP_TXHASH (that doesn't require an additional 32 witness bytes be provided) and the potential future addition of byte manipulation opcodes like OP_CAT, an additional cost is specified per TransactionHash execution. Using the same validation budget ("sigops budget") introduced in BIP-0342, each TransactionHash decreases the validation budget by 10. The following considerations should be made: * In "individual mode", a user can at most hash 64 items together, which we don't consider excessive for potential repeated use. * In "prefix mode", a caching strategy can be used where the SHA256 context is stored every N in/outputs so that multiple executions of the TransactionHash function can use the caches and only have to hash an additional N-1 items at most. If either budget requriement brings the budget below zero, the script fails immediately. ==Motivation== This BIP specifies a basic transaction introspection primitive that is useful to either reduce interactivity in multi-user protocols or to enforce some basic constraints on transactions. Additionally, the constructions specified in this BIP can lay the groundwork for some potential future upgrades: * The TxFieldSelector construction would work well with a hypothetical opcode OP_TX that allows for directly introspecting the transaction by putting the fields selected on the stack instead of hashing them together. * The TransactionHash obtained by OP_TXHASH can be combined with a hypothetical opcode OP_CHECKSIGFROMSTACK to effectively create an incredibly flexible signature hash, which would enable constructions like SIGHASH_ANYPREVOUT. ===Comparing with some alternative proposals=== * This proposal strictly supercedes BIP-119's OP_CHECKTEMPLATEVERIFY, as the default mode of our TxFieldSelector is effectively (though not byte-for-byte identical) the same as what OP_CTV acomplishes, without costing any additional bytes. Additionally, using OP_CHECKTXHASHVERIFY allows for more flexibility which can help in the case for * enabling adding fees to a transaction without breaking a multi-tx protocol; * multi-user protocols where users are only concerned about their own inputs and outputs. * Constructions like OP_IN_OUT_VALUE used with OP_EQUALVERIFY can be emulated by two OP_TXHASH instances by using the TxFieldSelector to select a single input value first and a single output value second and enforcing equality on the hashes. Neither of these alternatives can be used to enforce small value differencials without the use of 64-bit arithmetic. * Like mentioned above, SIGHASH_ANYPREVOUT can be emulated using OP_TXHASH when combined with OP_CHECKSIGFROMSTACK and OP_CAT. `OP_DUP OP_TXHASH OP_CAT <"some_tag"> OP_SWAP OP_SHA256 OP_CHECKSIGFROMSTACK` effectively emulates SIGHASH_ANYPREVOUT. ==Detailed Specification== wip ==Acknowledgement== Credit for this proposal mostly goes to Jeremy Rubin for his work on BIP-119's OP_CHECKTEMPLATEVERIFY and to Russell O'Connor for the original idea of OP_TXHASH. Additional thanks to Andrew Poelstra, Rearden Code, Rusty Russell and others for their feedback on the specification.
_______________________________________________
bitcoin-dev mailing list
bitcoin-dev@lists.linuxfoundation.org
https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev

Reply via email to