NAC_Blockchain/nac-cbpp-l1/src/redemption.rs

579 lines
17 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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<u64>,
/// 处罚金额
pub penalty_amount: Option<u64>,
/// 请求时间
pub requested_at: u64,
/// 赎回状态
pub status: RedemptionStatus,
/// 完成时间
pub completed_at: Option<u64>,
/// 失败原因
pub failure_reason: Option<String>,
}
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<Address, RedemptionRequest>,
/// 赎回记录
records: Vec<RedemptionRecord>,
/// 赎回条件
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);
}
}