use std::collections::HashMap;
use std::sync::{Mutex, RwLock};
use std::time::{Duration, Instant};
use alloy::primitives::{Address, U256};
use indexmap::IndexMap;
use tracing::warn;
use crate::dht::routing::NodeId;
use crate::dht::staker_set::StakerSet;
struct Entry {
operators: Vec<Address>,
expiry: Instant,
}
struct Inner {
entries: IndexMap<U256, Entry>,
cap: usize,
positive_ttl: Duration,
negative_ttl: Duration,
}
pub(crate) struct LazyOriginCache {
inner: Mutex<Inner>,
}
impl LazyOriginCache {
#[must_use]
pub(crate) fn new(cap: usize, positive_ttl: Duration, negative_ttl: Duration) -> Self {
let cap = cap.max(1);
Self {
inner: Mutex::new(Inner {
entries: IndexMap::with_capacity(cap),
cap,
positive_ttl,
negative_ttl,
}),
}
}
#[must_use]
pub(crate) fn get(&self, ns: &U256) -> Option<Vec<Address>> {
let now = Instant::now();
let mut guard = self.lock();
let entry = guard.entries.shift_remove(ns)?;
if entry.expiry <= now {
return None;
}
let operators = entry.operators.clone();
guard.entries.shift_insert(0, *ns, entry);
Some(operators)
}
pub(crate) fn insert(&self, ns: U256, operators: Vec<Address>) {
let mut guard = self.lock();
let ttl = if operators.is_empty() {
guard.negative_ttl
} else {
guard.positive_ttl
};
let expiry = Instant::now() + ttl;
let entry = Entry { operators, expiry };
guard.entries.shift_insert(0, ns, entry);
if guard.entries.len() > guard.cap {
guard.entries.pop();
}
}
#[must_use]
pub(crate) fn len(&self) -> usize {
self.lock().entries.len()
}
fn lock(&self) -> std::sync::MutexGuard<'_, Inner> {
match self.inner.lock() {
Ok(g) => g,
Err(poisoned) => {
warn!("LazyOriginCache mutex poisoned; recovering inner state");
poisoned.into_inner()
}
}
}
}
pub(crate) fn resolve_active(
operators: &[Address],
operator_to_node: &RwLock<HashMap<Address, NodeId>>,
staker_set: &dyn StakerSet,
) -> Vec<NodeId> {
let mut nodes: Vec<NodeId> = crate::dht::chain_projection::with_read(
operator_to_node,
"chain operator reverse map",
|rev| {
operators
.iter()
.filter_map(|op| rev.get(op).copied())
.filter(|nid| staker_set.is_active(nid))
.collect()
},
);
nodes.sort_unstable();
nodes.dedup();
nodes
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)]
mod tests;