Skip to content

Commit

Permalink
An idea
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Black committed Dec 14, 2023
1 parent 6943941 commit d4f9abe
Showing 1 changed file with 190 additions and 102 deletions.
292 changes: 190 additions & 102 deletions bip-txhash.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -52,117 +52,205 @@ OP_TXHASH does the following:
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.

* 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.
TXFS_SPECIAL_TEMPLATE is 4 bytes long, as follows:
# TXFS_ALL
# TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
** the 0x00 byte: it is set equal to TXFS_SPECIAL_ALL, which means "ALL"
and is primarily useful to emulate SIGHASH_ALL when OP_TXHASH is used in
combination with OP_CHECKSIGFROMSTACK.
TXFS_SPECIAL_TEMPLATE is 4 bytes long, as follows:
# TXFS_ALL
# TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
* The first byte of the TxFieldSelector has its 8 bits assigned as follows,
from lowest to highest:
# version (TXFS_VERSION)
# locktime (TXFS_LOCKTIME)
# current input index (TXFS_CURRENT_INPUT_IDX)
# current input control block (or empty) (TXFS_CURRENT_INPUT_CONTROL_BLOCK)
# current script last OP_CODESEPARATOR position (or 0xffffffff) (TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS)
# inputs (TXFS_INPUTS)
# outputs (TXFS_OUTPUTS)
* The last (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 and OP_CHECKTXHASHVERIFY, 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.)

* 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:
# prevouts (TXFS_INPUTS_PREVOUTS)
# sequences (TXFS_INPUTS_SEQUENCES)
# scriptSigs (TXFS_INPUTS_SCRIPTSIGS)
# prevout scriptPubkeys (TXFS_INPUTS_PREV_SCRIPTPUBKEYS)
# prevout values (TXFS_INPUTS_PREV_VALUED)
# taproot annexes (TXFS_INPUTS_TAPROOT_ANNEXES)
** Specifying which fields of the outputs will be selected:
# scriptPubkeys (TXFS_OUTPUTS_SCRIPTPUBKEYS)
# values (TXFS_OUTPUTS_VALUES)
//TODO(stevenroose) check that the 7 and 8 render correctly

* 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_INPUTS | TXFS_OUTPUTS | 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_INPUTS_TEMPLATE = TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_VALUES
| TXFS_INPUTS_TAPROOT_ANNEXES
** TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES
For both inputs and then outputs, do the following:

* If the "in/outputs" field is set to 1, another additional byte is expected:
** 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".
** The third highest bit (TXFS_INOUT_SELECTION_SIZE) is used to indicate the
"index size", i.e. the number of bytes will be used to represent in/output
indices.
** In "leading mode",
*** 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 remaining lowest 5 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.

Effectively, this allows a user to select
The TxFieldSelector has the following semantics. We will describe the algorithm
for parsing the TxFieldSelector and briefly summarize the transaction hashing.

The TxFieldSelector must be between 0 and 64 bytes in length.

If the TxFieldSelector is 0-bytes long, it is replaced with byte 0x00.

If the TxFieldSelector is 1-byte long, it is replaced with selectors as
specified below:

