590 lines
18 KiB
Rust
590 lines
18 KiB
Rust
//! NAC ACC-1594 Protocol Implementation
|
||
//! NAC ACC-1594协议实现 - 基于GNACS数字基因的核心收益与资产操作协议
|
||
//!
|
||
//! ACC-1594在继承ACC-1400的基础上,增加了:
|
||
//! 1. 资产生命周期操作(发行、赎回)
|
||
//! 2. 收益分配系统(分红、领取)
|
||
//! 3. GNACS收益扩展编码(80位)
|
||
//! 4. 宪法收据强制验证
|
||
//! 5. AI驱动的收益计算
|
||
|
||
pub mod error;
|
||
pub mod types;
|
||
|
||
pub use error::{Acc1594Error, Result};
|
||
pub use types::*;
|
||
|
||
use nac_acc_1400::Acc1400;
|
||
use nac_acc_1410::Acc1410;
|
||
use std::collections::HashMap;
|
||
|
||
/// ACC-1594核心收益与资产操作协议
|
||
#[derive(Debug)]
|
||
pub struct Acc1594 {
|
||
/// 基础ACC-1400实例
|
||
#[allow(dead_code)]
|
||
base: Acc1400,
|
||
|
||
/// ACC-1410实例(用于访问底层方法)
|
||
base1410: Acc1410,
|
||
|
||
/// 资产GNACS(含收益扩展)
|
||
asset_gnacs: FullDividendGNACS,
|
||
|
||
/// 是否可发行
|
||
is_issuable: bool,
|
||
|
||
/// 发行上限
|
||
issuance_limit: Option<u64>,
|
||
|
||
/// 发行记录
|
||
issuance_records: Vec<IssuanceRecord>,
|
||
|
||
/// 赎回记录
|
||
redemption_records: Vec<RedemptionRecord>,
|
||
|
||
/// 分红记录(按分区)
|
||
dividend_records: HashMap<String, Vec<DividendRecord>>,
|
||
|
||
/// 可领取分红(账户 -> 分区 -> 金额)
|
||
claimable_dividends: HashMap<String, HashMap<String, u64>>,
|
||
|
||
/// 账户持有开始时间(用于检查最低持有期)
|
||
holding_start_time: HashMap<String, u64>,
|
||
|
||
/// 授权角色(角色名 -> 地址列表)
|
||
authorized_roles: HashMap<String, Vec<String>>,
|
||
}
|
||
|
||
impl Acc1594 {
|
||
/// 创建新的ACC-1594实例
|
||
pub fn new(asset_gnacs: FullDividendGNACS) -> Self {
|
||
Self {
|
||
base: Acc1400::new(),
|
||
base1410: Acc1410::new(),
|
||
asset_gnacs,
|
||
is_issuable: true,
|
||
issuance_limit: None,
|
||
issuance_records: Vec::new(),
|
||
redemption_records: Vec::new(),
|
||
dividend_records: HashMap::new(),
|
||
claimable_dividends: HashMap::new(),
|
||
holding_start_time: HashMap::new(),
|
||
authorized_roles: HashMap::new(),
|
||
}
|
||
}
|
||
|
||
/// 设置发行上限
|
||
pub fn set_issuance_limit(&mut self, limit: u64) {
|
||
self.issuance_limit = Some(limit);
|
||
}
|
||
|
||
/// 设置是否可发行
|
||
pub fn set_issuable(&mut self, issuable: bool) {
|
||
self.is_issuable = issuable;
|
||
}
|
||
|
||
/// 授权角色
|
||
pub fn authorize_role(&mut self, role: &str, address: &str) {
|
||
self.authorized_roles
|
||
.entry(role.to_string())
|
||
.or_insert_with(Vec::new)
|
||
.push(address.to_string());
|
||
}
|
||
|
||
/// 检查角色权限
|
||
pub fn has_role(&self, role: &str, address: &str) -> bool {
|
||
self.authorized_roles
|
||
.get(role)
|
||
.map(|addresses| addresses.contains(&address.to_string()))
|
||
.unwrap_or(false)
|
||
}
|
||
|
||
// ==================== 资产生命周期操作 ====================
|
||
|
||
/// 发行新资产(增加总供应量)
|
||
pub fn issue(
|
||
&mut self,
|
||
operator: &str,
|
||
to: &str,
|
||
amount: u64,
|
||
data: Vec<u8>,
|
||
partition_id: &[u8; 32],
|
||
receipt_hash: [u8; 32],
|
||
) -> Result<()> {
|
||
// 检查权限
|
||
if !self.has_role("ISSUER", operator) {
|
||
return Err(Acc1594Error::Unauthorized {
|
||
operator: operator.to_string(),
|
||
required_role: "ISSUER".to_string(),
|
||
});
|
||
}
|
||
|
||
// 检查是否可发行
|
||
if !self.is_issuable {
|
||
return Err(Acc1594Error::NotIssuable("Asset is not issuable".to_string()));
|
||
}
|
||
|
||
// 检查发行上限
|
||
if let Some(limit) = self.issuance_limit {
|
||
let current_supply = self.total_supply(partition_id)?;
|
||
if current_supply + amount > limit {
|
||
return Err(Acc1594Error::ExceedsIssuanceLimit {
|
||
current: current_supply + amount,
|
||
limit,
|
||
});
|
||
}
|
||
}
|
||
|
||
// 发行到分区
|
||
self.base1410.issue_to_partition(partition_id, to, amount)?;
|
||
|
||
// 记录持有开始时间
|
||
if !self.holding_start_time.contains_key(to) {
|
||
self.holding_start_time.insert(to.to_string(), Self::current_timestamp());
|
||
}
|
||
|
||
// 记录发行
|
||
self.issuance_records.push(IssuanceRecord {
|
||
operator: operator.to_string(),
|
||
to: to.to_string(),
|
||
amount,
|
||
data,
|
||
timestamp: Self::current_timestamp(),
|
||
receipt_hash,
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 赎回资产(销毁)
|
||
pub fn redeem(
|
||
&mut self,
|
||
account: &str,
|
||
amount: u64,
|
||
data: Vec<u8>,
|
||
partition_id: &[u8; 32],
|
||
receipt_hash: [u8; 32],
|
||
) -> Result<()> {
|
||
// 检查赎回策略
|
||
let policy = RedemptionPolicy::from(self.asset_gnacs.dividend_extension.redemption_policy);
|
||
if policy == RedemptionPolicy::NonRedeemable {
|
||
return Err(Acc1594Error::RedemptionNotAllowed(
|
||
"Asset is non-redeemable".to_string(),
|
||
));
|
||
}
|
||
|
||
// 检查最低持有期
|
||
self.check_min_holding_period(account)?;
|
||
|
||
// 从分区销毁
|
||
self.base1410.burn_from_partition(partition_id, account, amount)?;
|
||
|
||
// 记录赎回
|
||
self.redemption_records.push(RedemptionRecord {
|
||
operator: account.to_string(),
|
||
from: account.to_string(),
|
||
amount,
|
||
data,
|
||
timestamp: Self::current_timestamp(),
|
||
receipt_hash,
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 操作员代表持有者赎回
|
||
pub fn operator_redeem(
|
||
&mut self,
|
||
operator: &str,
|
||
token_holder: &str,
|
||
amount: u64,
|
||
data: Vec<u8>,
|
||
partition_id: &[u8; 32],
|
||
receipt_hash: [u8; 32],
|
||
) -> Result<()> {
|
||
// 检查操作员权限
|
||
if !self.base1410.is_operator_for(token_holder, operator) {
|
||
return Err(Acc1594Error::Unauthorized {
|
||
operator: operator.to_string(),
|
||
required_role: "OPERATOR".to_string(),
|
||
});
|
||
}
|
||
|
||
// 检查赎回策略
|
||
let policy = RedemptionPolicy::from(self.asset_gnacs.dividend_extension.redemption_policy);
|
||
if policy == RedemptionPolicy::NonRedeemable {
|
||
return Err(Acc1594Error::RedemptionNotAllowed(
|
||
"Asset is non-redeemable".to_string(),
|
||
));
|
||
}
|
||
|
||
// 检查最低持有期
|
||
self.check_min_holding_period(token_holder)?;
|
||
|
||
// 从分区销毁
|
||
self.base1410.burn_from_partition(partition_id, token_holder, amount)?;
|
||
|
||
// 记录赎回
|
||
self.redemption_records.push(RedemptionRecord {
|
||
operator: operator.to_string(),
|
||
from: token_holder.to_string(),
|
||
amount,
|
||
data,
|
||
timestamp: Self::current_timestamp(),
|
||
receipt_hash,
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 检查资产是否可发行
|
||
pub fn is_issuable(&self) -> bool {
|
||
self.is_issuable
|
||
}
|
||
|
||
// ==================== 收益分配 ====================
|
||
|
||
/// 分配收益(由宪法执行引擎自动触发,或授权角色手动触发)
|
||
pub fn distribute_dividend(
|
||
&mut self,
|
||
operator: &str,
|
||
partition_id: &[u8; 32],
|
||
total_amount: u64,
|
||
period: u64,
|
||
receipt_hash: [u8; 32],
|
||
) -> Result<()> {
|
||
// 检查权限
|
||
if !self.has_role("DISTRIBUTOR", operator) {
|
||
return Err(Acc1594Error::Unauthorized {
|
||
operator: operator.to_string(),
|
||
required_role: "DISTRIBUTOR".to_string(),
|
||
});
|
||
}
|
||
|
||
// 获取分区总供应量
|
||
let total_supply = self.total_supply(partition_id)?;
|
||
if total_supply == 0 {
|
||
return Ok(());
|
||
}
|
||
|
||
// 获取所有持有者并分配
|
||
let partition_id_hex = hex::encode(partition_id);
|
||
let holders = self.get_all_holders(partition_id)?;
|
||
for account in holders {
|
||
let balance = self.base1410.balance_of_by_partition(partition_id, &account)?;
|
||
// 修复:先乘后除避免整数除法精度损失
|
||
let dividend = (balance * total_amount) / total_supply;
|
||
|
||
self.claimable_dividends
|
||
.entry(account.clone())
|
||
.or_insert_with(HashMap::new)
|
||
.entry(partition_id_hex.clone())
|
||
.and_modify(|e| *e += dividend)
|
||
.or_insert(dividend);
|
||
}
|
||
|
||
// 记录分红
|
||
let per_share_amount = if total_supply > 0 { total_amount / total_supply } else { 0 };
|
||
let record = DividendRecord {
|
||
period,
|
||
total_amount,
|
||
per_share_amount,
|
||
timestamp: Self::current_timestamp(),
|
||
receipt_hash,
|
||
};
|
||
|
||
self.dividend_records
|
||
.entry(partition_id_hex)
|
||
.or_insert_with(Vec::new)
|
||
.push(record);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 查询账户可领取的收益(分区级)
|
||
pub fn claimable_dividend(&self, account: &str, partition_id: &[u8; 32]) -> u64 {
|
||
let partition_id_hex = hex::encode(partition_id);
|
||
self.claimable_dividends
|
||
.get(account)
|
||
.and_then(|partitions| partitions.get(&partition_id_hex))
|
||
.copied()
|
||
.unwrap_or(0)
|
||
}
|
||
|
||
/// 领取收益(自动转入账户XTZH余额)
|
||
pub fn claim_dividend(
|
||
&mut self,
|
||
account: &str,
|
||
partition_id: &[u8; 32],
|
||
_receipt_hash: [u8; 32],
|
||
) -> Result<u64> {
|
||
let partition_id_hex = hex::encode(partition_id);
|
||
let amount = self.claimable_dividend(account, partition_id);
|
||
|
||
if amount == 0 {
|
||
return Err(Acc1594Error::NoClaimableDividend {
|
||
account: account.to_string(),
|
||
partition: partition_id_hex,
|
||
});
|
||
}
|
||
|
||
// 清除可领取分红
|
||
if let Some(partitions) = self.claimable_dividends.get_mut(account) {
|
||
partitions.remove(&partition_id_hex);
|
||
}
|
||
|
||
Ok(amount)
|
||
}
|
||
|
||
/// 批量领取收益(多个分区)
|
||
pub fn claim_multiple_dividends(
|
||
&mut self,
|
||
account: &str,
|
||
partition_ids: &[[u8; 32]],
|
||
receipt_hash: [u8; 32],
|
||
) -> Result<u64> {
|
||
let mut total = 0;
|
||
for partition_id in partition_ids {
|
||
if let Ok(amount) = self.claim_dividend(account, partition_id, receipt_hash) {
|
||
total += amount;
|
||
}
|
||
}
|
||
Ok(total)
|
||
}
|
||
|
||
/// 获取历史分红记录
|
||
pub fn get_dividend_history(
|
||
&self,
|
||
partition_id: &[u8; 32],
|
||
from: u64,
|
||
to: u64,
|
||
) -> Vec<DividendRecord> {
|
||
let partition_id_hex = hex::encode(partition_id);
|
||
self.dividend_records
|
||
.get(&partition_id_hex)
|
||
.map(|records| {
|
||
records
|
||
.iter()
|
||
.filter(|r| r.period >= from && r.period <= to)
|
||
.cloned()
|
||
.collect()
|
||
})
|
||
.unwrap_or_default()
|
||
}
|
||
|
||
// ==================== 资产状态查询 ====================
|
||
|
||
/// 获取资产GNACS(含收益扩展)
|
||
pub fn asset_gnacs(&self) -> &FullDividendGNACS {
|
||
&self.asset_gnacs
|
||
}
|
||
|
||
/// 获取资产总供应量
|
||
pub fn total_supply(&self, partition_id: &[u8; 32]) -> Result<u64> {
|
||
// 简化实现:返回分区信息中的总供应量
|
||
let info = self.base1410.get_partition_info(partition_id)?;
|
||
Ok(info.total_supply)
|
||
}
|
||
|
||
/// 获取账户余额(指定分区)
|
||
pub fn balance_of(&self, account: &str, partition_id: &[u8; 32]) -> Result<u64> {
|
||
Ok(self.base1410.balance_of_by_partition(partition_id, account)?)
|
||
}
|
||
|
||
/// 获取资产净值(由AI估值引擎提供)
|
||
pub fn net_asset_value(&self) -> (u64, u64) {
|
||
// 简化实现:返回模拟的净值和时间戳
|
||
(1000000, Self::current_timestamp())
|
||
}
|
||
|
||
// ==================== 辅助方法 ====================
|
||
|
||
/// 检查最低持有期
|
||
fn check_min_holding_period(&self, account: &str) -> Result<()> {
|
||
let min_period = self.asset_gnacs.dividend_extension.min_holding_period;
|
||
if min_period == 0 {
|
||
return Ok(());
|
||
}
|
||
|
||
if let Some(&start_time) = self.holding_start_time.get(account) {
|
||
let current_time = Self::current_timestamp();
|
||
let held_seconds = current_time - start_time;
|
||
let held_months = (held_seconds / (30 * 24 * 3600)) as u8;
|
||
|
||
if held_months < min_period {
|
||
return Err(Acc1594Error::MinHoldingPeriodNotMet {
|
||
required_months: min_period,
|
||
held_months,
|
||
});
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 获取分区的所有持有者
|
||
fn get_all_holders(&self, partition_id: &[u8; 32]) -> Result<Vec<String>> {
|
||
// 从持有开始时间记录中获取所有账户,筛选出在该分区有余额的账户
|
||
let mut holders = Vec::new();
|
||
for account in self.holding_start_time.keys() {
|
||
if let Ok(balance) = self.base1410.balance_of_by_partition(partition_id, account) {
|
||
if balance > 0 {
|
||
holders.push(account.clone());
|
||
}
|
||
}
|
||
}
|
||
Ok(holders)
|
||
}
|
||
|
||
/// 获取当前时间戳
|
||
fn current_timestamp() -> u64 {
|
||
std::time::SystemTime::now()
|
||
.duration_since(std::time::UNIX_EPOCH)
|
||
.unwrap()
|
||
.as_secs()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use nac_acc_1410::{ExtendedGNACS, GNACSExtension, PartitionType};
|
||
|
||
fn create_test_acc1594() -> (Acc1594, [u8; 32]) {
|
||
let dividend_gnacs = FullDividendGNACS {
|
||
base_gnacs: vec![0x94, 0x01, 0x00, 0x04, 0x02, 0x01],
|
||
dividend_extension: DividendGNACSExtension {
|
||
dividend_policy: 1, // 固定比例
|
||
dividend_period: 4, // 年度
|
||
dividend_rate: 5, // 5%
|
||
redemption_policy: 1, // 按面值赎回
|
||
min_holding_period: 0, // 无最低持有期
|
||
},
|
||
};
|
||
|
||
let mut acc1594 = Acc1594::new(dividend_gnacs);
|
||
|
||
// 授权角色
|
||
acc1594.authorize_role("ISSUER", "issuer1");
|
||
acc1594.authorize_role("DISTRIBUTOR", "distributor1");
|
||
|
||
// 创建分区
|
||
let extended_gnacs = ExtendedGNACS {
|
||
base_gnacs: vec![0x94, 0x01, 0x00, 0x04, 0x02, 0x01],
|
||
extension: GNACSExtension {
|
||
partition_type: 0x01,
|
||
vesting_years: 0,
|
||
voting_multiplier: 1,
|
||
dividend_priority: 1,
|
||
},
|
||
};
|
||
|
||
let partition_id = acc1594
|
||
.base1410
|
||
.create_partition(
|
||
"Test Partition".to_string(),
|
||
extended_gnacs,
|
||
PartitionType::CommonStock,
|
||
)
|
||
.unwrap();
|
||
|
||
(acc1594, partition_id)
|
||
}
|
||
|
||
#[test]
|
||
fn test_issue() {
|
||
let (mut acc1594, partition_id) = create_test_acc1594();
|
||
|
||
let result = acc1594.issue(
|
||
"issuer1",
|
||
"investor1",
|
||
1000,
|
||
vec![],
|
||
&partition_id,
|
||
[0u8; 32],
|
||
);
|
||
|
||
assert!(result.is_ok());
|
||
assert_eq!(acc1594.balance_of("investor1", &partition_id).unwrap(), 1000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_redeem() {
|
||
let (mut acc1594, partition_id) = create_test_acc1594();
|
||
|
||
acc1594
|
||
.issue("issuer1", "investor1", 1000, vec![], &partition_id, [0u8; 32])
|
||
.unwrap();
|
||
|
||
let result = acc1594.redeem("investor1", 300, vec![], &partition_id, [0u8; 32]);
|
||
|
||
assert!(result.is_ok());
|
||
assert_eq!(acc1594.balance_of("investor1", &partition_id).unwrap(), 700);
|
||
}
|
||
|
||
#[test]
|
||
fn test_distribute_dividend() {
|
||
let (mut acc1594, partition_id) = create_test_acc1594();
|
||
|
||
// 发行给两个投资者
|
||
acc1594
|
||
.issue("issuer1", "investor1", 600, vec![], &partition_id, [0u8; 32])
|
||
.unwrap();
|
||
acc1594
|
||
.issue("issuer1", "investor2", 400, vec![], &partition_id, [0u8; 32])
|
||
.unwrap();
|
||
|
||
// 分配分红
|
||
let result = acc1594.distribute_dividend(
|
||
"distributor1",
|
||
&partition_id,
|
||
1000,
|
||
202601,
|
||
[0u8; 32],
|
||
);
|
||
|
||
assert!(result.is_ok());
|
||
|
||
// 检查可领取分红
|
||
assert_eq!(acc1594.claimable_dividend("investor1", &partition_id), 600);
|
||
assert_eq!(acc1594.claimable_dividend("investor2", &partition_id), 400);
|
||
}
|
||
|
||
#[test]
|
||
fn test_claim_dividend() {
|
||
let (mut acc1594, partition_id) = create_test_acc1594();
|
||
|
||
acc1594
|
||
.issue("issuer1", "investor1", 1000, vec![], &partition_id, [0u8; 32])
|
||
.unwrap();
|
||
|
||
acc1594
|
||
.distribute_dividend("distributor1", &partition_id, 500, 202601, [0u8; 32])
|
||
.unwrap();
|
||
|
||
let claimed = acc1594
|
||
.claim_dividend("investor1", &partition_id, [0u8; 32])
|
||
.unwrap();
|
||
|
||
assert_eq!(claimed, 500);
|
||
assert_eq!(acc1594.claimable_dividend("investor1", &partition_id), 0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_issuance_limit() {
|
||
let (mut acc1594, partition_id) = create_test_acc1594();
|
||
|
||
acc1594.set_issuance_limit(1000);
|
||
|
||
// 第一次发行应该成功
|
||
assert!(acc1594
|
||
.issue("issuer1", "investor1", 500, vec![], &partition_id, [0u8; 32])
|
||
.is_ok());
|
||
|
||
// 第二次发行超过限额应该失败
|
||
let result = acc1594.issue("issuer1", "investor2", 600, vec![], &partition_id, [0u8; 32]);
|
||
assert!(result.is_err());
|
||
}
|
||
}
|