351 lines
13 KiB
Rust
351 lines
13 KiB
Rust
//! acc_governance - NAC 原生协议实现
|
||
|
||
use crate::primitives::{Address, Hash, Timestamp};
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||
/// ACCGovernanceError 错误类型
|
||
pub enum ACCGovernanceError {
|
||
/// ProposalNotFound 变体
|
||
ProposalNotFound(Hash),
|
||
/// ProposalAlreadyExecuted 变体
|
||
ProposalAlreadyExecuted(Hash),
|
||
/// VotingPeriodEnded 变体
|
||
VotingPeriodEnded(Hash),
|
||
/// AlreadyVoted 变体
|
||
AlreadyVoted(Address),
|
||
/// InsufficientVotingPower 变体
|
||
InsufficientVotingPower {
|
||
/// 所需数量
|
||
required: u128,
|
||
/// actual 字段
|
||
actual: u128,
|
||
},
|
||
/// QuorumNotReached 变体
|
||
QuorumNotReached {
|
||
/// 所需数量
|
||
required: u128,
|
||
/// actual 字段
|
||
actual: u128,
|
||
},
|
||
/// 宪法收据无效错误
|
||
InvalidConstitutionalReceipt,
|
||
/// 未授权操作错误
|
||
Unauthorized(Address),
|
||
}
|
||
impl std::fmt::Display for ACCGovernanceError {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
match self {
|
||
Self::ProposalNotFound(h) => write!(f, "提案不存在: {}", h.to_hex()),
|
||
Self::ProposalAlreadyExecuted(h) => write!(f, "提案已执行: {}", h.to_hex()),
|
||
Self::VotingPeriodEnded(h) => write!(f, "投票期已结束: {}", h.to_hex()),
|
||
Self::AlreadyVoted(a) => write!(f, "已投票: {}", a.to_hex()),
|
||
Self::InsufficientVotingPower { required, actual } => write!(f, "投票权不足: 需要 {},实际 {}", required, actual),
|
||
Self::QuorumNotReached { required, actual } => write!(f, "未达法定人数: 需要 {},实际 {}", required, actual),
|
||
Self::InvalidConstitutionalReceipt => write!(f, "宪法收据无效"),
|
||
Self::Unauthorized(a) => write!(f, "未授权: {}", a.to_hex()),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
/// ProposalType 类型
|
||
pub enum ProposalType {
|
||
/// ParameterChange 变体
|
||
ParameterChange {
|
||
/// parameter 字段
|
||
parameter: String,
|
||
/// new_value 字段
|
||
new_value: Vec<u8>,
|
||
},
|
||
/// ProtocolUpgrade 变体
|
||
ProtocolUpgrade {
|
||
/// new_version 字段
|
||
new_version: String,
|
||
/// upgrade_hash 哈希值(48字节 SHA3-384)
|
||
upgrade_hash: Hash,
|
||
},
|
||
/// AssetPolicyChange 变体
|
||
AssetPolicyChange {
|
||
/// 资产标识符
|
||
asset_id: Hash,
|
||
/// policy_hash 哈希值(48字节 SHA3-384)
|
||
policy_hash: Hash,
|
||
},
|
||
/// EmergencyPause 变体
|
||
EmergencyPause {
|
||
/// 操作原因说明
|
||
reason: String,
|
||
},
|
||
/// GovernanceMemberChange 变体
|
||
GovernanceMemberChange {
|
||
/// member 字段
|
||
member: Address,
|
||
/// action 字段
|
||
action: MemberAction,
|
||
},
|
||
}
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
/// MemberAction 枚举类型
|
||
pub enum MemberAction {
|
||
/// Add 变体
|
||
Add,
|
||
/// Remove 变体
|
||
Remove,
|
||
}
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
/// ProposalStatus 状态枚举
|
||
pub enum ProposalStatus {
|
||
/// 活跃状态
|
||
Active,
|
||
/// 已通过状态
|
||
Passed,
|
||
/// 已拒绝状态
|
||
Rejected,
|
||
/// 已执行状态
|
||
Executed,
|
||
/// 已取消状态
|
||
Cancelled,
|
||
/// 已过期状态
|
||
Expired,
|
||
}
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
/// VoteChoice 枚举类型
|
||
pub enum VoteChoice {
|
||
/// For 变体
|
||
For,
|
||
/// Against 变体
|
||
Against,
|
||
/// Abstain 变体
|
||
Abstain,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
/// GovernanceProposal 结构体
|
||
pub struct GovernanceProposal {
|
||
/// 治理提案标识符
|
||
pub proposal_id: Hash,
|
||
/// proposer 字段
|
||
pub proposer: Address,
|
||
/// proposal_type 字段
|
||
pub proposal_type: ProposalType,
|
||
/// 详细描述
|
||
pub description: String,
|
||
/// 状态
|
||
pub status: ProposalStatus,
|
||
/// votes_for 字段
|
||
pub votes_for: u128,
|
||
/// votes_against 字段
|
||
pub votes_against: u128,
|
||
/// votes_abstain 字段
|
||
pub votes_abstain: u128,
|
||
/// vote_records 字段
|
||
pub vote_records: HashMap<Address, VoteChoice>,
|
||
/// voting_start 字段
|
||
pub voting_start: Timestamp,
|
||
/// voting_end 字段
|
||
pub voting_end: Timestamp,
|
||
/// quorum_bps 字段
|
||
pub quorum_bps: u32,
|
||
/// pass_threshold_bps 字段
|
||
pub pass_threshold_bps: u32,
|
||
/// 宪法收据哈希(CBPP 共识凭证)
|
||
pub constitutional_receipt: Hash,
|
||
/// 创建时间戳
|
||
pub created_at: Timestamp,
|
||
/// 执行时间戳
|
||
pub executed_at: Option<Timestamp>,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
/// GovernanceProtocolEvent 协议事件
|
||
pub enum GovernanceProtocolEvent {
|
||
/// ProposalCreated 变体
|
||
ProposalCreated {
|
||
/// 治理提案标识符
|
||
proposal_id: Hash,
|
||
/// proposer 字段
|
||
proposer: Address,
|
||
/// 操作时间戳(UTC Unix 毫秒)
|
||
timestamp: Timestamp,
|
||
},
|
||
/// VoteCast 变体
|
||
VoteCast {
|
||
/// 治理提案标识符
|
||
proposal_id: Hash,
|
||
/// 投票者账户地址
|
||
voter: Address,
|
||
/// choice 字段
|
||
choice: VoteChoice,
|
||
/// voting_power 字段
|
||
voting_power: u128,
|
||
/// 操作时间戳(UTC Unix 毫秒)
|
||
timestamp: Timestamp,
|
||
},
|
||
/// ProposalPassed 变体
|
||
ProposalPassed {
|
||
/// 治理提案标识符
|
||
proposal_id: Hash,
|
||
/// votes_for 字段
|
||
votes_for: u128,
|
||
/// votes_against 字段
|
||
votes_against: u128,
|
||
/// 操作时间戳(UTC Unix 毫秒)
|
||
timestamp: Timestamp,
|
||
},
|
||
/// ProposalRejected 变体
|
||
ProposalRejected {
|
||
/// 治理提案标识符
|
||
proposal_id: Hash,
|
||
/// 操作原因说明
|
||
reason: String,
|
||
/// 操作时间戳(UTC Unix 毫秒)
|
||
timestamp: Timestamp,
|
||
},
|
||
/// ProposalExecuted 变体
|
||
ProposalExecuted {
|
||
/// 治理提案标识符
|
||
proposal_id: Hash,
|
||
/// 操作时间戳(UTC Unix 毫秒)
|
||
timestamp: Timestamp,
|
||
/// 宪法收据哈希(CBPP 共识凭证)
|
||
constitutional_receipt: Hash,
|
||
},
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
/// ACCGovernanceProtocol 协议实现
|
||
pub struct ACCGovernanceProtocol {
|
||
/// 协议唯一标识符
|
||
pub protocol_uid: String,
|
||
/// NAC-Lens 协议向量
|
||
pub lens_protocol_vector: String,
|
||
/// proposals 字段
|
||
pub proposals: HashMap<Hash, GovernanceProposal>,
|
||
/// voting_power_registry 字段
|
||
pub voting_power_registry: HashMap<Address, u128>,
|
||
/// total_voting_power 字段
|
||
pub total_voting_power: u128,
|
||
/// default_quorum_bps 字段
|
||
pub default_quorum_bps: u32,
|
||
/// default_pass_threshold_bps 字段
|
||
pub default_pass_threshold_bps: u32,
|
||
/// 待广播的 CSNP 协议事件队列
|
||
pub pending_events: Vec<GovernanceProtocolEvent>,
|
||
/// 创建时间戳
|
||
pub created_at: Timestamp,
|
||
/// 最后更新时间戳
|
||
pub updated_at: Timestamp,
|
||
}
|
||
impl ACCGovernanceProtocol {
|
||
/// new 方法
|
||
pub fn new(default_quorum_bps: u32, default_pass_threshold_bps: u32) -> Self {
|
||
Self {
|
||
protocol_uid: "nac.acc.ACCGovernanceProtocol.v1".to_string(),
|
||
lens_protocol_vector: "ACC-Governance".to_string(),
|
||
proposals: HashMap::new(),
|
||
voting_power_registry: HashMap::new(),
|
||
total_voting_power: 0,
|
||
default_quorum_bps, default_pass_threshold_bps,
|
||
pending_events: Vec::new(),
|
||
created_at: Timestamp::now(), updated_at: Timestamp::now(),
|
||
}
|
||
}
|
||
/// register_voting_power 方法
|
||
pub fn register_voting_power(&mut self, address: Address, power: u128) {
|
||
let old = self.voting_power_registry.insert(address, power).unwrap_or(0);
|
||
self.total_voting_power = self.total_voting_power.saturating_sub(old).saturating_add(power);
|
||
}
|
||
/// 创建治理提案
|
||
pub fn create_proposal(
|
||
&mut self, proposer: Address, proposal_type: ProposalType, description: String,
|
||
voting_duration_secs: u64, constitutional_receipt: Hash, timestamp: Timestamp,
|
||
) -> Result<Hash, ACCGovernanceError> {
|
||
if constitutional_receipt.is_zero() { return Err(ACCGovernanceError::InvalidConstitutionalReceipt); }
|
||
let proposer_power = self.voting_power_registry.get(&proposer).copied().unwrap_or(0);
|
||
if proposer_power == 0 {
|
||
return Err(ACCGovernanceError::InsufficientVotingPower { required: 1, actual: 0 });
|
||
}
|
||
let mut data = Vec::new();
|
||
data.extend_from_slice(proposer.as_bytes());
|
||
data.extend_from_slice(×tamp.as_secs().to_be_bytes());
|
||
let proposal_id = Hash::sha3_384(&data);
|
||
let proposal = GovernanceProposal {
|
||
proposal_id, proposer: proposer.clone(), proposal_type, description,
|
||
status: ProposalStatus::Active,
|
||
votes_for: 0, votes_against: 0, votes_abstain: 0,
|
||
vote_records: HashMap::new(),
|
||
voting_start: timestamp.clone(),
|
||
voting_end: timestamp.add_secs(voting_duration_secs),
|
||
quorum_bps: self.default_quorum_bps,
|
||
pass_threshold_bps: self.default_pass_threshold_bps,
|
||
constitutional_receipt, created_at: timestamp.clone(), executed_at: None,
|
||
};
|
||
self.proposals.insert(proposal_id, proposal);
|
||
self.pending_events.push(GovernanceProtocolEvent::ProposalCreated { proposal_id, proposer, timestamp });
|
||
self.updated_at = Timestamp::now();
|
||
Ok(proposal_id)
|
||
}
|
||
/// 投票
|
||
pub fn cast_vote(
|
||
&mut self, proposal_id: Hash, voter: Address, choice: VoteChoice, timestamp: Timestamp,
|
||
) -> Result<(), ACCGovernanceError> {
|
||
let voting_power = self.voting_power_registry.get(&voter).copied().unwrap_or(0);
|
||
if voting_power == 0 {
|
||
return Err(ACCGovernanceError::InsufficientVotingPower { required: 1, actual: 0 });
|
||
}
|
||
let proposal = self.proposals.get_mut(&proposal_id)
|
||
.ok_or(ACCGovernanceError::ProposalNotFound(proposal_id))?;
|
||
if proposal.status != ProposalStatus::Active {
|
||
return Err(ACCGovernanceError::VotingPeriodEnded(proposal_id));
|
||
}
|
||
if proposal.vote_records.contains_key(&voter) {
|
||
return Err(ACCGovernanceError::AlreadyVoted(voter));
|
||
}
|
||
match choice {
|
||
VoteChoice::For => proposal.votes_for = proposal.votes_for.saturating_add(voting_power),
|
||
VoteChoice::Against => proposal.votes_against = proposal.votes_against.saturating_add(voting_power),
|
||
VoteChoice::Abstain => proposal.votes_abstain = proposal.votes_abstain.saturating_add(voting_power),
|
||
}
|
||
proposal.vote_records.insert(voter.clone(), choice);
|
||
self.pending_events.push(GovernanceProtocolEvent::VoteCast { proposal_id, voter, choice, voting_power, timestamp });
|
||
Ok(())
|
||
}
|
||
/// finalize_proposal 方法
|
||
pub fn finalize_proposal(
|
||
&mut self, proposal_id: Hash, constitutional_receipt: Hash, timestamp: Timestamp,
|
||
) -> Result<bool, ACCGovernanceError> {
|
||
if constitutional_receipt.is_zero() { return Err(ACCGovernanceError::InvalidConstitutionalReceipt); }
|
||
let proposal = self.proposals.get_mut(&proposal_id)
|
||
.ok_or(ACCGovernanceError::ProposalNotFound(proposal_id))?;
|
||
let total_votes = proposal.votes_for + proposal.votes_against + proposal.votes_abstain;
|
||
let quorum_required = (self.total_voting_power as u64 * proposal.quorum_bps as u64 / 10000) as u128;
|
||
if total_votes < quorum_required {
|
||
proposal.status = ProposalStatus::Rejected;
|
||
self.pending_events.push(GovernanceProtocolEvent::ProposalRejected {
|
||
proposal_id, reason: format!("未达法定人数: {} < {}", total_votes, quorum_required), timestamp,
|
||
});
|
||
return Ok(false);
|
||
}
|
||
let effective_votes = proposal.votes_for + proposal.votes_against;
|
||
let pass_required = (effective_votes as u64 * proposal.pass_threshold_bps as u64 / 10000) as u128;
|
||
let passed = proposal.votes_for >= pass_required;
|
||
if passed {
|
||
proposal.status = ProposalStatus::Executed;
|
||
proposal.executed_at = Some(timestamp.clone());
|
||
self.pending_events.push(GovernanceProtocolEvent::ProposalExecuted { proposal_id, timestamp, constitutional_receipt });
|
||
} else {
|
||
proposal.status = ProposalStatus::Rejected;
|
||
self.pending_events.push(GovernanceProtocolEvent::ProposalRejected {
|
||
proposal_id, reason: "赞成票未达阈值".to_string(), timestamp,
|
||
});
|
||
}
|
||
Ok(passed)
|
||
}
|
||
/// 获取提案详情
|
||
pub fn get_proposal(&self, id: &Hash) -> Option<&GovernanceProposal> { self.proposals.get(id) }
|
||
/// 排空待广播事件队列
|
||
pub fn drain_pending_events(&mut self) -> Vec<GovernanceProtocolEvent> { std::mem::take(&mut self.pending_events) }
|
||
}
|