use std::collections::HashMap;
use std::sync::{LazyLock, Mutex, MutexGuard};
use crate::handshake::HandshakeError;
use super::LinkError;
const COUNTER_NAMES: &[&str] = &[
"session_proof_invalid",
"session_proof_missing",
"exporter_unavailable",
];
static COUNTERS: LazyLock<Mutex<HashMap<&'static str, u64>>> = LazyLock::new(Mutex::default);
static FLOORS: LazyLock<Mutex<HashMap<[u8; 32], i64>>> = LazyLock::new(Mutex::default);
fn floors() -> MutexGuard<'static, HashMap<[u8; 32], i64>> {
FLOORS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub(super) fn floor(node_id: &[u8; 32]) -> i64 {
floors().get(node_id).copied().unwrap_or(0)
}
pub(super) fn completed(node_id: &[u8; 32], version: i64) {
let mut f = floors();
let held = f.entry(*node_id).or_insert(version);
*held = (*held).max(version);
}
fn counters() -> MutexGuard<'static, HashMap<&'static str, u64>> {
COUNTERS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub(super) fn count_refusal(e: &LinkError) {
let name = match e {
LinkError::Handshake(HandshakeError::SessionProofInvalid) => "session_proof_invalid",
LinkError::Handshake(HandshakeError::SessionProofMissing) => "session_proof_missing",
LinkError::Handshake(HandshakeError::ExporterUnavailable) => "exporter_unavailable",
_ => return,
};
*counters().entry(name).or_default() += 1;
}
pub fn handshake_counters() -> HashMap<&'static str, u64> {
let c = counters();
COUNTER_NAMES
.iter()
.map(|name| (*name, c.get(name).copied().unwrap_or(0)))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_floor_is_raised_by_a_completed_handshake_and_never_lowered() {
let node = [0xF1; 32];
assert_eq!(floor(&node), 0);
completed(&node, 5);
assert_eq!(floor(&node), 5);
completed(&node, 4);
assert_eq!(floor(&node), 5, "never lowered");
}
#[test]
fn the_counters_name_every_handshake_outcome() {
let counters = handshake_counters();
for name in COUNTER_NAMES {
assert!(counters.contains_key(name), "{name}");
}
let before = handshake_counters()["session_proof_invalid"];
count_refusal(&LinkError::Handshake(HandshakeError::SessionProofInvalid));
count_refusal(&LinkError::Handshake(HandshakeError::ProofInvalid));
assert!(handshake_counters()["session_proof_invalid"] > before);
}
}