NAC_Blockchain/nvm_v2/acc-protocol/src/acc_governance.rs

580 lines
16 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-Governance: 资产治理协议Governance Protocol
//
// NAC原生的资产治理标准用于去中心化治理和投票
// 100% NAC原生协议不是任何现有标准的实现
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// 提案状态
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProposalStatus {
/// 草稿
Draft,
/// 活跃(投票中)
Active,
/// 已通过
Passed,
/// 已拒绝
Rejected,
/// 已执行
Executed,
/// 已取消
Cancelled,
}
/// 投票选项
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum VoteOption {
/// 赞成
For,
/// 反对
Against,
/// 弃权
Abstain,
}
/// 提案类型
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProposalType {
/// 参数修改
ParameterChange,
/// 资金分配
FundingAllocation,
/// 协议升级
ProtocolUpgrade,
/// 资产管理
AssetManagement,
/// 其他
Other(String),
}
/// 治理提案
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GovernanceProposal {
/// 提案ID
pub proposal_id: String,
/// 资产ID
pub asset_id: String,
/// 提案人地址
pub proposer: String,
/// 提案类型
pub proposal_type: ProposalType,
/// 提案标题
pub title: String,
/// 提案描述
pub description: String,
/// 提案状态
pub status: ProposalStatus,
/// 创建时间
pub created_at: u64,
/// 投票开始时间
pub voting_start: u64,
/// 投票结束时间
pub voting_end: u64,
/// 执行时间
pub executed_at: Option<u64>,
/// 赞成票数
pub votes_for: u128,
/// 反对票数
pub votes_against: u128,
/// 弃权票数
pub votes_abstain: u128,
/// 通过阈值百分比如5000表示50%
pub quorum_threshold: u16,
/// 投票记录 (voter -> VoteOption)
pub votes: HashMap<String, VoteOption>,
}
/// 投票记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VoteRecord {
/// 投票ID
pub vote_id: String,
/// 提案ID
pub proposal_id: String,
/// 投票人地址
pub voter: String,
/// 投票选项
pub option: VoteOption,
/// 投票权重
pub voting_power: u128,
/// 投票时间
pub voted_at: u64,
}
/// 治理错误类型
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum GovernanceError {
/// 提案不存在
ProposalNotFound,
/// 提案已存在
ProposalAlreadyExists,
/// 投票未开始
VotingNotStarted,
/// 投票已结束
VotingEnded,
/// 已投票
AlreadyVoted,
/// 提案未通过
ProposalNotPassed,
/// 提案已执行
ProposalAlreadyExecuted,
/// 未授权操作
Unauthorized,
/// 无效的投票权重
InvalidVotingPower,
}
/// 治理状态
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GovernanceState {
/// 提案映射 (proposal_id -> GovernanceProposal)
pub proposals: HashMap<String, GovernanceProposal>,
/// 资产提案列表 (asset_id -> [proposal_ids])
pub asset_proposals: HashMap<String, Vec<String>>,
/// 提案人提案列表 (proposer -> [proposal_ids])
pub proposer_proposals: HashMap<String, Vec<String>>,
/// 投票记录 (vote_id -> VoteRecord)
pub vote_records: HashMap<String, VoteRecord>,
}
/// ACC-Governance接口
pub trait ACCGovernance {
/// 创建提案
fn create_proposal(&mut self, proposal: GovernanceProposal) -> Result<(), GovernanceError>;
/// 获取提案
fn get_proposal(&self, proposal_id: &str) -> Result<GovernanceProposal, GovernanceError>;
/// 投票
fn vote(
&mut self,
proposal_id: &str,
voter: &str,
option: VoteOption,
voting_power: u128,
) -> Result<(), GovernanceError>;
/// 执行提案
fn execute_proposal(&mut self, proposal_id: &str) -> Result<(), GovernanceError>;
/// 取消提案
fn cancel_proposal(&mut self, proposal_id: &str) -> Result<(), GovernanceError>;
/// 获取提案结果
fn get_proposal_result(&self, proposal_id: &str) -> Result<(u128, u128, u128), GovernanceError>;
/// 检查提案是否通过
fn is_proposal_passed(&self, proposal_id: &str) -> bool;
/// 获取资产的提案列表
fn get_asset_proposals(&self, asset_id: &str) -> Vec<GovernanceProposal>;
/// 获取提案人的提案列表
fn get_proposer_proposals(&self, proposer: &str) -> Vec<GovernanceProposal>;
/// 获取投票记录
fn get_vote_record(&self, vote_id: &str) -> Result<VoteRecord, GovernanceError>;
/// 获取活跃提案列表
fn get_active_proposals(&self) -> Vec<GovernanceProposal>;
/// 更新提案状态
fn update_proposal_status(
&mut self,
proposal_id: &str,
status: ProposalStatus,
) -> Result<(), GovernanceError>;
}
/// ACC-Governance标准实现
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GovernanceToken {
state: GovernanceState,
}
impl GovernanceToken {
/// 创建新的治理管理器
pub fn new() -> Self {
Self {
state: GovernanceState {
proposals: HashMap::new(),
asset_proposals: HashMap::new(),
proposer_proposals: HashMap::new(),
vote_records: HashMap::new(),
},
}
}
/// 获取状态的可变引用
pub fn state_mut(&mut self) -> &mut GovernanceState {
&mut self.state
}
/// 获取状态的不可变引用
pub fn state(&self) -> &GovernanceState {
&self.state
}
/// 检查投票是否有效
fn is_voting_active(&self, proposal: &GovernanceProposal) -> bool {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
now >= proposal.voting_start && now <= proposal.voting_end
}
}
impl Default for GovernanceToken {
fn default() -> Self {
Self::new()
}
}
impl ACCGovernance for GovernanceToken {
fn create_proposal(&mut self, mut proposal: GovernanceProposal) -> Result<(), GovernanceError> {
// 检查提案是否已存在
if self.state.proposals.contains_key(&proposal.proposal_id) {
return Err(GovernanceError::ProposalAlreadyExists);
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
proposal.created_at = now;
proposal.status = ProposalStatus::Active;
proposal.votes_for = 0;
proposal.votes_against = 0;
proposal.votes_abstain = 0;
let proposal_id = proposal.proposal_id.clone();
let asset_id = proposal.asset_id.clone();
let proposer = proposal.proposer.clone();
// 保存提案
self.state
.proposals
.insert(proposal_id.clone(), proposal);
// 更新资产提案列表
self.state
.asset_proposals
.entry(asset_id)
.or_insert_with(Vec::new)
.push(proposal_id.clone());
// 更新提案人提案列表
self.state
.proposer_proposals
.entry(proposer)
.or_insert_with(Vec::new)
.push(proposal_id);
Ok(())
}
fn get_proposal(&self, proposal_id: &str) -> Result<GovernanceProposal, GovernanceError> {
self.state
.proposals
.get(proposal_id)
.cloned()
.ok_or(GovernanceError::ProposalNotFound)
}
fn vote(
&mut self,
proposal_id: &str,
voter: &str,
option: VoteOption,
voting_power: u128,
) -> Result<(), GovernanceError> {
let mut proposal = self.get_proposal(proposal_id)?;
// 检查投票是否有效
if !self.is_voting_active(&proposal) {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
if now < proposal.voting_start {
return Err(GovernanceError::VotingNotStarted);
} else {
return Err(GovernanceError::VotingEnded);
}
}
// 检查是否已投票
if proposal.votes.contains_key(voter) {
return Err(GovernanceError::AlreadyVoted);
}
// 检查投票权重
if voting_power == 0 {
return Err(GovernanceError::InvalidVotingPower);
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
// 更新投票统计
match option {
VoteOption::For => proposal.votes_for += voting_power,
VoteOption::Against => proposal.votes_against += voting_power,
VoteOption::Abstain => proposal.votes_abstain += voting_power,
}
// 记录投票
proposal.votes.insert(voter.to_string(), option.clone());
// 保存提案
self.state
.proposals
.insert(proposal_id.to_string(), proposal);
// 创建投票记录
let vote_id = format!("{}_{}", proposal_id, voter);
let vote_record = VoteRecord {
vote_id: vote_id.clone(),
proposal_id: proposal_id.to_string(),
voter: voter.to_string(),
option,
voting_power,
voted_at: now,
};
self.state.vote_records.insert(vote_id, vote_record);
Ok(())
}
fn execute_proposal(&mut self, proposal_id: &str) -> Result<(), GovernanceError> {
let mut proposal = self.get_proposal(proposal_id)?;
// 检查提案是否通过
if !self.is_proposal_passed(proposal_id) {
return Err(GovernanceError::ProposalNotPassed);
}
// 检查提案是否已执行
if proposal.status == ProposalStatus::Executed {
return Err(GovernanceError::ProposalAlreadyExecuted);
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
proposal.status = ProposalStatus::Executed;
proposal.executed_at = Some(now);
self.state
.proposals
.insert(proposal_id.to_string(), proposal);
Ok(())
}
fn cancel_proposal(&mut self, proposal_id: &str) -> Result<(), GovernanceError> {
let mut proposal = self.get_proposal(proposal_id)?;
if proposal.status == ProposalStatus::Executed {
return Err(GovernanceError::ProposalAlreadyExecuted);
}
proposal.status = ProposalStatus::Cancelled;
self.state
.proposals
.insert(proposal_id.to_string(), proposal);
Ok(())
}
fn get_proposal_result(&self, proposal_id: &str) -> Result<(u128, u128, u128), GovernanceError> {
let proposal = self.get_proposal(proposal_id)?;
Ok((
proposal.votes_for,
proposal.votes_against,
proposal.votes_abstain,
))
}
fn is_proposal_passed(&self, proposal_id: &str) -> bool {
if let Ok(proposal) = self.get_proposal(proposal_id) {
let total_votes = proposal.votes_for + proposal.votes_against + proposal.votes_abstain;
if total_votes == 0 {
return false;
}
let approval_rate = (proposal.votes_for * 10000 / total_votes) as u16;
approval_rate >= proposal.quorum_threshold
} else {
false
}
}
fn get_asset_proposals(&self, asset_id: &str) -> Vec<GovernanceProposal> {
self.state
.asset_proposals
.get(asset_id)
.map(|proposal_ids| {
proposal_ids
.iter()
.filter_map(|id| self.state.proposals.get(id).cloned())
.collect()
})
.unwrap_or_default()
}
fn get_proposer_proposals(&self, proposer: &str) -> Vec<GovernanceProposal> {
self.state
.proposer_proposals
.get(proposer)
.map(|proposal_ids| {
proposal_ids
.iter()
.filter_map(|id| self.state.proposals.get(id).cloned())
.collect()
})
.unwrap_or_default()
}
fn get_vote_record(&self, vote_id: &str) -> Result<VoteRecord, GovernanceError> {
self.state
.vote_records
.get(vote_id)
.cloned()
.ok_or(GovernanceError::ProposalNotFound)
}
fn get_active_proposals(&self) -> Vec<GovernanceProposal> {
self.state
.proposals
.values()
.filter(|proposal| proposal.status == ProposalStatus::Active)
.cloned()
.collect()
}
fn update_proposal_status(
&mut self,
proposal_id: &str,
status: ProposalStatus,
) -> Result<(), GovernanceError> {
let mut proposal = self.get_proposal(proposal_id)?;
proposal.status = status;
self.state
.proposals
.insert(proposal_id.to_string(), proposal);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_proposal() -> GovernanceProposal {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
GovernanceProposal {
proposal_id: "PROP-001".to_string(),
asset_id: "RWA-001".to_string(),
proposer: "alice".to_string(),
proposal_type: ProposalType::ParameterChange,
title: "Test Proposal".to_string(),
description: "This is a test proposal".to_string(),
status: ProposalStatus::Active,
created_at: now,
voting_start: now - 3600, // 1小时前开始
voting_end: now + 86400 * 7, // 7天后结束
executed_at: None,
votes_for: 0,
votes_against: 0,
votes_abstain: 0,
quorum_threshold: 5000, // 50%
votes: HashMap::new(),
}
}
#[test]
fn test_create_proposal() {
let mut governance = GovernanceToken::new();
let proposal = create_test_proposal();
assert!(governance.create_proposal(proposal).is_ok());
}
#[test]
fn test_vote() {
let mut governance = GovernanceToken::new();
let proposal = create_test_proposal();
governance.create_proposal(proposal).unwrap();
assert!(governance
.vote("PROP-001", "bob", VoteOption::For, 1000)
.is_ok());
let (votes_for, _, _) = governance.get_proposal_result("PROP-001").unwrap();
assert_eq!(votes_for, 1000);
}
#[test]
fn test_is_proposal_passed() {
let mut governance = GovernanceToken::new();
let proposal = create_test_proposal();
governance.create_proposal(proposal).unwrap();
governance
.vote("PROP-001", "bob", VoteOption::For, 6000)
.unwrap();
governance
.vote("PROP-001", "charlie", VoteOption::Against, 4000)
.unwrap();
assert!(governance.is_proposal_passed("PROP-001"));
}
#[test]
fn test_execute_proposal() {
let mut governance = GovernanceToken::new();
let proposal = create_test_proposal();
governance.create_proposal(proposal).unwrap();
governance
.vote("PROP-001", "bob", VoteOption::For, 6000)
.unwrap();
governance
.vote("PROP-001", "charlie", VoteOption::Against, 4000)
.unwrap();
assert!(governance.execute_proposal("PROP-001").is_ok());
assert_eq!(
governance.get_proposal("PROP-001").unwrap().status,
ProposalStatus::Executed
);
}
#[test]
fn test_cancel_proposal() {
let mut governance = GovernanceToken::new();
let proposal = create_test_proposal();
governance.create_proposal(proposal).unwrap();
assert!(governance.cancel_proposal("PROP-001").is_ok());
assert_eq!(
governance.get_proposal("PROP-001").unwrap().status,
ProposalStatus::Cancelled
);
}
}