use std::net::IpAddr;
use std::time::{Duration, Instant};
use super::error::PowError;
use super::extractors::AddressKey;
use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct RateLimitStatus {
pub is_limited: bool,
pub remaining: Option<u32>,
pub reset_at: Option<Instant>,
pub quota: u32,
pub is_banned: bool,
pub ban_expires_at: Option<Instant>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PenaltyReason {
AuthFailure,
TokenVerificationFailure,
SuspiciousActivity,
RepeatedViolation,
}
impl PenaltyReason {
pub fn failures_to_ban(&self) -> u32 {
match self {
PenaltyReason::AuthFailure => 5,
PenaltyReason::TokenVerificationFailure => 3,
PenaltyReason::SuspiciousActivity => 2,
PenaltyReason::RepeatedViolation => 1,
}
}
pub fn ban_duration(&self) -> Duration {
match self {
PenaltyReason::AuthFailure | PenaltyReason::TokenVerificationFailure => {
Duration::from_hours(1)
}
PenaltyReason::SuspiciousActivity => Duration::from_hours(2),
PenaltyReason::RepeatedViolation => Duration::from_hours(24),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PowPenaltyReason {
ConnSignatureFailure,
ConnDuplicatePending,
ConnRejected,
ConnPowCheckFailed,
}
impl PowPenaltyReason {
pub fn affects_network(&self) -> bool {
match self {
PowPenaltyReason::ConnRejected => false, PowPenaltyReason::ConnSignatureFailure
| PowPenaltyReason::ConnDuplicatePending
| PowPenaltyReason::ConnPowCheckFailed => true,
}
}
}
#[derive(Debug, Clone)]
pub struct BanEntry {
pub key: AddressKey,
pub reason: PenaltyReason,
pub created_at: Instant,
pub expires_at: Option<Instant>,
}
impl BanEntry {
pub fn is_expired(&self) -> bool {
self.expires_at.is_some_and(|exp| Instant::now() >= exp)
}
pub fn remaining_duration(&self) -> Option<Duration> {
self.expires_at.map(|exp| {
let now = Instant::now();
if now >= exp { Duration::ZERO } else { exp - now }
})
}
}
#[derive(Debug, Clone, Default)]
pub struct RateLimiterStats {
pub tracked_addresses: usize,
pub active_bans: usize,
pub total_requests_limited: u64,
pub total_bans_issued: u64,
pub pow_individual_entries: usize,
pub pow_network_entries: usize,
}
pub trait RateLimitApi: Send + Sync {
fn get_status(
&self,
addr: &IpAddr,
category: &str,
) -> ClResult<Vec<(AddressKey, RateLimitStatus)>>;
fn penalize(&self, addr: &IpAddr, reason: PenaltyReason, amount: u32) -> ClResult<()>;
fn grant(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
fn reset(&self, addr: &IpAddr) -> ClResult<()>;
fn ban(&self, addr: &IpAddr, duration: Duration, reason: PenaltyReason) -> ClResult<()>;
fn unban(&self, addr: &IpAddr) -> ClResult<()>;
fn is_banned(&self, addr: &IpAddr) -> bool;
fn list_bans(&self) -> Vec<BanEntry>;
fn stats(&self) -> RateLimiterStats;
fn get_pow_requirement(&self, addr: &IpAddr) -> u32;
fn increment_pow_counter(&self, addr: &IpAddr, reason: PowPenaltyReason) -> ClResult<()>;
fn decrement_pow_counter(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
fn verify_pow(&self, addr: &IpAddr, token: &str) -> Result<(), PowError>;
}