tycho_consensus/models/
stats.rs1use std::ops::RangeInclusive;
2
3#[derive(Debug, thiserror::Error)]
4pub enum MempoolStatsMergeError {
5 #[error("stat range {:?} cannot include new out-of-order round {}", .0, .1)]
6 RoundOutOfOrder(RangeInclusive<u32>, u32),
7 #[error("stat range {:?} overlaps with {:?}", .0, .1)]
8 OverlappingRanges(RangeInclusive<u32>, RangeInclusive<u32>),
9}
10
11#[derive(Debug)]
12pub struct MempoolPeerStats {
13 first_round: u32,
14 filled_rounds: u32,
15 cumulative: MempoolPeerCounters,
16}
17
18impl MempoolPeerStats {
19 pub fn new(round: u32) -> Self {
20 Self {
21 first_round: round, filled_rounds: 0,
23 cumulative: MempoolPeerCounters::default(), }
25 }
26
27 fn range(&self) -> RangeInclusive<u32> {
28 self.first_round..=self.cumulative.last_round
29 }
30
31 pub fn filled_rounds(&self) -> u32 {
32 self.filled_rounds
33 }
34
35 pub fn counters(&self) -> Option<&MempoolPeerCounters> {
36 (!self.range().is_empty()).then_some(&self.cumulative)
37 }
38
39 pub fn add_in_order(
40 &mut self,
41 stats: &MempoolPeerCounters,
42 ) -> Result<(), MempoolStatsMergeError> {
43 let old_range = self.range();
44 if stats.last_round < *old_range.start() || old_range.contains(&stats.last_round) {
45 return Err(MempoolStatsMergeError::RoundOutOfOrder(
46 old_range,
47 stats.last_round,
48 ));
49 }
50 self.filled_rounds += 1;
51 self.cumulative.merge(stats);
52 Ok(())
53 }
54
55 pub fn add_references_skipped(&mut self, value: u32) {
57 self.cumulative.references_skipped += value;
58 }
59
60 pub fn merge_with(&mut self, other: &Self) -> Result<(), MempoolStatsMergeError> {
61 let stat_overlap = if self.first_round < other.first_round {
62 self.range().contains(&other.first_round)
63 } else {
64 other.range().contains(&self.first_round)
65 };
66 if stat_overlap {
67 return Err(MempoolStatsMergeError::OverlappingRanges(
68 self.range(),
69 other.range(),
70 ));
71 }
72 self.first_round = self.first_round.min(other.first_round);
73 self.filled_rounds += other.filled_rounds;
74 self.cumulative.merge(&other.cumulative);
75 Ok(())
76 }
77}
78
79#[derive(Debug, Default)]
80pub struct MempoolPeerCounters {
81 pub last_round: u32,
82 pub was_leader: u32,
83 pub was_not_leader: u32,
84 pub skipped_rounds: u32,
85 pub valid_points: u32,
86 pub equivocated: u32,
87 pub invalid_points: u32,
88 pub ill_formed_points: u32,
89 pub references_skipped: u32,
90}
91
92impl MempoolPeerCounters {
93 fn merge(&mut self, other: &Self) {
94 self.last_round = self.last_round.max(other.last_round);
95 self.was_leader += other.was_leader;
96 self.was_not_leader += other.was_not_leader;
97 self.skipped_rounds += other.skipped_rounds;
98 self.valid_points += other.valid_points;
99 self.equivocated += other.equivocated;
100 self.invalid_points += other.invalid_points;
101 self.ill_formed_points += other.ill_formed_points;
102 self.references_skipped += other.references_skipped;
103 }
104}