summaryrefslogtreecommitdiffstats
path: root/src/transport/identity.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-27 05:02:39 +0100
committertv <tv@krebsco.de>2026-10-27 05:03:29 +0100
commitb5f756c3f623c41830aa6129a0fd63bd3f99a7ba (patch)
tree9faeb383fc899bf499749e843d4d4fb65b08adfd /src/transport/identity.rs
parent16d7da71c49b62dd975957e5694088b8b084f244 (diff)
add transport stats
Diffstat (limited to 'src/transport/identity.rs')
-rw-r--r--src/transport/identity.rs77
1 files changed, 72 insertions, 5 deletions
diff --git a/src/transport/identity.rs b/src/transport/identity.rs
index af0fe74..17c7795 100644
--- a/src/transport/identity.rs
+++ b/src/transport/identity.rs
@@ -38,9 +38,11 @@ use tracing::{debug, info};
use crate::identity::{self, NodeKey};
use crate::locks::MutexExt;
+use crate::transport::stats::{IdentityCounts, IdentityStats, TransportStats};
use crate::transport::traffic::Traffic;
use crate::transport::{
- EncodedMessage, HandlerFn, IdentityProof, MessageHandler, MessageKind, ParsedMessage, Transport,
+ Destination, EncodedMessage, HandlerFn, IdentityProof, MessageHandler, MessageKind,
+ ParsedMessage, Transport,
};
pub(crate) const WHO_LABEL: &[u8] = b"continuum/who/1";
@@ -132,6 +134,8 @@ struct Book {
asked: HashMap<String, Instant>,
/// Seeds being resolved: who waits for a ME from where.
waiting: HashMap<String, Vec<oneshot::Sender<String>>>,
+ /// Since start (`stats`).
+ counts: IdentityCounts,
}
/// The scheme of `address`.
@@ -151,6 +155,7 @@ impl Book {
if *slot != address {
let previous = std::mem::replace(slot, address.to_owned());
self.at.remove(&previous);
+ self.counts.address_changes += 1;
}
} else {
addresses.push(address.to_owned());
@@ -185,6 +190,25 @@ impl Book {
}
}
+ /// What the books hold now, and the counts.
+ fn stats(&self) -> IdentityStats {
+ let mut proven = std::collections::BTreeMap::new();
+ for addresses in self.proven.values() {
+ for address in addresses {
+ *proven.entry(scheme_of(address).to_owned()).or_insert(0) += 1;
+ }
+ }
+ IdentityStats {
+ proven,
+ hinted: self
+ .hints
+ .keys()
+ .filter(|node| !self.proven.contains_key(*node))
+ .count(),
+ counts: self.counts,
+ }
+ }
+
/// Whether to ask `address` WHO now (always, if `force`), noting that it was asked.
fn ask(&mut self, address: &str, now: Instant, force: bool) -> bool {
if !force
@@ -294,6 +318,7 @@ impl Shared {
) -> Option<String> {
let message = signed(label, address, &link.own_address());
if !identity::verify(&proof.key, &message, &proof.signature) {
+ self.lock().counts.proofs_rejected += 1;
debug!(from = %address, "Ignoring an identity proof that doesn't hold");
return None;
}
@@ -379,6 +404,7 @@ impl Shared {
match node {
Some(node) => handler(node, message, payload_len).await,
None => {
+ self.lock().counts.dropped_unproven += 1;
debug!(
from = %address,
kind = ?message.kind(),
@@ -512,6 +538,38 @@ impl Transport for IdentityTransport {
.collect()
}
+ /// The address `encode` picks, and the transport's connection there.
+ fn destination(&self, peer: &str) -> Option<Destination> {
+ let address = self.shared.destination(peer)?;
+ let proven = self
+ .shared
+ .lock()
+ .proven
+ .get(peer)
+ .is_some_and(|addresses| addresses.contains(&address));
+ let connection = self
+ .shared
+ .split(&address)
+ .and_then(|(link, target)| link.inner.connection(&target));
+ Some(Destination {
+ address,
+ proven,
+ connection,
+ })
+ }
+
+ /// The identity layer's, and each transport's.
+ fn stats(&self) -> TransportStats {
+ let mut stats = TransportStats {
+ identity: Some(self.shared.lock().stats()),
+ ..TransportStats::default()
+ };
+ for link in &self.shared.links {
+ stats.merge(link.inner.stats());
+ }
+ stats
+ }
+
/// Each transport's, with its scheme.
fn discovered_seeds(&self) -> Vec<String> {
self.shared
@@ -594,9 +652,12 @@ impl Transport for IdentityTransport {
info!(seed = %seed, node = %node, "Seed is node");
Ok(node)
}
- _ => Err(anyhow!(
- "no node answered at {address} within {RESOLVE_TIMEOUT:?}"
- )),
+ _ => {
+ self.shared.lock().counts.seeds_unanswered += 1;
+ Err(anyhow!(
+ "no node answered at {address} within {RESOLVE_TIMEOUT:?}"
+ ))
+ }
}
}
@@ -617,7 +678,7 @@ impl Transport for IdentityTransport {
async fn send_encoded(&self, message: &EncodedMessage) -> Result<()> {
let hints = {
- let book = self.shared.lock();
+ let mut book = self.shared.lock();
let proven = book
.proven
.get(&message.peer)
@@ -625,6 +686,7 @@ impl Transport for IdentityTransport {
if proven {
None
} else {
+ book.counts.refused_unproven += 1;
Some(book.hints.get(&message.peer).cloned().unwrap_or_default())
}
};
@@ -721,6 +783,11 @@ mod tests {
assert!(book.bind("s:y", "n2", now));
assert_eq!(book.proven["n1"], ["t:x"]);
assert_eq!(book.at["s:y"].0, "n2");
+ // Only n1's move from s:x to s:y changed an address.
+ assert_eq!(book.counts.address_changes, 1);
+ let stats = book.stats();
+ assert_eq!(stats.proven["s"], 1);
+ assert_eq!(stats.proven["t"], 1);
}
#[test]