@@ -2,6 +2,8 @@ use std::collections::VecDeque;
22use std:: sync:: Arc ;
33
44use tycho_network:: PeerId ;
5+ use tycho_slasher_traits:: { AnchorPeerStats , AnchorStats , AnchorStatsRange , CollatedAnchorStats } ;
6+ use tycho_util:: FastHashMap ;
57
68use crate :: collator:: messages_reader:: state:: ext:: ExternalKey ;
79use crate :: mempool:: { MempoolAnchor , MempoolAnchorId } ;
@@ -15,6 +17,7 @@ pub struct AnchorInfo {
1517 #[ allow( dead_code) ]
1618 pub our_exts_count : usize ,
1719 pub author : PeerId ,
20+ pub stats : AnchorStats ,
1821}
1922
2023impl AnchorInfo {
@@ -25,6 +28,7 @@ impl AnchorInfo {
2528 all_exts_count : anchor. externals . len ( ) ,
2629 our_exts_count,
2730 author : anchor. author ,
31+ stats : anchor. stats . clone ( ) ,
2832 }
2933 }
3034}
@@ -311,6 +315,62 @@ impl<'a> AnchorsCacheTransaction<'a> {
311315 self . cache . last_imported_anchor_info ( )
312316 }
313317
318+ pub fn collect_imported_anchor_stats (
319+ & self ,
320+ prev_imported_chain_time : u64 ,
321+ next_chain_time : u64 ,
322+ ) -> Option < CollatedAnchorStats > {
323+ fn merge_anchor_stats ( acc : & mut FastHashMap < u16 , AnchorPeerStats > , stats : & AnchorStats ) {
324+ for ( validator_idx, peer_stats) in stats. 0 . iter ( ) {
325+ acc. entry ( * validator_idx)
326+ . and_modify ( |exists| {
327+ exists. points_proven =
328+ ( exists. points_proven ) . saturating_add ( peer_stats. points_proven ) ;
329+ } )
330+ . or_insert ( AnchorPeerStats {
331+ points_proven : peer_stats. points_proven ,
332+ } ) ;
333+ }
334+ }
335+
336+ if prev_imported_chain_time >= next_chain_time {
337+ return None ;
338+ }
339+
340+ let mut prev_top_anchor = None ;
341+ let mut top_anchor = None ;
342+ let mut acc = None ;
343+
344+ for info in & self . cache . imported_anchors_info_history {
345+ if info. ct <= prev_imported_chain_time {
346+ prev_top_anchor = Some ( info. id ) ;
347+ continue ;
348+ }
349+ if info. ct > next_chain_time {
350+ break ;
351+ }
352+
353+ top_anchor = Some ( info. id ) ;
354+
355+ match & mut acc {
356+ None => acc = Some ( info. stats . 0 . as_ref ( ) . clone ( ) ) ,
357+ Some ( acc) => merge_anchor_stats ( acc, & info. stats ) ,
358+ } ;
359+ }
360+
361+ if prev_top_anchor >= top_anchor {
362+ return None ;
363+ }
364+
365+ Some ( CollatedAnchorStats {
366+ range : AnchorStatsRange {
367+ prev_top_anchor : prev_top_anchor?,
368+ top_anchor : top_anchor?,
369+ } ,
370+ stats : AnchorStats ( Arc :: new ( acc?) ) ,
371+ } )
372+ }
373+
314374 pub fn get ( & self , index : usize ) -> Option < ( MempoolAnchorId , Arc < MempoolAnchor > ) > {
315375 self . cache . get ( index)
316376 }
@@ -334,16 +394,36 @@ impl Drop for AnchorsCacheTransaction<'_> {
334394
335395#[ cfg( test) ]
336396mod tests {
397+ use tycho_slasher_traits:: AnchorPeerStats ;
398+
337399 use super :: * ;
338400
339401 fn make_anchor ( id : MempoolAnchorId , chain_time : u64 ) -> Arc < MempoolAnchor > {
402+ make_anchor_with_stats ( id, chain_time, & [ ] )
403+ }
404+
405+ fn make_anchor_with_stats (
406+ id : MempoolAnchorId ,
407+ chain_time : u64 ,
408+ validator_idx_stats : & [ ( u16 , u16 ) ] ,
409+ ) -> Arc < MempoolAnchor > {
340410 Arc :: new ( MempoolAnchor {
341411 id,
342412 prev_id : if id > 0 { Some ( id - 1 ) } else { None } ,
343413 chain_time,
344414 author : PeerId ( [ 0 ; 32 ] ) ,
345415 externals : Default :: default ( ) ,
346- stats : Default :: default ( ) ,
416+ stats : AnchorStats ( Arc :: new (
417+ validator_idx_stats
418+ . iter ( )
419+ . map ( |( validator_idx, points_proven) | {
420+ let stats = AnchorPeerStats {
421+ points_proven : * points_proven,
422+ } ;
423+ ( * validator_idx, stats)
424+ } )
425+ . collect ( ) ,
426+ ) ) ,
347427 } )
348428 }
349429
@@ -366,6 +446,28 @@ mod tests {
366446 assert_eq ! ( cache. get( 0 ) . unwrap( ) . 0 , 2 ) ;
367447 }
368448
449+ #[ test]
450+ fn test_collect_imported_anchor_stats_uses_trimmed_interval ( ) {
451+ let mut cache = AnchorsCache :: default ( ) ;
452+ cache. add ( make_anchor_with_stats ( 10 , 100 , & [ ( 1 , 1 ) ] ) , 0 ) ;
453+ cache. add ( make_anchor_with_stats ( 11 , 200 , & [ ( 1 , 2 ) , ( 2 , 3 ) ] ) , 0 ) ;
454+ cache. add ( make_anchor_with_stats ( 12 , 300 , & [ ( 1 , 4 ) ] ) , 0 ) ;
455+ cache. add ( make_anchor_with_stats ( 13 , 400 , & [ ( 2 , 5 ) ] ) , 0 ) ;
456+
457+ let mut tx = AnchorsCacheTransaction :: new ( & mut cache) ;
458+ tx. remove_last_imported_above ( 320 ) ;
459+
460+ let collated_stats = tx. collect_imported_anchor_stats ( 100 , 320 ) . unwrap ( ) ;
461+
462+ assert_eq ! ( collated_stats. range, AnchorStatsRange {
463+ prev_top_anchor: 10 ,
464+ top_anchor: 12 ,
465+ } ) ;
466+ assert_eq ! ( collated_stats. stats. 0 . len( ) , 2 ) ;
467+ assert_eq ! ( collated_stats. stats. 0 . get( & 1 ) . unwrap( ) . points_proven, 6 ) ;
468+ assert_eq ! ( collated_stats. stats. 0 . get( & 2 ) . unwrap( ) . points_proven, 3 ) ;
469+ }
470+
369471 #[ test]
370472 fn test_pop_front_rollback ( ) {
371473 let mut cache = AnchorsCache :: default ( ) ;
0 commit comments