@@ -169,6 +169,33 @@ where
169169 )
170170}
171171
172+ #[ repr( transparent) ]
173+ struct DisplayList < ' a , T > ( & ' a T ) ;
174+
175+ impl < ' a , T > std:: fmt:: Display for DisplayList < ' a , T >
176+ where
177+ & ' a T : IntoIterator ,
178+ <& ' a T as IntoIterator >:: Item : std:: fmt:: Display ,
179+ {
180+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
181+ #[ repr( transparent) ]
182+ struct DebugDisplay < T > ( T ) ;
183+
184+ impl < T > std:: fmt:: Debug for DebugDisplay < T >
185+ where
186+ T : std:: fmt:: Display ,
187+ {
188+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
189+ std:: fmt:: Display :: fmt ( & self . 0 , f)
190+ }
191+ }
192+
193+ f. debug_list ( )
194+ . entries ( self . 0 . into_iter ( ) . map ( DebugDisplay ) )
195+ . finish ( )
196+ }
197+ }
198+
172199/// Compute the block reward for the specified height
173200fn get_block_reward ( height : u32 , fees : Amount , network : Network ) -> Amount {
174201 let subsidy_sats = 50 * Amount :: ONE_BTC . to_sat ( ) ;
@@ -336,6 +363,8 @@ where
336363 MempoolRemove ( #[ from] crate :: mempool:: MempoolRemoveError ) ,
337364 #[ error( transparent) ]
338365 SuffixTxs ( BP :: SuffixTxsError ) ,
366+ #[ error( "weight overflow" ) ]
367+ WeightOverflow ,
339368}
340369
341370// select block txs, and coinbase txouts if coinbasetxn is set
@@ -368,16 +397,16 @@ async fn block_txs<const COINBASE_TXN: bool, BP>(
368397 . iter ( )
369398 . map ( |( tx, _fee) | tx. compute_txid ( ) )
370399 . collect ( ) ;
400+ let suffix_txids: hashlink:: LinkedHashSet < Txid > = initial_block_template
401+ . suffix_txs
402+ . iter ( )
403+ . map ( |( tx, _fee) | tx. compute_txid ( ) )
404+ . collect ( ) ;
371405 {
372- let prefix_txids: String = format ! (
373- "[{}]" ,
374- prefix_txids
375- . iter( )
376- . map( |txid| txid. to_string( ) )
377- . collect:: <Vec <_>>( )
378- . join( ", " )
406+ tracing:: debug!(
407+ prefix_txids = %DisplayList ( & prefix_txids) ,
408+ suffix_txids = %DisplayList ( & suffix_txids) ,
379409 ) ;
380- tracing:: debug!( %prefix_txids) ;
381410 }
382411 let mut mempool = mempool. clone ( ) ;
383412 tracing:: debug!( "Inserting prefix txs into cloned mempool" ) ;
@@ -407,7 +436,7 @@ async fn block_txs<const COINBASE_TXN: bool, BP>(
407436 } )
408437 . collect ( )
409438 } ;
410- // Remove prefix txs
439+ // Remove prefix/excluded/suffix txs
411440 {
412441 tracing:: debug!( "Removing prefix txs" ) ;
413442 let _removed_txs = mempool
@@ -418,41 +447,41 @@ async fn block_txs<const COINBASE_TXN: bool, BP>(
418447 . map_err ( |err| match err {
419448 either:: Either :: Left ( err) => err,
420449 } ) ?;
421- {
422- let excluded_txids: String = format ! (
423- "[{}]" ,
424- initial_block_template
425- . exclude_mempool_txs
426- . iter( )
427- . map( |txid| txid. to_string( ) )
428- . collect:: <Vec <_>>( )
429- . join( ", " )
430- ) ;
431- tracing:: debug!( %excluded_txids, "Removing excluded txs" ) ;
432- }
450+ tracing:: debug!( "Removing prefix txs" ) ;
451+ tracing:: debug!(
452+ excluded_txids = %DisplayList (
453+ & initial_block_template. exclude_mempool_txs
454+ ) ,
455+ "Removing excluded/suffix txs"
456+ ) ;
433457 let _removed_txs = mempool
434458 . try_filter ( true , |tx, _| {
435459 let txid = tx. compute_txid ( ) ;
436- Result :: < _ , Infallible > :: Ok (
437- !initial_block_template. exclude_mempool_txs . contains ( & txid) ,
438- )
460+ let excluded =
461+ initial_block_template. exclude_mempool_txs . contains ( & txid)
462+ || suffix_txids. contains ( & txid) ;
463+ Result :: < _ , Infallible > :: Ok ( !excluded)
439464 } )
440465 . map_err ( |err| match err {
441466 either:: Either :: Left ( err) => err,
442467 } ) ?;
443468 }
444469 tracing:: debug!( "Proposing txs for inclusion in block" ) ;
445470 let coinbase_txouts_weight = {
446- let txouts_weight = match typewit:: MakeTypeWitness :: MAKE {
471+ let mut txouts_weight = Weight :: ZERO ;
472+ match typewit:: MakeTypeWitness :: MAKE {
447473 typewit:: const_marker:: BoolWit :: True ( wit) => {
448474 let wit = wit. map ( cusf_block_producer:: CoinbaseTxouts ) ;
449- wit. in_ref ( )
475+ for tx_out in wit
476+ . in_ref ( )
450477 . to_right ( & initial_block_template. coinbase_txouts )
451- . iter ( )
452- . map ( |tx_out| tx_out. weight ( ) )
453- . sum ( )
478+ {
479+ txouts_weight = txouts_weight
480+ . checked_add ( tx_out. weight ( ) )
481+ . ok_or ( BuildBlockError :: WeightOverflow ) ?;
482+ }
454483 }
455- typewit:: const_marker:: BoolWit :: False ( _) => Weight :: ZERO ,
484+ typewit:: const_marker:: BoolWit :: False ( _) => ( ) ,
456485 } ;
457486 const COINBASE_WITNESS_COMMITMENT_TXOUT_WEIGHT : Weight = {
458487 let weight_wu = Weight :: from_non_witness_data_size ( Amount :: SIZE as u64 ) . to_wu ( )
@@ -461,20 +490,39 @@ async fn block_txs<const COINBASE_TXN: bool, BP>(
461490 Weight :: from_wu ( weight_wu)
462491 } ;
463492 Weight :: from_wu (
464- txouts_weight. to_wu ( )
465- + COINBASE_WITNESS_COMMITMENT_TXOUT_WEIGHT . to_wu ( ) ,
493+ txouts_weight
494+ . to_wu ( )
495+ . checked_add ( COINBASE_WITNESS_COMMITMENT_TXOUT_WEIGHT . to_wu ( ) )
496+ . ok_or ( BuildBlockError :: WeightOverflow ) ?,
466497 )
467498 } ;
468- let prefix_txs_weight = initial_block_template
469- . prefix_txs
470- . iter ( )
471- . map ( |( tx, _) | tx. weight ( ) )
472- . sum ( ) ;
473- let initial_block_template_weight =
474- coinbase_txouts_weight + prefix_txs_weight;
475- let mempool_txs = mempool. propose_txs ( Some (
476- mempool:: MAX_USABLE_BLOCK_WEIGHT - initial_block_template_weight,
477- ) ) ?;
499+ let prefix_txs_weight = {
500+ let mut weight = Weight :: ZERO ;
501+ for ( tx, _) in & initial_block_template. prefix_txs {
502+ weight = weight
503+ . checked_add ( tx. weight ( ) )
504+ . ok_or ( BuildBlockError :: WeightOverflow ) ?;
505+ }
506+ weight
507+ } ;
508+ let suffix_txs_weight = {
509+ let mut weight = Weight :: ZERO ;
510+ for ( tx, _) in & initial_block_template. suffix_txs {
511+ weight = weight
512+ . checked_add ( tx. weight ( ) )
513+ . ok_or ( BuildBlockError :: WeightOverflow ) ?;
514+ }
515+ weight
516+ } ;
517+ let initial_block_template_weight = coinbase_txouts_weight
518+ . checked_add ( prefix_txs_weight)
519+ . and_then ( |weight| weight. checked_add ( suffix_txs_weight) )
520+ . ok_or ( BuildBlockError :: WeightOverflow ) ?;
521+ let mempool_txs = mempool. propose_txs ( Some ( Weight :: from_wu (
522+ mempool:: MAX_USABLE_BLOCK_WEIGHT
523+ . to_wu ( )
524+ . saturating_sub ( initial_block_template_weight. to_wu ( ) ) ,
525+ ) ) ) ?;
478526 {
479527 let proposed_txids: String = {
480528 use std:: fmt:: Write ;
@@ -531,6 +579,17 @@ async fn block_txs<const COINBASE_TXN: bool, BP>(
531579 typewit:: const_marker:: BoolWit :: False ( _) => ( ) ,
532580 }
533581 res_txs. extend ( mempool_txs) ;
582+ res_txs. extend ( initial_block_template. suffix_txs . iter ( ) . map (
583+ |( tx, fee) | BlockTemplateTransaction {
584+ data : bitcoin:: consensus:: serialize ( tx) ,
585+ txid : tx. compute_txid ( ) ,
586+ hash : tx. compute_wtxid ( ) ,
587+ depends : Vec :: new ( ) ,
588+ fee : ( * fee) . try_into ( ) . unwrap ( ) ,
589+ sigops : None ,
590+ weight : tx. weight ( ) . to_wu ( ) ,
591+ } ,
592+ ) ) ;
534593 res_txs. extend ( template_suffix. txs . iter ( ) . map ( |( tx, fee) | {
535594 BlockTemplateTransaction {
536595 data : bitcoin:: consensus:: serialize ( tx) ,
0 commit comments