//! CBP处罚机制 //! //! 实现CBP节点的违规检测、处罚执行、处罚记录和申诉机制 use crate::{CbpNode, CbpStatus, CbppL1Error}; use nac_udm::primitives::Address; use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// 违规类型 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ViolationType { /// 双签 DoubleSign, /// 连续错过区块 ConsecutiveMissedBlocks, /// 恶意行为 MaliciousBehavior, /// 硬件不达标 HardwareFailure, /// KYC过期 KycExpired, /// 质押不足 InsufficientStake, /// 宪法违规 ConstitutionViolation, } /// 处罚类型 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum PenaltyType { /// 警告 Warning, /// 罚款 Fine, /// 暂停 Suspension, /// 强制退出 ForceExit, /// 削减质押 Slashing, } /// 违规检测结果 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ViolationDetection { /// CBP地址 pub address: Address, /// 违规类型 pub violation_type: ViolationType, /// 检测时间 pub detected_at: u64, /// 证据 pub evidence: String, /// 严重程度(1-10) pub severity: u8, } /// 处罚记录 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PenaltyRecord { /// 记录ID pub id: u64, /// CBP地址 pub address: Address, /// 违规类型 pub violation_type: ViolationType, /// 处罚类型 pub penalty_type: PenaltyType, /// 罚款金额 pub fine_amount: u64, /// 暂停时长(秒) pub suspension_duration: Option, /// 削减金额 pub slashed_amount: u64, /// 处罚时间 pub penalized_at: u64, /// 处罚原因 pub reason: String, /// 证据 pub evidence: String, /// 是否已申诉 pub appealed: bool, } /// 申诉状态 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum AppealStatus { /// 待审核 Pending, /// 审核通过 Approved, /// 审核拒绝 Rejected, } /// 申诉请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppealRequest { /// 申诉ID pub id: u64, /// 处罚记录ID pub penalty_id: u64, /// CBP地址 pub address: Address, /// 申诉理由 pub reason: String, /// 申诉证据 pub evidence: String, /// 申诉时间 pub appealed_at: u64, /// 申诉状态 pub status: AppealStatus, /// 审核时间 pub reviewed_at: Option, /// 审核人 pub reviewer: Option
, /// 审核意见 pub review_comment: Option, } /// 处罚配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PenaltyConfig { /// 违规处罚映射 pub violation_penalties: HashMap, /// 警告阈值 pub warning_threshold: u8, /// 暂停阈值 pub suspension_threshold: u8, /// 强制退出阈值 pub force_exit_threshold: u8, /// 申诉期限(秒) pub appeal_period: u64, } impl Default for PenaltyConfig { fn default() -> Self { let mut violation_penalties = HashMap::new(); violation_penalties.insert(ViolationType::DoubleSign, (PenaltyType::Slashing, 50_000_000_000)); violation_penalties.insert(ViolationType::ConsecutiveMissedBlocks, (PenaltyType::Fine, 1_000_000_000)); violation_penalties.insert(ViolationType::MaliciousBehavior, (PenaltyType::ForceExit, 100_000_000_000)); violation_penalties.insert(ViolationType::HardwareFailure, (PenaltyType::Warning, 0)); violation_penalties.insert(ViolationType::KycExpired, (PenaltyType::Suspension, 0)); violation_penalties.insert(ViolationType::InsufficientStake, (PenaltyType::Suspension, 0)); violation_penalties.insert(ViolationType::ConstitutionViolation, (PenaltyType::Fine, 5_000_000_000)); Self { violation_penalties, warning_threshold: 3, suspension_threshold: 5, force_exit_threshold: 10, appeal_period: 7 * 24 * 3600, // 7天 } } } /// 处罚管理器 pub struct PenaltyManager { /// 处罚配置 config: PenaltyConfig, /// 处罚记录 records: Vec, /// 申诉请求 appeals: Vec, /// 下一个记录ID next_record_id: u64, /// 下一个申诉ID next_appeal_id: u64, /// 节点警告计数 warning_counts: HashMap, } impl PenaltyManager { pub fn new() -> Self { Self { config: PenaltyConfig::default(), records: Vec::new(), appeals: Vec::new(), next_record_id: 1, next_appeal_id: 1, warning_counts: HashMap::new(), } } pub fn with_config(config: PenaltyConfig) -> Self { Self { config, records: Vec::new(), appeals: Vec::new(), next_record_id: 1, next_appeal_id: 1, warning_counts: HashMap::new(), } } /// 检测违规 pub fn detect_violation( &self, node: &CbpNode, violation_type: ViolationType, evidence: String, severity: u8, timestamp: u64, ) -> ViolationDetection { ViolationDetection { address: node.address, violation_type, detected_at: timestamp, evidence, severity, } } /// 执行处罚 pub fn execute_penalty( &mut self, node: &mut CbpNode, detection: ViolationDetection, timestamp: u64, ) -> Result { // 获取处罚配置 let (penalty_type, base_amount) = self.config.violation_penalties .get(&detection.violation_type) .copied() .ok_or(CbppL1Error::UnknownViolationType)?; // 根据严重程度调整金额 let adjusted_amount = (base_amount as f64 * detection.severity as f64 / 5.0) as u64; let mut fine_amount = 0u64; let mut suspension_duration = None; let mut slashed_amount = 0u64; // 执行处罚 match penalty_type { PenaltyType::Warning => { let count = self.warning_counts.entry(node.address).or_insert(0); *count += 1; // 警告次数过多自动升级为暂停 if *count >= self.config.warning_threshold { node.status = CbpStatus::Suspended; suspension_duration = Some(7 * 24 * 3600); // 7天 } } PenaltyType::Fine => { fine_amount = adjusted_amount; // 从质押中扣除 if node.stake_amount >= fine_amount { node.stake_amount -= fine_amount; } else { return Err(CbppL1Error::InsufficientStakeForPenalty); } } PenaltyType::Suspension => { node.status = CbpStatus::Suspended; suspension_duration = Some(30 * 24 * 3600); // 30天 } PenaltyType::ForceExit => { node.status = CbpStatus::Exited; slashed_amount = adjusted_amount.min(node.stake_amount); node.stake_amount -= slashed_amount; } PenaltyType::Slashing => { slashed_amount = adjusted_amount.min(node.stake_amount); node.stake_amount -= slashed_amount; // 严重违规同时暂停 if detection.severity >= 8 { node.status = CbpStatus::Suspended; suspension_duration = Some(90 * 24 * 3600); // 90天 } } } // 创建处罚记录 let record = PenaltyRecord { id: self.next_record_id, address: node.address, violation_type: detection.violation_type, penalty_type, fine_amount, suspension_duration, slashed_amount, penalized_at: timestamp, reason: format!("Violation detected: {:?}", detection.violation_type), evidence: detection.evidence, appealed: false, }; let record_id = record.id; self.records.push(record); self.next_record_id += 1; Ok(record_id) } /// 提交申诉 pub fn submit_appeal( &mut self, penalty_id: u64, address: Address, reason: String, evidence: String, timestamp: u64, ) -> Result { // 查找处罚记录 let record = self.records.iter_mut() .find(|r| r.id == penalty_id) .ok_or(CbppL1Error::PenaltyRecordNotFound)?; // 检查地址 if record.address != address { return Err(CbppL1Error::InvalidAddress); } // 检查是否已申诉 if record.appealed { return Err(CbppL1Error::AlreadyAppealed); } // 检查申诉期限 if timestamp > record.penalized_at + self.config.appeal_period { return Err(CbppL1Error::AppealPeriodExpired); } // 标记已申诉 record.appealed = true; // 创建申诉请求 let appeal = AppealRequest { id: self.next_appeal_id, penalty_id, address, reason, evidence, appealed_at: timestamp, status: AppealStatus::Pending, reviewed_at: None, reviewer: None, review_comment: None, }; let appeal_id = appeal.id; self.appeals.push(appeal); self.next_appeal_id += 1; Ok(appeal_id) } /// 审核申诉 pub fn review_appeal( &mut self, appeal_id: u64, approved: bool, reviewer: Address, comment: Option, timestamp: u64, ) -> Result<(), CbppL1Error> { let appeal = self.appeals.iter_mut() .find(|a| a.id == appeal_id) .ok_or(CbppL1Error::AppealNotFound)?; // 检查申诉状态 if appeal.status != AppealStatus::Pending { return Err(CbppL1Error::InvalidStatus( format!("Appeal status is {:?}, expected Pending", appeal.status) )); } // 更新申诉状态 appeal.status = if approved { AppealStatus::Approved } else { AppealStatus::Rejected }; appeal.reviewed_at = Some(timestamp); appeal.reviewer = Some(reviewer); appeal.review_comment = comment; Ok(()) } /// 撤销处罚(申诉成功后) pub fn revoke_penalty( &mut self, penalty_id: u64, node: &mut CbpNode, ) -> Result<(), CbppL1Error> { let record = self.records.iter() .find(|r| r.id == penalty_id) .ok_or(CbppL1Error::PenaltyRecordNotFound)?; // 检查地址 if record.address != node.address { return Err(CbppL1Error::InvalidAddress); } // 恢复质押 node.stake_amount += record.fine_amount + record.slashed_amount; // 恢复状态(如果是暂停或退出) if node.status == CbpStatus::Suspended || node.status == CbpStatus::Exited { node.status = CbpStatus::Active; } // 清除警告计数 if record.penalty_type == PenaltyType::Warning { if let Some(count) = self.warning_counts.get_mut(&node.address) { *count = count.saturating_sub(1); } } Ok(()) } /// 获取处罚记录 pub fn get_penalty_records(&self, address: Option<&Address>) -> Vec<&PenaltyRecord> { if let Some(addr) = address { self.records.iter() .filter(|r| &r.address == addr) .collect() } else { self.records.iter().collect() } } /// 获取申诉请求 pub fn get_appeals(&self, status: Option) -> Vec<&AppealRequest> { if let Some(s) = status { self.appeals.iter() .filter(|a| a.status == s) .collect() } else { self.appeals.iter().collect() } } /// 获取处罚统计 pub fn get_penalty_statistics(&self) -> PenaltyStatistics { let total_penalties = self.records.len(); let warnings = self.records.iter().filter(|r| r.penalty_type == PenaltyType::Warning).count(); let fines = self.records.iter().filter(|r| r.penalty_type == PenaltyType::Fine).count(); let suspensions = self.records.iter().filter(|r| r.penalty_type == PenaltyType::Suspension).count(); let force_exits = self.records.iter().filter(|r| r.penalty_type == PenaltyType::ForceExit).count(); let slashings = self.records.iter().filter(|r| r.penalty_type == PenaltyType::Slashing).count(); let total_fines = self.records.iter().map(|r| r.fine_amount).sum(); let total_slashed = self.records.iter().map(|r| r.slashed_amount).sum(); let total_appeals = self.appeals.len(); let approved_appeals = self.appeals.iter().filter(|a| a.status == AppealStatus::Approved).count(); PenaltyStatistics { total_penalties, warnings, fines, suspensions, force_exits, slashings, total_fines, total_slashed, total_appeals, approved_appeals, } } /// 更新配置 pub fn update_config(&mut self, config: PenaltyConfig) { self.config = config; } /// 获取配置 pub fn get_config(&self) -> &PenaltyConfig { &self.config } } impl Default for PenaltyManager { fn default() -> Self { Self::new() } } /// 处罚统计 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PenaltyStatistics { /// 总处罚数 pub total_penalties: usize, /// 警告数 pub warnings: usize, /// 罚款数 pub fines: usize, /// 暂停数 pub suspensions: usize, /// 强制退出数 pub force_exits: usize, /// 削减数 pub slashings: usize, /// 总罚款金额 pub total_fines: u64, /// 总削减金额 pub total_slashed: u64, /// 总申诉数 pub total_appeals: usize, /// 通过申诉数 pub approved_appeals: usize, } #[cfg(test)] mod tests { use super::*; fn create_test_node() -> CbpNode { CbpNode { address: Address::new([1u8; 32]), did: "did:nac:test".to_string(), stake_amount: 100_000_000_000, kyc_level: 2, hardware_score: 8000, constitution_score: 80, status: CbpStatus::Active, registered_at: 1000, last_active_at: 2000, blocks_produced: 100, reputation: 0.8, } } #[test] fn test_detect_violation() { let manager = PenaltyManager::new(); let node = create_test_node(); let detection = manager.detect_violation( &node, ViolationType::DoubleSign, "Evidence of double signing".to_string(), 8, 3000, ); assert_eq!(detection.violation_type, ViolationType::DoubleSign); assert_eq!(detection.severity, 8); } #[test] fn test_execute_penalty_warning() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let detection = manager.detect_violation( &node, ViolationType::HardwareFailure, "Hardware benchmark failed".to_string(), 3, 3000, ); let result = manager.execute_penalty(&mut node, detection, 3000); assert!(result.is_ok()); let warning_count = manager.warning_counts.get(&node.address).copied().unwrap_or(0); assert_eq!(warning_count, 1); } #[test] fn test_execute_penalty_fine() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let original_stake = node.stake_amount; let detection = manager.detect_violation( &node, ViolationType::ConsecutiveMissedBlocks, "Missed 10 consecutive blocks".to_string(), 5, 3000, ); let result = manager.execute_penalty(&mut node, detection, 3000); assert!(result.is_ok()); assert!(node.stake_amount < original_stake); } #[test] fn test_execute_penalty_suspension() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let detection = manager.detect_violation( &node, ViolationType::KycExpired, "KYC expired".to_string(), 5, 3000, ); let result = manager.execute_penalty(&mut node, detection, 3000); assert!(result.is_ok()); assert_eq!(node.status, CbpStatus::Suspended); } #[test] fn test_execute_penalty_slashing() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let original_stake = node.stake_amount; let detection = manager.detect_violation( &node, ViolationType::DoubleSign, "Double signing detected".to_string(), 10, 3000, ); let result = manager.execute_penalty(&mut node, detection, 3000); assert!(result.is_ok()); assert!(node.stake_amount < original_stake); } #[test] fn test_submit_appeal() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let detection = manager.detect_violation( &node, ViolationType::ConsecutiveMissedBlocks, "Evidence".to_string(), 5, 1000, ); let penalty_id = manager.execute_penalty(&mut node, detection, 1000).expect("FIX-006: unexpected None/Err"); let result = manager.submit_appeal( penalty_id, node.address, "False positive".to_string(), "Counter evidence".to_string(), 1000 + 3600, ); assert!(result.is_ok()); } #[test] fn test_appeal_period_expired() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let detection = manager.detect_violation( &node, ViolationType::ConsecutiveMissedBlocks, "Evidence".to_string(), 5, 1000, ); let penalty_id = manager.execute_penalty(&mut node, detection, 1000).expect("FIX-006: unexpected None/Err"); // 超过申诉期限 let result = manager.submit_appeal( penalty_id, node.address, "False positive".to_string(), "Counter evidence".to_string(), 1000 + 8 * 24 * 3600, ); assert!(result.is_err()); } #[test] fn test_review_appeal() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let reviewer = Address::new([2u8; 32]); let detection = manager.detect_violation( &node, ViolationType::ConsecutiveMissedBlocks, "Evidence".to_string(), 5, 1000, ); let penalty_id = manager.execute_penalty(&mut node, detection, 1000).expect("FIX-006: unexpected None/Err"); let appeal_id = manager.submit_appeal( penalty_id, node.address, "False positive".to_string(), "Counter evidence".to_string(), 1000 + 3600, ).expect("FIX-006: unexpected None/Err"); let result = manager.review_appeal( appeal_id, true, reviewer, Some("Approved".to_string()), 1000 + 7200, ); assert!(result.is_ok()); let appeals = manager.get_appeals(Some(AppealStatus::Approved)); assert_eq!(appeals.len(), 1); } #[test] fn test_revoke_penalty() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let original_stake = node.stake_amount; let detection = manager.detect_violation( &node, ViolationType::ConsecutiveMissedBlocks, "Evidence".to_string(), 5, 1000, ); let penalty_id = manager.execute_penalty(&mut node, detection, 1000).expect("FIX-006: unexpected None/Err"); let stake_after_penalty = node.stake_amount; let result = manager.revoke_penalty(penalty_id, &mut node); assert!(result.is_ok()); assert!(node.stake_amount > stake_after_penalty); } #[test] fn test_penalty_statistics() { let mut manager = PenaltyManager::new(); let mut node = create_test_node(); let detection1 = manager.detect_violation( &node, ViolationType::HardwareFailure, "Evidence".to_string(), 3, 1000, ); manager.execute_penalty(&mut node, detection1, 1000).expect("FIX-006: unexpected None/Err"); let detection2 = manager.detect_violation( &node, ViolationType::ConsecutiveMissedBlocks, "Evidence".to_string(), 5, 2000, ); manager.execute_penalty(&mut node, detection2, 2000).expect("FIX-006: unexpected None/Err"); let stats = manager.get_penalty_statistics(); assert_eq!(stats.total_penalties, 2); assert_eq!(stats.warnings, 1); assert_eq!(stats.fines, 1); } }