* 0x00 => 0x716BAA <ref>'''DefaultCheckTemplateVerifyHash'''
TXFS_VERSION|TXFS_LOCKTIME|TXFS_CURRENT_INPUT_INDEX|TXFS_IO_FLAGS
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_VALUES|TXFS_OUTPUTS_SCRIPTPUBKEYS|TXFS_OUTPUTS_VALUES
TXFS_NUM_INPUTS|TXFS_EXCLUDE_TRAILING_INPUTS|TXFS_NUM_OUTPUTS|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
* 0x01 => 0xFFFB <ref>'''SIGHASH_ALL'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH</ref>
* 0x02 => 0xFFF8 <ref>'''SIGHASH_ALL|SIGHASH_NONE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH</ref>
* 0x03 => 0xFFFB20 <ref>'''SIGHASH_ALL|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH
TXFS_EXCLUDE_TRAILING_INPUTS|TXFS_CORRESPONDING_OUTPUT</ref>
* 0x41 => 0xFF7B02 <ref>'''SIGHASH_ANYPREVOUT|SIGHASH_ALL'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_SCRIPTPUBKEYS|TXFS_INPUTS_VALUES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
* 0x42 => 0xFF7801 <ref>'''SIGHASH_ANYPREVOUT|SIGHASH_NONE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_SCRIPTPUBKEYS|TXFS_INPUTS_VALUES
TXFS_CORRESPONDING_INPUT|TXFS_LEADING_OUTPUTS</ref>
* 0x43 => 0xFF7B00 <ref>'''SIGHASH_ANYPREVOUT|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_SCRIPTPUBKEYS|TXFS_INPUTS_VALUES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_CORRESPONDING_OUTPUT</ref>
* 0x81 => 0xFFFB02 <ref>'''SIGHASH_ANYONECANPAY|SIGHASH_ALL'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH
TXFS_CORRESPONDING_INPUT|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
* 0x82 => 0xFFF801 <ref>'''SIGHASH_ANYONECANPAY|SIGHASH_NONE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH
TXFS_CORRESPONDING_INPUT|TXFS_LEADING_OUTPUTS</ref>
* 0x83 => 0xFFFB00 <ref>'''SIGHASH_ANYONECANPAY|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH
TXFS_CORRESPONDING_INPUT|TXFS_CORRESPONDING_OUTPUT</ref>
* 0xc1 => 0xFB4302 <ref>'''SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_ALL'''
TXFS_FLAGS_ALL & ~TXFS_CURRENT_INPUT_SCRIPT
TXFS_INPUTS_SEQUENCES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
* 0xc2 => 0xFB4001 <ref>'''SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_NONE'''
TXFS_FLAGS_ALL & ~TXFS_CURRENT_INPUT_SCRIPT
TXFS_INPUTS_SEQUENCES
TXFS_CORRESPONDING_INPUT|TXFS_LEADING_OUTPUTS</ref>
* 0xc3 => 0xFB4300 <ref>'''SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL & ~TXFS_CURRENT_INPUT_SCRIPT
TXFS_INPUTS_SEQUENCES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_CORRESPONDING_OUTPUT</ref>
The modified TxFieldSelector must be between 2 and 64-bytes long.

We name 3 bytes of the field selector and their bits:

TxFlags - always the first byte
* 7 <ref>'''control''' The control bit is used in TXHASH(/VERIFY) to specify whether to include the TxFieldSelector itself in the hash. Other applications of the TxFieldSelector may repurpose this bit.</ref> (TXFS_CONTROL)
* 6 version (TXFS_VERSION)
* 5 locktime (TXFS_LOCKTIME)
* 4 current input index (TXFS_CURRENT_INPUT_INDEX)
* 3 current input <ref>'''control block or script code''' The first non-empty item of: control block, witness script, redeem script, scriptPubkey.</ref> (TXFS_CURRENT_INPUT_SCRIPT)
* 2 current script last OP_CODESEPARATOR position (or 0xffffffff) (TXFS_CURRENT_INPUT_CODESEP)
* 1 current input annex (TXFS_CURRENT_INPUT_ANNEX)
* 0 io flags (TXFS_IO_FLAGS)
We define the byte 0xFF as TXFS_FLAGS_ALL.

IOFlags - second byte if the lowest byte of TxFlags is set, otherwise absent
* 7 prevouts (TXFS_INPUTS_PREVOUTS)
* 6 sequences (TXFS_INPUTS_SEQUENCES)
* 5 scriptSigs (TXFS_INPUTS_SCRIPTSIGS)
* 4 prevout scriptPubkeys (TXFS_INPUTS_SCRIPTPUBKEYS)
* 3 prevout values (TXFS_INPUTS_VALUES)
* 2 taproot annexes (TXFS_INPUTS_ANNEXES)
* 1 output scriptPubkeys (TXFS_OUTPUTS_SCRIPTPUBKEYS)
* 0 output values (TXFS_OUTPUTS_VALUES)
We define the byte 0xF8 as TXFS_INPUTS_COMMON and 0x03 as TXFS_OUTPUTS_COMMON

IOSel - optional third byte if the lowest byte of TxFlags is set, otherwise required second
* 7 numInputs (TXFS_NUM_INPUTS)
* 6 inputTotal (TXFS_INPUTS_TOTAL)
* 5 input selection 1
* 4 input selection 2
* 3 numOutputs (TXFS_NUM_OUTPUTS)
* 2 outputTotal (TXFS_OUTPUTS_TOTAL)
* 1 output selection 1
* 0 output selection 2
For input and output selection bits, we define the following four values:
* 00 corresponding (TXFS_CORRESPONDING_INPUT, TXFS_CORRESPONDING_OUTPUT)
* 01 leading (TXFS_LEADING_INPUTS, TXFS_LEADING_OUTPUTS)
* 10 exclude trailing (TXFS_EXCLUDE_TRAILING_INPUTS, TXFS_EXCLUDE_TRAILING_OUTPUTS)
* 11 individual (TXFS_INDIVIDUAL_INPUTS, TXFS_INDIVIDUAL_OUTPUTS)
The first 2-5 bytes of the TxFieldSelector are then parsed as follows:

