313 lines
8.5 KiB
Rust
313 lines
8.5 KiB
Rust
// ACC-Compliance: 资产合规协议(Compliance Protocol)
|
||
//
|
||
// NAC原生的资产合规标准,用于KYC/AML等合规检查
|
||
// 100% NAC原生协议,不是任何现有标准的实现
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// 合规状态
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ComplianceStatus {
|
||
/// 待审核
|
||
Pending,
|
||
/// 已通过
|
||
Approved,
|
||
/// 已拒绝
|
||
Rejected,
|
||
/// 已过期
|
||
Expired,
|
||
}
|
||
|
||
/// KYC级别
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum KYCLevel {
|
||
/// 未认证
|
||
None,
|
||
/// 基础认证
|
||
Basic,
|
||
/// 中级认证
|
||
Intermediate,
|
||
/// 高级认证
|
||
Advanced,
|
||
}
|
||
|
||
/// 合规记录
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ComplianceRecord {
|
||
/// 地址
|
||
pub address: String,
|
||
/// KYC级别
|
||
pub kyc_level: KYCLevel,
|
||
/// 合规状态
|
||
pub status: ComplianceStatus,
|
||
/// 认证时间
|
||
pub verified_at: u64,
|
||
/// 过期时间
|
||
pub expires_at: u64,
|
||
/// 司法辖区
|
||
pub jurisdiction: String,
|
||
/// 是否在黑名单
|
||
pub blacklisted: bool,
|
||
}
|
||
|
||
/// 合规错误类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ComplianceError {
|
||
/// 记录不存在
|
||
RecordNotFound,
|
||
/// 记录已存在
|
||
RecordAlreadyExists,
|
||
/// 合规检查失败
|
||
ComplianceFailed,
|
||
/// KYC级别不足
|
||
InsufficientKYCLevel,
|
||
/// 地址在黑名单
|
||
Blacklisted,
|
||
/// 认证已过期
|
||
Expired,
|
||
}
|
||
|
||
/// 合规状态
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ComplianceState {
|
||
/// 合规记录映射 (address -> ComplianceRecord)
|
||
pub records: HashMap<String, ComplianceRecord>,
|
||
/// 黑名单
|
||
pub blacklist: Vec<String>,
|
||
}
|
||
|
||
/// ACC-Compliance接口
|
||
pub trait ACCCompliance {
|
||
/// 添加合规记录
|
||
fn add_record(&mut self, record: ComplianceRecord) -> Result<(), ComplianceError>;
|
||
|
||
/// 获取合规记录
|
||
fn get_record(&self, address: &str) -> Result<ComplianceRecord, ComplianceError>;
|
||
|
||
/// 更新KYC级别
|
||
fn update_kyc_level(&mut self, address: &str, level: KYCLevel) -> Result<(), ComplianceError>;
|
||
|
||
/// 检查合规性
|
||
fn check_compliance(&self, address: &str) -> Result<bool, ComplianceError>;
|
||
|
||
/// 添加到黑名单
|
||
fn add_to_blacklist(&mut self, address: &str) -> Result<(), ComplianceError>;
|
||
|
||
/// 从黑名单移除
|
||
fn remove_from_blacklist(&mut self, address: &str) -> Result<(), ComplianceError>;
|
||
|
||
/// 检查是否在黑名单
|
||
fn is_blacklisted(&self, address: &str) -> bool;
|
||
|
||
/// 更新认证状态
|
||
fn update_status(&mut self, address: &str, status: ComplianceStatus) -> Result<(), ComplianceError>;
|
||
}
|
||
|
||
/// ACC-Compliance标准实现
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ComplianceToken {
|
||
state: ComplianceState,
|
||
}
|
||
|
||
impl ComplianceToken {
|
||
/// 创建新的合规管理器
|
||
pub fn new() -> Self {
|
||
Self {
|
||
state: ComplianceState {
|
||
records: HashMap::new(),
|
||
blacklist: Vec::new(),
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 获取状态的可变引用
|
||
pub fn state_mut(&mut self) -> &mut ComplianceState {
|
||
&mut self.state
|
||
}
|
||
|
||
/// 获取状态的不可变引用
|
||
pub fn state(&self) -> &ComplianceState {
|
||
&self.state
|
||
}
|
||
}
|
||
|
||
impl Default for ComplianceToken {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
impl ACCCompliance for ComplianceToken {
|
||
fn add_record(&mut self, record: ComplianceRecord) -> Result<(), ComplianceError> {
|
||
if self.state.records.contains_key(&record.address) {
|
||
return Err(ComplianceError::RecordAlreadyExists);
|
||
}
|
||
|
||
self.state.records.insert(record.address.clone(), record);
|
||
Ok(())
|
||
}
|
||
|
||
fn get_record(&self, address: &str) -> Result<ComplianceRecord, ComplianceError> {
|
||
self.state
|
||
.records
|
||
.get(address)
|
||
.cloned()
|
||
.ok_or(ComplianceError::RecordNotFound)
|
||
}
|
||
|
||
fn update_kyc_level(&mut self, address: &str, level: KYCLevel) -> Result<(), ComplianceError> {
|
||
let mut record = self.get_record(address)?;
|
||
record.kyc_level = level;
|
||
self.state.records.insert(address.to_string(), record);
|
||
Ok(())
|
||
}
|
||
|
||
fn check_compliance(&self, address: &str) -> Result<bool, ComplianceError> {
|
||
if self.is_blacklisted(address) {
|
||
return Err(ComplianceError::Blacklisted);
|
||
}
|
||
|
||
let record = self.get_record(address)?;
|
||
|
||
if record.status != ComplianceStatus::Approved {
|
||
return Ok(false);
|
||
}
|
||
|
||
// 检查是否过期(使用当前时间)
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
|
||
if now > record.expires_at {
|
||
return Err(ComplianceError::Expired);
|
||
}
|
||
|
||
Ok(true)
|
||
}
|
||
|
||
fn add_to_blacklist(&mut self, address: &str) -> Result<(), ComplianceError> {
|
||
if !self.state.blacklist.contains(&address.to_string()) {
|
||
self.state.blacklist.push(address.to_string());
|
||
}
|
||
|
||
// 更新记录的黑名单状态
|
||
if let Ok(mut record) = self.get_record(address) {
|
||
record.blacklisted = true;
|
||
self.state.records.insert(address.to_string(), record);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn remove_from_blacklist(&mut self, address: &str) -> Result<(), ComplianceError> {
|
||
self.state.blacklist.retain(|a| a != address);
|
||
|
||
// 更新记录的黑名单状态
|
||
if let Ok(mut record) = self.get_record(address) {
|
||
record.blacklisted = false;
|
||
self.state.records.insert(address.to_string(), record);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn is_blacklisted(&self, address: &str) -> bool {
|
||
self.state.blacklist.contains(&address.to_string())
|
||
}
|
||
|
||
fn update_status(&mut self, address: &str, status: ComplianceStatus) -> Result<(), ComplianceError> {
|
||
let mut record = self.get_record(address)?;
|
||
record.status = status;
|
||
self.state.records.insert(address.to_string(), record);
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn create_test_record() -> ComplianceRecord {
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
|
||
ComplianceRecord {
|
||
address: "alice".to_string(),
|
||
kyc_level: KYCLevel::Basic,
|
||
status: ComplianceStatus::Approved,
|
||
verified_at: now,
|
||
expires_at: now + 31536000, // 1年后过期
|
||
jurisdiction: "US".to_string(),
|
||
blacklisted: false,
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_add_record() {
|
||
let mut compliance = ComplianceToken::new();
|
||
let record = create_test_record();
|
||
assert!(compliance.add_record(record).is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_update_kyc_level() {
|
||
let mut compliance = ComplianceToken::new();
|
||
let record = create_test_record();
|
||
compliance.add_record(record).unwrap();
|
||
|
||
assert!(compliance
|
||
.update_kyc_level("alice", KYCLevel::Advanced)
|
||
.is_ok());
|
||
assert_eq!(
|
||
compliance.get_record("alice").unwrap().kyc_level,
|
||
KYCLevel::Advanced
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_check_compliance() {
|
||
let mut compliance = ComplianceToken::new();
|
||
let record = create_test_record();
|
||
compliance.add_record(record).unwrap();
|
||
|
||
assert!(compliance.check_compliance("alice").is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_blacklist() {
|
||
let mut compliance = ComplianceToken::new();
|
||
let record = create_test_record();
|
||
compliance.add_record(record).unwrap();
|
||
|
||
assert!(compliance.add_to_blacklist("alice").is_ok());
|
||
assert!(compliance.is_blacklisted("alice"));
|
||
|
||
assert_eq!(
|
||
compliance.check_compliance("alice"),
|
||
Err(ComplianceError::Blacklisted)
|
||
);
|
||
|
||
assert!(compliance.remove_from_blacklist("alice").is_ok());
|
||
assert!(!compliance.is_blacklisted("alice"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_update_status() {
|
||
let mut compliance = ComplianceToken::new();
|
||
let record = create_test_record();
|
||
compliance.add_record(record).unwrap();
|
||
|
||
assert!(compliance
|
||
.update_status("alice", ComplianceStatus::Rejected)
|
||
.is_ok());
|
||
assert_eq!(
|
||
compliance.get_record("alice").unwrap().status,
|
||
ComplianceStatus::Rejected
|
||
);
|
||
}
|
||
}
|