use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
use parking_lot::Mutex;
use super::org::OrgId;
use crate::adapter::net::identity::EntityId;
pub const DEFAULT_MAX_REPLAY_ENTRIES: usize = 65_536;
pub const DEFAULT_MAX_REPLAY_ENTRIES_PER_CALLER: usize = 4_096;
pub const DEFAULT_OWNER_RESERVED_REPLAY_ENTRIES: usize = 16_384;
pub const DEFAULT_MAX_REPLAY_ENTRIES_PER_EXTERNAL_ORG: usize = 4_096;
#[derive(Debug, Clone, Copy)]
pub struct AdmissionReplayConfig {
pub max_entries: usize,
pub max_entries_per_caller: usize,
pub owner_reserved_entries: usize,
pub max_entries_per_external_org: usize,
}
impl Default for AdmissionReplayConfig {
fn default() -> Self {
Self {
max_entries: DEFAULT_MAX_REPLAY_ENTRIES,
max_entries_per_caller: DEFAULT_MAX_REPLAY_ENTRIES_PER_CALLER,
owner_reserved_entries: DEFAULT_OWNER_RESERVED_REPLAY_ENTRIES,
max_entries_per_external_org: DEFAULT_MAX_REPLAY_ENTRIES_PER_EXTERNAL_ORG,
}
}
}
impl AdmissionReplayConfig {
pub fn validate(&self) -> Result<(), ReplayConfigError> {
if self.max_entries == 0 {
return Err(ReplayConfigError::ZeroGlobalCeiling);
}
if self.max_entries_per_caller == 0 {
return Err(ReplayConfigError::ZeroPerCallerCeiling);
}
if self.max_entries_per_caller >= self.max_entries {
return Err(ReplayConfigError::PerCallerNotBelowGlobal {
per_caller: self.max_entries_per_caller,
global: self.max_entries,
});
}
if self.max_entries_per_external_org == 0 {
return Err(ReplayConfigError::ZeroPerExternalOrgCeiling);
}
if self.owner_reserved_entries >= self.max_entries {
return Err(ReplayConfigError::OwnerReserveNotBelowGlobal {
reserved: self.owner_reserved_entries,
global: self.max_entries,
});
}
let external_pool = self.max_entries - self.owner_reserved_entries;
if self.max_entries_per_external_org > external_pool {
return Err(ReplayConfigError::PerExternalOrgAboveExternalPool {
per_org: self.max_entries_per_external_org,
external_pool,
});
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum ReplayConfigError {
#[error("replay max_entries must be > 0")]
ZeroGlobalCeiling,
#[error("replay max_entries_per_caller must be > 0")]
ZeroPerCallerCeiling,
#[error("replay max_entries_per_caller ({per_caller}) must be < max_entries ({global})")]
PerCallerNotBelowGlobal {
per_caller: usize,
global: usize,
},
#[error("replay max_entries_per_external_org must be > 0")]
ZeroPerExternalOrgCeiling,
#[error(
"replay owner_reserved_entries ({reserved}) must be < max_entries ({global}); \
a reserve at or above the global cap leaves external callers nothing"
)]
OwnerReserveNotBelowGlobal {
reserved: usize,
global: usize,
},
#[error(
"replay max_entries_per_external_org ({per_org}) must be <= the external pool \
({external_pool} = max_entries - owner_reserved_entries)"
)]
PerExternalOrgAboveExternalPool {
per_org: usize,
external_pool: usize,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReplayOutcome {
Admitted,
Replay,
CallIdCollision,
CapacityExhausted,
PerCallerCapacityExhausted,
PerOrganizationCapacityExhausted,
ExternalPoolCapacityExhausted,
}
#[derive(Debug, Clone, Copy)]
pub struct ReplayPrincipal<'a> {
pub caller: &'a EntityId,
pub acting_org: &'a OrgId,
pub provider_owner_org: &'a OrgId,
}
impl ReplayPrincipal<'_> {
fn is_owner_org(&self) -> bool {
self.acting_org == self.provider_owner_org
}
}
struct ReplayEntry {
binding_digest: [u8; 32],
expires_at: Instant,
acting_org: OrgId,
external: bool,
}
#[derive(Default)]
struct ReplayState {
by_caller: HashMap<EntityId, HashMap<u64, ReplayEntry>>,
total: usize,
by_org: HashMap<OrgId, usize>,
external_total: usize,
}
impl ReplayState {
fn charge(&mut self, entry_external: bool, acting_org: &OrgId) {
self.total += 1;
*self.by_org.entry(*acting_org).or_insert(0) += 1;
if entry_external {
self.external_total += 1;
}
}
fn release(&mut self, entry: &ReplayEntry) {
self.total -= 1;
if let Some(count) = self.by_org.get_mut(&entry.acting_org) {
*count -= 1;
if *count == 0 {
self.by_org.remove(&entry.acting_org);
}
}
if entry.external {
self.external_total -= 1;
}
}
fn reclaim_caller(&mut self, caller: &EntityId, now: Instant) {
let Some(inner) = self.by_caller.get_mut(caller) else {
return;
};
let expired: Vec<ReplayEntry> = {
let mut drained = Vec::new();
inner.retain(|_, e| {
if e.expires_at > now {
true
} else {
drained.push(ReplayEntry {
binding_digest: e.binding_digest,
expires_at: e.expires_at,
acting_org: e.acting_org,
external: e.external,
});
false
}
});
drained
};
let empty = inner.is_empty();
for entry in &expired {
self.release(entry);
}
if empty {
self.by_caller.remove(caller);
}
}
fn reclaim_all(&mut self, now: Instant) -> usize {
let mut released: Vec<ReplayEntry> = Vec::new();
self.by_caller.retain(|_, inner| {
inner.retain(|_, e| {
if e.expires_at > now {
true
} else {
released.push(ReplayEntry {
binding_digest: e.binding_digest,
expires_at: e.expires_at,
acting_org: e.acting_org,
external: e.external,
});
false
}
});
!inner.is_empty()
});
for entry in &released {
self.release(entry);
}
released.len()
}
fn org_live(&self, org: &OrgId) -> usize {
self.by_org.get(org).copied().unwrap_or(0)
}
}
pub const DEFAULT_MAX_FAILED_ADMISSIONS_PER_PEER: u32 = 64;
pub const DEFAULT_FAILED_ADMISSION_REFILL_PER_SEC: u32 = 8;
pub const DEFAULT_MAX_RATE_LIMITED_PEERS: usize = 4_096;
pub struct AdmissionFailureLimiter {
buckets: Mutex<HashMap<u64, PeerBucket>>,
config: AdmissionRateLimitConfig,
throttled: AtomicU64,
}
#[derive(Debug, Clone, Copy)]
pub struct AdmissionRateLimitConfig {
pub max_failed_per_peer: u32,
pub refill_per_sec: u32,
pub max_tracked_peers: usize,
}
impl Default for AdmissionRateLimitConfig {
fn default() -> Self {
Self {
max_failed_per_peer: DEFAULT_MAX_FAILED_ADMISSIONS_PER_PEER,
refill_per_sec: DEFAULT_FAILED_ADMISSION_REFILL_PER_SEC,
max_tracked_peers: DEFAULT_MAX_RATE_LIMITED_PEERS,
}
}
}
impl AdmissionRateLimitConfig {
pub fn validate(&self) -> Result<(), ReplayConfigError> {
if self.max_failed_per_peer == 0 {
return Err(ReplayConfigError::ZeroPerCallerCeiling);
}
if self.refill_per_sec == 0 {
return Err(ReplayConfigError::ZeroPerCallerCeiling);
}
if self.max_tracked_peers == 0 {
return Err(ReplayConfigError::ZeroGlobalCeiling);
}
Ok(())
}
}
struct PeerBucket {
tokens: u32,
last_refill: Instant,
last_seen: Instant,
}
impl AdmissionFailureLimiter {
pub fn try_new(config: AdmissionRateLimitConfig) -> Result<Self, ReplayConfigError> {
config.validate()?;
Ok(Self {
buckets: Mutex::new(HashMap::new()),
config,
throttled: AtomicU64::new(0),
})
}
pub fn with_defaults() -> Self {
Self {
buckets: Mutex::new(HashMap::new()),
config: AdmissionRateLimitConfig::default(),
throttled: AtomicU64::new(0),
}
}
pub fn may_attempt(&self, from_node: u64, now: Instant) -> bool {
let mut buckets = self.buckets.lock();
let cfg = self.config;
match buckets.get_mut(&from_node) {
None => true, Some(bucket) => {
Self::refill(bucket, cfg, now);
bucket.last_seen = now;
if bucket.tokens > 0 {
true
} else {
self.throttled.fetch_add(1, Ordering::Relaxed);
false
}
}
}
}
pub fn on_failure(&self, from_node: u64, now: Instant) {
let mut buckets = self.buckets.lock();
let cfg = self.config;
if !buckets.contains_key(&from_node) {
if buckets.len() >= cfg.max_tracked_peers {
if let Some(oldest) = buckets
.iter()
.min_by_key(|(_, b)| b.last_seen)
.map(|(peer, _)| *peer)
{
buckets.remove(&oldest);
}
}
buckets.insert(
from_node,
PeerBucket {
tokens: cfg.max_failed_per_peer,
last_refill: now,
last_seen: now,
},
);
}
if let Some(bucket) = buckets.get_mut(&from_node) {
Self::refill(bucket, cfg, now);
bucket.tokens = bucket.tokens.saturating_sub(1);
bucket.last_seen = now;
}
}
fn refill(bucket: &mut PeerBucket, cfg: AdmissionRateLimitConfig, now: Instant) {
let elapsed = now.saturating_duration_since(bucket.last_refill);
let secs = elapsed.as_secs();
if secs == 0 {
return;
}
let gained = secs.saturating_mul(u64::from(cfg.refill_per_sec));
let gained = u32::try_from(gained).unwrap_or(u32::MAX);
bucket.tokens = bucket
.tokens
.saturating_add(gained)
.min(cfg.max_failed_per_peer);
bucket.last_refill = now;
}
pub fn throttled_denials(&self) -> u64 {
self.throttled.load(Ordering::Relaxed)
}
pub fn tokens_for(&self, from_node: u64) -> u32 {
self.buckets
.lock()
.get(&from_node)
.map_or(self.config.max_failed_per_peer, |b| b.tokens)
}
pub fn tracked_peers(&self) -> usize {
self.buckets.lock().len()
}
}
pub struct AdmissionReplayGuard {
entries: Mutex<ReplayState>,
config: AdmissionReplayConfig,
capacity_denials: AtomicU64,
per_caller_denials: AtomicU64,
per_org_denials: AtomicU64,
external_pool_denials: AtomicU64,
}
impl AdmissionReplayGuard {
pub fn try_new(config: AdmissionReplayConfig) -> Result<Self, ReplayConfigError> {
config.validate()?;
Ok(Self::from_validated(config))
}
fn from_validated(config: AdmissionReplayConfig) -> Self {
Self {
entries: Mutex::new(ReplayState::default()),
config,
capacity_denials: AtomicU64::new(0),
per_caller_denials: AtomicU64::new(0),
per_org_denials: AtomicU64::new(0),
external_pool_denials: AtomicU64::new(0),
}
}
pub fn new(config: AdmissionReplayConfig) -> Self {
match Self::try_new(config) {
Ok(guard) => guard,
Err(e) => panic!("invalid AdmissionReplayConfig: {e}"),
}
}
pub fn with_defaults() -> Self {
Self::from_validated(AdmissionReplayConfig::default())
}
pub fn admit(
&self,
principal: ReplayPrincipal<'_>,
call_id: u64,
binding_digest: [u8; 32],
expires_at: Instant,
now: Instant,
) -> ReplayOutcome {
let caller = principal.caller;
let external = !principal.is_owner_org();
let mut st = self.entries.lock();
if let Some(inner) = st.by_caller.get_mut(caller) {
if let Some(existing) = inner.get(&call_id) {
if existing.expires_at > now {
return if existing.binding_digest == binding_digest {
ReplayOutcome::Replay
} else {
ReplayOutcome::CallIdCollision
};
}
inner.insert(
call_id,
ReplayEntry {
binding_digest,
expires_at,
acting_org: *principal.acting_org,
external,
},
);
return ReplayOutcome::Admitted;
}
}
let caller_live = st.by_caller.get(caller).map_or(0, HashMap::len);
if caller_live >= self.config.max_entries_per_caller {
st.reclaim_caller(caller, now);
let caller_live = st.by_caller.get(caller).map_or(0, HashMap::len);
if caller_live >= self.config.max_entries_per_caller {
self.per_caller_denials.fetch_add(1, Ordering::Relaxed);
return ReplayOutcome::PerCallerCapacityExhausted;
}
}
if external {
let org_live = st.org_live(principal.acting_org);
if org_live >= self.config.max_entries_per_external_org {
st.reclaim_all(now);
if st.org_live(principal.acting_org) >= self.config.max_entries_per_external_org {
self.per_org_denials.fetch_add(1, Ordering::Relaxed);
return ReplayOutcome::PerOrganizationCapacityExhausted;
}
}
let external_pool = self
.config
.max_entries
.saturating_sub(self.config.owner_reserved_entries);
if st.external_total >= external_pool {
st.reclaim_all(now);
if st.external_total >= external_pool {
self.external_pool_denials.fetch_add(1, Ordering::Relaxed);
return ReplayOutcome::ExternalPoolCapacityExhausted;
}
}
}
if st.total >= self.config.max_entries {
st.reclaim_all(now);
if st.total >= self.config.max_entries {
self.capacity_denials.fetch_add(1, Ordering::Relaxed);
return ReplayOutcome::CapacityExhausted;
}
}
st.by_caller.entry(caller.clone()).or_default().insert(
call_id,
ReplayEntry {
binding_digest,
expires_at,
acting_org: *principal.acting_org,
external,
},
);
st.charge(external, principal.acting_org);
ReplayOutcome::Admitted
}
pub fn evict_expired(&self, now: Instant) -> usize {
self.entries.lock().reclaim_all(now)
}
pub fn len(&self) -> usize {
self.entries.lock().total
}
pub fn is_empty(&self) -> bool {
self.entries.lock().total == 0
}
pub fn caller_len(&self, caller: &EntityId) -> usize {
self.entries
.lock()
.by_caller
.get(caller)
.map_or(0, HashMap::len)
}
pub fn capacity_denials(&self) -> u64 {
self.capacity_denials.load(Ordering::Relaxed)
}
pub fn org_len(&self, org: &OrgId) -> usize {
self.entries.lock().org_live(org)
}
pub fn external_len(&self) -> usize {
self.entries.lock().external_total
}
pub fn per_org_denials(&self) -> u64 {
self.per_org_denials.load(Ordering::Relaxed)
}
pub fn external_pool_denials(&self) -> u64 {
self.external_pool_denials.load(Ordering::Relaxed)
}
pub fn per_caller_denials(&self) -> u64 {
self.per_caller_denials.load(Ordering::Relaxed)
}
}
impl std::fmt::Debug for AdmissionReplayGuard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AdmissionReplayGuard")
.field("entries", &self.len())
.field("max_entries", &self.config.max_entries)
.field(
"max_entries_per_caller",
&self.config.max_entries_per_caller,
)
.field("capacity_denials", &self.capacity_denials())
.field("per_caller_denials", &self.per_caller_denials())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn caller(byte: u8) -> EntityId {
EntityId::from_bytes([byte; 32])
}
fn owner_org() -> OrgId {
OrgId([0xAA; 32])
}
fn external_org(byte: u8) -> OrgId {
OrgId([byte; 32])
}
fn admit_owner(
guard: &AdmissionReplayGuard,
caller: &EntityId,
call_id: u64,
digest: [u8; 32],
expires: Instant,
now: Instant,
) -> ReplayOutcome {
let owner = owner_org();
guard.admit(
ReplayPrincipal {
caller,
acting_org: &owner,
provider_owner_org: &owner,
},
call_id,
digest,
expires,
now,
)
}
fn admit_external(
guard: &AdmissionReplayGuard,
org: &OrgId,
caller: &EntityId,
call_id: u64,
digest: [u8; 32],
expires: Instant,
now: Instant,
) -> ReplayOutcome {
let owner = owner_org();
guard.admit(
ReplayPrincipal {
caller,
acting_org: org,
provider_owner_org: &owner,
},
call_id,
digest,
expires,
now,
)
}
fn limiter() -> AdmissionFailureLimiter {
AdmissionFailureLimiter::try_new(AdmissionRateLimitConfig {
max_failed_per_peer: 4,
refill_per_sec: 2,
max_tracked_peers: 8,
})
.expect("valid envelope")
}
#[test]
fn a_peer_whose_admissions_succeed_is_never_throttled() {
let lim = limiter();
let now = Instant::now();
const PEER: u64 = 7;
for _ in 0..1_000 {
assert!(
lim.may_attempt(PEER, now),
"a successful caller must never be throttled — charging per \
ATTEMPT would penalise exactly the traffic we want",
);
}
assert_eq!(lim.throttled_denials(), 0);
assert_eq!(
lim.tracked_peers(),
0,
"a peer with no failures costs nothing to track"
);
}
#[test]
fn a_failing_peer_exhausts_its_budget_and_is_refused() {
let lim = limiter();
let now = Instant::now();
const PEER: u64 = 9;
for _ in 0..4 {
assert!(lim.may_attempt(PEER, now));
lim.on_failure(PEER, now);
}
assert_eq!(lim.tokens_for(PEER), 0);
assert!(
!lim.may_attempt(PEER, now),
"the peer must be refused once its failure budget is spent",
);
assert_eq!(lim.throttled_denials(), 1);
}
#[test]
fn throttling_one_peer_leaves_others_untouched() {
let lim = limiter();
let now = Instant::now();
const NOISY: u64 = 1;
const QUIET: u64 = 2;
for _ in 0..4 {
lim.on_failure(NOISY, now);
}
assert!(!lim.may_attempt(NOISY, now));
assert!(
lim.may_attempt(QUIET, now),
"a second peer's allowance must be untouched by the first's abuse",
);
}
#[test]
fn the_budget_refills_over_time() {
let lim = limiter();
let t0 = Instant::now();
const PEER: u64 = 3;
for _ in 0..4 {
lim.on_failure(PEER, t0);
}
assert!(!lim.may_attempt(PEER, t0));
let later = t0 + Duration::from_secs(1);
assert!(
lim.may_attempt(PEER, later),
"a throttled peer must recover on its own — otherwise one expired \
proof at startup takes a client out until restart",
);
assert_eq!(lim.tokens_for(PEER), 2);
let much_later = t0 + Duration::from_secs(3_600);
lim.may_attempt(PEER, much_later);
assert_eq!(lim.tokens_for(PEER), 4);
}
#[test]
fn tracked_peers_are_bounded() {
let lim = limiter();
let now = Instant::now();
for peer in 0..64u64 {
lim.on_failure(peer, now + Duration::from_millis(peer));
}
assert!(
lim.tracked_peers() <= 8,
"peer tracking must stay within max_tracked_peers, got {}",
lim.tracked_peers(),
);
}
#[test]
fn a_degenerate_rate_limit_envelope_is_refused() {
assert!(AdmissionRateLimitConfig {
max_failed_per_peer: 0,
refill_per_sec: 1,
max_tracked_peers: 8,
}
.validate()
.is_err());
assert!(AdmissionRateLimitConfig {
max_failed_per_peer: 4,
refill_per_sec: 0,
max_tracked_peers: 8,
}
.validate()
.is_err());
assert!(AdmissionRateLimitConfig::default().validate().is_ok());
}
fn partitioned() -> AdmissionReplayGuard {
AdmissionReplayGuard::new(AdmissionReplayConfig {
max_entries: 40,
owner_reserved_entries: 10, max_entries_per_external_org: 8,
max_entries_per_caller: 4, })
}
fn flood_org(
guard: &AdmissionReplayGuard,
org: &OrgId,
identities: impl IntoIterator<Item = u8>,
expires: Instant,
now: Instant,
) -> (usize, usize) {
let (mut admitted, mut denied) = (0usize, 0usize);
for identity in identities {
for call in 0..4u64 {
match admit_external(
guard,
org,
&caller(identity),
call + u64::from(identity) * 1_000,
[identity; 32],
expires,
now,
) {
ReplayOutcome::Admitted => admitted += 1,
ReplayOutcome::PerOrganizationCapacityExhausted => denied += 1,
ReplayOutcome::ExternalPoolCapacityExhausted => denied += 1,
other => panic!("unexpected outcome {other:?}"),
}
}
}
(admitted, denied)
}
#[test]
fn many_identities_from_one_external_org_share_one_allocation() {
let guard = partitioned();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let org = external_org(0xB1);
let (admitted, denied) = flood_org(&guard, &org, 0..16u8, expires, now);
assert_eq!(
admitted, 8,
"sixteen identities from one org must share the SINGLE 8-entry org \
allocation — if each got its own, the coalition wins",
);
assert!(denied > 0);
assert_eq!(guard.org_len(&org), 8);
assert_eq!(
guard.per_org_denials(),
denied as u64,
"the per-ORG denial metric must fire, so an operator can attribute \
this to one grantee rather than to fleet-wide pressure",
);
assert_eq!(
guard.per_caller_denials(),
0,
"not a per-caller denial: naming a single identity would point the \
operator at the wrong subject, since identities are free to mint",
);
}
#[test]
fn a_full_external_pool_never_denies_the_owner_org() {
let guard = partitioned();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let mut external_admitted = 0usize;
for org_byte in 0..8u8 {
let org = external_org(0xC0 + org_byte);
let (a, _) = flood_org(
&guard,
&org,
[org_byte * 2 + 100, org_byte * 2 + 101],
expires,
now,
);
external_admitted += a;
}
assert_eq!(
external_admitted, 30,
"external traffic must be capped at max_entries - owner_reserved",
);
assert_eq!(guard.external_len(), 30);
assert_eq!(
admit_external(
&guard,
&external_org(0xFE),
&caller(99),
1,
[9u8; 32],
expires,
now
),
ReplayOutcome::ExternalPoolCapacityExhausted,
);
assert_eq!(
admit_owner(&guard, &caller(200), 1, [7u8; 32], expires, now),
ReplayOutcome::Admitted,
"an external coalition of ANY size must never deny the provider's \
own org — this is the entire point of the reserve",
);
}
#[test]
fn owner_traffic_borrows_idle_external_capacity() {
let guard = partitioned();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let mut admitted = 0usize;
for identity in 0..10u8 {
for call in 0..4u64 {
if admit_owner(
&guard,
&caller(identity),
call,
[identity; 32],
expires,
now,
) == ReplayOutcome::Admitted
{
admitted += 1;
}
}
}
assert_eq!(
admitted, 40,
"owner traffic must borrow idle external capacity up to the global \
cap; stopping at the 10-entry reserve would make the partition a \
throughput regression rather than a safety property",
);
assert_eq!(guard.len(), 40);
}
#[test]
fn distinct_external_orgs_have_independent_allocations() {
let guard = partitioned();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let noisy = external_org(0xB1);
let quiet = external_org(0xB2);
let (admitted, _) = flood_org(&guard, &noisy, [1u8, 11u8], expires, now);
assert_eq!(admitted, 8);
assert_eq!(
admit_external(&guard, &noisy, &caller(21), 0, [21u8; 32], expires, now),
ReplayOutcome::PerOrganizationCapacityExhausted,
"a THIRD fresh identity must still be refused — the quota is the \
org's, not the identity's",
);
let (quiet_admitted, quiet_denied) = flood_org(&guard, &quiet, [2u8, 12u8], expires, now);
assert_eq!(
quiet_admitted, 8,
"a second org's quota must be untouched by the first's abuse",
);
assert_eq!(quiet_denied, 0);
assert_eq!(guard.org_len(&noisy), 8);
assert_eq!(guard.org_len(&quiet), 8);
}
#[test]
fn reclamation_releases_every_counter_in_step() {
let guard = partitioned();
let t0 = Instant::now();
let short = t0 + Duration::from_secs(5);
let org = external_org(0xB1);
let (admitted, _) = flood_org(&guard, &org, [1u8, 11u8], short, t0);
assert_eq!(admitted, 8);
assert_eq!(guard.len(), 8, "global");
assert_eq!(guard.org_len(&org), 8, "per-org");
assert_eq!(guard.external_len(), 8, "external pool");
assert_eq!(guard.caller_len(&caller(1)), 4, "per-caller");
let later = t0 + Duration::from_secs(6);
assert_eq!(guard.evict_expired(later), 8);
assert_eq!(guard.len(), 0, "global count leaked");
assert_eq!(guard.org_len(&org), 0, "per-org count leaked");
assert_eq!(guard.external_len(), 0, "external-pool count leaked");
assert_eq!(guard.caller_len(&caller(1)), 0, "per-caller count leaked");
assert_eq!(
admit_external(
&guard,
&org,
&caller(1),
100,
[1u8; 32],
later + Duration::from_secs(30),
later
),
ReplayOutcome::Admitted,
"a reclaimed org slot must be usable again, or the quota is a \
one-way ratchet",
);
}
#[test]
fn the_three_capacity_outcomes_are_distinguishable() {
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let guard = partitioned();
let org = external_org(0xB1);
flood_org(&guard, &org, [1u8, 11u8], expires, now);
assert_eq!(
admit_external(&guard, &org, &caller(21), 0, [21u8; 32], expires, now),
ReplayOutcome::PerOrganizationCapacityExhausted,
);
assert_eq!(guard.per_org_denials(), 1);
assert_eq!(guard.external_pool_denials(), 0);
assert_eq!(guard.capacity_denials(), 0);
let guard = partitioned();
for org_byte in 0..8u8 {
let org = external_org(0xC0 + org_byte);
flood_org(
&guard,
&org,
[org_byte * 2 + 100, org_byte * 2 + 101],
expires,
now,
);
}
assert_eq!(
admit_external(
&guard,
&external_org(0xFE),
&caller(99),
1,
[9u8; 32],
expires,
now
),
ReplayOutcome::ExternalPoolCapacityExhausted,
);
assert!(guard.external_pool_denials() >= 1);
assert_eq!(
guard.capacity_denials(),
0,
"external saturation is NOT global exhaustion — the owner reserve \
is free by construction, so reporting it as global would send an \
operator looking for fleet-wide pressure that does not exist",
);
let guard = partitioned();
for identity in 0..10u8 {
for call in 0..4u64 {
admit_owner(
&guard,
&caller(identity),
call,
[identity; 32],
expires,
now,
);
}
}
assert_eq!(guard.len(), 40);
assert_eq!(
admit_owner(&guard, &caller(50), 1, [50u8; 32], expires, now),
ReplayOutcome::CapacityExhausted,
);
assert_eq!(guard.capacity_denials(), 1);
assert_eq!(guard.per_org_denials(), 0);
assert_eq!(guard.external_pool_denials(), 0);
}
#[test]
fn an_inconsistent_envelope_is_refused() {
let reserve_too_big = AdmissionReplayConfig {
max_entries: 100,
owner_reserved_entries: 100,
max_entries_per_external_org: 10,
max_entries_per_caller: 10,
};
assert!(matches!(
reserve_too_big.validate(),
Err(ReplayConfigError::OwnerReserveNotBelowGlobal { .. })
));
let org_quota_too_big = AdmissionReplayConfig {
max_entries: 100,
owner_reserved_entries: 95, max_entries_per_external_org: 10,
max_entries_per_caller: 10,
};
assert!(matches!(
org_quota_too_big.validate(),
Err(ReplayConfigError::PerExternalOrgAboveExternalPool { .. })
));
assert!(AdmissionReplayConfig::default().validate().is_ok());
}
#[test]
fn first_admit_records_and_replay_is_denied() {
let guard = AdmissionReplayGuard::with_defaults();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let digest = [1u8; 32];
assert_eq!(
admit_owner(&guard, &caller(1), 7, digest, expires, now),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(1), 7, digest, expires, now),
ReplayOutcome::Replay
);
assert_eq!(guard.len(), 1);
}
#[test]
fn same_call_id_different_binding_is_a_collision() {
let guard = AdmissionReplayGuard::with_defaults();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
assert_eq!(
admit_owner(&guard, &caller(1), 7, [1u8; 32], expires, now),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(1), 7, [2u8; 32], expires, now),
ReplayOutcome::CallIdCollision
);
}
#[test]
fn distinct_callers_and_call_ids_are_independent() {
let guard = AdmissionReplayGuard::with_defaults();
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let digest = [1u8; 32];
assert_eq!(
admit_owner(&guard, &caller(1), 7, digest, expires, now),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(1), 8, digest, expires, now),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(2), 7, digest, expires, now),
ReplayOutcome::Admitted
);
assert_eq!(guard.len(), 3);
}
#[test]
fn expired_entry_permits_legitimate_call_id_reuse() {
let guard = AdmissionReplayGuard::with_defaults();
let t0 = Instant::now();
let expires = t0 + Duration::from_secs(30);
let digest = [1u8; 32];
assert_eq!(
admit_owner(&guard, &caller(1), 7, digest, expires, t0),
ReplayOutcome::Admitted
);
let later = t0 + Duration::from_secs(31);
let new_expires = later + Duration::from_secs(30);
assert_eq!(
admit_owner(&guard, &caller(1), 7, digest, new_expires, later),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(1), 7, digest, new_expires, later),
ReplayOutcome::Replay
);
}
#[test]
fn capacity_denies_without_evicting_a_live_guard() {
let guard = AdmissionReplayGuard::new(AdmissionReplayConfig {
max_entries: 2,
max_entries_per_caller: 1,
owner_reserved_entries: 0,
max_entries_per_external_org: 2,
});
let now = Instant::now();
let expires = now + Duration::from_secs(30);
assert_eq!(
admit_owner(&guard, &caller(1), 1, [1u8; 32], expires, now),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(2), 2, [2u8; 32], expires, now),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(3), 3, [3u8; 32], expires, now),
ReplayOutcome::CapacityExhausted
);
assert_eq!(guard.capacity_denials(), 1);
assert_eq!(guard.len(), 2);
assert_eq!(
admit_owner(&guard, &caller(1), 1, [1u8; 32], expires, now),
ReplayOutcome::Replay
);
}
#[test]
fn capacity_reclaims_expired_slots_before_denying() {
let guard = AdmissionReplayGuard::new(AdmissionReplayConfig {
max_entries: 2,
max_entries_per_caller: 1,
owner_reserved_entries: 0,
max_entries_per_external_org: 2,
});
let t0 = Instant::now();
let short = t0 + Duration::from_secs(10);
let long = t0 + Duration::from_secs(60);
assert_eq!(
admit_owner(&guard, &caller(1), 1, [1u8; 32], short, t0),
ReplayOutcome::Admitted
);
assert_eq!(
admit_owner(&guard, &caller(2), 2, [2u8; 32], long, t0),
ReplayOutcome::Admitted
);
let later = t0 + Duration::from_secs(11);
assert_eq!(
admit_owner(
&guard,
&caller(3),
3,
[3u8; 32],
later + Duration::from_secs(30),
later
),
ReplayOutcome::Admitted
);
assert_eq!(guard.capacity_denials(), 0);
assert_eq!(guard.len(), 2);
}
#[test]
fn evict_expired_reclaims_only_closed_windows() {
let guard = AdmissionReplayGuard::with_defaults();
let t0 = Instant::now();
admit_owner(
&guard,
&caller(1),
1,
[1u8; 32],
t0 + Duration::from_secs(10),
t0,
);
admit_owner(
&guard,
&caller(2),
2,
[2u8; 32],
t0 + Duration::from_secs(60),
t0,
);
let reclaimed = guard.evict_expired(t0 + Duration::from_secs(11));
assert_eq!(reclaimed, 1);
assert_eq!(guard.len(), 1);
assert_eq!(
admit_owner(
&guard,
&caller(2),
2,
[2u8; 32],
t0 + Duration::from_secs(60),
t0 + Duration::from_secs(11)
),
ReplayOutcome::Replay
);
}
#[test]
fn concurrent_admissions_admit_exactly_once() {
use std::sync::Arc;
let guard = Arc::new(AdmissionReplayGuard::with_defaults());
let now = Instant::now();
let expires = now + Duration::from_secs(30);
let digest = [7u8; 32];
let admitted = Arc::new(AtomicU64::new(0));
let replayed = Arc::new(AtomicU64::new(0));
let mut handles = Vec::new();
for _ in 0..16 {
let guard = guard.clone();
let admitted = admitted.clone();
let replayed = replayed.clone();
handles.push(std::thread::spawn(move || {
match admit_owner(&guard, &caller(1), 42, digest, expires, now) {
ReplayOutcome::Admitted => {
admitted.fetch_add(1, Ordering::Relaxed);
}
ReplayOutcome::Replay => {
replayed.fetch_add(1, Ordering::Relaxed);
}
other => panic!("unexpected outcome {other:?}"),
}
}));
}
for h in handles {
h.join().expect("join");
}
assert_eq!(admitted.load(Ordering::Relaxed), 1, "exactly one admit");
assert_eq!(replayed.load(Ordering::Relaxed), 15, "the rest replay");
}
#[test]
fn per_caller_ceiling_isolates_a_flooding_caller() {
let guard = AdmissionReplayGuard::new(AdmissionReplayConfig {
max_entries: 1_000,
max_entries_per_caller: 3,
owner_reserved_entries: 0,
max_entries_per_external_org: 1_000,
});
let now = Instant::now();
let expires = now + Duration::from_secs(30);
for call_id in 0..3u64 {
assert_eq!(
admit_owner(
&guard,
&caller(1),
call_id,
[call_id as u8; 32],
expires,
now
),
ReplayOutcome::Admitted
);
}
assert_eq!(guard.caller_len(&caller(1)), 3);
assert_eq!(
admit_owner(&guard, &caller(1), 99, [9u8; 32], expires, now),
ReplayOutcome::PerCallerCapacityExhausted
);
assert_eq!(guard.per_caller_denials(), 1);
assert_eq!(guard.capacity_denials(), 0, "global cap never fired");
for call_id in 0..3u64 {
assert_eq!(
admit_owner(
&guard,
&caller(2),
call_id,
[call_id as u8; 32],
expires,
now
),
ReplayOutcome::Admitted
);
}
assert_eq!(guard.caller_len(&caller(2)), 3);
assert_eq!(
admit_owner(&guard, &caller(1), 0, [0u8; 32], expires, now),
ReplayOutcome::Replay
);
}
#[test]
fn per_caller_ceiling_reclaims_expired_before_denying() {
let guard = AdmissionReplayGuard::new(AdmissionReplayConfig {
max_entries: 1_000,
max_entries_per_caller: 2,
owner_reserved_entries: 0,
max_entries_per_external_org: 1_000,
});
let t0 = Instant::now();
let short = t0 + Duration::from_secs(10);
admit_owner(&guard, &caller(1), 1, [1u8; 32], short, t0);
admit_owner(&guard, &caller(1), 2, [2u8; 32], short, t0);
assert_eq!(guard.caller_len(&caller(1)), 2);
let later = t0 + Duration::from_secs(11);
assert_eq!(
admit_owner(
&guard,
&caller(1),
3,
[3u8; 32],
later + Duration::from_secs(30),
later
),
ReplayOutcome::Admitted
);
assert_eq!(guard.per_caller_denials(), 0);
assert_eq!(guard.caller_len(&caller(1)), 1, "expired slots reclaimed");
}
#[test]
fn replay_config_validation_boundaries() {
assert!(AdmissionReplayConfig {
max_entries: 10,
max_entries_per_caller: 9,
owner_reserved_entries: 0,
max_entries_per_external_org: 10,
}
.validate()
.is_ok());
assert!(AdmissionReplayGuard::try_new(AdmissionReplayConfig {
max_entries: 10,
max_entries_per_caller: 9,
owner_reserved_entries: 0,
max_entries_per_external_org: 10,
})
.is_ok());
assert!(AdmissionReplayConfig::default().validate().is_ok());
assert_eq!(
AdmissionReplayConfig {
max_entries: 8,
max_entries_per_caller: 8,
owner_reserved_entries: 0,
max_entries_per_external_org: 8,
}
.validate(),
Err(ReplayConfigError::PerCallerNotBelowGlobal {
per_caller: 8,
global: 8,
}),
);
assert!(matches!(
AdmissionReplayConfig {
max_entries: 8,
max_entries_per_caller: 9,
owner_reserved_entries: 0,
max_entries_per_external_org: 8,
}
.validate(),
Err(ReplayConfigError::PerCallerNotBelowGlobal { .. }),
));
assert_eq!(
AdmissionReplayConfig {
max_entries: 0,
max_entries_per_caller: 0,
owner_reserved_entries: 0,
max_entries_per_external_org: 0,
}
.validate(),
Err(ReplayConfigError::ZeroGlobalCeiling),
);
assert_eq!(
AdmissionReplayConfig {
max_entries: 4,
max_entries_per_caller: 0,
owner_reserved_entries: 0,
max_entries_per_external_org: 4,
}
.validate(),
Err(ReplayConfigError::ZeroPerCallerCeiling),
);
assert!(AdmissionReplayGuard::try_new(AdmissionReplayConfig {
max_entries: 8,
max_entries_per_caller: 8,
owner_reserved_entries: 0,
max_entries_per_external_org: 8,
})
.is_err());
}
#[test]
#[should_panic(expected = "invalid AdmissionReplayConfig")]
fn replay_new_panics_on_invalid_config() {
let _ = AdmissionReplayGuard::new(AdmissionReplayConfig {
max_entries: 4,
max_entries_per_caller: 4,
owner_reserved_entries: 0,
max_entries_per_external_org: 4,
});
}
}