2
2
//!
3
3
//! This module provides logic to create extended mining jobs given a template from
4
4
//! a template provider as well as logic to clean up old templates when new blocks are mined.
5
- use crate :: { errors, utils:: Id , Error } ;
6
- use binary_sv2:: B064K ;
5
+ use crate :: {
6
+ utils:: { Coinbase , Id } ,
7
+ Error ,
8
+ } ;
7
9
use mining_sv2:: NewExtendedMiningJob ;
8
10
use nohash_hasher:: BuildNoHashHasher ;
9
11
use std:: { collections:: HashMap , convert:: TryInto } ;
10
12
use template_distribution_sv2:: { NewTemplate , SetNewPrevHash } ;
11
13
use tracing:: debug;
12
14
13
- use stratum_common:: {
14
- bitcoin,
15
- bitcoin:: {
16
- blockdata:: {
17
- transaction:: { OutPoint , Transaction , TxIn , TxOut } ,
18
- witness:: Witness ,
19
- } ,
20
- consensus:: Decodable ,
21
- util:: psbt:: serialize:: { Deserialize , Serialize } ,
22
- } ,
15
+ use stratum_common:: bitcoin:: {
16
+ blockdata:: transaction:: { Transaction , TxOut } ,
17
+ consensus:: Decodable ,
18
+ util:: psbt:: serialize:: Deserialize ,
23
19
} ;
24
20
25
21
#[ derive( Debug ) ]
@@ -204,17 +200,16 @@ fn new_extended_job(
204
200
. map_err ( |_| Error :: TxVersionTooBig ) ?;
205
201
206
202
let script_sig_prefix = new_template. coinbase_prefix . to_vec ( ) ;
207
- let script_sig_prefix_len = script_sig_prefix. len ( ) + additional_coinbase_script_data. len ( ) ;
208
203
209
- let coinbase = coinbase (
204
+ let coinbase = Coinbase :: new (
210
205
script_sig_prefix,
211
206
tx_version,
212
207
new_template. coinbase_tx_locktime ,
213
208
new_template. coinbase_tx_input_sequence ,
214
- coinbase_outputs,
209
+ coinbase_outputs. to_vec ( ) ,
215
210
additional_coinbase_script_data,
216
211
extranonce_len,
217
- ) ? ;
212
+ ) ;
218
213
219
214
let min_ntime = binary_sv2:: Sv2Option :: new ( if new_template. future_template {
220
215
None
@@ -229,8 +224,8 @@ fn new_extended_job(
229
224
version : new_template. version ,
230
225
version_rolling_allowed,
231
226
merkle_path : new_template. merkle_path . clone ( ) . into_static ( ) ,
232
- coinbase_tx_prefix : coinbase_tx_prefix ( & coinbase , script_sig_prefix_len ) ?,
233
- coinbase_tx_suffix : coinbase_tx_suffix ( & coinbase , extranonce_len , script_sig_prefix_len ) ?,
227
+ coinbase_tx_prefix : coinbase . clone ( ) . coinbase_tx_prefix ( ) ?,
228
+ coinbase_tx_suffix : coinbase . coinbase_tx_suffix ( ) ?,
234
229
} ;
235
230
236
231
debug ! (
@@ -240,89 +235,6 @@ fn new_extended_job(
240
235
Ok ( new_extended_mining_job)
241
236
}
242
237
243
- // Used to extract the coinbase transaction prefix for extended jobs
244
- // so the extranonce search space can be introduced
245
- fn coinbase_tx_prefix (
246
- coinbase : & Transaction ,
247
- script_sig_prefix_len : usize ,
248
- ) -> Result < B064K < ' static > , Error > {
249
- let encoded = coinbase. serialize ( ) ;
250
- // If script_sig_prefix_len is not 0 we are not in a test environment and the coinbase will have
251
- // the 0 witness
252
- let segwit_bytes = match script_sig_prefix_len {
253
- 0 => 0 ,
254
- _ => 2 ,
255
- } ;
256
- let index = 4 // tx version
257
- + segwit_bytes
258
- + 1 // number of inputs TODO can be also 3
259
- + 32 // prev OutPoint
260
- + 4 // index
261
- + 1 // bytes in script TODO can be also 3
262
- + script_sig_prefix_len; // script_sig_prefix
263
- let r = encoded[ 0 ..index] . to_vec ( ) ;
264
- r. try_into ( ) . map_err ( Error :: BinarySv2Error )
265
- }
266
-
267
- // Used to extract the coinbase transaction suffix for extended jobs
268
- // so the extranonce search space can be introduced
269
- fn coinbase_tx_suffix (
270
- coinbase : & Transaction ,
271
- extranonce_len : u8 ,
272
- script_sig_prefix_len : usize ,
273
- ) -> Result < B064K < ' static > , Error > {
274
- let encoded = coinbase. serialize ( ) ;
275
- // If script_sig_prefix_len is not 0 we are not in a test environment and the coinbase have the
276
- // 0 witness
277
- let segwit_bytes = match script_sig_prefix_len {
278
- 0 => 0 ,
279
- _ => 2 ,
280
- } ;
281
- let r = encoded[ 4 // tx version
282
- + segwit_bytes
283
- + 1 // number of inputs TODO can be also 3
284
- + 32 // prev OutPoint
285
- + 4 // index
286
- + 1 // bytes in script TODO can be also 3
287
- + script_sig_prefix_len // script_sig_prefix
288
- + ( extranonce_len as usize ) ..]
289
- . to_vec ( ) ;
290
- r. try_into ( ) . map_err ( Error :: BinarySv2Error )
291
- }
292
-
293
- // try to build a Transaction coinbase
294
- fn coinbase (
295
- script_sig_prefix : Vec < u8 > ,
296
- version : i32 ,
297
- lock_time : u32 ,
298
- sequence : u32 ,
299
- coinbase_outputs : & [ TxOut ] ,
300
- additional_coinbase_script_data : Vec < u8 > ,
301
- extranonce_len : u8 ,
302
- ) -> Result < Transaction , Error > {
303
- // If script_sig_prefix_len is not 0 we are not in a test environment and the coinbase have the
304
- // 0 witness
305
- let witness = match script_sig_prefix. len ( ) {
306
- 0 => Witness :: from_vec ( vec ! [ ] ) ,
307
- _ => Witness :: from_vec ( vec ! [ vec![ 0 ; 32 ] ] ) ,
308
- } ;
309
- let mut script_sig = script_sig_prefix;
310
- script_sig. extend_from_slice ( & additional_coinbase_script_data) ;
311
- script_sig. extend_from_slice ( & vec ! [ 0 ; extranonce_len as usize ] ) ;
312
- let tx_in = TxIn {
313
- previous_output : OutPoint :: null ( ) ,
314
- script_sig : script_sig. into ( ) ,
315
- sequence : bitcoin:: Sequence ( sequence) ,
316
- witness,
317
- } ;
318
- Ok ( Transaction {
319
- version,
320
- lock_time : bitcoin:: PackedLockTime ( lock_time) ,
321
- input : vec ! [ tx_in] ,
322
- output : coinbase_outputs. to_vec ( ) ,
323
- } )
324
- }
325
-
326
238
/// Helper type to strip a segwit data from the coinbase_tx_prefix and coinbase_tx_suffix
327
239
/// to ensure miners are hashing with the correct coinbase
328
240
pub fn extended_job_to_non_segwit (
@@ -335,8 +247,20 @@ pub fn extended_job_to_non_segwit(
335
247
let extranonce = vec ! [ 0_u8 ; full_extranonce_len] ;
336
248
encoded. extend_from_slice ( & extranonce[ ..] ) ;
337
249
encoded. extend_from_slice ( job. coinbase_tx_suffix . inner_as_ref ( ) ) ;
338
- let coinbase = Transaction :: deserialize ( & encoded) . map_err ( |_| Error :: InvalidCoinbase ) ?;
339
- let stripped_tx = StrippedCoinbaseTx :: from_coinbase ( coinbase, full_extranonce_len) ?;
250
+ let tx = Transaction :: deserialize ( & encoded) . map_err ( |_| Error :: InvalidCoinbase ) ?;
251
+
252
+ let coinbase = Coinbase {
253
+ tx,
254
+ script_sig_prefix_len : job. coinbase_tx_prefix . len ( )
255
+ - (
256
+ 4 + // version
257
+ 1 + // input count
258
+ 32 + // outpoint
259
+ 4 + // input index
260
+ 1
261
+ // script length byte
262
+ ) ,
263
+ } ;
340
264
341
265
Ok ( NewExtendedMiningJob {
342
266
channel_id : job. channel_id ,
@@ -345,95 +269,10 @@ pub fn extended_job_to_non_segwit(
345
269
version : job. version ,
346
270
version_rolling_allowed : job. version_rolling_allowed ,
347
271
merkle_path : job. merkle_path ,
348
- coinbase_tx_prefix : stripped_tx . into_coinbase_tx_prefix ( ) ?,
349
- coinbase_tx_suffix : stripped_tx . into_coinbase_tx_suffix ( ) ?,
272
+ coinbase_tx_prefix : coinbase . clone ( ) . coinbase_tx_prefix ( ) ?,
273
+ coinbase_tx_suffix : coinbase . coinbase_tx_suffix ( ) ?,
350
274
} )
351
275
}
352
- // Helper type to strip a segwit data from the coinbase_tx_prefix and coinbase_tx_suffix
353
- // to ensure miners are hashing with the correct coinbase
354
- struct StrippedCoinbaseTx {
355
- version : u32 ,
356
- inputs : Vec < Vec < u8 > > ,
357
- outputs : Vec < Vec < u8 > > ,
358
- lock_time : u32 ,
359
- // helper field
360
- bip141_bytes_len : usize ,
361
- }
362
-
363
- impl StrippedCoinbaseTx {
364
- // create
365
- fn from_coinbase ( tx : Transaction , full_extranonce_len : usize ) -> Result < Self , Error > {
366
- let bip141_bytes_len = tx
367
- . input
368
- . last ( )
369
- . ok_or ( Error :: BadPayloadSize ) ?
370
- . script_sig
371
- . len ( )
372
- - full_extranonce_len;
373
- Ok ( Self {
374
- version : tx. version as u32 ,
375
- inputs : tx
376
- . input
377
- . iter ( )
378
- . map ( |txin| {
379
- let mut ser: Vec < u8 > = vec ! [ ] ;
380
- ser. extend_from_slice ( & txin. previous_output . txid ) ;
381
- ser. extend_from_slice ( & txin. previous_output . vout . to_le_bytes ( ) ) ;
382
- ser. push ( txin. script_sig . len ( ) as u8 ) ;
383
- ser. extend_from_slice ( txin. script_sig . as_bytes ( ) ) ;
384
- ser. extend_from_slice ( & txin. sequence . 0 . to_le_bytes ( ) ) ;
385
- ser
386
- } )
387
- . collect ( ) ,
388
- outputs : tx. output . iter ( ) . map ( |o| o. serialize ( ) ) . collect ( ) ,
389
- lock_time : tx. lock_time . into ( ) ,
390
- bip141_bytes_len,
391
- } )
392
- }
393
-
394
- // The coinbase tx prefix is the LE bytes concatenation of the tx version and all
395
- // of the tx inputs minus the 32 bytes after the script_sig_prefix bytes
396
- // and the last input's sequence (used as the first entry in the coinbase tx suffix).
397
- // The last 32 bytes after the bip34 bytes in the script will be used to allow extranonce
398
- // space for the miner. We remove the bip141 marker and flag since it is only used for
399
- // computing the `wtxid` and the legacy `txid` is what is used for computing the merkle root
400
- // clippy allow because we don't want to consume self
401
- #[ allow( clippy:: wrong_self_convention) ]
402
- fn into_coinbase_tx_prefix ( & self ) -> Result < B064K < ' static > , errors:: Error > {
403
- let mut inputs = self . inputs . clone ( ) ;
404
- let last_input = inputs. last_mut ( ) . ok_or ( Error :: BadPayloadSize ) ?;
405
- let new_last_input_len =
406
- 32 // outpoint
407
- + 4 // vout
408
- + 1 // script length byte -> TODO can be also 3 (based on TODO in `coinbase_tx_prefix()`)
409
- + self . bip141_bytes_len // space for bip34 bytes
410
- ;
411
- last_input. truncate ( new_last_input_len) ;
412
- let mut prefix: Vec < u8 > = vec ! [ ] ;
413
- prefix. extend_from_slice ( & self . version . to_le_bytes ( ) ) ;
414
- prefix. push ( self . inputs . len ( ) as u8 ) ;
415
- prefix. extend_from_slice ( & inputs. concat ( ) ) ;
416
- prefix. try_into ( ) . map_err ( Error :: BinarySv2Error )
417
- }
418
-
419
- // This coinbase tx suffix is the sequence of the last tx input plus
420
- // the serialized tx outputs and the lock time. Note we do not use the witnesses
421
- // (placed between txouts and lock time) since it is only used for
422
- // computing the `wtxid` and the legacy `txid` is what is used for computing the merkle root
423
- // clippy allow because we don't want to consume self
424
- #[ allow( clippy:: wrong_self_convention) ]
425
- fn into_coinbase_tx_suffix ( & self ) -> Result < B064K < ' static > , errors:: Error > {
426
- let mut suffix: Vec < u8 > = vec ! [ ] ;
427
- let last_input = self . inputs . last ( ) . ok_or ( Error :: BadPayloadSize ) ?;
428
- // only take the last intput's sequence u32 (bytes after the extranonce space)
429
- let last_input_sequence = & last_input[ last_input. len ( ) - 4 ..] ;
430
- suffix. extend_from_slice ( last_input_sequence) ;
431
- suffix. push ( self . outputs . len ( ) as u8 ) ;
432
- suffix. extend_from_slice ( & self . outputs . concat ( ) ) ;
433
- suffix. extend_from_slice ( & self . lock_time . to_le_bytes ( ) ) ;
434
- suffix. try_into ( ) . map_err ( Error :: BinarySv2Error )
435
- }
436
- }
437
276
438
277
// Test
439
278
#[ cfg( test) ]
@@ -682,15 +521,27 @@ pub mod tests {
682
521
235 , 216 , 54 , 151 , 78 , 140 , 249 , 1 , 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
683
522
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
684
523
] ;
685
- let coinbase = Transaction :: deserialize ( encoded) . unwrap ( ) ;
686
- let stripped = StrippedCoinbaseTx :: from_coinbase ( coinbase. clone ( ) , 32 ) . unwrap ( ) ;
687
- let prefix = stripped. into_coinbase_tx_prefix ( ) . unwrap ( ) . to_vec ( ) ;
688
- let suffix = stripped. into_coinbase_tx_suffix ( ) . unwrap ( ) . to_vec ( ) ;
524
+
525
+ let tx = Transaction :: deserialize ( encoded) . unwrap ( ) ;
526
+
527
+ // Calculate script_sig_prefix_len by examining the actual script_sig length
528
+ // and subtracting the extranonce length (32 in this case)
529
+ let extranonce_len = 32 ;
530
+ let script_sig_len = tx. input [ 0 ] . script_sig . len ( ) ;
531
+ let script_sig_prefix_len = script_sig_len - extranonce_len;
532
+
533
+ let coinbase = Coinbase {
534
+ tx,
535
+ script_sig_prefix_len,
536
+ } ;
537
+
538
+ let prefix = coinbase. clone ( ) . coinbase_tx_prefix ( ) . unwrap ( ) . to_vec ( ) ;
539
+ let suffix = coinbase. clone ( ) . coinbase_tx_suffix ( ) . unwrap ( ) . to_vec ( ) ;
689
540
let extranonce = & [ 0_u8 ; 32 ] ;
690
541
let path: & [ binary_sv2:: U256 ] = & [ ] ;
691
542
let stripped_merkle_root =
692
543
merkle_root_from_path ( & prefix[ ..] , & suffix[ ..] , extranonce, path) . unwrap ( ) ;
693
- let og_merkle_root = coinbase. txid ( ) . to_vec ( ) ;
544
+ let og_merkle_root = coinbase. tx . txid ( ) . to_vec ( ) ;
694
545
assert ! (
695
546
stripped_merkle_root == og_merkle_root,
696
547
"stripped tx hash is not the same as bitcoin crate"
@@ -720,15 +571,24 @@ pub mod tests {
720
571
let mut encoded_clone = encoded. clone ( ) ;
721
572
encoded_clone. extend_from_slice ( & extranonce[ ..] ) ;
722
573
encoded_clone. extend_from_slice ( coinbase_suffix) ;
723
- // let mut i = 1;
724
- // while let Err(_) = Transaction::deserialize(&encoded_clone) {
725
- // encoded_clone = encoded.clone();
726
- // extranonce.push(0);
727
- // encoded_clone.extend_from_slice(&extranonce[..]);
728
- // encoded_clone.extend_from_slice(coinbase_suffix);
729
- // i+=1;
730
- // }
731
- // println!("SIZE: {:?}", i);
732
- Transaction :: deserialize ( & encoded_clone) . unwrap ( ) ;
574
+
575
+ let parsed_tx = Transaction :: deserialize ( & encoded_clone) . unwrap ( ) ;
576
+
577
+ let coinbase = Coinbase {
578
+ tx : parsed_tx. clone ( ) ,
579
+ script_sig_prefix_len : 60 ,
580
+ } ;
581
+
582
+ let prefix = coinbase. clone ( ) . coinbase_tx_prefix ( ) . unwrap ( ) . to_vec ( ) ;
583
+ let suffix = coinbase. clone ( ) . coinbase_tx_suffix ( ) . unwrap ( ) . to_vec ( ) ;
584
+
585
+ let mut encoded_b = vec ! [ ] ;
586
+ encoded_b. extend_from_slice ( & prefix) ;
587
+ encoded_b. extend_from_slice ( & extranonce[ ..] ) ;
588
+ encoded_b. extend_from_slice ( & suffix) ;
589
+
590
+ // parse one last time to ensure that Coinbase::coinbase_tx_prefix and
591
+ // Coinbase::coinbase_tx_suffix functions are sane
592
+ let _parsed_tx_b = Transaction :: deserialize ( & encoded_b) . unwrap ( ) ;
733
593
}
734
594
}
0 commit comments