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

474 lines
14 KiB
Rust

//! 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 ExitStatus {
/// 待审核
Pending,
/// 审核通过
Approved,
/// 审核拒绝
Rejected,
/// 已确认
Confirmed,
/// 已取消
Cancelled,
}
/// 退出原因
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExitReason {
/// 主动退出
Voluntary,
/// 硬件故障
HardwareFailure,
/// 违规被迫退出
Violation,
/// 质押不足
InsufficientStake,
/// 其他原因
Other(String),
}
/// 退出申请
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExitRequest {
/// CBP地址
pub address: Address,
/// 退出原因
pub reason: ExitReason,
/// 申请时间
pub requested_at: u64,
/// 退出状态
pub status: ExitStatus,
/// 审核时间
pub reviewed_at: Option<u64>,
/// 审核人
pub reviewer: Option<Address>,
/// 审核意见
pub review_comment: Option<String>,
/// 确认时间
pub confirmed_at: Option<u64>,
}
impl ExitRequest {
pub fn new(address: Address, reason: ExitReason, timestamp: u64) -> Self {
Self {
address,
reason,
requested_at: timestamp,
status: ExitStatus::Pending,
reviewed_at: None,
reviewer: None,
review_comment: None,
confirmed_at: None,
}
}
}
/// 退出记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExitRecord {
/// CBP地址
pub address: Address,
/// 退出原因
pub reason: ExitReason,
/// 申请时间
pub requested_at: u64,
/// 确认时间
pub confirmed_at: u64,
/// 质押金额
pub stake_amount: u64,
/// 赎回金额
pub redemption_amount: u64,
/// 处罚金额
pub penalty_amount: u64,
/// 声誉分数
pub final_reputation: f64,
/// 生产区块数
pub blocks_produced: u64,
}
/// 退出管理器
pub struct ExitManager {
/// 退出申请
requests: HashMap<Address, ExitRequest>,
/// 退出记录
records: Vec<ExitRecord>,
/// 审核等待期(秒)
review_period: u64,
/// 确认等待期(秒)
confirmation_period: u64,
}
impl ExitManager {
pub fn new() -> Self {
Self {
requests: HashMap::new(),
records: Vec::new(),
review_period: 7 * 24 * 3600, // 7天
confirmation_period: 14 * 24 * 3600, // 14天
}
}
/// 提交退出申请
pub fn submit_exit_request(
&mut self,
node: &CbpNode,
reason: ExitReason,
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 == ExitStatus::Pending || existing.status == ExitStatus::Approved {
return Err(CbppL1Error::ExitRequestExists);
}
}
// 创建退出申请
let request = ExitRequest::new(node.address, reason, timestamp);
self.requests.insert(node.address, request);
Ok(())
}
/// 审核退出申请
pub fn review_exit_request(
&mut self,
address: &Address,
approved: bool,
reviewer: Address,
comment: Option<String>,
timestamp: u64,
) -> Result<(), CbppL1Error> {
let request = self.requests.get_mut(address)
.ok_or(CbppL1Error::ExitRequestNotFound)?;
// 检查申请状态
if request.status != ExitStatus::Pending {
return Err(CbppL1Error::InvalidStatus(
format!("Request status is {:?}, expected Pending", request.status)
));
}
// 检查审核等待期
if timestamp < request.requested_at + self.review_period {
return Err(CbppL1Error::ReviewPeriodNotMet);
}
// 更新申请状态
request.status = if approved {
ExitStatus::Approved
} else {
ExitStatus::Rejected
};
request.reviewed_at = Some(timestamp);
request.reviewer = Some(reviewer);
request.review_comment = comment;
Ok(())
}
/// 确认退出
pub fn confirm_exit(
&mut self,
address: &Address,
node: &CbpNode,
redemption_amount: u64,
penalty_amount: u64,
timestamp: u64,
) -> Result<(), CbppL1Error> {
let request = self.requests.get_mut(address)
.ok_or(CbppL1Error::ExitRequestNotFound)?;
// 检查申请状态
if request.status != ExitStatus::Approved {
return Err(CbppL1Error::InvalidStatus(
format!("Request status is {:?}, expected Approved", request.status)
));
}
// 检查确认等待期
let reviewed_at = request.reviewed_at.ok_or(CbppL1Error::InvalidStatus(
"Request not reviewed".to_string()
))?;
if timestamp < reviewed_at + self.confirmation_period {
return Err(CbppL1Error::ConfirmationPeriodNotMet);
}
// 更新申请状态
request.status = ExitStatus::Confirmed;
request.confirmed_at = Some(timestamp);
// 创建退出记录
let record = ExitRecord {
address: *address,
reason: request.reason.clone(),
requested_at: request.requested_at,
confirmed_at: timestamp,
stake_amount: node.stake_amount,
redemption_amount,
penalty_amount,
final_reputation: node.reputation,
blocks_produced: node.blocks_produced,
};
self.records.push(record);
Ok(())
}
/// 取消退出申请
pub fn cancel_exit_request(&mut self, address: &Address) -> Result<(), CbppL1Error> {
let request = self.requests.get_mut(address)
.ok_or(CbppL1Error::ExitRequestNotFound)?;
// 只能取消待审核的申请
if request.status != ExitStatus::Pending {
return Err(CbppL1Error::InvalidStatus(
format!("Cannot cancel request with status {:?}", request.status)
));
}
request.status = ExitStatus::Cancelled;
Ok(())
}
/// 获取退出申请
pub fn get_exit_request(&self, address: &Address) -> Option<&ExitRequest> {
self.requests.get(address)
}
/// 获取所有待审核的申请
pub fn get_pending_requests(&self) -> Vec<&ExitRequest> {
self.requests.values()
.filter(|r| r.status == ExitStatus::Pending)
.collect()
}
/// 获取退出记录
pub fn get_exit_records(&self, address: Option<&Address>) -> Vec<&ExitRecord> {
if let Some(addr) = address {
self.records.iter()
.filter(|r| &r.address == addr)
.collect()
} else {
self.records.iter().collect()
}
}
/// 获取退出统计
pub fn get_exit_statistics(&self) -> ExitStatistics {
let total_exits = self.records.len();
let voluntary_exits = self.records.iter()
.filter(|r| r.reason == ExitReason::Voluntary)
.count();
let violation_exits = self.records.iter()
.filter(|r| r.reason == ExitReason::Violation)
.count();
let total_penalties = self.records.iter()
.map(|r| r.penalty_amount)
.sum();
ExitStatistics {
total_exits,
voluntary_exits,
violation_exits,
total_penalties,
}
}
}
impl Default for ExitManager {
fn default() -> Self {
Self::new()
}
}
/// 退出统计
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExitStatistics {
/// 总退出数
pub total_exits: usize,
/// 主动退出数
pub voluntary_exits: usize,
/// 违规退出数
pub violation_exits: usize,
/// 总处罚金额
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_exit_request() {
let mut manager = ExitManager::new();
let node = create_test_node();
let result = manager.submit_exit_request(
&node,
ExitReason::Voluntary,
3000,
);
assert!(result.is_ok());
let request = manager.get_exit_request(&node.address).expect("FIX-006: unexpected None/Err");
assert_eq!(request.status, ExitStatus::Pending);
assert_eq!(request.reason, ExitReason::Voluntary);
}
#[test]
fn test_duplicate_exit_request() {
let mut manager = ExitManager::new();
let node = create_test_node();
manager.submit_exit_request(&node, ExitReason::Voluntary, 3000).expect("FIX-006: unexpected None/Err");
let result = manager.submit_exit_request(&node, ExitReason::Voluntary, 3000);
assert!(result.is_err());
}
#[test]
fn test_review_exit_request() {
let mut manager = ExitManager::new();
let node = create_test_node();
let reviewer = Address::new([2u8; 32]);
manager.submit_exit_request(&node, ExitReason::Voluntary, 1000).expect("FIX-006: unexpected None/Err");
// 审核(等待期满足)
let result = manager.review_exit_request(
&node.address,
true,
reviewer,
Some("Approved".to_string()),
1000 + 7 * 24 * 3600,
);
assert!(result.is_ok());
let request = manager.get_exit_request(&node.address).expect("FIX-006: unexpected None/Err");
assert_eq!(request.status, ExitStatus::Approved);
}
#[test]
fn test_review_period_not_met() {
let mut manager = ExitManager::new();
let node = create_test_node();
let reviewer = Address::new([2u8; 32]);
manager.submit_exit_request(&node, ExitReason::Voluntary, 1000).expect("FIX-006: unexpected None/Err");
// 审核(等待期不满足)
let result = manager.review_exit_request(
&node.address,
true,
reviewer,
None,
1000 + 3600, // 只过了1小时
);
assert!(result.is_err());
}
#[test]
fn test_confirm_exit() {
let mut manager = ExitManager::new();
let node = create_test_node();
let reviewer = Address::new([2u8; 32]);
manager.submit_exit_request(&node, ExitReason::Voluntary, 1000).expect("FIX-006: unexpected None/Err");
manager.review_exit_request(
&node.address,
true,
reviewer,
None,
1000 + 7 * 24 * 3600,
).expect("FIX-006: unexpected None/Err");
// 确认退出
let result = manager.confirm_exit(
&node.address,
&node,
95_000_000_000,
5_000_000_000,
1000 + 21 * 24 * 3600,
);
assert!(result.is_ok());
let records = manager.get_exit_records(Some(&node.address));
assert_eq!(records.len(), 1);
assert_eq!(records[0].redemption_amount, 95_000_000_000);
}
#[test]
fn test_cancel_exit_request() {
let mut manager = ExitManager::new();
let node = create_test_node();
manager.submit_exit_request(&node, ExitReason::Voluntary, 1000).expect("FIX-006: unexpected None/Err");
let result = manager.cancel_exit_request(&node.address);
assert!(result.is_ok());
let request = manager.get_exit_request(&node.address).expect("FIX-006: unexpected None/Err");
assert_eq!(request.status, ExitStatus::Cancelled);
}
#[test]
fn test_get_pending_requests() {
let mut manager = ExitManager::new();
let node1 = create_test_node();
let mut node2 = create_test_node();
node2.address = Address::new([2u8; 32]);
manager.submit_exit_request(&node1, ExitReason::Voluntary, 1000).expect("FIX-006: unexpected None/Err");
manager.submit_exit_request(&node2, ExitReason::HardwareFailure, 1000).expect("FIX-006: unexpected None/Err");
let pending = manager.get_pending_requests();
assert_eq!(pending.len(), 2);
}
#[test]
fn test_exit_statistics() {
let mut manager = ExitManager::new();
let node = create_test_node();
let reviewer = Address::new([2u8; 32]);
manager.submit_exit_request(&node, ExitReason::Voluntary, 1000).expect("FIX-006: unexpected None/Err");
manager.review_exit_request(&node.address, true, reviewer, None, 1000 + 7 * 24 * 3600).expect("FIX-006: unexpected None/Err");
manager.confirm_exit(&node.address, &node, 95_000_000_000, 5_000_000_000, 1000 + 21 * 24 * 3600).expect("FIX-006: unexpected None/Err");
let stats = manager.get_exit_statistics();
assert_eq!(stats.total_exits, 1);
assert_eq!(stats.voluntary_exits, 1);
assert_eq!(stats.total_penalties, 5_000_000_000);
}
}