330 lines
9.5 KiB
Rust
330 lines
9.5 KiB
Rust
// ACC-Valuation: 资产估值协议(Valuation Protocol)
|
||
//
|
||
// NAC原生的资产估值标准,用于资产价值评估和管理
|
||
// 100% NAC原生协议,不是任何现有标准的实现
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// 估值方法
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ValuationMethod {
|
||
/// 市场法
|
||
MarketApproach,
|
||
/// 收益法
|
||
IncomeApproach,
|
||
/// 成本法
|
||
CostApproach,
|
||
/// 其他
|
||
Other(String),
|
||
}
|
||
|
||
/// 估值记录
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ValuationRecord {
|
||
/// 估值ID
|
||
pub valuation_id: String,
|
||
/// 资产ID
|
||
pub asset_id: String,
|
||
/// 估值方法
|
||
pub method: ValuationMethod,
|
||
/// 估值金额(USD,以分为单位)
|
||
pub value: u128,
|
||
/// 估值师地址
|
||
pub appraiser: String,
|
||
/// 估值时间
|
||
pub timestamp: u64,
|
||
/// 有效期(秒)
|
||
pub validity_period: u64,
|
||
/// 备注
|
||
pub notes: Vec<String>,
|
||
}
|
||
|
||
/// 估值错误类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ValuationError {
|
||
/// 估值记录不存在
|
||
RecordNotFound,
|
||
/// 估值记录已存在
|
||
RecordAlreadyExists,
|
||
/// 估值已过期
|
||
ValuationExpired,
|
||
/// 无效的估值金额
|
||
InvalidValue,
|
||
/// 未授权操作
|
||
Unauthorized,
|
||
}
|
||
|
||
/// 估值状态
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ValuationState {
|
||
/// 估值记录映射 (valuation_id -> ValuationRecord)
|
||
pub valuations: HashMap<String, ValuationRecord>,
|
||
/// 资产估值历史 (asset_id -> [valuation_ids])
|
||
pub asset_valuations: HashMap<String, Vec<String>>,
|
||
}
|
||
|
||
/// ACC-Valuation接口
|
||
pub trait ACCValuation {
|
||
/// 添加估值记录
|
||
fn add_valuation(&mut self, record: ValuationRecord) -> Result<(), ValuationError>;
|
||
|
||
/// 获取估值记录
|
||
fn get_valuation(&self, valuation_id: &str) -> Result<ValuationRecord, ValuationError>;
|
||
|
||
/// 获取资产的最新估值
|
||
fn get_latest_valuation(&self, asset_id: &str) -> Result<ValuationRecord, ValuationError>;
|
||
|
||
/// 获取资产的估值历史
|
||
fn get_valuation_history(&self, asset_id: &str) -> Vec<ValuationRecord>;
|
||
|
||
/// 检查估值是否有效
|
||
fn is_valuation_valid(&self, valuation_id: &str) -> bool;
|
||
|
||
/// 计算平均估值
|
||
fn calculate_average_valuation(&self, asset_id: &str, period: u64) -> Option<u128>;
|
||
|
||
/// 更新估值备注
|
||
fn add_note(&mut self, valuation_id: &str, note: String) -> Result<(), ValuationError>;
|
||
}
|
||
|
||
/// ACC-Valuation标准实现
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ValuationToken {
|
||
state: ValuationState,
|
||
}
|
||
|
||
impl ValuationToken {
|
||
/// 创建新的估值管理器
|
||
pub fn new() -> Self {
|
||
Self {
|
||
state: ValuationState {
|
||
valuations: HashMap::new(),
|
||
asset_valuations: HashMap::new(),
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 获取状态的可变引用
|
||
pub fn state_mut(&mut self) -> &mut ValuationState {
|
||
&mut self.state
|
||
}
|
||
|
||
/// 获取状态的不可变引用
|
||
pub fn state(&self) -> &ValuationState {
|
||
&self.state
|
||
}
|
||
}
|
||
|
||
impl Default for ValuationToken {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
impl ACCValuation for ValuationToken {
|
||
fn add_valuation(&mut self, mut record: ValuationRecord) -> Result<(), ValuationError> {
|
||
if self.state.valuations.contains_key(&record.valuation_id) {
|
||
return Err(ValuationError::RecordAlreadyExists);
|
||
}
|
||
|
||
if record.value == 0 {
|
||
return Err(ValuationError::InvalidValue);
|
||
}
|
||
|
||
// 如果timestamp为0,使用当前时间
|
||
if record.timestamp == 0 {
|
||
record.timestamp = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
}
|
||
|
||
let valuation_id = record.valuation_id.clone();
|
||
let asset_id = record.asset_id.clone();
|
||
|
||
self.state
|
||
.valuations
|
||
.insert(valuation_id.clone(), record);
|
||
|
||
self.state
|
||
.asset_valuations
|
||
.entry(asset_id)
|
||
.or_insert_with(Vec::new)
|
||
.push(valuation_id);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn get_valuation(&self, valuation_id: &str) -> Result<ValuationRecord, ValuationError> {
|
||
self.state
|
||
.valuations
|
||
.get(valuation_id)
|
||
.cloned()
|
||
.ok_or(ValuationError::RecordNotFound)
|
||
}
|
||
|
||
fn get_latest_valuation(&self, asset_id: &str) -> Result<ValuationRecord, ValuationError> {
|
||
let valuation_ids = self
|
||
.state
|
||
.asset_valuations
|
||
.get(asset_id)
|
||
.ok_or(ValuationError::RecordNotFound)?;
|
||
|
||
if valuation_ids.is_empty() {
|
||
return Err(ValuationError::RecordNotFound);
|
||
}
|
||
|
||
// 获取所有估值记录并按时间排序
|
||
let mut valuations: Vec<ValuationRecord> = valuation_ids
|
||
.iter()
|
||
.filter_map(|id| self.state.valuations.get(id).cloned())
|
||
.collect();
|
||
|
||
valuations.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
|
||
|
||
valuations
|
||
.into_iter()
|
||
.next()
|
||
.ok_or(ValuationError::RecordNotFound)
|
||
}
|
||
|
||
fn get_valuation_history(&self, asset_id: &str) -> Vec<ValuationRecord> {
|
||
self.state
|
||
.asset_valuations
|
||
.get(asset_id)
|
||
.map(|valuation_ids| {
|
||
let mut valuations: Vec<ValuationRecord> = valuation_ids
|
||
.iter()
|
||
.filter_map(|id| self.state.valuations.get(id).cloned())
|
||
.collect();
|
||
valuations.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
|
||
valuations
|
||
})
|
||
.unwrap_or_default()
|
||
}
|
||
|
||
fn is_valuation_valid(&self, valuation_id: &str) -> bool {
|
||
if let Ok(record) = self.get_valuation(valuation_id) {
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
now <= record.timestamp + record.validity_period
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
|
||
fn calculate_average_valuation(&self, asset_id: &str, period: u64) -> Option<u128> {
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
|
||
let valuations = self.get_valuation_history(asset_id);
|
||
let recent_valuations: Vec<&ValuationRecord> = valuations
|
||
.iter()
|
||
.filter(|v| now - v.timestamp <= period)
|
||
.collect();
|
||
|
||
if recent_valuations.is_empty() {
|
||
return None;
|
||
}
|
||
|
||
let sum: u128 = recent_valuations.iter().map(|v| v.value).sum();
|
||
Some(sum / recent_valuations.len() as u128)
|
||
}
|
||
|
||
fn add_note(&mut self, valuation_id: &str, note: String) -> Result<(), ValuationError> {
|
||
let mut record = self.get_valuation(valuation_id)?;
|
||
record.notes.push(note);
|
||
self.state
|
||
.valuations
|
||
.insert(valuation_id.to_string(), record);
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn create_test_valuation() -> ValuationRecord {
|
||
let now = std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs();
|
||
|
||
ValuationRecord {
|
||
valuation_id: "VAL-001".to_string(),
|
||
asset_id: "RWA-001".to_string(),
|
||
method: ValuationMethod::MarketApproach,
|
||
value: 1000000_00,
|
||
appraiser: "appraiser1".to_string(),
|
||
timestamp: now,
|
||
validity_period: 31536000, // 1年
|
||
notes: Vec::new(),
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_add_valuation() {
|
||
let mut valuation = ValuationToken::new();
|
||
let record = create_test_valuation();
|
||
assert!(valuation.add_valuation(record).is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_get_latest_valuation() {
|
||
let mut valuation = ValuationToken::new();
|
||
let record = create_test_valuation();
|
||
valuation.add_valuation(record).unwrap();
|
||
|
||
assert!(valuation.get_latest_valuation("RWA-001").is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_is_valuation_valid() {
|
||
let mut valuation = ValuationToken::new();
|
||
let record = create_test_valuation();
|
||
valuation.add_valuation(record).unwrap();
|
||
|
||
assert!(valuation.is_valuation_valid("VAL-001"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_calculate_average_valuation() {
|
||
let mut valuation = ValuationToken::new();
|
||
|
||
let mut record1 = create_test_valuation();
|
||
record1.valuation_id = "VAL-001".to_string();
|
||
record1.value = 1000000_00;
|
||
valuation.add_valuation(record1).unwrap();
|
||
|
||
let mut record2 = create_test_valuation();
|
||
record2.valuation_id = "VAL-002".to_string();
|
||
record2.value = 1200000_00;
|
||
valuation.add_valuation(record2).unwrap();
|
||
|
||
let avg = valuation.calculate_average_valuation("RWA-001", 31536000);
|
||
assert!(avg.is_some());
|
||
assert_eq!(avg.unwrap(), 1100000_00);
|
||
}
|
||
|
||
#[test]
|
||
fn test_add_note() {
|
||
let mut valuation = ValuationToken::new();
|
||
let record = create_test_valuation();
|
||
valuation.add_valuation(record).unwrap();
|
||
|
||
assert!(valuation
|
||
.add_note("VAL-001", "Test note".to_string())
|
||
.is_ok());
|
||
assert_eq!(
|
||
valuation.get_valuation("VAL-001").unwrap().notes.len(),
|
||
1
|
||
);
|
||
}
|
||
}
|