1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
use std::{
    sync::atomic::{AtomicU64, Ordering},
    time::{Duration, Instant},
};

use mempool::tx::Priority;

mod block_template;
pub(crate) mod cache;
pub mod errors;
pub mod manager;
mod manager_tests;
pub mod mempool;
pub mod model;
pub mod monitor;

#[cfg(test)]
pub mod testutils;

pub struct MiningCounters {
    pub creation_time: Instant,

    // Counters
    pub high_priority_tx_counts: AtomicU64,
    pub low_priority_tx_counts: AtomicU64,
    pub block_tx_counts: AtomicU64,
    pub tx_accepted_counts: AtomicU64,
    pub input_counts: AtomicU64,
    pub output_counts: AtomicU64,

    // Samples
    pub ready_txs_sample: AtomicU64,
    pub txs_sample: AtomicU64,
    pub orphans_sample: AtomicU64,
    pub accepted_sample: AtomicU64,
}

impl Default for MiningCounters {
    fn default() -> Self {
        Self {
            creation_time: Instant::now(),
            high_priority_tx_counts: Default::default(),
            low_priority_tx_counts: Default::default(),
            block_tx_counts: Default::default(),
            tx_accepted_counts: Default::default(),
            input_counts: Default::default(),
            output_counts: Default::default(),
            ready_txs_sample: Default::default(),
            txs_sample: Default::default(),
            orphans_sample: Default::default(),
            accepted_sample: Default::default(),
        }
    }
}

impl MiningCounters {
    pub fn snapshot(&self) -> MempoolCountersSnapshot {
        MempoolCountersSnapshot {
            elapsed_time: (Instant::now() - self.creation_time),
            high_priority_tx_counts: self.high_priority_tx_counts.load(Ordering::Relaxed),
            low_priority_tx_counts: self.low_priority_tx_counts.load(Ordering::Relaxed),
            block_tx_counts: self.block_tx_counts.load(Ordering::Relaxed),
            tx_accepted_counts: self.tx_accepted_counts.load(Ordering::Relaxed),
            input_counts: self.input_counts.load(Ordering::Relaxed),
            output_counts: self.output_counts.load(Ordering::Relaxed),
            ready_txs_sample: self.ready_txs_sample.load(Ordering::Relaxed),
            txs_sample: self.txs_sample.load(Ordering::Relaxed),
            orphans_sample: self.orphans_sample.load(Ordering::Relaxed),
            accepted_sample: self.accepted_sample.load(Ordering::Relaxed),
        }
    }

    pub fn p2p_tx_count_sample(&self) -> P2pTxCountSample {
        P2pTxCountSample {
            elapsed_time: (Instant::now() - self.creation_time),
            low_priority_tx_counts: self.low_priority_tx_counts.load(Ordering::Relaxed),
        }
    }

    pub fn increase_tx_counts(&self, value: u64, priority: Priority) {
        match priority {
            Priority::Low => {
                self.low_priority_tx_counts.fetch_add(value, Ordering::Relaxed);
            }
            Priority::High => {
                self.high_priority_tx_counts.fetch_add(value, Ordering::Relaxed);
            }
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct MempoolCountersSnapshot {
    pub elapsed_time: Duration,
    pub high_priority_tx_counts: u64,
    pub low_priority_tx_counts: u64,
    pub block_tx_counts: u64,
    pub tx_accepted_counts: u64,
    pub input_counts: u64,
    pub output_counts: u64,
    pub ready_txs_sample: u64,
    pub txs_sample: u64,
    pub orphans_sample: u64,
    pub accepted_sample: u64,
}

impl MempoolCountersSnapshot {
    pub fn in_tx_counts(&self) -> u64 {
        self.high_priority_tx_counts + self.low_priority_tx_counts
    }

    /// Indicates whether this snapshot has any TPS activity which is worth logging
    pub fn has_tps_activity(&self) -> bool {
        self.tx_accepted_counts > 0 || self.block_tx_counts > 0 || self.low_priority_tx_counts > 0 || self.high_priority_tx_counts > 0
    }

    /// Returns an estimate of _Unique-TPS_, i.e. the number of unique transactions per second on average
    /// (excluding coinbase transactions)
    pub fn u_tps(&self) -> f64 {
        let elapsed = self.elapsed_time.as_secs_f64();
        if elapsed != 0f64 {
            self.tx_accepted_counts as f64 / elapsed
        } else {
            0f64
        }
    }

    /// Returns an estimate to the _Effective-TPS_ fraction which is a measure of how much of DAG capacity
    /// is utilized compared to the number of available mempool transactions. For instance a max
    /// value of `1.0` indicates that we cannot do any better in terms of throughput vs. current
    /// demand. A value close to `0.0` means that DAG capacity is mostly filled with duplicate
    /// transactions even though the mempool (demand) offers a much larger amount of unique transactions.   
    pub fn e_tps(&self) -> f64 {
        let accepted_txs = u64::min(self.ready_txs_sample, self.tx_accepted_counts); // The throughput
        let total_txs = u64::min(self.ready_txs_sample, self.block_tx_counts); // The min of demand and capacity
        if total_txs > 0 {
            accepted_txs as f64 / total_txs as f64
        } else {
            1f64 // No demand means we are 100% efficient
        }
    }
}

impl core::ops::Sub for &MempoolCountersSnapshot {
    type Output = MempoolCountersSnapshot;

    fn sub(self, rhs: Self) -> Self::Output {
        Self::Output {
            elapsed_time: self.elapsed_time.checked_sub(rhs.elapsed_time).unwrap_or_default(),
            high_priority_tx_counts: self.high_priority_tx_counts.checked_sub(rhs.high_priority_tx_counts).unwrap_or_default(),
            low_priority_tx_counts: self.low_priority_tx_counts.checked_sub(rhs.low_priority_tx_counts).unwrap_or_default(),
            block_tx_counts: self.block_tx_counts.checked_sub(rhs.block_tx_counts).unwrap_or_default(),
            tx_accepted_counts: self.tx_accepted_counts.checked_sub(rhs.tx_accepted_counts).unwrap_or_default(),
            input_counts: self.input_counts.checked_sub(rhs.input_counts).unwrap_or_default(),
            output_counts: self.output_counts.checked_sub(rhs.output_counts).unwrap_or_default(),
            ready_txs_sample: (self.ready_txs_sample + rhs.ready_txs_sample) / 2,
            txs_sample: (self.txs_sample + rhs.txs_sample) / 2,
            orphans_sample: (self.orphans_sample + rhs.orphans_sample) / 2,
            accepted_sample: (self.accepted_sample + rhs.accepted_sample) / 2,
        }
    }
}

/// Contains a snapshot of only the P2P transaction counter and time elapsed
pub struct P2pTxCountSample {
    pub elapsed_time: Duration,
    pub low_priority_tx_counts: u64,
}

impl core::ops::Sub for &P2pTxCountSample {
    type Output = P2pTxCountSample;

    fn sub(self, rhs: Self) -> Self::Output {
        Self::Output {
            elapsed_time: self.elapsed_time.checked_sub(rhs.elapsed_time).unwrap_or_default(),
            low_priority_tx_counts: self.low_priority_tx_counts.checked_sub(rhs.low_priority_tx_counts).unwrap_or_default(),
        }
    }
}