380 lines
9.8 KiB
Rust
380 lines
9.8 KiB
Rust
//! NAC CBPP L1层 - Charter合约实现
|
||
//!
|
||
//! 实现CBP(Constitutional Block Producer)注册与管理合约
|
||
//! 基于CBPP技术白皮书第5章"开放生产网络(OPN)"
|
||
|
||
use nac_udm::primitives::{Address, Hash};
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
use thiserror::Error;
|
||
|
||
// 导出子模块
|
||
pub mod exit;
|
||
pub mod redemption;
|
||
pub mod reputation;
|
||
pub mod penalty;
|
||
|
||
// 重新导出常用类型
|
||
pub use exit::{ExitManager, ExitRequest, ExitRecord, ExitStatus, ExitReason};
|
||
pub use redemption::{RedemptionManager, RedemptionRequest, RedemptionRecord, RedemptionType, RedemptionStatus};
|
||
pub use reputation::{ReputationManager, ReputationEvent, ReputationChange, ReputationConfig};
|
||
pub use penalty::{PenaltyManager, ViolationType, PenaltyType, PenaltyRecord, AppealRequest};
|
||
|
||
#[derive(Debug, Error)]
|
||
pub enum CbppL1Error {
|
||
#[error("CBP already registered: {0:?}")]
|
||
AlreadyRegistered(Address),
|
||
|
||
#[error("CBP not found: {0:?}")]
|
||
NotFound(Address),
|
||
|
||
#[error("Insufficient stake: required {required}, got {actual}")]
|
||
InsufficientStake { required: u64, actual: u64 },
|
||
|
||
#[error("Invalid KYC level: {0}")]
|
||
InvalidKycLevel(u8),
|
||
|
||
#[error("Hardware benchmark failed")]
|
||
HardwareBenchmarkFailed,
|
||
|
||
#[error("Constitution test failed: score {0}/100")]
|
||
ConstitutionTestFailed(u8),
|
||
|
||
// 退出相关错误
|
||
#[error("Exit request already exists")]
|
||
ExitRequestExists,
|
||
|
||
#[error("Exit request not found")]
|
||
ExitRequestNotFound,
|
||
|
||
#[error("Review period not met")]
|
||
ReviewPeriodNotMet,
|
||
|
||
#[error("Confirmation period not met")]
|
||
ConfirmationPeriodNotMet,
|
||
|
||
// 赎回相关错误
|
||
#[error("Redemption request already exists")]
|
||
RedemptionRequestExists,
|
||
|
||
#[error("Redemption request not found")]
|
||
RedemptionRequestNotFound,
|
||
|
||
#[error("Lock period not met")]
|
||
LockPeriodNotMet,
|
||
|
||
#[error("Invalid redemption amount")]
|
||
InvalidRedemptionAmount,
|
||
|
||
#[error("Insufficient remaining stake")]
|
||
InsufficientRemainingStake,
|
||
|
||
#[error("Insufficient stake for penalty")]
|
||
InsufficientStakeForPenalty,
|
||
|
||
// 处罚相关错误
|
||
#[error("Unknown violation type")]
|
||
UnknownViolationType,
|
||
|
||
#[error("Penalty record not found")]
|
||
PenaltyRecordNotFound,
|
||
|
||
#[error("Already appealed")]
|
||
AlreadyAppealed,
|
||
|
||
#[error("Appeal period expired")]
|
||
AppealPeriodExpired,
|
||
|
||
#[error("Appeal not found")]
|
||
AppealNotFound,
|
||
|
||
#[error("Invalid address")]
|
||
InvalidAddress,
|
||
|
||
// 通用错误
|
||
#[error("Invalid status: {0}")]
|
||
InvalidStatus(String),
|
||
}
|
||
|
||
/// CBP节点状态
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum CbpStatus {
|
||
Candidate,
|
||
Active,
|
||
Suspended,
|
||
Exited,
|
||
}
|
||
|
||
/// CBP节点信息
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct CbpNode {
|
||
pub address: Address,
|
||
pub did: String,
|
||
pub stake_amount: u64,
|
||
pub kyc_level: u8,
|
||
pub hardware_score: u32,
|
||
pub constitution_score: u8,
|
||
pub status: CbpStatus,
|
||
pub registered_at: u64,
|
||
pub last_active_at: u64,
|
||
pub blocks_produced: u64,
|
||
pub reputation: f64,
|
||
}
|
||
|
||
/// CBP注册要求
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct CbpRequirements {
|
||
pub min_stake: u64,
|
||
pub min_kyc_level: u8,
|
||
pub min_hardware_score: u32,
|
||
pub min_constitution_score: u8,
|
||
}
|
||
|
||
impl Default for CbpRequirements {
|
||
fn default() -> Self {
|
||
Self {
|
||
min_stake: 100_000_000_000,
|
||
min_kyc_level: 2,
|
||
min_hardware_score: 7000,
|
||
min_constitution_score: 70,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// CBP注册与管理合约
|
||
pub struct CbpRegistry {
|
||
nodes: HashMap<Address, CbpNode>,
|
||
requirements: CbpRequirements,
|
||
active_cbps: Vec<Address>,
|
||
exit_manager: ExitManager,
|
||
redemption_manager: RedemptionManager,
|
||
reputation_manager: ReputationManager,
|
||
penalty_manager: PenaltyManager,
|
||
}
|
||
|
||
impl CbpRegistry {
|
||
pub fn new() -> Self {
|
||
Self {
|
||
nodes: HashMap::new(),
|
||
requirements: CbpRequirements::default(),
|
||
active_cbps: Vec::new(),
|
||
exit_manager: ExitManager::new(),
|
||
redemption_manager: RedemptionManager::new(),
|
||
reputation_manager: ReputationManager::new(),
|
||
penalty_manager: PenaltyManager::new(),
|
||
}
|
||
}
|
||
|
||
pub fn register_cbp(
|
||
&mut self,
|
||
address: Address,
|
||
did: String,
|
||
stake_amount: u64,
|
||
kyc_level: u8,
|
||
hardware_score: u32,
|
||
constitution_score: u8,
|
||
timestamp: u64,
|
||
) -> Result<(), CbppL1Error> {
|
||
if self.nodes.contains_key(&address) {
|
||
return Err(CbppL1Error::AlreadyRegistered(address));
|
||
}
|
||
|
||
if stake_amount < self.requirements.min_stake {
|
||
return Err(CbppL1Error::InsufficientStake {
|
||
required: self.requirements.min_stake,
|
||
actual: stake_amount,
|
||
});
|
||
}
|
||
|
||
if kyc_level < self.requirements.min_kyc_level {
|
||
return Err(CbppL1Error::InvalidKycLevel(kyc_level));
|
||
}
|
||
|
||
if hardware_score < self.requirements.min_hardware_score {
|
||
return Err(CbppL1Error::HardwareBenchmarkFailed);
|
||
}
|
||
|
||
if constitution_score < self.requirements.min_constitution_score {
|
||
return Err(CbppL1Error::ConstitutionTestFailed(constitution_score));
|
||
}
|
||
|
||
let node = CbpNode {
|
||
address,
|
||
did,
|
||
stake_amount,
|
||
kyc_level,
|
||
hardware_score,
|
||
constitution_score,
|
||
status: CbpStatus::Candidate,
|
||
registered_at: timestamp,
|
||
last_active_at: timestamp,
|
||
blocks_produced: 0,
|
||
reputation: 0.5,
|
||
};
|
||
|
||
self.nodes.insert(address, node);
|
||
Ok(())
|
||
}
|
||
|
||
pub fn activate_cbp(&mut self, address: &Address) -> Result<(), CbppL1Error> {
|
||
let node = self.nodes.get_mut(address)
|
||
.ok_or(CbppL1Error::NotFound(*address))?;
|
||
|
||
if node.status == CbpStatus::Candidate {
|
||
node.status = CbpStatus::Active;
|
||
self.active_cbps.push(*address);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
pub fn suspend_cbp(&mut self, address: &Address) -> Result<(), CbppL1Error> {
|
||
let node = self.nodes.get_mut(address)
|
||
.ok_or(CbppL1Error::NotFound(*address))?;
|
||
|
||
if node.status == CbpStatus::Active {
|
||
node.status = CbpStatus::Suspended;
|
||
self.active_cbps.retain(|addr| addr != address);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
pub fn get_cbp(&self, address: &Address) -> Option<&CbpNode> {
|
||
self.nodes.get(address)
|
||
}
|
||
|
||
pub fn get_cbp_mut(&mut self, address: &Address) -> Option<&mut CbpNode> {
|
||
self.nodes.get_mut(address)
|
||
}
|
||
|
||
pub fn get_active_cbps(&self) -> &[Address] {
|
||
&self.active_cbps
|
||
}
|
||
|
||
pub fn get_all_nodes(&self) -> Vec<&CbpNode> {
|
||
self.nodes.values().collect()
|
||
}
|
||
|
||
// 退出管理
|
||
pub fn exit_manager(&self) -> &ExitManager {
|
||
&self.exit_manager
|
||
}
|
||
|
||
pub fn exit_manager_mut(&mut self) -> &mut ExitManager {
|
||
&mut self.exit_manager
|
||
}
|
||
|
||
// 赎回管理
|
||
pub fn redemption_manager(&self) -> &RedemptionManager {
|
||
&self.redemption_manager
|
||
}
|
||
|
||
pub fn redemption_manager_mut(&mut self) -> &mut RedemptionManager {
|
||
&mut self.redemption_manager
|
||
}
|
||
|
||
// 声誉管理
|
||
pub fn reputation_manager(&self) -> &ReputationManager {
|
||
&self.reputation_manager
|
||
}
|
||
|
||
pub fn reputation_manager_mut(&mut self) -> &mut ReputationManager {
|
||
&mut self.reputation_manager
|
||
}
|
||
|
||
// 处罚管理
|
||
pub fn penalty_manager(&self) -> &PenaltyManager {
|
||
&self.penalty_manager
|
||
}
|
||
|
||
pub fn penalty_manager_mut(&mut self) -> &mut PenaltyManager {
|
||
&mut self.penalty_manager
|
||
}
|
||
}
|
||
|
||
impl Default for CbpRegistry {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_register_cbp() {
|
||
let mut registry = CbpRegistry::new();
|
||
let address = Address::new([1u8; 32]);
|
||
|
||
let result = registry.register_cbp(
|
||
address,
|
||
"did:nac:test".to_string(),
|
||
100_000_000_000,
|
||
2,
|
||
8000,
|
||
80,
|
||
1000,
|
||
);
|
||
|
||
assert!(result.is_ok());
|
||
assert!(registry.get_cbp(&address).is_some());
|
||
}
|
||
|
||
#[test]
|
||
fn test_activate_cbp() {
|
||
let mut registry = CbpRegistry::new();
|
||
let address = Address::new([1u8; 32]);
|
||
|
||
registry.register_cbp(
|
||
address,
|
||
"did:nac:test".to_string(),
|
||
100_000_000_000,
|
||
2,
|
||
8000,
|
||
80,
|
||
1000,
|
||
).unwrap();
|
||
|
||
let result = registry.activate_cbp(&address);
|
||
assert!(result.is_ok());
|
||
|
||
let node = registry.get_cbp(&address).unwrap();
|
||
assert_eq!(node.status, CbpStatus::Active);
|
||
}
|
||
|
||
#[test]
|
||
fn test_suspend_cbp() {
|
||
let mut registry = CbpRegistry::new();
|
||
let address = Address::new([1u8; 32]);
|
||
|
||
registry.register_cbp(
|
||
address,
|
||
"did:nac:test".to_string(),
|
||
100_000_000_000,
|
||
2,
|
||
8000,
|
||
80,
|
||
1000,
|
||
).unwrap();
|
||
|
||
registry.activate_cbp(&address).unwrap();
|
||
let result = registry.suspend_cbp(&address);
|
||
assert!(result.is_ok());
|
||
|
||
let node = registry.get_cbp(&address).unwrap();
|
||
assert_eq!(node.status, CbpStatus::Suspended);
|
||
}
|
||
|
||
#[test]
|
||
fn test_integrated_managers() {
|
||
let registry = CbpRegistry::new();
|
||
|
||
// 测试所有管理器都已初始化
|
||
assert!(registry.exit_manager().get_pending_requests().is_empty());
|
||
assert!(registry.redemption_manager().get_pending_requests().is_empty());
|
||
assert!(registry.penalty_manager().get_penalty_records(None).is_empty());
|
||
}
|
||
}
|