mirror of
https://github.com/bitcoin/bips.git
synced 2026-02-09 15:23:09 +00:00
442 lines
18 KiB
Markdown
442 lines
18 KiB
Markdown
```
|
|
BIP: 346
|
|
Layer: Consensus (soft fork)
|
|
Title: OP_TXHASH
|
|
Authors: Steven Roose <steven@roose.io>
|
|
Brandon Black <freedom@reardencode.com>
|
|
Status: Draft
|
|
Type: Specification
|
|
Assigned: 2024-04-24
|
|
License: BSD-3-Clause
|
|
```
|
|
|
|
# Abstract
|
|
|
|
This BIP proposes a new opcode `OP_TXHASH`, to be activated as a change to the
|
|
semantics of `OP_SUCCESS189` in tapscript contexts.
|
|
|
|
This opcode provides a generalized method for introspecting certain details of
|
|
the spending transaction, which enables non-interactive enforcement of certain
|
|
properties of the transaction spending a certain UTXO.
|
|
|
|
Together with an opcode like `OP_CHECKSIGFROMSTACK`, this opcode effectively
|
|
provides a fully generalized signature hash construction, fully supporting
|
|
all existing SIGHASH flags, the proposed sighash flags from
|
|
[BIP-118](bip-0118.mediawiki) (`SIGHASH_ANYPREVOUT`) and many other new signature hash
|
|
combinations.
|
|
|
|
The constructions specified in this BIP also open up the way for other
|
|
potential updates; see Motivation section for more details.
|
|
|
|
|
|
# Specification
|
|
|
|
|
|
## OP_TXHASH
|
|
|
|
`OP_TXHASH` redefines the `OP_SUCCESS189` tapscript opcode (`0xbd`) as a soft
|
|
fork upgrade. This opcode is only active in tapscript contexts.
|
|
|
|
Note that `OP_SUCCESS187` is used by [BIP-443](bip-0443.mediawiki) (`OP_CHECKCONTRACTVERIFY`) and
|
|
`OP_SUCCESS188` was used by [BIP-345](bip-0345.mediawiki) (`OP_VAULT`) at the time of first draft
|
|
of this BIP, making `OP_SUCCESS189` the next available opcode for this purpose.
|
|
|
|
It has the following semantics:
|
|
|
|
* There is at least one element on the stack, fail otherwise.
|
|
* The element is interpreted as the TxFieldSelector and is popped off the stack.
|
|
* The TxFieldSelector must be valid, fail otherwise.
|
|
* The 32-byte TxHash of the transaction at the current input index, calculated
|
|
using the given TxFieldSelector is pushed onto the stack.
|
|
|
|
## TxFieldSelector
|
|
|
|
The TxFieldSelector has the following encoding. We will give a brief conceptual
|
|
summary, followed by a reference implementation of the CalculateTxHash function.
|
|
|
|
In the following specifications, the `|` operator is used for the bitwise OR
|
|
operation.
|
|
|
|
* There are two special cases for the TxFieldSelector:
|
|
* the empty value, zero bytes long: it is set equal to `TXFS_SPECIAL_TEMPLATE`,
|
|
the de-facto default value which means everything except the prevouts and
|
|
the prevout scriptPubkeys and amounts.
|
|
|
|
Special case `TXFS_SPECIAL_TEMPLATE` is 4 bytes long, as follows:
|
|
* 1: `TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX`
|
|
* 2: `TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_OUTPUTS_ALL`
|
|
* 3: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
|
|
* 4: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
|
|
|
|
* If the TxFieldSelector has exactly 1 byte, we use a _short notation_.
|
|
It has its 8 bits assigned as follows, from lowest to highest:
|
|
* 2/1: Inputs
|
|
* 00: `TXFS_INOUT_SELECTION_NONE`
|
|
* 01: `TXFS_INOUT_SELECTION_CURRENT`
|
|
* 11: `TXFS_INOUT_SELECTION_ALL`
|
|
* 4/3: Outputs
|
|
* 00: `TXFS_INOUT_SELECTION_NONE`
|
|
* 01: `TXFS_INOUT_SELECTION_CURRENT`
|
|
* 11: `TXFS_INOUT_SELECTION_ALL`
|
|
* 5: `TXFS_INPUTS_PREVOUTS`
|
|
* 6: `TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES`
|
|
* 7: `TXFS_CURRENT_INPUT_CONTROL_BLOCK | TXFS_CURRENT_INPUT_SPENTSCRIPT | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS`
|
|
* 8: `TXFS_CURRENT_INPUT_IDX`
|
|
|
|
Additionally, it includes `TXFS_VERSION | TXFS_LOCKTIME | TXFS_CONTROL | TXFS_CURRENT_INPUT_TAPROOT_ANNEX`
|
|
as global fields and `TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_OUTPUTS_ALL`
|
|
as input and output fields.
|
|
|
|
These 1-byte selections allow the TxFieldSelector to emulate current
|
|
signature hashing modes and those defined in [BIP-118](bip-0118.mediawiki):
|
|
|
|
| BIP-341/118 sighash type | 1-byte TxFieldSelector |
|
|
| :--------------------------- | :--------------------- |
|
|
| `ALL` | `0b11111111` |
|
|
| `SINGLE` | `0b11110111` |
|
|
| `NONE` | `0b11110011` |
|
|
| `ALL|ANYONECANPAY` | `0b11111101` |
|
|
| `SINGLE|ANYONECANPAY` | `0b11110101` |
|
|
| `NONE|ANYONECANPAY` | `0b11110001` |
|
|
| `ALL|ANYPREVOUT` | `0b11101101` |
|
|
| `SINGLE|ANYPREVOUT` | `0b11100101` |
|
|
| `NONE|ANYPREVOUT` | `0b11100001` |
|
|
| `ALL|ANYPREVOUTANYSCRIPT` | `0b11001101` |
|
|
| `SINGLE|ANYPREVOUTANYSCRIPT` | `0b11000101` |
|
|
| `NONE|ANYPREVOUTANYSCRIPT` | `0b11000001` |
|
|
|
|
|
|
* If the TxFieldSelector is longer than one byte, the first byte of the TxFieldSelector
|
|
has its 8 bits assigned as follows, from lowest to highest:
|
|
* 1: version (`TXFS_VERSION`)
|
|
* 2: locktime (`TXFS_LOCKTIME`)
|
|
* 3: current input index (`TXFS_CURRENT_INPUT_IDX`)
|
|
* 4: current input control block (`TXFS_CURRENT_INPUT_CONTROL_BLOCK`)
|
|
* 5: current input spent script (`TXFS_CURRENT_INPUT_SPENTSCRIPT`)
|
|
* 6: current script last `OP_CODESEPARATOR` position (or 0xffffffff)
|
|
(`TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS`)
|
|
* 7: current input annex including prefix byte (or empty) (`TXFS_CURRENT_INPUT_TAPROOT_ANNEX`)
|
|
* 8: `TXFS_CONTROL` (i.e. include TxFieldSelector into hash)
|
|
|
|
* The highest bit of the first byte (`TXFS_CONTROL`), we will call the
|
|
"control bit", and it can be used to control the behavior of the opcode. For
|
|
`OP_TXHASH`, the control bit is used to determine
|
|
whether the TxFieldSelector itself has to be included in the resulting hash.
|
|
(For potential other uses of the TxFieldSelector (like a hypothetical
|
|
`OP_TX`), this bit can be repurposed.)
|
|
|
|
* The second byte will be used to indicate fields from the inputs and outputs.
|
|
The 8 bits are assigned the following variables, from lowest to highest:
|
|
* Specifying which fields of the inputs will be selected:
|
|
* 1: prevouts (`TXFS_INPUTS_PREVOUTS`)
|
|
* 2: sequences (`TXFS_INPUTS_SEQUENCES`)
|
|
* 3: scriptSigs (`TXFS_INPUTS_SCRIPTSIGS`)
|
|
* 4: prevout scriptPubkeys (`TXFS_INPUTS_PREV_SCRIPTPUBKEYS`)
|
|
* 5: prevout values (`TXFS_INPUTS_PREV_VALUES`)
|
|
* 6: taproot annexes (`TXFS_INPUTS_TAPROOT_ANNEXES`)
|
|
|
|
* Specifying which fields of the outputs will be selected:
|
|
* 7: scriptPubkeys (`TXFS_OUTPUTS_SCRIPTPUBKEYS`)
|
|
* 8: values (`TXFS_OUTPUTS_VALUES`)
|
|
|
|
* We define as follows:
|
|
* `TXFS_ALL = TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX | TXFS_CURRENT_INPUT_CONTROL_BLOCK | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS | TXFS_CONTROL`
|
|
* `TXFS_INPUTS_ALL = TXFS_INPUTS_PREVOUTS | TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES`
|
|
* `TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES`
|
|
|
|
|
|
* For both inputs and then outputs, expect an additional byte as follows:
|
|
* The highest bit (`TXFS_INOUT_NUMBER`) indicates whether the "number of
|
|
in-/outputs" should be committed to.
|
|
* For the remaining bits, there are three exceptional values:
|
|
* 0x00 (`TXFS_INOUT_SELECTION_NONE`) means "no in/outputs" (hence only the
|
|
number of them as `0x80` (`TXFS_INOUT_NUMBER`)).
|
|
* `0x40` (`TXFS_INOUT_SELECTION_CURRENT`) means "select only the in/output
|
|
of the current input index" (it is invalid when current index exceeds
|
|
number of outputs).
|
|
* `0x3f` (`TXFS_INOUT_SELECTION_ALL`) means "select all in/outputs".
|
|
|
|
* The second highest bit (`TXFS_INOUT_SELECTION_MODE`) is the "specification mode":
|
|
* Set to 0 it means "leading mode".
|
|
* Set to 1 it means "individual mode".
|
|
|
|
* In "leading mode", the third highest bit (`TXFS_INOUT_LEADING_SIZE`) is
|
|
used to indicate the "index size", i.e. the number of bytes will be used to
|
|
represent the number of in/output.
|
|
* With "index size" set to 0, the remaining lowest 5 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 5 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 third highest bit (`TXFS_INOUT_INDIVIDUAL_MODE`)
|
|
indicates whether we are passing absolute indices (0) or indices relative
|
|
to the current input (1), the remaining lowest 5 bits will be interpreted
|
|
as `n`, the number of individual in/outputs follow.
|
|
* In absolute mode (second highest bit is 0), for each of the `n` indices,
|
|
at least one extra byte is expected.
|
|
* If that byte's highest bit is set to 0, the remaining 7 bits represent
|
|
the absolute index to select.
|
|
* If that byte's highest bit is set to 1, the remaining 7 bits, together
|
|
with the next byte's 8 bits represent the absolute index to select.
|
|
* In relative mode (second highest bit is 1), for each of the `n` indices,
|
|
at least one extra byte is expected.
|
|
* If that byte's highest bit is set to 0, the remaining 7 bits represent
|
|
the relative index in two's complement.
|
|
* If that byte's highest bit is set to 1, the remaining 7 bits, together
|
|
with the next byte's 8 bits represent the relative index in two's
|
|
complement.
|
|
|
|
|
|
Effectively, this allows a user to select
|
|
* all in/outputs
|
|
* the current input index
|
|
* the leading in/outputs up to 8,191
|
|
* up to 32 individually selected in/outputs
|
|
** using absolute indices up to 32,767
|
|
** using indices relative to the current input index from -16382 to +16383.
|
|
|
|
|
|
### TxFieldSelector malleability
|
|
|
|
It is possible to represent the same selected data using multiple different
|
|
TxFieldSelectors. For this reason, users are advised to always set the
|
|
`TXFS_CONTROL` field flag that commits to the TxFieldSelector that was used
|
|
to get the hash.
|
|
|
|
|
|
### Visualization
|
|
|
|
* first byte
|
|
|
|
```
|
|
1 1 1 1 1 1 1 1
|
|
| | | | | | | ^ version
|
|
| | | | | | ^ locktime
|
|
| | | | | ^ current input index
|
|
| | | | ^ current input control block
|
|
| | | ^ current input spent script
|
|
| | ^ current script last OP_CODESEPARATOR
|
|
| ^ current input taproot annex
|
|
^ control bit (ie. include TXFS in hash)
|
|
```
|
|
|
|
* second byte
|
|
|
|
```
|
|
<-> outputs
|
|
| | <---------> inputs
|
|
1 1 1 1 1 1 1 1
|
|
| | | | | | | ^ prevouts
|
|
| | | | | | ^ sequences
|
|
| | | | | ^ scriptSigs
|
|
| | | | ^ prevout scriptPubkeys
|
|
| | | ^ prevout values
|
|
| | ^ taproot annexes
|
|
| ^ scriptPubkeys
|
|
^ values
|
|
```
|
|
|
|
* in/output selector byte
|
|
|
|
"leading 3 in/outputs"
|
|
```
|
|
1 0 0 0 0 0 1 1
|
|
| | | <-------> integer 0b00011 == 3
|
|
| | ^ index size: single byte
|
|
| ^ leading mode
|
|
^ commit the number of in/outputs
|
|
```
|
|
|
|
"leading 257 in/outputs"
|
|
```
|
|
1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1
|
|
| | | <------------------------> integer 0b00001 00000001 == 257
|
|
| | ^ index size 1: two bytes
|
|
| ^ leading mode
|
|
^ commit the number of in/outputs
|
|
```
|
|
|
|
"indices 1 and 3"
|
|
```
|
|
0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1
|
|
| | | | <--------------> second idx: 3
|
|
| | | | <-------------> first idx: 1
|
|
| | | | <-----> selection count: 0b0010 == 2 indices
|
|
| | | ^ index size: single byte per index
|
|
| | ^ absolute index
|
|
| ^ individual mode
|
|
^ don't commit the number of in/outputs
|
|
```
|
|
|
|
* total example
|
|
|
|
```
|
|
ff ff c2 01 03 83
|
|
| | ^ commit number of outputs + leading 3 outputs
|
|
| | <------> commit number of inputs + inputs at indices 1 and 3
|
|
| ^ all input and output fields
|
|
^ all regular fields
|
|
```
|
|
|
|
|
|
## Resource limits
|
|
|
|
Using the same validation budget ("sigops budget") introduced in BIP-0342,
|
|
each TransactionHash decreases the validation budget by 25. If this brings the
|
|
budget below zero, the script fails immediately.<br>The following
|
|
considerations should be made:
|
|
|
|
* All fields that can be of arbitrary size are cacheable as TransactionHash
|
|
always hashes their hashed values.
|
|
* In "individual mode", a user can at most commit 32 inputs or outputs,
|
|
which we don't consider excessive for potential repeated use.
|
|
* In "leading 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.
|
|
|
|
|
|
# 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 `OP_CHECKSIGFROMSTACK`
|
|
(see [BIP-348](bip-0348.md)) to effectively create an
|
|
incredibly flexible signature hash, which would enable constructions like
|
|
`SIGHASH_ANYPREVOUT`.
|
|
* The TransactionHash obtained by `OP_TXHASH` can be introduced as a native
|
|
sighash calculation in a future segwit upgrade, so that signatures using the
|
|
TransactionHash as their sighash can be used in keyspend context.
|
|
|
|
|
|
## Comparing with some alternative proposals
|
|
|
|
* This proposal strictly generalizes BIP-119's `OP_CHECKTEMPLATEVERIFY`, as the
|
|
default mode of our TxFieldSelector is semantically the same (though not
|
|
byte-for-byte identical) as what `OP_CTV` accomplishes, without costing any
|
|
additional bytes. Additionally, using `OP_TXHASH` 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 differences without the availability of 64-bit arithmetic in
|
|
Script.
|
|
|
|
* Like mentioned above, `SIGHASH_ANYPREVOUT` can be emulated using `OP_TXHASH`
|
|
when combined with `OP_CHECKSIGFROMSTACK`:
|
|
`<txfs> OP_TXHASH <pubkey> OP_CHECKSIGFROMSTACK` effectively emulates `SIGHASH_ANYPREVOUT`.
|
|
|
|
|
|
# Reference Implementation
|
|
|
|
A reference implementation in Rust is provided attached as part of this BIP
|
|
together with a JSON file of test vectors generated using the reference
|
|
implementation.
|
|
|
|
|
|
# Design Considerations
|
|
|
|
This specification in in _Draft_ and there is definitely still room for feedback
|
|
and improvements. Some considerations that were made but could be revisited:
|
|
|
|
- The `0b10` in/output selector for the shorthand is unused.
|
|
Could possibly be filled in with "current + next", "current + previous" or
|
|
any other semantics.
|
|
- The individual index selection semantics allow for absolute indices up to ~32k
|
|
and relative ones up to +/- ~16k, which is probably excessive. When removing
|
|
the second byte there would reduce that to just 256 and +/- 128 respectively.
|
|
- Similar to `OP_TEMPLATEHASH` (BIP number to be assigned, [PR](https://github.com/bitcoin/bips/pull/1974)),
|
|
we could not support `scriptSigs` anymore and remove
|
|
`TXFS_INPUTS_SCRIPTSIGS`. This field could then possibly be repurposed.
|
|
|
|
|
|
# Backwards Compatibility
|
|
|
|
`OP_TXHASH` replaces `OP_SUCCESS189`. The `SUCCESS` opcodes were
|
|
introduced in taproot (BIP-342) to support changing the semantics of opcodes in
|
|
ways that do allow the new semantics to change the stack. For this reason,
|
|
`OP_TXHASH` only works in tapscript context. Since it is overriding a `SUCCESS`
|
|
opcode, any older version of the software will always accept any script that
|
|
uses the opcode, while the new versions of the software will validate the
|
|
scripts according to the semantics outlined in this BIP. As such, this is also a
|
|
soft fork change.
|
|
|
|
## Interactions with other BIPs
|
|
|
|
This proposal interacts with several other BIPs:
|
|
|
|
* **[BIP-118](bip-0118.mediawiki) (SIGHASH_ANYPREVOUT)**: `OP_TXHASH` can be combined with
|
|
`OP_CHECKSIGFROMSTACK` (BIP-348) to emulate `SIGHASH_ANYPREVOUT` and other
|
|
signature hash modes defined in BIP-118. The 1-byte TxFieldSelector format
|
|
explicitly supports these modes.
|
|
|
|
* **[BIP-119](bip-0119.mediawiki) (OP_CHECKTEMPLATEVERIFY)**: `OP_TXHASH` with the empty
|
|
TxFieldSelector produces a hash semantically equivalent to BIP-119's
|
|
`OP_CHECKTEMPLATEVERIFY`, making this proposal a generalization of BIP-119.
|
|
|
|
* **[BIP-347](bip-0347.mediawiki) (OP_CAT)**: When combined with `OP_CAT`, `OP_TXHASH` enables
|
|
powerful transaction introspection capabilities. The bit encoding format is
|
|
designed to be explicit about endianness to ensure correct interaction with
|
|
concatenation operations.
|
|
|
|
* **[BIP-348](bip-0348.md) (OP_CHECKSIGFROMSTACK)**: Together with `OP_CHECKSIGFROMSTACK`,
|
|
`OP_TXHASH` provides a fully generalized signature hash construction,
|
|
enabling flexible covenant designs and multi-user protocols.
|
|
|
|
|
|
# Implementation
|
|
|
|
A reference implementation is included as part of the BIP, see
|
|
[here](./bip-0346/ref-impl/src/main.rs). This implementation focusses on clarity
|
|
and correctness, not on efficiency. A rudimentary set of test vectors is also
|
|
generated from this implementation and included
|
|
[here](./bip-0346/ref-impl/txhash_vectors.json).
|
|
|
|
Furthermore, following other implementation attempts exist:
|
|
|
|
* A proposed implementation for Bitcoin Core is available here:
|
|
https://github.com/bitcoin/bitcoin/pull/29050
|
|
* A proposed implementation for rust-bitcoin is available here:
|
|
https://github.com/rust-bitcoin/rust-bitcoin/pull/2275
|
|
|
|
NOTE: These implementations are slightly outdated as they were made for an
|
|
earlier version of this specification. Updates are in progress.
|
|
|
|
Both of the above implementations perform effective caching to avoid potential
|
|
denial-of-service attack vectors.
|
|
|
|
|
|
# Deployment
|
|
|
|
This BIP can be deployed using a BIP 9 VersionBits deployment. The specific
|
|
strategy and bit assignment are left unspecified and can later be amended to the
|
|
BIP depending on community preference.
|
|
|
|
|
|
# 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
|
|
generalizing `OP_CHECKTEMPLATEVERIFY` into `OP_TXHASH`.
|
|
|
|
Additional thanks to Andrew Poelstra, Greg Sanders, Rearden Code, Rusty Russell
|
|
and others for their feedback on the specification.
|
|
|
|
|
|
# Copyright
|
|
|
|
This document is licensed under the 3-clause BSD license.
|
|
|