@@ -77,7 +77,11 @@ use kaspa_consensus_notify::{
7777 root:: ConsensusNotificationRoot ,
7878} ;
7979use kaspa_consensusmanager:: SessionLock ;
80- use kaspa_core:: { debug, info, time:: unix_now, trace, warn} ;
80+ use kaspa_core:: {
81+ debug, info,
82+ time:: { unix_now, Stopwatch } ,
83+ trace, warn,
84+ } ;
8185use kaspa_database:: prelude:: { StoreError , StoreResultEmptyTuple , StoreResultExtensions } ;
8286use kaspa_hashes:: { Hash , ZERO_HASH } ;
8387use kaspa_muhash:: MuHash ;
@@ -106,6 +110,10 @@ use std::{
106110 sync:: { atomic:: Ordering , Arc } ,
107111} ;
108112
113+ // 100ms - since at 10BPS, average block time is 100ms and so must expect
114+ // the block template to build faster than that
115+ pub const BUILD_BLOCK_TEMPLATE_SPEED_THRESHOLD : u128 = 100 ;
116+
109117pub struct VirtualStateProcessor {
110118 // Channels
111119 receiver : CrossbeamReceiver < VirtualStateProcessingMessage > ,
@@ -715,7 +723,10 @@ impl VirtualStateProcessor {
715723 let max_candidates = self . max_virtual_parent_candidates ( max_block_parents) ;
716724
717725 // Prioritize half the blocks with highest blue work and pick the rest randomly to ensure diversity between nodes
718- if candidates. len ( ) > max_candidates {
726+ if self . mining_rules . blue_only_mergeset . load ( Ordering :: Relaxed ) {
727+ // pick 100% of the top blue work blocks
728+ candidates. truncate ( max_candidates) ;
729+ } else if candidates. len ( ) > max_candidates {
719730 // make_contiguous should be a no op since the deque was just built
720731 let slice = candidates. make_contiguous ( ) ;
721732
@@ -804,25 +815,32 @@ impl VirtualStateProcessor {
804815 let mut ghostdag_data = self . ghostdag_manager . ghostdag ( & virtual_parents) ;
805816 let merge_depth_root = self . depth_manager . calc_merge_depth_root ( & ghostdag_data, current_pruning_point) ;
806817 let mut kosherizing_blues: Option < Vec < Hash > > = None ;
807- let mut bad_reds = Vec :: new ( ) ;
818+ let bad_reds = if self . mining_rules . blue_only_mergeset . load ( Ordering :: Relaxed ) {
819+ // Treat all reds as bad reds when this rule is triggered
820+ ghostdag_data. mergeset_reds . as_ref ( ) . to_vec ( )
821+ } else {
822+ let mut inner_bad_reds = Vec :: new ( ) ;
808823
809- //
810- // Note that the code below optimizes for the usual case where there are no merge-bound-violating blocks.
811- //
824+ //
825+ // Note that the code below optimizes for the usual case where there are no merge-bound-violating blocks.
826+ //
812827
813- // Find red blocks violating the merge bound and which are not kosherized by any blue
814- for red in ghostdag_data. mergeset_reds . iter ( ) . copied ( ) {
815- if self . reachability_service . is_dag_ancestor_of ( merge_depth_root, red) {
816- continue ;
817- }
818- // Lazy load the kosherizing blocks since this case is extremely rare
819- if kosherizing_blues. is_none ( ) {
820- kosherizing_blues = Some ( self . depth_manager . kosherizing_blues ( & ghostdag_data, merge_depth_root) . collect ( ) ) ;
821- }
822- if !self . reachability_service . is_dag_ancestor_of_any ( red, & mut kosherizing_blues. as_ref ( ) . unwrap ( ) . iter ( ) . copied ( ) ) {
823- bad_reds. push ( red) ;
828+ // Find red blocks violating the merge bound and which are not kosherized by any blue
829+ for red in ghostdag_data. mergeset_reds . iter ( ) . copied ( ) {
830+ if self . reachability_service . is_dag_ancestor_of ( merge_depth_root, red) {
831+ continue ;
832+ }
833+ // Lazy load the kosherizing blocks since this case is extremely rare
834+ if kosherizing_blues. is_none ( ) {
835+ kosherizing_blues = Some ( self . depth_manager . kosherizing_blues ( & ghostdag_data, merge_depth_root) . collect ( ) ) ;
836+ }
837+ if !self . reachability_service . is_dag_ancestor_of_any ( red, & mut kosherizing_blues. as_ref ( ) . unwrap ( ) . iter ( ) . copied ( ) ) {
838+ inner_bad_reds. push ( red) ;
839+ }
824840 }
825- }
841+
842+ inner_bad_reds
843+ } ;
826844
827845 if !bad_reds. is_empty ( ) {
828846 // Remove all parents which lead to merging a bad red
@@ -1053,6 +1071,7 @@ impl VirtualStateProcessor {
10531071 mut txs : Vec < Transaction > ,
10541072 calculated_fees : Vec < u64 > ,
10551073 ) -> Result < BlockTemplate , RuleError > {
1074+ let swo = Stopwatch :: new ( "virtual_processor.resolve_virtual" ) ;
10561075 // [`calc_block_parents`] can use deep blocks below the pruning point for this calculation, so we
10571076 // need to hold the pruning lock.
10581077 let _prune_guard = self . pruning_lock . blocking_read ( ) ;
@@ -1102,6 +1121,13 @@ impl VirtualStateProcessor {
11021121 let selected_parent_hash = virtual_state. ghostdag_data . selected_parent ;
11031122 let selected_parent_timestamp = self . headers_store . get_timestamp ( selected_parent_hash) . unwrap ( ) ;
11041123 let selected_parent_daa_score = self . headers_store . get_daa_score ( selected_parent_hash) . unwrap ( ) ;
1124+
1125+ if swo. elapsed ( ) . as_millis ( ) <= BUILD_BLOCK_TEMPLATE_SPEED_THRESHOLD {
1126+ self . counters . build_block_template_within_threshold . fetch_add ( 1 , Ordering :: SeqCst ) ;
1127+ } else {
1128+ self . counters . build_block_template_above_threshold . fetch_add ( 1 , Ordering :: SeqCst ) ;
1129+ }
1130+
11051131 Ok ( BlockTemplate :: new (
11061132 MutableBlock :: new ( header, txs) ,
11071133 miner_data,
0 commit comments