//! 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, Serialize, Deserialize)] pub enum RedemptionStatus { /// 待处理 Pending, /// 处理中 Processing, /// 已完成 Completed, /// 已取消 Cancelled, /// 失败 Failed, } /// 赎回类型 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum RedemptionType { /// 全额赎回 Full, /// 部分赎回 Partial, /// 紧急赎回 Emergency, } /// 赎回请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RedemptionRequest { /// CBP地址 pub address: Address, /// 赎回类型 pub redemption_type: RedemptionType, /// 请求赎回金额 pub requested_amount: u64, /// 实际赎回金额 pub actual_amount: Option, /// 处罚金额 pub penalty_amount: Option, /// 请求时间 pub requested_at: u64, /// 赎回状态 pub status: RedemptionStatus, /// 完成时间 pub completed_at: Option, /// 失败原因 pub failure_reason: Option, } impl RedemptionRequest { pub fn new( address: Address, redemption_type: RedemptionType, requested_amount: u64, timestamp: u64, ) -> Self { Self { address, redemption_type, requested_amount, actual_amount: None, penalty_amount: None, requested_at: timestamp, status: RedemptionStatus::Pending, completed_at: None, failure_reason: None, } } } /// 赎回记录 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RedemptionRecord { /// CBP地址 pub address: Address, /// 赎回类型 pub redemption_type: RedemptionType, /// 原始质押金额 pub original_stake: u64, /// 赎回金额 pub redeemed_amount: u64, /// 处罚金额 pub penalty_amount: u64, /// 请求时间 pub requested_at: u64, /// 完成时间 pub completed_at: u64, /// 声誉分数 pub reputation_at_redemption: f64, } /// 赎回条件 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RedemptionConditions { /// 最小锁定期(秒) pub min_lock_period: u64, /// 部分赎回最小保留金额 pub min_remaining_stake: u64, /// 紧急赎回处罚率(百分比) pub emergency_penalty_rate: u8, /// 声誉处罚阈值 pub reputation_penalty_threshold: f64, /// 低声誉处罚率(百分比) pub low_reputation_penalty_rate: u8, } impl Default for RedemptionConditions { fn default() -> Self { Self { min_lock_period: 30 * 24 * 3600, // 30天 min_remaining_stake: 50_000_000_000, // 50 XIC emergency_penalty_rate: 20, // 20% reputation_penalty_threshold: 0.5, low_reputation_penalty_rate: 10, // 10% } } } /// 赎回管理器 pub struct RedemptionManager { /// 赎回请求 requests: HashMap, /// 赎回记录 records: Vec, /// 赎回条件 conditions: RedemptionConditions, } impl RedemptionManager { pub fn new() -> Self { Self { requests: HashMap::new(), records: Vec::new(), conditions: RedemptionConditions::default(), } } pub fn with_conditions(conditions: RedemptionConditions) -> Self { Self { requests: HashMap::new(), records: Vec::new(), conditions, } } /// 提交赎回请求 pub fn submit_redemption_request( &mut self, node: &CbpNode, redemption_type: RedemptionType, requested_amount: u64, timestamp: u64, ) -> Result<(), CbppL1Error> { // 检查节点状态 if node.status == CbpStatus::Exited { return Err(CbppL1Error::InvalidStatus("Node already exited".to_string())); } // 检查是否已有待处理的请求 if let Some(existing) = self.requests.get(&node.address) { if existing.status == RedemptionStatus::Pending || existing.status == RedemptionStatus::Processing { return Err(CbppL1Error::RedemptionRequestExists); } } // 检查锁定期 if timestamp < node.registered_at + self.conditions.min_lock_period { return Err(CbppL1Error::LockPeriodNotMet); } // 检查赎回金额 if requested_amount > node.stake_amount { return Err(CbppL1Error::InvalidRedemptionAmount); } // 检查部分赎回的最小保留金额 if redemption_type == RedemptionType::Partial { let remaining = node.stake_amount - requested_amount; if remaining < self.conditions.min_remaining_stake { return Err(CbppL1Error::InsufficientRemainingStake); } } // 创建赎回请求 let request = RedemptionRequest::new( node.address, redemption_type, requested_amount, timestamp, ); self.requests.insert(node.address, request); Ok(()) } /// 处理赎回请求 pub fn process_redemption( &mut self, address: &Address, node: &CbpNode, timestamp: u64, ) -> Result<(u64, u64), CbppL1Error> { // 检查请求状态并保存信息 let (redemption_type, requested_amount, requested_at) = { let request = self.requests.get_mut(address) .ok_or(CbppL1Error::RedemptionRequestNotFound)?; // 检查请求状态 if request.status != RedemptionStatus::Pending { return Err(CbppL1Error::InvalidStatus( format!("Request status is {:?}, expected Pending", request.status) )); } // 更新状态为处理中 request.status = RedemptionStatus::Processing; (request.redemption_type, request.requested_amount, request.requested_at) }; // 计算赎回金额和处罚 let (actual_amount, penalty) = self.calculate_redemption( redemption_type, requested_amount, node.reputation, ); // 更新请求 let request = self.requests.get_mut(address).unwrap(); request.actual_amount = Some(actual_amount); request.penalty_amount = Some(penalty); request.status = RedemptionStatus::Completed; request.completed_at = Some(timestamp); // 创建赎回记录 let record = RedemptionRecord { address: *address, redemption_type, original_stake: node.stake_amount, redeemed_amount: actual_amount, penalty_amount: penalty, requested_at, completed_at: timestamp, reputation_at_redemption: node.reputation, }; self.records.push(record); Ok((actual_amount, penalty)) } /// 计算赎回金额 fn calculate_redemption( &self, redemption_type: RedemptionType, requested_amount: u64, reputation: f64, ) -> (u64, u64) { let mut penalty = 0u64; // 紧急赎回处罚 if redemption_type == RedemptionType::Emergency { penalty += (requested_amount as f64 * self.conditions.emergency_penalty_rate as f64 / 100.0) as u64; } // 低声誉处罚 if reputation < self.conditions.reputation_penalty_threshold { penalty += (requested_amount as f64 * self.conditions.low_reputation_penalty_rate as f64 / 100.0) as u64; } let actual_amount = requested_amount.saturating_sub(penalty); (actual_amount, penalty) } /// 取消赎回请求 pub fn cancel_redemption_request(&mut self, address: &Address) -> Result<(), CbppL1Error> { let request = self.requests.get_mut(address) .ok_or(CbppL1Error::RedemptionRequestNotFound)?; // 只能取消待处理的请求 if request.status != RedemptionStatus::Pending { return Err(CbppL1Error::InvalidStatus( format!("Cannot cancel request with status {:?}", request.status) )); } request.status = RedemptionStatus::Cancelled; Ok(()) } /// 获取赎回请求 pub fn get_redemption_request(&self, address: &Address) -> Option<&RedemptionRequest> { self.requests.get(address) } /// 获取所有待处理的请求 pub fn get_pending_requests(&self) -> Vec<&RedemptionRequest> { self.requests.values() .filter(|r| r.status == RedemptionStatus::Pending) .collect() } /// 获取赎回记录 pub fn get_redemption_records(&self, address: Option<&Address>) -> Vec<&RedemptionRecord> { if let Some(addr) = address { self.records.iter() .filter(|r| &r.address == addr) .collect() } else { self.records.iter().collect() } } /// 获取赎回统计 pub fn get_redemption_statistics(&self) -> RedemptionStatistics { let total_redemptions = self.records.len(); let full_redemptions = self.records.iter() .filter(|r| r.redemption_type == RedemptionType::Full) .count(); let partial_redemptions = self.records.iter() .filter(|r| r.redemption_type == RedemptionType::Partial) .count(); let emergency_redemptions = self.records.iter() .filter(|r| r.redemption_type == RedemptionType::Emergency) .count(); let total_redeemed = self.records.iter() .map(|r| r.redeemed_amount) .sum(); let total_penalties = self.records.iter() .map(|r| r.penalty_amount) .sum(); RedemptionStatistics { total_redemptions, full_redemptions, partial_redemptions, emergency_redemptions, total_redeemed, total_penalties, } } /// 更新赎回条件 pub fn update_conditions(&mut self, conditions: RedemptionConditions) { self.conditions = conditions; } /// 获取赎回条件 pub fn get_conditions(&self) -> &RedemptionConditions { &self.conditions } } impl Default for RedemptionManager { fn default() -> Self { Self::new() } } /// 赎回统计 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RedemptionStatistics { /// 总赎回数 pub total_redemptions: usize, /// 全额赎回数 pub full_redemptions: usize, /// 部分赎回数 pub partial_redemptions: usize, /// 紧急赎回数 pub emergency_redemptions: usize, /// 总赎回金额 pub total_redeemed: u64, /// 总处罚金额 pub total_penalties: u64, } #[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_submit_redemption_request() { let mut manager = RedemptionManager::new(); let node = create_test_node(); // 满足锁定期 let timestamp = node.registered_at + 31 * 24 * 3600; let result = manager.submit_redemption_request( &node, RedemptionType::Full, node.stake_amount, timestamp, ); assert!(result.is_ok()); let request = manager.get_redemption_request(&node.address).unwrap(); assert_eq!(request.status, RedemptionStatus::Pending); } #[test] fn test_lock_period_not_met() { let mut manager = RedemptionManager::new(); let node = create_test_node(); // 不满足锁定期 let timestamp = node.registered_at + 10 * 24 * 3600; let result = manager.submit_redemption_request( &node, RedemptionType::Full, node.stake_amount, timestamp, ); assert!(result.is_err()); } #[test] fn test_partial_redemption_insufficient_remaining() { let mut manager = RedemptionManager::new(); let node = create_test_node(); let timestamp = node.registered_at + 31 * 24 * 3600; // 赎回金额太多,剩余不足 let result = manager.submit_redemption_request( &node, RedemptionType::Partial, 60_000_000_000, // 剩余40XIC,小于最小保留50XIC timestamp, ); assert!(result.is_err()); } #[test] fn test_process_redemption_full() { let mut manager = RedemptionManager::new(); let node = create_test_node(); let timestamp = node.registered_at + 31 * 24 * 3600; manager.submit_redemption_request( &node, RedemptionType::Full, node.stake_amount, timestamp, ).unwrap(); let result = manager.process_redemption(&node.address, &node, timestamp + 100); assert!(result.is_ok()); let (actual, penalty) = result.unwrap(); assert_eq!(actual + penalty, node.stake_amount); } #[test] fn test_process_redemption_emergency() { let mut manager = RedemptionManager::new(); let node = create_test_node(); let timestamp = node.registered_at + 31 * 24 * 3600; manager.submit_redemption_request( &node, RedemptionType::Emergency, node.stake_amount, timestamp, ).unwrap(); let result = manager.process_redemption(&node.address, &node, timestamp + 100); assert!(result.is_ok()); let (actual, penalty) = result.unwrap(); // 紧急赎回有20%处罚 assert_eq!(penalty, node.stake_amount * 20 / 100); assert_eq!(actual, node.stake_amount - penalty); } #[test] fn test_process_redemption_low_reputation() { let mut manager = RedemptionManager::new(); let mut node = create_test_node(); node.reputation = 0.3; // 低于0.5阈值 let timestamp = node.registered_at + 31 * 24 * 3600; manager.submit_redemption_request( &node, RedemptionType::Full, node.stake_amount, timestamp, ).unwrap(); let result = manager.process_redemption(&node.address, &node, timestamp + 100); assert!(result.is_ok()); let (actual, penalty) = result.unwrap(); // 低声誉有10%处罚 assert_eq!(penalty, node.stake_amount * 10 / 100); } #[test] fn test_cancel_redemption_request() { let mut manager = RedemptionManager::new(); let node = create_test_node(); let timestamp = node.registered_at + 31 * 24 * 3600; manager.submit_redemption_request( &node, RedemptionType::Full, node.stake_amount, timestamp, ).unwrap(); let result = manager.cancel_redemption_request(&node.address); assert!(result.is_ok()); let request = manager.get_redemption_request(&node.address).unwrap(); assert_eq!(request.status, RedemptionStatus::Cancelled); } #[test] fn test_redemption_statistics() { let mut manager = RedemptionManager::new(); let node = create_test_node(); let timestamp = node.registered_at + 31 * 24 * 3600; manager.submit_redemption_request( &node, RedemptionType::Full, node.stake_amount, timestamp, ).unwrap(); manager.process_redemption(&node.address, &node, timestamp + 100).unwrap(); let stats = manager.get_redemption_statistics(); assert_eq!(stats.total_redemptions, 1); assert_eq!(stats.full_redemptions, 1); } #[test] fn test_update_conditions() { let mut manager = RedemptionManager::new(); let new_conditions = RedemptionConditions { min_lock_period: 60 * 24 * 3600, // 60天 min_remaining_stake: 80_000_000_000, emergency_penalty_rate: 30, reputation_penalty_threshold: 0.6, low_reputation_penalty_rate: 15, }; manager.update_conditions(new_conditions.clone()); let conditions = manager.get_conditions(); assert_eq!(conditions.min_lock_period, 60 * 24 * 3600); assert_eq!(conditions.emergency_penalty_rate, 30); } }