NAC_Blockchain/nac-udm/src/l1_protocol/acc/acc20.rs

805 lines
24 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! ACC-20: NAC 原生同质化代币标准
//! 协议 UID: nac.acc.ACC20Token.v2
//! 完全基于 NAC 原生类型系统,无以太坊模式残留
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::primitives::{Address, Hash, Timestamp};
// ========================
// 错误类型
// ========================
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
/// ACC20Error 错误类型
pub enum ACC20Error {
/// 余额不足错误
InsufficientBalance {
/// 持有方账户地址
holder: Address,
/// 所需数量
required: u128,
/// 可用数量
available: u128,
},
/// 授权额度不足错误
InsufficientAllowance {
/// 所有者账户地址
owner: Address,
/// 被授权方账户地址
spender: Address,
/// 所需数量
required: u128,
/// 可用数量
available: u128,
},
/// 宪法收据无效错误
InvalidConstitutionalReceipt,
/// 账户已冻结错误
AccountFrozen(Address),
/// 转账已暂停错误
TransferHalted,
/// 金额为零错误
ZeroAmount,
/// 数值溢出错误
Overflow,
/// 未授权操作错误
Unauthorized(Address),
/// 无效地址错误
InvalidAddress,
/// 超出供应上限错误
SupplyCapExceeded {
/// 供应量上限
cap: u128,
/// 当前数量
current: u128,
/// 请求数量
requested: u128,
},
}
impl std::fmt::Display for ACC20Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InsufficientBalance { holder, required, available } =>
write!(f, "余额不足: 持有者 {} 需要 {} 实际 {}", holder.to_hex(), required, available),
Self::InsufficientAllowance { owner, spender, required, available } =>
write!(f, "授权额度不足: 所有者 {} 授权给 {} 需要 {} 实际 {}", owner.to_hex(), spender.to_hex(), required, available),
Self::InvalidConstitutionalReceipt => write!(f, "宪法收据无效"),
Self::AccountFrozen(a) => write!(f, "账户已冻结: {}", a.to_hex()),
Self::TransferHalted => write!(f, "转账已暂停"),
Self::ZeroAmount => write!(f, "金额不能为零"),
Self::Overflow => write!(f, "数值溢出"),
Self::Unauthorized(a) => write!(f, "未授权操作: {}", a.to_hex()),
Self::InvalidAddress => write!(f, "无效地址"),
Self::SupplyCapExceeded { cap, current, requested } =>
write!(f, "超出供应上限: 上限 {} 当前 {} 请求 {}", cap, current, requested),
}
}
}
// ========================
// 协议事件CSNP 广播)
// ========================
#[derive(Debug, Clone, Serialize, Deserialize)]
/// ACC20Event 协议事件
pub enum ACC20Event {
/// 代币转移
Transfer {
/// 发送方账户地址
from: Address,
/// 接收方账户地址
to: Address,
/// 代币数量(最小单位)
amount: u128,
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 授权额度变更
Approval {
/// 所有者账户地址
owner: Address,
/// 被授权方账户地址
spender: Address,
/// 代币数量(最小单位)
amount: u128,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 铸造
Mint {
/// 接收方账户地址
to: Address,
/// 代币数量(最小单位)
amount: u128,
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 销毁
Burn {
/// 发送方账户地址
from: Address,
/// 代币数量(最小单位)
amount: u128,
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 账户冻结
AccountFrozen {
/// account 字段
account: Address,
/// 操作原因说明
reason: String,
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 账户解冻
AccountUnfrozen {
/// account 字段
account: Address,
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 转账暂停
TransferHalted {
/// 操作原因说明
reason: String,
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
/// 转账恢复
TransferResumed {
/// 宪法收据哈希CBPP 共识凭证)
constitutional_receipt: Hash,
/// 操作时间戳UTC Unix 毫秒)
timestamp: Timestamp,
},
}
// ========================
// 主结构体
// ========================
/// ACC-20 生产级别同质化代币协议
#[derive(Debug, Clone, Serialize, Deserialize)]
/// ACC20Token 代币协议实现
pub struct ACC20Token {
// 协议标识
/// 协议唯一标识符
pub protocol_uid: String,
/// NAC-Lens 协议向量
pub lens_protocol_vector: String,
// 代币基本信息
/// 名称
pub name: String,
/// 代币符号
pub symbol: String,
/// 代币精度(小数位数)
pub decimals: u8,
/// 代币总供应量
pub total_supply: u128,
/// 供应量上限None 表示无上限)
pub supply_cap: Option<u128>,
// 核心状态NAC 原生holdings 而非 balances
/// 持有量映射(地址 → 数量)
pub holdings: HashMap<Address, u128>,
/// 主权授权NAC 原生sovereignty_authorizations 而非 allowances
pub sovereignty_authorizations: HashMap<Address, HashMap<Address, u128>>,
// 合规控制
/// 已冻结账户映射(地址 → 冻结原因)
pub frozen_accounts: HashMap<Address, String>,
/// 转账是否已暂停
pub transfer_halted: bool,
/// 暂停原因None 表示未暂停)
pub halt_reason: Option<String>,
// 权限
/// 所有者账户地址
pub owner: Address,
/// 铸造者地址列表
pub minters: Vec<Address>,
// 待广播事件
/// 待广播的 CSNP 协议事件队列
pub pending_events: Vec<ACC20Event>,
// 时间戳
/// 创建时间戳
pub created_at: Timestamp,
/// 最后更新时间戳
pub updated_at: Timestamp,
}
impl ACC20Token {
/// 创建新的 ACC-20 代币
pub fn new(
name: String,
symbol: String,
decimals: u8,
owner: Address,
initial_supply: u128,
supply_cap: Option<u128>,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<Self, ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if let Some(cap) = supply_cap {
if initial_supply > cap {
return Err(ACC20Error::SupplyCapExceeded {
cap,
current: 0,
requested: initial_supply,
});
}
}
let mut holdings = HashMap::new();
if initial_supply > 0 {
holdings.insert(owner.clone(), initial_supply);
}
let mut token = Self {
protocol_uid: "nac.acc.ACC20Token.v2".to_string(),
lens_protocol_vector: "ACC-20".to_string(),
name,
symbol,
decimals,
total_supply: initial_supply,
supply_cap,
holdings,
sovereignty_authorizations: HashMap::new(),
frozen_accounts: HashMap::new(),
transfer_halted: false,
halt_reason: None,
owner: owner.clone(),
minters: vec![owner.clone()],
pending_events: Vec::new(),
created_at: timestamp.clone(),
updated_at: timestamp.clone(),
};
if initial_supply > 0 {
token.pending_events.push(ACC20Event::Mint {
to: owner,
amount: initial_supply,
constitutional_receipt,
timestamp,
});
}
Ok(token)
}
// ========================
// 查询方法
// ========================
/// 查询持仓余额
pub fn balance_of(&self, holder: &Address) -> u128 {
self.holdings.get(holder).copied().unwrap_or(0)
}
/// 查询主权授权额度
pub fn allowance(&self, owner: &Address, spender: &Address) -> u128 {
self.sovereignty_authorizations
.get(owner)
.and_then(|m| m.get(spender))
.copied()
.unwrap_or(0)
}
/// 检查账户是否冻结
pub fn is_frozen(&self, account: &Address) -> bool {
self.frozen_accounts.contains_key(account)
}
// ========================
// 转账方法
// ========================
/// 直接转账
pub fn transfer(
&mut self,
from: &Address,
to: &Address,
amount: u128,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if amount == 0 {
return Err(ACC20Error::ZeroAmount);
}
if self.transfer_halted {
return Err(ACC20Error::TransferHalted);
}
if self.is_frozen(from) {
return Err(ACC20Error::AccountFrozen(from.clone()));
}
if self.is_frozen(to) {
return Err(ACC20Error::AccountFrozen(to.clone()));
}
let from_balance = self.balance_of(from);
if from_balance < amount {
return Err(ACC20Error::InsufficientBalance {
holder: from.clone(),
required: amount,
available: from_balance,
});
}
*self.holdings.get_mut(from).expect("FIX-006: unexpected None/Err") -= amount;
*self.holdings.entry(to.clone()).or_insert(0) += amount;
self.pending_events.push(ACC20Event::Transfer {
from: from.clone(),
to: to.clone(),
amount,
constitutional_receipt,
timestamp,
});
self.updated_at = Timestamp::now();
Ok(())
}
/// 代理转账(消耗授权额度)
pub fn transfer_from(
&mut self,
spender: &Address,
from: &Address,
to: &Address,
amount: u128,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if amount == 0 {
return Err(ACC20Error::ZeroAmount);
}
if self.transfer_halted {
return Err(ACC20Error::TransferHalted);
}
if self.is_frozen(from) {
return Err(ACC20Error::AccountFrozen(from.clone()));
}
if self.is_frozen(to) {
return Err(ACC20Error::AccountFrozen(to.clone()));
}
let current_allowance = self.allowance(from, spender);
if current_allowance < amount {
return Err(ACC20Error::InsufficientAllowance {
owner: from.clone(),
spender: spender.clone(),
required: amount,
available: current_allowance,
});
}
let from_balance = self.balance_of(from);
if from_balance < amount {
return Err(ACC20Error::InsufficientBalance {
holder: from.clone(),
required: amount,
available: from_balance,
});
}
// 扣减授权额度
*self.sovereignty_authorizations
.get_mut(from)
.expect("FIX-006: unexpected None/Err")
.get_mut(spender)
.expect("FIX-006: unexpected None/Err") -= amount;
// 执行转账
*self.holdings.get_mut(from).expect("FIX-006: unexpected None/Err") -= amount;
*self.holdings.entry(to.clone()).or_insert(0) += amount;
self.pending_events.push(ACC20Event::Transfer {
from: from.clone(),
to: to.clone(),
amount,
constitutional_receipt,
timestamp,
});
self.updated_at = Timestamp::now();
Ok(())
}
// ========================
// 授权方法
// ========================
/// 设置主权授权额度
pub fn approve(
&mut self,
owner: &Address,
spender: &Address,
amount: u128,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if self.is_frozen(owner) {
return Err(ACC20Error::AccountFrozen(owner.clone()));
}
self.sovereignty_authorizations
.entry(owner.clone())
.or_insert_with(HashMap::new)
.insert(spender.clone(), amount);
self.pending_events.push(ACC20Event::Approval {
owner: owner.clone(),
spender: spender.clone(),
amount,
timestamp,
});
Ok(())
}
// ========================
// 铸造/销毁方法
// ========================
/// 铸造代币
pub fn mint(
&mut self,
minter: &Address,
to: &Address,
amount: u128,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if amount == 0 {
return Err(ACC20Error::ZeroAmount);
}
if !self.minters.contains(minter) {
return Err(ACC20Error::Unauthorized(minter.clone()));
}
if self.is_frozen(to) {
return Err(ACC20Error::AccountFrozen(to.clone()));
}
if let Some(cap) = self.supply_cap {
if self.total_supply.saturating_add(amount) > cap {
return Err(ACC20Error::SupplyCapExceeded {
cap,
current: self.total_supply,
requested: amount,
});
}
}
self.total_supply = self.total_supply.saturating_add(amount);
*self.holdings.entry(to.clone()).or_insert(0) += amount;
self.pending_events.push(ACC20Event::Mint {
to: to.clone(),
amount,
constitutional_receipt,
timestamp,
});
self.updated_at = Timestamp::now();
Ok(())
}
/// 销毁代币
pub fn burn(
&mut self,
from: &Address,
amount: u128,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if amount == 0 {
return Err(ACC20Error::ZeroAmount);
}
let balance = self.balance_of(from);
if balance < amount {
return Err(ACC20Error::InsufficientBalance {
holder: from.clone(),
required: amount,
available: balance,
});
}
*self.holdings.get_mut(from).expect("FIX-006: unexpected None/Err") -= amount;
self.total_supply = self.total_supply.saturating_sub(amount);
self.pending_events.push(ACC20Event::Burn {
from: from.clone(),
amount,
constitutional_receipt,
timestamp,
});
self.updated_at = Timestamp::now();
Ok(())
}
// ========================
// 合规控制方法
// ========================
/// 冻结账户
pub fn freeze_account(
&mut self,
caller: &Address,
account: &Address,
reason: String,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if caller != &self.owner {
return Err(ACC20Error::Unauthorized(caller.clone()));
}
self.frozen_accounts.insert(account.clone(), reason.clone());
self.pending_events.push(ACC20Event::AccountFrozen {
account: account.clone(),
reason,
constitutional_receipt,
timestamp,
});
Ok(())
}
/// 解冻账户
pub fn unfreeze_account(
&mut self,
caller: &Address,
account: &Address,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if caller != &self.owner {
return Err(ACC20Error::Unauthorized(caller.clone()));
}
self.frozen_accounts.remove(account);
self.pending_events.push(ACC20Event::AccountUnfrozen {
account: account.clone(),
constitutional_receipt,
timestamp,
});
Ok(())
}
/// 暂停所有转账
pub fn halt_transfers(
&mut self,
caller: &Address,
reason: String,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if caller != &self.owner {
return Err(ACC20Error::Unauthorized(caller.clone()));
}
self.transfer_halted = true;
self.halt_reason = Some(reason.clone());
self.pending_events.push(ACC20Event::TransferHalted {
reason,
constitutional_receipt,
timestamp,
});
Ok(())
}
/// 恢复所有转账
pub fn resume_transfers(
&mut self,
caller: &Address,
constitutional_receipt: Hash,
timestamp: Timestamp,
) -> Result<(), ACC20Error> {
if constitutional_receipt.is_zero() {
return Err(ACC20Error::InvalidConstitutionalReceipt);
}
if caller != &self.owner {
return Err(ACC20Error::Unauthorized(caller.clone()));
}
self.transfer_halted = false;
self.halt_reason = None;
self.pending_events.push(ACC20Event::TransferResumed {
constitutional_receipt,
timestamp,
});
Ok(())
}
/// 添加铸造者
pub fn add_minter(
&mut self,
caller: &Address,
minter: Address,
) -> Result<(), ACC20Error> {
if caller != &self.owner {
return Err(ACC20Error::Unauthorized(caller.clone()));
}
if !self.minters.contains(&minter) {
self.minters.push(minter);
}
Ok(())
}
/// 排空待广播事件
pub fn drain_pending_events(&mut self) -> Vec<ACC20Event> {
std::mem::take(&mut self.pending_events)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_addr(b: u8) -> Address { Address::new([b; 32]) }
fn make_hash(b: u8) -> Hash { Hash::sha3_384(&[b; 48]) }
#[test]
fn test_acc20_creation() {
let owner = make_addr(1);
let receipt = make_hash(9);
let ts = Timestamp::now();
let token = ACC20Token::new(
"NAC Test Token".to_string(),
"NTT".to_string(),
18,
owner.clone(),
1_000_000 * 10u128.pow(18),
Some(10_000_000 * 10u128.pow(18)),
receipt,
ts,
).expect("FIX-006: unexpected None/Err");
assert_eq!(token.balance_of(&owner), 1_000_000 * 10u128.pow(18));
assert_eq!(token.total_supply, 1_000_000 * 10u128.pow(18));
}
#[test]
fn test_transfer() {
let owner = make_addr(1);
let receiver = make_addr(2);
let receipt = make_hash(9);
let ts = Timestamp::now();
let mut token = ACC20Token::new(
"Test".to_string(), "TST".to_string(), 18,
owner.clone(), 1000, None, receipt.clone(), ts.clone(),
).expect("FIX-006: unexpected None/Err");
token.transfer(&owner, &receiver, 300, receipt, ts).expect("FIX-006: unexpected None/Err");
assert_eq!(token.balance_of(&owner), 700);
assert_eq!(token.balance_of(&receiver), 300);
}
#[test]
fn test_freeze_prevents_transfer() {
let owner = make_addr(1);
let victim = make_addr(2);
let receipt = make_hash(9);
let ts = Timestamp::now();
let mut token = ACC20Token::new(
"Test".to_string(), "TST".to_string(), 18,
owner.clone(), 1000, None, receipt.clone(), ts.clone(),
).expect("FIX-006: unexpected None/Err");
token.transfer(&owner, &victim, 500, receipt.clone(), ts.clone()).expect("FIX-006: unexpected None/Err");
token.freeze_account(&owner, &victim, "AML".to_string(), receipt.clone(), ts.clone()).expect("FIX-006: unexpected None/Err");
let result = token.transfer(&victim, &owner, 100, receipt, ts);
assert!(matches!(result, Err(ACC20Error::AccountFrozen(_))));
}
#[test]
fn test_supply_cap() {
let owner = make_addr(1);
let receipt = make_hash(9);
let ts = Timestamp::now();
let mut token = ACC20Token::new(
"Test".to_string(), "TST".to_string(), 18,
owner.clone(), 900, Some(1000), receipt.clone(), ts.clone(),
).expect("FIX-006: unexpected None/Err");
// 铸造到上限
token.mint(&owner, &owner, 100, receipt.clone(), ts.clone()).expect("FIX-006: unexpected None/Err");
// 超出上限
let result = token.mint(&owner, &owner, 1, receipt, ts);
assert!(matches!(result, Err(ACC20Error::SupplyCapExceeded { .. })));
}
}
// ============================================================
// ACC-20 上层接口(供 nac-asset-onboarding 等模块使用)
// ============================================================
/// ACC-20 代币部署请求
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TokenDeployRequest {
/// 代币名称
pub name: String,
/// 代币符号
pub symbol: String,
/// 总供应量
pub total_supply: u128,
/// 精度(小数位数)
pub decimals: u8,
/// GNACS 资产分类码
pub gnacs_code: String,
/// 部署者地址32 字节NAC 原生地址格式)
pub deployer: String,
}
/// ACC-20 代币元数据
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TokenMetadata {
/// 合约地址32 字节)
pub contract_address: String,
/// 代币名称
pub name: String,
/// 代币符号
pub symbol: String,
/// 总供应量
pub total_supply: u128,
/// 精度
pub decimals: u8,
/// 部署区块高度
pub deploy_block: u64,
/// 部署交易哈希SHA3-38448 字节)
pub deploy_tx_hash: String,
}
/// ACC-20 协议接口
pub struct ACC20Protocol;
impl ACC20Protocol {
/// 部署新的 ACC-20 代币合约
pub async fn deploy(&self, request: TokenDeployRequest) -> Result<TokenMetadata, String> {
// TODO: 通过 NVM 部署 Charter 合约
Ok(TokenMetadata {
contract_address: format!("nac1{}", &request.name.to_lowercase()),
name: request.name,
symbol: request.symbol,
total_supply: request.total_supply,
decimals: request.decimals,
deploy_block: 0,
deploy_tx_hash: String::new(),
})
}
}