summaryrefslogtreecommitdiffstats
path: root/src/transport/stats.rs
blob: 398ffdfc540d4ebc4c0eb030cdae8ad02884e516 (plain)
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
180
181
182
183
184
185
186
187
188
189
//! Figures about the transports themselves (`Transport::stats`), for /admin/stats
//! (`transports`, `docs/measurements.md`): how they find and reach peers, besides the
//! messages counted per peer (`traffic`).

use std::collections::BTreeMap;

use serde::Serialize;

#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TransportStats {
    /// The identity layer's (`transport::identity`); absent without one.
    pub(crate) identity: Option<IdentityStats>,
    /// The paths layer's (`transport::paths`); absent without one.
    pub(crate) paths: Option<PathsStats>,
    /// The libp2p transport's; absent if it isn't one of this node's.
    pub(crate) libp2p: Option<Libp2pStats>,
}

impl TransportStats {
    /// Takes what `other` has.
    pub(crate) fn merge(&mut self, other: TransportStats) {
        if other.identity.is_some() {
            self.identity = other.identity;
        }
        if other.paths.is_some() {
            self.paths = other.paths;
        }
        if other.libp2p.is_some() {
            self.libp2p = other.libp2p;
        }
    }
}

/// How the paths to peers with several addresses do (`transport::paths`).
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PathsStats {
    /// Every path measured: those of peers this node chose among lately, and those its
    /// keepalives went over.
    pub(crate) paths: Vec<PathInfo>,
    #[serde(flatten)]
    pub(crate) counts: PathCounts,
}

/// One path, as `transport::paths` measures it.
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PathInfo {
    /// `<scheme>:<where it goes>`: where the transport says the path goes now
    /// (`Transport::location`; libp2p: the remote address of its connection, ending in
    /// the peer ID), else whom it goes to, `<scheme>:<transport identity>` (a libp2p
    /// peer ID without a connection, a Mycelium public key, an onion service).
    pub(crate) address: String,
    /// Smoothed round-trip time of its PINGs (ours, and our keepalives); absent before
    /// an answer.
    pub(crate) rtt_ms: Option<f64>,
    /// Its PINGs went unanswered three times in a row, or a send there failed within a
    /// minute: used only when no other path works.
    pub(crate) down: bool,
}

/// Counts since start.
#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PathCounts {
    pub(crate) probes_sent: u64,
    pub(crate) probes_answered: u64,
    pub(crate) probes_unanswered: u64,
    /// Sends that failed on a path (each tried at the next).
    pub(crate) send_failures: u64,
    /// Sends that went over another path after one failed.
    pub(crate) failovers: u64,
    /// Changes of the path in use for one faster.
    pub(crate) switches: u64,
}

/// Who is where (`transport::identity`): the books now, and counts since start.
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct IdentityStats {
    /// Nodes proven at an address, by transport (scheme); a node on several counts on
    /// each.
    pub(crate) proven: BTreeMap<String, usize>,
    /// Nodes known only from hints (the member list, HELLO_REPLYs), proven nowhere.
    pub(crate) hinted: usize,
    #[serde(flatten)]
    pub(crate) counts: IdentityCounts,
}

#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct IdentityCounts {
    /// Proofs that didn't hold (a bad signature, or made for other addresses): normally
    /// none.
    pub(crate) proofs_rejected: u64,
    /// Nodes that proved themselves at another address on a transport than before: a
    /// node restarted with a new transport identity (a new libp2p key or Mycelium key).
    pub(crate) address_changes: u64,
    /// Messages dropped because no node proved itself at their address: some after a
    /// restart are normal, a steady stream is not.
    pub(crate) dropped_unproven: u64,
    /// Sends refused because the peer hadn't proven where it is yet (`NotProven`).
    pub(crate) refused_unproven: u64,
    /// Seeds no node answered at in time.
    pub(crate) seeds_unanswered: u64,
}

/// Whether others can dial this node, as AutoNAT found out.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) enum Reachability {
    #[default]
    Unknown,
    /// Others can't dial it: it needs a relay.
    Private,
    Public,
}

impl Reachability {
    pub(crate) const ALL: [Reachability; 3] = [Self::Unknown, Self::Private, Self::Public];

    pub(crate) fn as_str(self) -> &'static str {
        match self {
            Self::Unknown => "unknown",
            Self::Private => "private",
            Self::Public => "public",
        }
    }
}

#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Libp2pStats {
    pub(crate) reachability: Reachability,
    /// The address AutoNAT confirmed others can dial, while public.
    pub(crate) public_address: Option<String>,
    /// Open connections, now.
    pub(crate) connections: Connections,
    pub(crate) relay_slots: RelaySlots,
    /// Direct connections tried over a relayed one (DCUtR), since start.
    pub(crate) hole_punches: HolePunches,
    /// Connections that couldn't be made, by cause, since start.
    pub(crate) dial_failures: DialFailures,
}

#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Connections {
    pub(crate) direct: usize,
    /// Through a relay (a circuit).
    pub(crate) relayed: usize,
}

/// Slots this node holds on relays, to be reachable through them.
#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct RelaySlots {
    /// Held now.
    pub(crate) held: usize,
    /// Relays picked that never granted a slot, not picked again for a while now.
    pub(crate) paused: usize,
    /// Since start: slots granted (not counting renewals), lost after being granted, and
    /// asked for but never granted (a relay unreachable, or another peer at its
    /// address: a relay restarted without its libp2p key).
    pub(crate) granted: u64,
    pub(crate) lost: u64,
    pub(crate) not_granted: u64,
}

#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct HolePunches {
    pub(crate) succeeded: u64,
    /// The relay carries on.
    pub(crate) failed: u64,
}

#[derive(Debug, Clone, Copy, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DialFailures {
    /// No address of the peer could be reached (refused, timed out, no route).
    pub(crate) unreachable: u64,
    /// Another peer answered at the address: one restarted without its libp2p key.
    pub(crate) wrong_peer_id: u64,
    /// No address known for the peer.
    pub(crate) no_addresses: u64,
    pub(crate) other: u64,
}