673 lines
18 KiB
Rust
673 lines
18 KiB
Rust
// ACC-Custody: 资产托管协议(Custody Protocol)
|
||
//
|
||
// NAC原生的资产托管标准,用于多签托管和安全保管
|
||
// 100% NAC原生协议,不是任何现有标准的实现
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// 托管状态
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum CustodyStatus {
|
||
/// 活跃
|
||
Active,
|
||
/// 已暂停
|
||
Suspended,
|
||
/// 已终止
|
||
Terminated,
|
||
/// 待审核
|
||
Pending,
|
||
}
|
||
|
||
/// 托管类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum CustodyType {
|
||
/// 单一托管人
|
||
Single,
|
||
/// 多签托管(M-of-N)
|
||
MultiSig { required: u8, total: u8 },
|
||
/// 时间锁托管
|
||
TimeLock { unlock_at: u64 },
|
||
/// 条件托管
|
||
Conditional,
|
||
}
|
||
|
||
/// 托管操作类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum CustodyOperation {
|
||
/// 存入
|
||
Deposit,
|
||
/// 取出
|
||
Withdraw,
|
||
/// 转移
|
||
Transfer,
|
||
/// 暂停
|
||
Suspend,
|
||
/// 恢复
|
||
Resume,
|
||
/// 终止
|
||
Terminate,
|
||
}
|
||
|
||
/// 托管记录
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct CustodyRecord {
|
||
/// 托管ID
|
||
pub custody_id: String,
|
||
/// 资产ID
|
||
pub asset_id: String,
|
||
/// 所有者地址
|
||
pub owner: String,
|
||
/// 托管人列表
|
||
pub custodians: Vec<String>,
|
||
/// 托管类型
|
||
pub custody_type: CustodyType,
|
||
/// 托管状态
|
||
pub status: CustodyStatus,
|
||
/// 创建时间
|
||
pub created_at: u64,
|
||
/// 更新时间
|
||
pub updated_at: u64,
|
||
/// 托管金额(如果是代币)
|
||
pub amount: Option<u128>,
|
||
/// 托管条件
|
||
pub conditions: Vec<String>,
|
||
/// 操作历史
|
||
pub operations: Vec<CustodyOperationRecord>,
|
||
}
|
||
|
||
/// 托管操作记录
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct CustodyOperationRecord {
|
||
/// 操作类型
|
||
pub operation: CustodyOperation,
|
||
/// 操作发起人
|
||
pub initiator: String,
|
||
/// 签名列表
|
||
pub signatures: Vec<String>,
|
||
/// 操作时间
|
||
pub timestamp: u64,
|
||
/// 操作数据
|
||
pub data: Option<String>,
|
||
}
|
||
|
||
/// 托管错误类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum CustodyError {
|
||
/// 托管记录不存在
|
||
RecordNotFound,
|
||
/// 托管记录已存在
|
||
RecordAlreadyExists,
|
||
/// 资产不存在
|
||
AssetNotFound,
|
||
/// 未授权操作
|
||
Unauthorized,
|
||
/// 签名不足
|
||
InsufficientSignatures,
|
||
/// 托管已暂停
|
||
CustodySuspended,
|
||
/// 托管已终止
|
||
CustodyTerminated,
|
||
/// 时间锁未解锁
|
||
TimeLockNotUnlocked,
|
||
/// 条件未满足
|
||
ConditionsNotMet,
|
||
/// 无效的托管人
|
||
InvalidCustodian,
|
||
}
|
||
|
||
/// 托管状态
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct CustodyState {
|
||
/// 托管记录映射 (custody_id -> CustodyRecord)
|
||
pub custodies: HashMap<String, CustodyRecord>,
|
||
/// 资产托管映射 (asset_id -> custody_id)
|
||
pub asset_custodies: HashMap<String, String>,
|
||
/// 所有者托管列表 (owner -> [custody_ids])
|
||
pub owner_custodies: HashMap<String, Vec<String>>,
|
||
/// 托管人托管列表 (custodian -> [custody_ids])
|
||
pub custodian_custodies: HashMap<String, Vec<String>>,
|
||
}
|
||
|
||
/// ACC-Custody接口
|
||
pub trait ACCCustody {
|
||
/// 创建托管
|
||
fn create_custody(&mut self, record: CustodyRecord) -> Result<(), CustodyError>;
|
||
|
||
/// 获取托管记录
|
||
fn get_custody(&self, custody_id: &str) -> Result<CustodyRecord, CustodyError>;
|
||
|
||
/// 获取资产的托管记录
|
||
fn get_asset_custody(&self, asset_id: &str) -> Result<CustodyRecord, CustodyError>;
|
||
|
||
/// 存入资产
|
||
fn deposit(
|
||
&mut self,
|
||
custody_id: &str,
|
||
initiator: &str,
|
||
amount: Option<u128>,
|
||
) -> Result<(), CustodyError>;
|
||
|
||
/// 取出资产
|
||
fn withdraw(
|
||
&mut self,
|
||
custody_id: &str,
|
||
initiator: &str,
|
||
signatures: Vec<String>,
|
||
amount: Option<u128>,
|
||
) -> Result<(), CustodyError>;
|
||
|
||
/// 转移托管
|
||
fn transfer_custody(
|
||
&mut self,
|
||
custody_id: &str,
|
||
new_owner: String,
|
||
signatures: Vec<String>,
|
||
) -> Result<(), CustodyError>;
|
||
|
||
/// 暂停托管
|
||
fn suspend_custody(
|
||
&mut self,
|
||
custody_id: &str,
|
||
initiator: &str,
|
||
) -> Result<(), CustodyError>;
|
||
|
||
/// 恢复托管
|
||
fn resume_custody(&mut self, custody_id: &str, initiator: &str) -> Result<(), CustodyError>;
|
||
|
||
/// 终止托管
|
||
fn terminate_custody(
|
||
&mut self,
|
||
custody_id: &str,
|
||
signatures: Vec<String>,
|
||
) -> Result<(), CustodyError>;
|
||
|
||
/// 添加托管人
|
||
fn add_custodian(&mut self, custody_id: &str, custodian: String) -> Result<(), CustodyError>;
|
||
|
||
/// 移除托管人
|
||
fn remove_custodian(&mut self, custody_id: &str, custodian: &str) -> Result<(), CustodyError>;
|
||
|
||
/// 验证签名
|
||
fn verify_signatures(&self, custody_id: &str, signatures: &[String]) -> bool;
|
||
|
||
/// 获取所有者的托管列表
|
||
fn get_owner_custodies(&self, owner: &str) -> Vec<CustodyRecord>;
|
||
|
||
/// 获取托管人的托管列表
|
||
fn get_custodian_custodies(&self, custodian: &str) -> Vec<CustodyRecord>;
|
||
}
|
||
|
||
/// ACC-Custody标准实现
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct CustodyToken {
|
||
state: CustodyState,
|
||
}
|
||
|
||
impl CustodyToken {
|
||
/// 创建新的托管管理器
|
||
pub fn new() -> Self {
|
||
Self {
|
||
state: CustodyState {
|
||
custodies: HashMap::new(),
|
||
asset_custodies: HashMap::new(),
|
||
owner_custodies: HashMap::new(),
|
||
custodian_custodies: HashMap::new(),
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 获取状态的可变引用
|
||
pub fn state_mut(&mut self) -> &mut CustodyState {
|
||
&mut self.state
|
||
}
|
||
|
||
/// 获取状态的不可变引用
|
||
pub fn state(&self) -> &CustodyState {
|
||
&self.state
|
||
}
|
||
|
||
/// 检查托管状态
|
||
fn check_custody_status(&self, record: &CustodyRecord) -> Result<(), CustodyError> {
|
||
match record.status {
|
||
CustodyStatus::Suspended => Err(CustodyError::CustodySuspended),
|
||
CustodyStatus::Terminated => Err(CustodyError::CustodyTerminated),
|
||
_ => Ok(()),
|
||
}
|
||
}
|
||
|
||
/// 记录操作
|
||
fn record_operation(
|
||
&mut self,
|
||
custody_id: &str,
|
||
operation: CustodyOperation,
|
||
initiator: &str,
|
||
signatures: Vec<String>,
|
||
data: Option<String>,
|
||
) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
|
||
let op_record = CustodyOperationRecord {
|
||
operation,
|
||
initiator: initiator.to_string(),
|
||
signatures,
|
||
timestamp: now,
|
||
data,
|
||
};
|
||
|
||
record.operations.push(op_record);
|
||
record.updated_at = now;
|
||
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl Default for CustodyToken {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
impl ACCCustody for CustodyToken {
|
||
fn create_custody(&mut self, mut record: CustodyRecord) -> Result<(), CustodyError> {
|
||
// 检查托管记录是否已存在
|
||
if self.state.custodies.contains_key(&record.custody_id) {
|
||
return Err(CustodyError::RecordAlreadyExists);
|
||
}
|
||
|
||
// 检查资产是否已被托管
|
||
if self.state.asset_custodies.contains_key(&record.asset_id) {
|
||
return Err(CustodyError::RecordAlreadyExists);
|
||
}
|
||
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
|
||
record.created_at = now;
|
||
record.updated_at = now;
|
||
record.status = CustodyStatus::Active;
|
||
|
||
let custody_id = record.custody_id.clone();
|
||
let asset_id = record.asset_id.clone();
|
||
let owner = record.owner.clone();
|
||
let custodians = record.custodians.clone();
|
||
|
||
// 保存托管记录
|
||
self.state.custodies.insert(custody_id.clone(), record);
|
||
|
||
// 更新资产托管映射
|
||
self.state
|
||
.asset_custodies
|
||
.insert(asset_id, custody_id.clone());
|
||
|
||
// 更新所有者托管列表
|
||
self.state
|
||
.owner_custodies
|
||
.entry(owner)
|
||
.or_insert_with(Vec::new)
|
||
.push(custody_id.clone());
|
||
|
||
// 更新托管人托管列表
|
||
for custodian in custodians {
|
||
self.state
|
||
.custodian_custodies
|
||
.entry(custodian)
|
||
.or_insert_with(Vec::new)
|
||
.push(custody_id.clone());
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn get_custody(&self, custody_id: &str) -> Result<CustodyRecord, CustodyError> {
|
||
self.state
|
||
.custodies
|
||
.get(custody_id)
|
||
.cloned()
|
||
.ok_or(CustodyError::RecordNotFound)
|
||
}
|
||
|
||
fn get_asset_custody(&self, asset_id: &str) -> Result<CustodyRecord, CustodyError> {
|
||
let custody_id = self
|
||
.state
|
||
.asset_custodies
|
||
.get(asset_id)
|
||
.ok_or(CustodyError::AssetNotFound)?;
|
||
self.get_custody(custody_id)
|
||
}
|
||
|
||
fn deposit(
|
||
&mut self,
|
||
custody_id: &str,
|
||
initiator: &str,
|
||
amount: Option<u128>,
|
||
) -> Result<(), CustodyError> {
|
||
let record = self.get_custody(custody_id)?;
|
||
self.check_custody_status(&record)?;
|
||
|
||
self.record_operation(
|
||
custody_id,
|
||
CustodyOperation::Deposit,
|
||
initiator,
|
||
vec![],
|
||
amount.map(|a| a.to_string()),
|
||
)
|
||
}
|
||
|
||
fn withdraw(
|
||
&mut self,
|
||
custody_id: &str,
|
||
initiator: &str,
|
||
signatures: Vec<String>,
|
||
amount: Option<u128>,
|
||
) -> Result<(), CustodyError> {
|
||
let record = self.get_custody(custody_id)?;
|
||
self.check_custody_status(&record)?;
|
||
|
||
// 验证签名
|
||
if !self.verify_signatures(custody_id, &signatures) {
|
||
return Err(CustodyError::InsufficientSignatures);
|
||
}
|
||
|
||
self.record_operation(
|
||
custody_id,
|
||
CustodyOperation::Withdraw,
|
||
initiator,
|
||
signatures,
|
||
amount.map(|a| a.to_string()),
|
||
)
|
||
}
|
||
|
||
fn transfer_custody(
|
||
&mut self,
|
||
custody_id: &str,
|
||
new_owner: String,
|
||
signatures: Vec<String>,
|
||
) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
self.check_custody_status(&record)?;
|
||
|
||
// 验证签名
|
||
if !self.verify_signatures(custody_id, &signatures) {
|
||
return Err(CustodyError::InsufficientSignatures);
|
||
}
|
||
|
||
// 更新所有者
|
||
let old_owner = record.owner.clone();
|
||
record.owner = new_owner.clone();
|
||
|
||
// 更新所有者托管列表
|
||
if let Some(custodies) = self.state.owner_custodies.get_mut(&old_owner) {
|
||
custodies.retain(|id| id != custody_id);
|
||
}
|
||
self.state
|
||
.owner_custodies
|
||
.entry(new_owner)
|
||
.or_insert_with(Vec::new)
|
||
.push(custody_id.to_string());
|
||
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
self.record_operation(
|
||
custody_id,
|
||
CustodyOperation::Transfer,
|
||
&old_owner,
|
||
signatures,
|
||
None,
|
||
)
|
||
}
|
||
|
||
fn suspend_custody(
|
||
&mut self,
|
||
custody_id: &str,
|
||
initiator: &str,
|
||
) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
record.status = CustodyStatus::Suspended;
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
self.record_operation(
|
||
custody_id,
|
||
CustodyOperation::Suspend,
|
||
initiator,
|
||
vec![],
|
||
None,
|
||
)
|
||
}
|
||
|
||
fn resume_custody(&mut self, custody_id: &str, initiator: &str) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
record.status = CustodyStatus::Active;
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
self.record_operation(
|
||
custody_id,
|
||
CustodyOperation::Resume,
|
||
initiator,
|
||
vec![],
|
||
None,
|
||
)
|
||
}
|
||
|
||
fn terminate_custody(
|
||
&mut self,
|
||
custody_id: &str,
|
||
signatures: Vec<String>,
|
||
) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
|
||
// 验证签名
|
||
if !self.verify_signatures(custody_id, &signatures) {
|
||
return Err(CustodyError::InsufficientSignatures);
|
||
}
|
||
|
||
record.status = CustodyStatus::Terminated;
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
self.record_operation(
|
||
custody_id,
|
||
CustodyOperation::Terminate,
|
||
"",
|
||
signatures,
|
||
None,
|
||
)
|
||
}
|
||
|
||
fn add_custodian(&mut self, custody_id: &str, custodian: String) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
|
||
if !record.custodians.contains(&custodian) {
|
||
record.custodians.push(custodian.clone());
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
// 更新托管人托管列表
|
||
self.state
|
||
.custodian_custodies
|
||
.entry(custodian)
|
||
.or_insert_with(Vec::new)
|
||
.push(custody_id.to_string());
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn remove_custodian(&mut self, custody_id: &str, custodian: &str) -> Result<(), CustodyError> {
|
||
let mut record = self.get_custody(custody_id)?;
|
||
record.custodians.retain(|c| c != custodian);
|
||
self.state
|
||
.custodies
|
||
.insert(custody_id.to_string(), record);
|
||
|
||
// 更新托管人托管列表
|
||
if let Some(custodies) = self.state.custodian_custodies.get_mut(custodian) {
|
||
custodies.retain(|id| id != custody_id);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn verify_signatures(&self, custody_id: &str, signatures: &[String]) -> bool {
|
||
if let Ok(record) = self.get_custody(custody_id) {
|
||
match &record.custody_type {
|
||
CustodyType::Single => signatures.len() >= 1,
|
||
CustodyType::MultiSig { required, .. } => {
|
||
signatures.len() >= *required as usize
|
||
}
|
||
CustodyType::TimeLock { unlock_at } => {
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
now >= *unlock_at
|
||
}
|
||
CustodyType::Conditional => true, // 简化实现
|
||
}
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
|
||
fn get_owner_custodies(&self, owner: &str) -> Vec<CustodyRecord> {
|
||
self.state
|
||
.owner_custodies
|
||
.get(owner)
|
||
.map(|custody_ids| {
|
||
custody_ids
|
||
.iter()
|
||
.filter_map(|id| self.state.custodies.get(id).cloned())
|
||
.collect()
|
||
})
|
||
.unwrap_or_default()
|
||
}
|
||
|
||
fn get_custodian_custodies(&self, custodian: &str) -> Vec<CustodyRecord> {
|
||
self.state
|
||
.custodian_custodies
|
||
.get(custodian)
|
||
.map(|custody_ids| {
|
||
custody_ids
|
||
.iter()
|
||
.filter_map(|id| self.state.custodies.get(id).cloned())
|
||
.collect()
|
||
})
|
||
.unwrap_or_default()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn create_test_record() -> CustodyRecord {
|
||
CustodyRecord {
|
||
custody_id: "CUSTODY-001".to_string(),
|
||
asset_id: "RWA-001".to_string(),
|
||
owner: "alice".to_string(),
|
||
custodians: vec!["custodian1".to_string(), "custodian2".to_string()],
|
||
custody_type: CustodyType::MultiSig {
|
||
required: 2,
|
||
total: 2,
|
||
},
|
||
status: CustodyStatus::Active,
|
||
created_at: 0,
|
||
updated_at: 0,
|
||
amount: Some(1000000),
|
||
conditions: Vec::new(),
|
||
operations: Vec::new(),
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_create_custody() {
|
||
let mut custody = CustodyToken::new();
|
||
let record = create_test_record();
|
||
assert!(custody.create_custody(record).is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_deposit() {
|
||
let mut custody = CustodyToken::new();
|
||
let record = create_test_record();
|
||
custody.create_custody(record).unwrap();
|
||
|
||
assert!(custody
|
||
.deposit("CUSTODY-001", "alice", Some(500000))
|
||
.is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_withdraw() {
|
||
let mut custody = CustodyToken::new();
|
||
let record = create_test_record();
|
||
custody.create_custody(record).unwrap();
|
||
|
||
let signatures = vec!["sig1".to_string(), "sig2".to_string()];
|
||
assert!(custody
|
||
.withdraw("CUSTODY-001", "alice", signatures, Some(500000))
|
||
.is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_suspend_and_resume() {
|
||
let mut custody = CustodyToken::new();
|
||
let record = create_test_record();
|
||
custody.create_custody(record).unwrap();
|
||
|
||
assert!(custody.suspend_custody("CUSTODY-001", "alice").is_ok());
|
||
assert_eq!(
|
||
custody.get_custody("CUSTODY-001").unwrap().status,
|
||
CustodyStatus::Suspended
|
||
);
|
||
|
||
assert!(custody.resume_custody("CUSTODY-001", "alice").is_ok());
|
||
assert_eq!(
|
||
custody.get_custody("CUSTODY-001").unwrap().status,
|
||
CustodyStatus::Active
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_add_remove_custodian() {
|
||
let mut custody = CustodyToken::new();
|
||
let record = create_test_record();
|
||
custody.create_custody(record).unwrap();
|
||
|
||
assert!(custody
|
||
.add_custodian("CUSTODY-001", "custodian3".to_string())
|
||
.is_ok());
|
||
assert_eq!(
|
||
custody.get_custody("CUSTODY-001").unwrap().custodians.len(),
|
||
3
|
||
);
|
||
|
||
assert!(custody
|
||
.remove_custodian("CUSTODY-001", "custodian3")
|
||
.is_ok());
|
||
assert_eq!(
|
||
custody.get_custody("CUSTODY-001").unwrap().custodians.len(),
|
||
2
|
||
);
|
||
}
|
||
}
|