@@ -54,19 +54,30 @@ OP_TXHASH does the following:
54
54
The TxFieldSelector has the following semantics. We will give a brief conceptual
55
55
summary, followed by a reference implementation of the CalculateTxHash function.
56
56
57
- * If the TxFieldSelector is zero bytes long, it is set equal to
58
- 0xff|0xf6|0x3f|0x3f, the de-facto default value which means everything except
59
- the prevouts and the prevout scriptPubkeys.
57
+ * There are two special cases for the TxFieldSelector:
58
+ - the empty value, zero bytes long: it is set equal to 0xff|0xf6|0xbf|0xbf,
59
+ the de-facto default value which means everything except the prevouts and
60
+ the prevout scriptPubkeys.
61
+ - the 0x00 byte: it is set equal to 0xff|0xff|0xbf|0xbf, which means "ALL"
62
+ and is primarily useful to emulate SIGHASH_ALL when OP_TXHASH is used in
63
+ combination with OP_CHECKSIGFROMSTACK.
64
+
60
65
* The first byte of the TxFieldSelector has its 8 bits assigned as follows,
61
66
from lowest to highest:
62
67
1. version
63
68
2. locktime
64
- 3. nb_inputs
65
- 4. nb_outputs
66
- 5. current input index
67
- 6. current input control block (only in case of tapscript spend)
68
- 7. inputs
69
- 8. outputs
69
+ 3. current input index
70
+ 4. current input control block (only in case of tapscript spend)
71
+ 5. current script last OP_CODESEPARATOR position
72
+ 6. inputs
73
+ 7. outputs
74
+
75
+ * The last (highest) bit of the first byte, we will call the "control bit", and
76
+ it can be used to control the behavior of the opcode. For OP_TXHASH and
77
+ OP_CHECKTXHASHVERIFY, the control bit is used to determine whether the
78
+ TxFieldSelector itself has to be included in the resulting hash. (For
79
+ potential other uses of the TxFieldSelector (like a hypothetical OP_TX), this
80
+ bit can be repurposed.)
70
81
71
82
* If either "inputs" or "outputs" is set to 1, expect another byte with its 8
72
83
bits assigning the following variables, from lowest to highest:
@@ -84,21 +95,24 @@ summary, followed by a reference implementation of the CalculateTxHash function.
84
95
For both inputs and then outputs, do the following:
85
96
86
97
* If the "in/outputs" field is set to 1, another additional byte is expected:
87
- * There are two exceptional values:
88
- - 0x00 means "select only the in/output of the current input index".
89
- - 0x3f means "select all in/outputs". //TODO(stevenroose) make this 0xff?
90
- * The highest bit is the "specification mode":
98
+ * The highest bit indicates whether the "number of in-/outputs" should be
99
+ committed to.
100
+ * For the remaining bits, there are three exceptional values:
101
+ - 0x00 means "no in/outputs" (hence only the number of them as 0x80)
102
+ - 0x40 means "select only the in/output of the current input index".
103
+ - 0x3f means "select all in/outputs".
104
+ * The second highest bit is the "specification mode":
91
105
- Set to 0 it means "prefix mode".
92
106
- Set to 1 it means "individual mode".
93
- * The second highest bit is used to indicate the "index size", i.e. the number
107
+ * The third highest bit is used to indicate the "index size", i.e. the number
94
108
of bytes will be used to represent in/output indices.
95
109
* In "prefix mode",
96
- - With "index size" set to 0, the remaining lowest 6 bits of the first byte
110
+ - With "index size" set to 0, the remaining lowest 5 bits of the first byte
97
111
will be interpreted as the number of leading in/outputs to select.
98
- - With "index size" set to 1, the remaining lowest 6 bits of the first byte
112
+ - With "index size" set to 1, the remaining lowest 5 bits of the first byte
99
113
together with the 8 bits of the next byte will be interpreted as the
100
114
number of leading in/outputs to select.
101
- * In "individual mode", the remaining lowest 6 bits of the first byte will be
115
+ * In "individual mode", the remaining lowest 5 bits of the first byte will be
102
116
interpreted as `n`, the number of individual in/outputs to select.
103
117
- With "index size" set to 0, interpret the following `n` individual bytes
104
118
as the indices of an individual in/outputs to select.
@@ -224,21 +238,22 @@ some potential future upgrades:
224
238
<source lang ="rust" >
225
239
pub const TXFS_VERSION: u8 = 1 << 0;
226
240
pub const TXFS_LOCKTIME: u8 = 1 << 1;
227
- pub const TXFS_NB_INPUTS: u8 = 1 << 2;
228
- pub const TXFS_NB_OUTPUTS: u8 = 1 << 3;
229
- pub const TXFS_CURRENT_INPUT_IDX: u8 = 1 << 4;
230
- pub const TXFS_CURRENT_INPUT_CONTROL_BLOCK: u8 = 1 << 5;
231
- pub const TXFS_INPUTS: u8 = 1 << 6;
232
- pub const TXFS_OUTPUTS: u8 = 1 << 7;
241
+ pub const TXFS_CURRENT_INPUT_IDX: u8 = 1 << 2;
242
+ pub const TXFS_CURRENT_INPUT_CONTROL_BLOCK: u8 = 1 << 3;
243
+ pub const TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS: u8 = 1 << 4;
244
+ pub const TXFS_INPUTS: u8 = 1 << 5;
245
+ pub const TXFS_OUTPUTS: u8 = 1 << 6;
246
+
247
+ pub const TXFS_CONTROL: u8 = 1 << 7;
233
248
234
249
pub const TXFS_ALL: u8 = TXFS_VERSION
235
250
| TXFS_LOCKTIME
236
- | TXFS_NB_INPUTS
237
- | TXFS_NB_OUTPUTS
238
251
| TXFS_CURRENT_INPUT_IDX
239
252
| TXFS_CURRENT_INPUT_CONTROL_BLOCK
253
+ | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS
240
254
| TXFS_INPUTS
241
- | TXFS_OUTPUTS;
255
+ | TXFS_OUTPUTS
256
+ | TXFS_CONTROL;
242
257
243
258
pub const TXFS_INPUTS_PREVOUTS: u8 = 1 << 0;
244
259
pub const TXFS_INPUTS_SEQUENCES: u8 = 1 << 1;
@@ -261,25 +276,39 @@ pub const TXFS_INPUTS_DEFAULT: u8 = TXFS_INPUTS_SEQUENCES
261
276
| TXFS_INPUTS_TAPROOT_ANNEXES;
262
277
pub const TXFS_OUTPUTS_ALL: u8 = TXFS_OUTPUTS_SCRIPT_PUBKEYS | TXFS_OUTPUTS_VALUES;
263
278
264
- pub const TXFS_INOUT_RANGE_CURRENT: u8 = 0x00;
265
- pub const TXFS_INOUT_RANGE_ALL: u8 = 0xf3;
266
- pub const TXFS_INOUT_RANGE_MODE: u8 = 1 << 7;
267
- pub const TXFS_INOUT_RANGE_SIZE: u8 = 1 << 6;
268
- pub const TXFS_INOUT_RANGE_MASK: u8 = 0xff ^ TXFS_INOUT_RANGE_MODE ^ TXFS_INOUT_RANGE_SIZE;
279
+ pub const TXFS_INOUT_NUMBER: u8 = 1 << 7;
280
+ pub const TXFS_INOUT_RANGE_NONE: u8 = 0x00;
281
+ pub const TXFS_INOUT_RANGE_CURRENT: u8 = 0x40;
282
+ pub const TXFS_INOUT_RANGE_ALL: u8 = 0x3f;
283
+ pub const TXFS_INOUT_RANGE_MODE: u8 = 1 << 6;
284
+ pub const TXFS_INOUT_RANGE_SIZE: u8 = 1 << 5;
285
+ pub const TXFS_INOUT_RANGE_MASK: u8 =
286
+ 0xff ^ TXFS_INOUT_NUMBER ^ TXFS_INOUT_RANGE_MODE ^ TXFS_INOUT_RANGE_SIZE;
269
287
270
- pub const DEFAULT_TXFS: [u8; 4] = [
288
+ pub const TXFS_TEMPLATE_ALL: [u8; 4] = [
289
+ TXFS_ALL,
290
+ TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL,
291
+ TXFS_INOUT_NUMBER | TXFS_INOUT_RANGE_ALL,
292
+ TXFS_INOUT_NUMBER | TXFS_INOUT_RANGE_ALL,
293
+ ];
294
+ pub const TXFS_TEMPLATE_DEFAULT: [u8; 4] = [
271
295
TXFS_ALL,
272
296
TXFS_INPUTS_DEFAULT | TXFS_OUTPUTS_ALL,
273
- TXFS_INOUT_RANGE_ALL,
274
- TXFS_INOUT_RANGE_ALL,
297
+ TXFS_INOUT_NUMBER | TXFS_INOUT_RANGE_ALL,
298
+ TXFS_INOUT_NUMBER | TXFS_INOUT_RANGE_ALL,
275
299
];
276
300
301
+
277
302
fn validate_txfieldselector_inout_range(
278
303
bytes: &mut impl Iterator<Item = u8>,
279
304
nb_items: usize,
280
305
) -> Result<(), &'static str> {
281
- let range = bytes.next().ok_or("unexpected EOF")?;
282
- if range == TXFS_INOUT_RANGE_ALL || range == TXFS_INOUT_RANGE_CURRENT {
306
+ let nb_mask = 0xff ^ TXFS_INOUT_NUMBER;
307
+ let range = bytes.next().ok_or("unexpected EOF")? & nb_mask;
308
+ if range == TXFS_INOUT_RANGE_NONE
309
+ || range == TXFS_INOUT_RANGE_ALL
310
+ || range == TXFS_INOUT_RANGE_CURRENT
311
+ {
283
312
return Ok(())
284
313
} else if range & TXFS_INOUT_RANGE_MODE == 0 { // leading mode
285
314
if range & TXFS_INOUT_RANGE_SIZE == 0 {
@@ -343,7 +372,7 @@ pub fn validate_txfieldselector(
343
372
nb_inputs: usize,
344
373
nb_outputs: usize,
345
374
) -> Result<(), &'static str> {
346
- if txfs.is_empty() {
375
+ if txfs.is_empty() || (txfs.len() == 1 && txfs[0] == 0x00) {
347
376
return Ok(());
348
377
}
349
378
@@ -383,54 +412,70 @@ pub fn validate_txfieldselector(
383
412
Ok(())
384
413
}
385
414
415
+ ///
416
+ ///
417
+ /// Assumes that TxFieldSelector is valid.
386
418
pub fn calculate_txhash(
387
419
txfs: &[u8],
388
420
tx: &Transaction,
389
421
prevouts: &[TxOut],
390
422
current_input_idx: u32,
423
+ current_input_last_codeseparator_pos: Option<u32>,
391
424
) -> sha256::Hash {
392
425
assert!(validate_txfieldselector(txfs, tx.input.len(), tx.output.len()).is_ok());
393
426
assert_eq!(tx.input.len(), prevouts.len());
394
427
395
428
let txfs = if txfs.is_empty() {
396
- &DEFAULT_TXFS
429
+ &TXFS_TEMPLATE_DEFAULT
430
+ } else if txfs.len() == 1 && txfs[0] == 0x00 {
431
+ &TXFS_TEMPLATE_ALL
397
432
} else {
398
433
txfs
399
434
};
400
435
401
436
let mut engine = sha256::Hash::engine();
402
437
403
- //TODO(stevenroose) up for debate still
404
- // engine.input(txfs).unwrap();
438
+ if txfs[0] & TXFS_CONTROL != 0 {
439
+ engine.input(txfs);
440
+ }
405
441
406
442
let mut bytes = txfs.iter().copied();
407
443
let global = bytes.next().unwrap();
408
444
if global & TXFS_VERSION != 0 {
409
445
tx.version.consensus_encode(&mut engine).unwrap();
410
446
}
411
-
412
447
if global & TXFS_LOCKTIME != 0 {
413
448
tx.lock_time.consensus_encode(&mut engine).unwrap();
414
449
}
415
-
416
- if global & TXFS_NB_INPUTS != 0 {
417
- (tx.input.len() as u32).consensus_encode(&mut engine).unwrap();
418
- }
419
-
420
- if global & TXFS_NB_OUTPUTS != 0 {
421
- (tx.output.len() as u32).consensus_encode(&mut engine).unwrap();
422
- }
423
-
424
450
if global & TXFS_CURRENT_INPUT_IDX != 0 {
425
451
(current_input_idx as u32).consensus_encode(&mut engine).unwrap();
426
452
}
453
+ let cur = current_input_idx as usize;
454
+ if global & TXFS_CURRENT_INPUT_CONTROL_BLOCK != 0 {
455
+ if prevouts[cur].script_pubkey.is_v1_p2tr() {
456
+ if let Some(cb) = tx.input[cur].witness.taproot_control_block() {
457
+ encode::consensus_encode_with_size(cb, &mut engine).unwrap();
458
+ }
459
+ //TODO(stevenroose) should we hash something in case of no CB?
460
+ }
461
+ }
462
+ if global & TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS != 0 {
463
+ if let Some(pos) = current_input_last_codeseparator_pos {
464
+ (pos as u32).consensus_encode(&mut engine).unwrap();
465
+ }
466
+ //TODO(stevenroose) should we hash something in case of no codeseparator?
467
+ }
427
468
428
469
let inout_fields = bytes.next();
429
470
if global & TXFS_INPUTS != 0 {
430
471
let inout_fields = inout_fields.unwrap();
431
- let range = bytes.next().unwrap();
472
+ let input_byte = bytes.next().unwrap();
473
+ let number = input_byte & TXFS_INOUT_NUMBER != 0;
474
+ let range = input_byte & (0xff ^ TXFS_INOUT_NUMBER);
432
475
433
- let inputs: Vec<usize> = if range == TXFS_INOUT_RANGE_ALL {
476
+ let inputs: Vec<usize> = if range == TXFS_INOUT_RANGE_NONE {
477
+ vec![]
478
+ } else if range == TXFS_INOUT_RANGE_ALL {
434
479
(0..tx.input.len()).collect()
435
480
} else if range == TXFS_INOUT_RANGE_CURRENT {
436
481
vec![current_input_idx as usize]
@@ -457,7 +502,11 @@ pub fn calculate_txhash(
457
502
selected
458
503
};
459
504
460
- if inout_fields & TXFS_INPUTS_PREVOUTS != 0 {
505
+ if number {
506
+ (tx.input.len() as u32).consensus_encode(&mut engine).unwrap();
507
+ }
508
+
509
+ if !inputs.is_empty() && inout_fields & TXFS_INPUTS_PREVOUTS != 0 {
461
510
let hash = {
462
511
let mut engine = sha256::Hash::engine();
463
512
for i in &inputs {
@@ -468,7 +517,7 @@ pub fn calculate_txhash(
468
517
engine.input(&hash[..]);
469
518
}
470
519
471
- if inout_fields & TXFS_INPUTS_SEQUENCES != 0 {
520
+ if !inputs.is_empty() && inout_fields & TXFS_INPUTS_SEQUENCES != 0 {
472
521
let hash = {
473
522
let mut engine = sha256::Hash::engine();
474
523
for i in &inputs {
@@ -479,7 +528,7 @@ pub fn calculate_txhash(
479
528
engine.input(&hash[..]);
480
529
}
481
530
482
- if inout_fields & TXFS_INPUTS_SCRIPTSIGS != 0 {
531
+ if !inputs.is_empty() && inout_fields & TXFS_INPUTS_SCRIPTSIGS != 0 {
483
532
let hash = {
484
533
let mut engine = sha256::Hash::engine();
485
534
for i in &inputs {
@@ -490,7 +539,7 @@ pub fn calculate_txhash(
490
539
engine.input(&hash[..]);
491
540
}
492
541
493
- if inout_fields & TXFS_INPUTS_PREV_SCRIPTPUBKEYS != 0 {
542
+ if !inputs.is_empty() && inout_fields & TXFS_INPUTS_PREV_SCRIPTPUBKEYS != 0 {
494
543
let hash = {
495
544
let mut engine = sha256::Hash::engine();
496
545
for i in &inputs {
@@ -501,7 +550,7 @@ pub fn calculate_txhash(
501
550
engine.input(&hash[..]);
502
551
}
503
552
504
- if inout_fields & TXFS_INPUTS_PREV_VALUES != 0 {
553
+ if !inputs.is_empty() && inout_fields & TXFS_INPUTS_PREV_VALUES != 0 {
505
554
let hash = {
506
555
let mut engine = sha256::Hash::engine();
507
556
for i in &inputs {
@@ -512,7 +561,7 @@ pub fn calculate_txhash(
512
561
engine.input(&hash[..]);
513
562
}
514
563
515
- if inout_fields & TXFS_INPUTS_TAPROOT_ANNEXES != 0 {
564
+ if !inputs.is_empty() && inout_fields & TXFS_INPUTS_TAPROOT_ANNEXES != 0 {
516
565
let hash = {
517
566
let mut engine = sha256::Hash::engine();
518
567
for i in &inputs {
@@ -532,9 +581,13 @@ pub fn calculate_txhash(
532
581
533
582
if global & TXFS_OUTPUTS != 0 {
534
583
let output_fields = inout_fields.unwrap();
535
- let range = bytes.next().unwrap();
584
+ let output_byte = bytes.next().unwrap();
585
+ let number = output_byte & TXFS_INOUT_NUMBER != 0;
586
+ let range = output_byte & (0xff ^ TXFS_INOUT_NUMBER);
536
587
537
- let outputs: Vec<usize> = if range == TXFS_INOUT_RANGE_ALL {
588
+ let outputs: Vec<usize> = if range == TXFS_INOUT_RANGE_NONE {
589
+ vec![]
590
+ } else if range == TXFS_INOUT_RANGE_ALL {
538
591
(0..tx.output.len()).collect()
539
592
} else if range == TXFS_INOUT_RANGE_CURRENT {
540
593
vec![current_input_idx as usize]
@@ -561,7 +614,11 @@ pub fn calculate_txhash(
561
614
selected
562
615
};
563
616
564
- if output_fields & TXFS_OUTPUTS_SCRIPT_PUBKEYS != 0 {
617
+ if number {
618
+ (tx.output.len() as u32).consensus_encode(&mut engine).unwrap();
619
+ }
620
+
621
+ if !outputs.is_empty() && output_fields & TXFS_OUTPUTS_SCRIPT_PUBKEYS != 0 {
565
622
let hash = {
566
623
let mut engine = sha256::Hash::engine();
567
624
for i in &outputs {
@@ -572,7 +629,7 @@ pub fn calculate_txhash(
572
629
hash.consensus_encode(&mut engine).unwrap();
573
630
}
574
631
575
- if output_fields & TXFS_OUTPUTS_VALUES != 0 {
632
+ if !outputs.is_empty() && output_fields & TXFS_OUTPUTS_VALUES != 0 {
576
633
let hash = {
577
634
let mut engine = sha256::Hash::engine();
578
635
for i in &outputs {
0 commit comments