//! Privacy budget management and accounting use crate::error::{FederatedError, Result}; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; use tracing::info; /// Privacy budget for differential privacy #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PrivacyBudget { pub epsilon: f64, pub delta: f64, } impl PrivacyBudget { pub fn new(epsilon: f64) -> Self { Self { epsilon, delta: 1e-5, } } } /// Privacy accountant for tracking budget consumption #[derive(Debug)] pub struct PrivacyAccountant { total_budget: PrivacyBudget, consumed_budget: PrivacyBudget, transaction_history: VecDeque, max_history_size: usize, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PrivacyTransaction { pub epsilon_consumed: f64, pub delta_consumed: f64, pub timestamp: chrono::DateTime, pub description: String, } impl PrivacyAccountant { pub fn new(total_budget: PrivacyBudget) -> Self { Self { total_budget, consumed_budget: PrivacyBudget { epsilon: 0.0, delta: 0.0, }, transaction_history: VecDeque::new(), max_history_size: 1000, } } pub fn consume(&mut self, epsilon: f64, delta: f64, description: String) -> Result<()> { if self.consumed_budget.epsilon + epsilon > self.total_budget.epsilon { return Err(FederatedError::PrivacyBudgetExceeded { remaining: self.total_budget.epsilon - self.consumed_budget.epsilon, requested: epsilon, }); } self.consumed_budget.epsilon += epsilon; self.consumed_budget.delta += delta; let transaction = PrivacyTransaction { epsilon_consumed: epsilon, delta_consumed: delta, timestamp: chrono::Utc::now(), description: description.clone(), }; self.transaction_history.push_back(transaction); if self.transaction_history.len() > self.max_history_size { self.transaction_history.pop_front(); } info!( "🔒 Privacy budget consumed: ε={:.6}, δ={:.2e} ({})", epsilon, delta, description ); Ok(()) } pub fn remaining_budget(&self) -> PrivacyBudget { PrivacyBudget { epsilon: (self.total_budget.epsilon - self.consumed_budget.epsilon).max(0.0), delta: (self.total_budget.delta - self.consumed_budget.delta).max(0.0), } } pub fn budget_utilization(&self) -> f64 { self.consumed_budget.epsilon / self.total_budget.epsilon } }