* Let txFlags = TxFieldSelector[0]
* If txFlags & TXFS_IO_FLAGS:
** ioFlags = TxFieldSelector[1]
** ioSelPos = 2
* Else:
** ioFlags = TXFS_INPUTS_COMMON | TXFS_OUTPUTS_COMMON
** ioSelPos = 1
* If len(TxFieldSelector) > ioSelPos:
** ioSel = TxFieldSelector[ioSelPos]
* Else:
** If ioFlags & 0xFC = 0:
*** iSel = TXFS_LEADING_INPUTS
** Else:
*** iSel = TXFS_EXCLUDE_TRAILING_INPUTS
** If ioFlags & 0x03 = 0:
*** oSel = TXFS_LEADING_OUTPUTS
** Else:
*** oSel = TXFS_EXCLUDE_TRAILING_OUTPUTS
** ioSel = iSel | oSel
* If ioSel & 0x30 = TXFS_INDIVIDUAL_INPUTS and ioSel & 0x03 = TXFS_INDIVIDUAL_OUTPUTS:
** Fail if len(TxFieldSelector) <= ioSelPos + 1
** nInputs = TxFieldSelector[ioSelPos + 1]
** nOutputs = -1
** selPos = ioSelPos + 2
* Else:
** If ioSel & 0x30 = TXFS_LEADING_INPUTS or ioSel & 0x30 = TXFS_EXCLUDE_TRAILING_INPUTS
*** If len(TxFieldSelector) > ioSelPos + 1:
**** nOutputsPos, nInputs = readCompactVarInt(TxFieldSelector, ioSelPos + 1)
*** Else:
**** nOutputsPos = ioSelPos + 1
**** nInputs = 0
** Else:
*** nInputs = -1
*** nOutputsPos = ioSelPos + 1
** If ioSel & 0x03 = TXFS_LEADING_OUTPUTS or ioSel & 0x03 = TXFS_EXCLUDE_TRAILING_OUTPUTS
*** If len(TxFieldSelector) > nOutputsPos:
**** selPos, nOutputs = readCompactVarInt(TxFieldSelector, nOutputsPos)
*** Else:
**** nOutputs = 0
**** selPos = nOutputsPos
** Else:
*** nOutputs = -1
*** selPos = nOutputsPos
If both individual inputs and individual outputs are selected, read ''nInputs''
compact varints starting from ''selPos'' as ''selectedInputs'' and read the
remaining compact varints as ''selectedOutputs''. Otherwise, if individual
inputs are selected read the compact varints starting from ''selPos'' as
''selectedInputs''. Otherwise, if individual outputs are selected, read the
compact varints starting from ''selPos'' as ''selectedOutputs''.

If corresponding input is selected, set ''selectedInputs'' to this input.
If leading inputs are selected, set ''selectedInputs'' to the first ''nInputs''
inputs.
If exclude trailing inputs are selected, set ''selectedInputs'' to all but the
last ''nInputs'' inputs.

If corresponding output is selected, set ''selectedOutputs'' to the output
corresponding to this input. Fail if there is no such output.
If leading outputs are selected, set ''selectedOutputs'' to the first
''nOutputs'' outputs.
If exclude trailing outputs are selected, set ''selectedOutputs'' to all but
the last ''nOutputs'' outputs.

The values ''txFlags'', ''ioFlags'', and ''ioSel''; and the arrays
''selectedInputs'', and ''selectedOutputs'' are then passed to the tx hashing
function.

This allows a user to select
* all in/outputs
* the current input index
* the leading in/outputs up to 8192
* up to 32 individually selected in/outputs
* no in/outputs
* the current input (and/or its corresponding output)
* any number of leading in/outputs (including by excluding any number of
trailing in/outputs)
* up to 61 individual in/outputs (exact number depending on whether their
indices can be represented by a 1-byte compact varint)
The TxFieldSelector is invalid when
* a byte is expected but missing
* additional unexpected bytes are present
* index size is set to 1 while not being necessary
* a leading number of individual index is selected out of bounds of the in/outputs
* individual indices are duplicated or not in increasing order
* fewer individual input indices are available than requested
* a leading or trailing index, or individual index is selected out of bounds of
the in/outputs
* individual in/output indices are duplicated or not in increasing order
* individual in/output mode is selected but no indices are specified
These limitations are to avoid potential TxFieldSelector malleability. It is
however allowed to use leading mode where it could be "all". This
is important to allow for optional addition of extra inputs or outputs.
//TODO(stevenroose) should we disallow individual that could be leading?

=== Notes ===

<references />

===Resource limits===

Expand Down

0 comments on commit d4f9abe

Please sign in to comment.