Skip to content

Commit 52df2b8

Browse files
committed
txhash: Add relative indices and other improvements
1 parent 54150c7 commit 52df2b8

File tree

4 files changed

+1039
-775
lines changed

4 files changed

+1039
-775
lines changed

bip-txhash.md

+148-62
Original file line numberDiff line numberDiff line change
@@ -65,111 +65,121 @@ summary, followed by a reference implementation of the CalculateTxHash function.
6565
scriptPubkeys.
6666

6767
Special case `TXFS_SPECIAL_TEMPLATE` is 4 bytes long, as follows:
68-
*  1. `TXFS_ALL`
69-
*  2. `TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL`
70-
*  3. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
71-
*  4. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
68+
* 1: `TXFS_ALL`
69+
* 2: `TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL`
70+
* 3: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
71+
* 4: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
7272

7373
* the `0x00` byte: it is set equal to `TXFS_SPECIAL_ALL`, which means "ALL" and is primarily
7474
useful to emulate `SIGHASH_ALL` when `OP_TXHASH` is used in combination
7575
with `OP_CHECKSIGFROMSTACK`.
7676

7777
Special case `TXFS_SPECIAL_ALL` is 4 bytes long, as follows:
78-
*  1. `TXFS_ALL`
79-
*  2. `TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL`
80-
*  3. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
81-
*  4. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
78+
* 1: `TXFS_ALL`
79+
* 2: `TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL`
80+
* 3: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
81+
* 4: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL`
8282

8383
* The first byte of the TxFieldSelector has its 8 bits assigned as follows, from lowest to highest:
84-
*  1. version (`TXFS_VERSION`)
85-
*  2. locktime (`TXFS_LOCKTIME`)
86-
*  3. current input index (`TXFS_CURRENT_INPUT_IDX`)
87-
*  4. current input control block (or empty) (`TXFS_CURRENT_INPUT_CONTROL_BLOCK`)
88-
*  5. current input spent script (i.e. witness script or tapscript) (`TXFS_CURRENT_INPUT_SPENTSCRIPT`)
89-
*  6. current script last `OP_CODESEPARATOR` position (or 0xffffffff)
84+
* 1: version (`TXFS_VERSION`)
85+
* 2: locktime (`TXFS_LOCKTIME`)
86+
* 3: current input index (`TXFS_CURRENT_INPUT_IDX`)
87+
* 4: current input control block (or empty) (`TXFS_CURRENT_INPUT_CONTROL_BLOCK`)
88+
* 5: current input spent script (i.e. witness script or tapscript) (`TXFS_CURRENT_INPUT_SPENTSCRIPT`)
89+
* 6: current script last `OP_CODESEPARATOR` position (or 0xffffffff)
9090
(`TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS`)
91-
*  7. (unused)
91+
* 7: (unused)
92+
* 8: `TXFS_CONTROL` (i.e. include TxFieldSelector into hash)
9293

93-
* The last (highest) bit of the first byte (`TXFS_CONTROL`), we will call the
94+
* The highest bit of the first byte (`TXFS_CONTROL`), we will call the
9495
"control bit", and it can be used to control the behavior of the opcode. For
9596
`OP_TXHASH` and `OP_CHECKTXHASHVERIFY`, the control bit is used to determine
9697
whether the TxFieldSelector itself has to be included in the resulting hash.
9798
(For potential other uses of the TxFieldSelector (like a hypothetical
9899
`OP_TX`), this bit can be repurposed.)
99100

100-
* If either "inputs" or "outputs" is set to 1, expect another byte with its 8
101-
bits assigning the following variables, from lowest to highest:
101+
* The second byte will be used to indicate fields from the inputs and outputs.
102+
If there is only a single byte present, no information from the inputs and
103+
outputs will be committed. Otherwise, of the second byte, the 8 bits are
104+
assigned the following variables, from lowest to highest:
102105
* Specifying which fields of the inputs will be selected:
103-
*  1. prevouts (`TXFS_INPUTS_PREVOUTS`)
104-
*  2. sequences (`TXFS_INPUTS_SEQUENCES`)
105-
*  3. scriptSigs (`TXFS_INPUTS_SCRIPTSIGS`)
106-
*  4. prevout scriptPubkeys (`TXFS_INPUTS_PREV_SCRIPTPUBKEYS`)
107-
*  5. prevout values (`TXFS_INPUTS_PREV_VALUED`)
108-
*  6. taproot annexes (`TXFS_INPUTS_TAPROOT_ANNEXES`)
106+
* 1: prevouts (`TXFS_INPUTS_PREVOUTS`)
107+
* 2: sequences (`TXFS_INPUTS_SEQUENCES`)
108+
* 3: scriptSigs (`TXFS_INPUTS_SCRIPTSIGS`)
109+
* 4: prevout scriptPubkeys (`TXFS_INPUTS_PREV_SCRIPTPUBKEYS`)
110+
* 5: prevout values (`TXFS_INPUTS_PREV_VALUED`)
111+
* 6: taproot annexes (`TXFS_INPUTS_TAPROOT_ANNEXES`)
109112

110113
* Specifying which fields of the outputs will be selected:
111-
*  7. scriptPubkeys (`TXFS_OUTPUTS_SCRIPTPUBKEYS`)
112-
*  8. values (`TXFS_OUTPUTS_VALUES`)
114+
* 7: scriptPubkeys (`TXFS_OUTPUTS_SCRIPTPUBKEYS`)
115+
* 8: values (`TXFS_OUTPUTS_VALUES`)
113116

114117
* We define as follows:
115118
* `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`
116119
* `TXFS_INPUTS_ALL = TXFS_INPUTS_PREVOUTS | TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES`
117120
* `TXFS_INPUTS_TEMPLATE = TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES`
118121
* `TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES`
119122

120-
For both inputs and then outputs, do the following:
121123

122-
* If the "in/outputs" field is set to 1, another additional byte is expected:
123-
* The highest bit (`TXFS_INOUT_NUMBER`) indicates whether the "number of in-/outputs"
124-
should be committed to.
124+
* For both inputs and then outputs, expect an additional byte as follows:
125+
* The highest bit (`TXFS_INOUT_NUMBER`) indicates whether the "number of
126+
in-/outputs" should be committed to.
125127
* For the remaining bits, there are three exceptional values:
126-
* 0x00 (`TXFS_INOUT_SELECTION_NONE`) means "no in/outputs"
127-
(hence only the number of them as `0x80` (`TXFS_INOUT_NUMBER`)).
128-
* `0x40` (`TXFS_INOUT_SELECTION_CURRENT`) means "select only the in/output of the current input index"
129-
(it is invalid when current index exceeds number of outputs).
128+
* 0x00 (`TXFS_INOUT_SELECTION_NONE`) means "no in/outputs" (hence only the
129+
number of them as `0x80` (`TXFS_INOUT_NUMBER`)).
130+
* `0x40` (`TXFS_INOUT_SELECTION_CURRENT`) means "select only the in/output
131+
of the current input index" (it is invalid when current index exceeds
132+
number of outputs).
130133
* `0x3f` (`TXFS_INOUT_SELECTION_ALL`) means "select all in/outputs".
131134

132135
* The second highest bit (`TXFS_INOUT_SELECTION_MODE`) is the "specification mode":
133136
* Set to 0 it means "leading mode".
134137
* Set to 1 it means "individual mode".
135-
* In "leading mode", the third highest bit (`TXFS_INOUT_SELECTION_SIZE`) is
136-
used to indicate the "count size", i.e. the number of bytes will be used to
138+
139+
* In "leading mode", the third highest bit (`TXFS_INOUT_LEADING_SIZE`) is
140+
used to indicate the "index size", i.e. the number of bytes will be used to
137141
represent the number of in/output.
138-
* With "index size" set to 0, the remaining lowest 5 bits of the first byte will
139-
be interpreted as the number of leading in/outputs to select.
140-
* With "index size" set to 1, the remaining lowest 5 bits of the first byte together with the
141-
8 bits of the next byte will be interpreted as the number of leading in/outputs to select.
142-
* In "individual mode", the remaining lowest 6 bits of the first byte will be
143-
interpreted as `n`, the number of individual in/outputs to select. For each
144-
individual input, (at least) one byte is expected, of this byte. The
145-
highest bit is used to indicate "absolute or relative" indices.
146-
* If the highest bit is set to 0, it is an absolute index. The second
147-
highest bit is used to indicate the amount of bytes are used to represent
148-
the index.
149-
* If the second-highest bit is 0, the remaining 6 bits represent the index to be selected.
150-
* If the second-highest bit is 1, the remaining 6 bits, together with the 8 bits of the next
151-
byte, represent the index to be selected.
152-
* If the highest bit is set to 1, it is a relative index. The second highest bit is used to
153-
indicate the sign of the index.
154-
* If the second-highest bit is set to 0, the remaining 6 bits represent the positive relative
155-
index to be selected.
156-
* If the second-highest bit is set to 1, the remaining 6 bits represent the negative relative
157-
index to be selected.
142+
* With "index size" set to 0, the remaining lowest 5 bits of the first byte
143+
will be interpreted as the number of leading in/outputs to select.
144+
* With "index size" set to 1, the remaining lowest 5 bits of the first byte
145+
together with the 8 bits of the next byte will be interpreted as the
146+
number of leading in/outputs to select.
147+
148+
* In "individual mode", the third highest bit (`TXFS_INOUT_INDIVIDUAL_MODE`)
149+
indicates whether we are passing absolute indices (0) or indices relative
150+
to the current input (1), the remaining lowest 5 bits will be interpreted
151+
as `n`, the number of individual in/outputs follow.
152+
* In absolute mode (second highest bit is 0), for each of the `n` indices,
153+
at least one extra byte is expected.
154+
* If that byte's highest bit is set to 0, the remaining 7 bits represent
155+
the absolute index to select.
156+
* If that byte's highest bit is set to 1, the remaining 7 bits, together
157+
with the next byte's 8 bits represent the absolute index to select.
158+
* In relative mode (second highest bit is 1), for each of the `n` indices,
159+
at least one extra byte is expected.
160+
* If that byte's highest bit is set to 0, the remaining 7 bits represent
161+
the relative index in two's complement.
162+
* If that byte's highest bit is set to 1, the remaining 7 bits, together
163+
with the next byte's 8 bits represent the relative index in two's
164+
complement.
165+
158166

159167
Effectively, this allows a user to select
160168
* all in/outputs
161169
* the current input index
162-
* the leading in/outputs up to 8192
163-
* up to 64 individually selected in/outputs
170+
* the leading in/outputs up to 7936
171+
* up to 32 individually selected in/outputs
164172
** using absolute indices up to 16384
165-
** using indices relative to the current input index from -64 to +64.
173+
** using indices relative to the current input index from -8191 to +8192.
166174

167175
The TxFieldSelector is invalid when
168176
* a byte is expected but missing
169177
* additional unexpected bytes are present
170178
* index size is set to 1 while not being necessary
171-
* a leading number of individual index is selected out of bounds of the in/outputs
179+
* a leading number or individual index is selected out of bounds of the in/outputs
172180
* individual indices are duplicated or not in increasing order
181+
* single relative index of +0, which could be just `TXFS_INOUT_SELECTION_CURRENT`
182+
* input or output fields bits are set, but no inputs or outputs are selected
173183

174184
These limitations are to avoid potential TxFieldSelector malleability. It is
175185
however allowed to use leading mode where it could be "all". This
@@ -178,6 +188,81 @@ is important to allow for optional addition of extra inputs or outputs.
178188
//TODO(stevenroose) should we disallow individual that could be leading?
179189

180190

191+
### Visualization
192+
193+
* first byte
194+
195+
```
196+
1 0 1 1 1 1 1 1
197+
| | | | | | | ^ version
198+
| | | | | | ^ locktime
199+
| | | | | ^ current input index
200+
| | | | ^ current input control block
201+
| | | ^ current input spend script
202+
| | ^ current script last OP_CODESEPARATOR
203+
| ^ currently unused
204+
^ control bit (ie. include TXFS in hash)
205+
```
206+
207+
* second byte
208+
209+
```
210+
v outputs
211+
<-> <---------> inputs
212+
1 1 1 1 1 1 1 1
213+
| | | | | | | ^ prevouts
214+
| | | | | | ^ sequences
215+
| | | | | ^ scriptSigs
216+
| | | | ^ prevout scriptPubkeys
217+
| | | ^ prevout values
218+
| | ^ taproot annexes
219+
| ^ scriptPubkeys
220+
^ values
221+
```
222+
223+
* in/output selector byte
224+
225+
"only the first 3"
226+
```
227+
1 0 0 0 0 0 1 1
228+
| | | <-------> integer 0b00011 == 3
229+
| | ^ index size 0: single byte
230+
| ^ leading mode
231+
^ commit the number of in/outputs
232+
```
233+
234+
"only the first 257"
235+
```
236+
1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1
237+
| | | <------------------------> integer 0b00001 00000001 == 257
238+
| | ^ index size 1: two bytes
239+
| ^ leading mode
240+
^ commit the number of in/outputs
241+
```
242+
243+
"only indices 0 and 2"
244+
```
245+
0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1
246+
| | | | <--------------> second idx: 3
247+
| | | | <--------------> first idx: 1
248+
| | | | <-----> selection count: 0b10 == 2
249+
| | | ^ index size 0: single byte per index
250+
| | ^ absolute index
251+
| ^ individual mode
252+
^ don't commit the number of in/outputs
253+
```
254+
255+
* total example
256+
257+
```
258+
bf ff c2 01 03 83
259+
| | ^ commit number of outputs + leading 3 outputs
260+
| | <------> commit number of inputs + inputs at indices 1 and 3
261+
| ^ all input and output fields
262+
^ all regular fields, except for the unused one
263+
```
264+
265+
181266
## Resource limits
182267

183268
* For legacy scripts and segwit, we don't add any extra resource limitations,
@@ -195,9 +280,10 @@ is important to allow for optional addition of extra inputs or outputs.
195280
future addition of byte manipulation opcodes like `OP_CAT`, an additional
196281
cost is specified per TransactionHash execution. Using the same validation
197282
budget ("sigops budget") introduced in BIP-0342, each TransactionHash
198-
decreases the validation budget by 10. If this brings the budget below zero,
283+
decreases the validation budget by 15. If this brings the budget below zero,
199284
the script fails immediately.<br>The following considerations should be made:
200-
* All fields that can be of arbitrary size are cachable as TransactionHash always hashes their hashed values.
285+
* All fields that can be of arbitrary size are cachable as TransactionHash
286+
always hashes their hashed values.
201287
* In "individual mode", a user can at most commit 32 inputs or outputs,
202288
which we don't consider excessive for potential repeated use.
203289
* In "leading mode", a caching strategy can be used where the SHA256 context

bip-txhash/ref-impl/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ version = "0.0.0"
44
edition = "2021"
55

66
[dependencies]
7-
bitcoin = { version = "0.31.0", features = [ "serde" ] }
7+
bitcoin = { version = "=0.31.0", features = [ "serde" ] }
88
serde_json = "1.0.108"
99

1010
# until bitcoin-io is released and https://github.com/rust-bitcoin/rust-bitcoin/pull/2274 is merged
1111
[patch.crates-io]
1212
bitcoin = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash", features = [ "serde" ] }
1313
bitcoin_hashes = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash" }
14-
bitcoin-io = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash" }

0 commit comments

Comments
 (0)