NAC_Blockchain/nac-acc-1400/src/voting.rs

809 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.

//! 投票权系统
//!
//! 实现证券型资产的投票机制、权重计算、投票记录和结果统计
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
/// 投票提案
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Proposal {
/// 提案ID
pub id: String,
/// 提案标题
pub title: String,
/// 提案描述
pub description: String,
/// 提案类型
pub proposal_type: ProposalType,
/// 创建者
pub creator: String,
/// 创建时间
pub created_at: u64,
/// 投票开始时间
pub voting_start: u64,
/// 投票结束时间
pub voting_end: u64,
/// 状态
pub status: ProposalStatus,
/// 需要的最小投票率(百分比)
pub quorum_percentage: u8,
/// 需要的赞成率(百分比)
pub approval_percentage: u8,
/// 证券分区ID
pub security_id: [u8; 32],
}
/// 提案类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProposalType {
/// 董事会选举
BoardElection,
/// 章程修改
CharterAmendment,
/// 重大交易
MajorTransaction,
/// 股息分配
DividendDistribution,
/// 资产重组
Restructuring,
/// 其他
Other,
}
/// 提案状态
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProposalStatus {
/// 草案
Draft,
/// 投票中
Active,
/// 已通过
Passed,
/// 未通过
Rejected,
/// 已取消
Cancelled,
/// 已执行
Executed,
}
/// 投票选项
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum VoteOption {
/// 赞成
For,
/// 反对
Against,
/// 弃权
Abstain,
}
/// 投票记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VoteRecord {
/// 提案ID
pub proposal_id: String,
/// 投票人
pub voter: String,
/// 投票选项
pub option: VoteOption,
/// 持股数量
pub shares: u64,
/// 投票权重(考虑投票倍数)
pub voting_power: u64,
/// 投票时间
pub timestamp: u64,
/// 是否为代理投票
pub is_proxy: bool,
/// 代理人(如果是代理投票)
pub proxy: Option<String>,
}
/// 投票结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VotingResult {
/// 提案ID
pub proposal_id: String,
/// 赞成票数
pub votes_for: u64,
/// 反对票数
pub votes_against: u64,
/// 弃权票数
pub votes_abstain: u64,
/// 总投票权重
pub total_voting_power: u64,
/// 总股份数
pub total_shares: u64,
/// 投票率(百分比)
pub turnout_percentage: u8,
/// 赞成率(百分比,基于有效票)
pub approval_percentage: u8,
/// 是否达到法定人数
pub quorum_reached: bool,
/// 是否通过
pub passed: bool,
}
/// 投票权配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VotingRights {
/// 账户地址
pub account: String,
/// 持股数量
pub shares: u64,
/// 投票倍数(例如:优先股可能有更高的投票权)
pub voting_multiplier: u32,
/// 是否被限制投票
pub restricted: bool,
/// 限制原因
pub restriction_reason: Option<String>,
}
/// 代理投票授权
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProxyAuthorization {
/// 授权人
pub principal: String,
/// 代理人
pub proxy: String,
/// 授权的提案ID如果为None则表示全部提案
pub proposal_id: Option<String>,
/// 授权开始时间
pub valid_from: u64,
/// 授权结束时间
pub valid_until: u64,
/// 是否已撤销
pub revoked: bool,
}
/// 投票系统
#[derive(Debug)]
pub struct VotingSystem {
/// 提案列表
proposals: HashMap<String, Proposal>,
/// 投票记录
votes: HashMap<String, Vec<VoteRecord>>,
/// 投票权配置
voting_rights: HashMap<String, VotingRights>,
/// 代理授权
proxy_authorizations: Vec<ProxyAuthorization>,
/// 下一个提案ID
next_proposal_id: u64,
}
impl VotingSystem {
/// 创建新的投票系统
pub fn new() -> Self {
Self {
proposals: HashMap::new(),
votes: HashMap::new(),
voting_rights: HashMap::new(),
proxy_authorizations: Vec::new(),
next_proposal_id: 1,
}
}
/// 创建提案
pub fn create_proposal(
&mut self,
title: String,
description: String,
proposal_type: ProposalType,
creator: String,
security_id: [u8; 32],
voting_start: u64,
voting_end: u64,
quorum_percentage: u8,
approval_percentage: u8,
) -> Result<String, String> {
// 验证参数
if title.is_empty() {
return Err("Title cannot be empty".to_string());
}
if voting_start >= voting_end {
return Err("Voting start must be before voting end".to_string());
}
if quorum_percentage > 100 || approval_percentage > 100 {
return Err("Percentages must be between 0 and 100".to_string());
}
let current_time = Self::current_timestamp();
if voting_start < current_time {
return Err("Voting start must be in the future".to_string());
}
// 生成提案ID
let proposal_id = format!("PROP-{:08}", self.next_proposal_id);
self.next_proposal_id += 1;
// 创建提案
let proposal = Proposal {
id: proposal_id.clone(),
title,
description,
proposal_type,
creator,
created_at: current_time,
voting_start,
voting_end,
status: ProposalStatus::Draft,
quorum_percentage,
approval_percentage,
security_id,
};
self.proposals.insert(proposal_id.clone(), proposal);
self.votes.insert(proposal_id.clone(), Vec::new());
Ok(proposal_id)
}
/// 激活提案(开始投票)
pub fn activate_proposal(&mut self, proposal_id: &str) -> Result<(), String> {
let proposal = self.proposals.get_mut(proposal_id)
.ok_or_else(|| "Proposal not found".to_string())?;
if proposal.status != ProposalStatus::Draft {
return Err(format!("Proposal is not in draft status: {:?}", proposal.status));
}
let current_time = Self::current_timestamp();
if current_time < proposal.voting_start {
return Err("Voting period has not started yet".to_string());
}
proposal.status = ProposalStatus::Active;
Ok(())
}
/// 设置投票权
pub fn set_voting_rights(
&mut self,
account: String,
shares: u64,
voting_multiplier: u32,
) {
let rights = VotingRights {
account: account.clone(),
shares,
voting_multiplier,
restricted: false,
restriction_reason: None,
};
self.voting_rights.insert(account, rights);
}
/// 限制投票权
pub fn restrict_voting(&mut self, account: &str, reason: String) -> Result<(), String> {
let rights = self.voting_rights.get_mut(account)
.ok_or_else(|| "Account not found".to_string())?;
rights.restricted = true;
rights.restriction_reason = Some(reason);
Ok(())
}
/// 恢复投票权
pub fn unrestrict_voting(&mut self, account: &str) -> Result<(), String> {
let rights = self.voting_rights.get_mut(account)
.ok_or_else(|| "Account not found".to_string())?;
rights.restricted = false;
rights.restriction_reason = None;
Ok(())
}
/// 授权代理投票
pub fn authorize_proxy(
&mut self,
principal: String,
proxy: String,
proposal_id: Option<String>,
valid_from: u64,
valid_until: u64,
) -> Result<(), String> {
if principal == proxy {
return Err("Cannot authorize self as proxy".to_string());
}
if valid_from >= valid_until {
return Err("Valid from must be before valid until".to_string());
}
// 检查是否已存在相同的授权
for auth in &self.proxy_authorizations {
if auth.principal == principal
&& auth.proxy == proxy
&& auth.proposal_id == proposal_id
&& !auth.revoked {
return Err("Proxy authorization already exists".to_string());
}
}
let authorization = ProxyAuthorization {
principal,
proxy,
proposal_id,
valid_from,
valid_until,
revoked: false,
};
self.proxy_authorizations.push(authorization);
Ok(())
}
/// 撤销代理授权
pub fn revoke_proxy(
&mut self,
principal: &str,
proxy: &str,
proposal_id: Option<&str>,
) -> Result<(), String> {
let mut found = false;
for auth in &mut self.proxy_authorizations {
if auth.principal == principal
&& auth.proxy == proxy
&& auth.proposal_id.as_deref() == proposal_id
&& !auth.revoked {
auth.revoked = true;
found = true;
}
}
if found {
Ok(())
} else {
Err("Proxy authorization not found".to_string())
}
}
/// 检查代理授权是否有效
fn is_proxy_valid(
&self,
principal: &str,
proxy: &str,
proposal_id: &str,
) -> bool {
let current_time = Self::current_timestamp();
self.proxy_authorizations.iter().any(|auth| {
auth.principal == principal
&& auth.proxy == proxy
&& !auth.revoked
&& auth.valid_from <= current_time
&& auth.valid_until >= current_time
&& (auth.proposal_id.is_none() || auth.proposal_id.as_deref() == Some(proposal_id))
})
}
/// 投票
pub fn cast_vote(
&mut self,
proposal_id: &str,
voter: &str,
option: VoteOption,
as_proxy_for: Option<&str>,
) -> Result<(), String> {
// 检查提案是否存在
let proposal = self.proposals.get(proposal_id)
.ok_or_else(|| "Proposal not found".to_string())?;
// 检查提案状态
if proposal.status != ProposalStatus::Active {
return Err(format!("Proposal is not active: {:?}", proposal.status));
}
// 检查投票时间
let current_time = Self::current_timestamp();
if current_time < proposal.voting_start {
return Err("Voting has not started yet".to_string());
}
if current_time > proposal.voting_end {
return Err("Voting has ended".to_string());
}
// 确定实际投票人
let actual_voter = if let Some(principal) = as_proxy_for {
// 代理投票:检查授权
if !self.is_proxy_valid(principal, voter, proposal_id) {
return Err("Proxy authorization is not valid".to_string());
}
principal
} else {
voter
};
// 检查是否已投票
let votes = self.votes.get(proposal_id).unwrap();
if votes.iter().any(|v| v.voter == actual_voter) {
return Err("Already voted on this proposal".to_string());
}
// 获取投票权
let rights = self.voting_rights.get(actual_voter)
.ok_or_else(|| "Voter has no voting rights".to_string())?;
// 检查是否被限制
if rights.restricted {
return Err(format!(
"Voting rights restricted: {}",
rights.restriction_reason.as_deref().unwrap_or("Unknown reason")
));
}
// 计算投票权重
let voting_power = rights.shares as u64 * rights.voting_multiplier as u64;
// 创建投票记录
let vote_record = VoteRecord {
proposal_id: proposal_id.to_string(),
voter: actual_voter.to_string(),
option,
shares: rights.shares,
voting_power,
timestamp: current_time,
is_proxy: as_proxy_for.is_some(),
proxy: as_proxy_for.map(|_| voter.to_string()),
};
// 添加投票记录
self.votes.get_mut(proposal_id).unwrap().push(vote_record);
Ok(())
}
/// 计算投票结果
pub fn calculate_result(&self, proposal_id: &str) -> Result<VotingResult, String> {
let proposal = self.proposals.get(proposal_id)
.ok_or_else(|| "Proposal not found".to_string())?;
let votes = self.votes.get(proposal_id)
.ok_or_else(|| "No votes found".to_string())?;
// 统计各选项的票数
let mut votes_for = 0u64;
let mut votes_against = 0u64;
let mut votes_abstain = 0u64;
for vote in votes {
match vote.option {
VoteOption::For => votes_for += vote.voting_power,
VoteOption::Against => votes_against += vote.voting_power,
VoteOption::Abstain => votes_abstain += vote.voting_power,
}
}
// 计算总投票权重
let total_voting_power = votes_for + votes_against + votes_abstain;
// 计算总股份数(所有有投票权的股份)
let total_shares: u64 = self.voting_rights.values()
.filter(|r| !r.restricted)
.map(|r| r.shares as u64 * r.voting_multiplier as u64)
.sum();
// 计算投票率
let turnout_percentage = if total_shares > 0 {
((total_voting_power * 100) / total_shares) as u8
} else {
0
};
// 计算赞成率(基于有效票:赞成+反对)
let effective_votes = votes_for + votes_against;
let approval_percentage = if effective_votes > 0 {
((votes_for * 100) / effective_votes) as u8
} else {
0
};
// 检查是否达到法定人数
let quorum_reached = turnout_percentage >= proposal.quorum_percentage;
// 检查是否通过
let passed = quorum_reached && approval_percentage >= proposal.approval_percentage;
Ok(VotingResult {
proposal_id: proposal_id.to_string(),
votes_for,
votes_against,
votes_abstain,
total_voting_power,
total_shares,
turnout_percentage,
approval_percentage,
quorum_reached,
passed,
})
}
/// 结束投票并更新提案状态
pub fn finalize_proposal(&mut self, proposal_id: &str) -> Result<VotingResult, String> {
let proposal = self.proposals.get_mut(proposal_id)
.ok_or_else(|| "Proposal not found".to_string())?;
if proposal.status != ProposalStatus::Active {
return Err(format!("Proposal is not active: {:?}", proposal.status));
}
let current_time = Self::current_timestamp();
if current_time < proposal.voting_end {
return Err("Voting period has not ended yet".to_string());
}
// 计算结果
let result = self.calculate_result(proposal_id)?;
// 更新提案状态
let proposal = self.proposals.get_mut(proposal_id).unwrap();
proposal.status = if result.passed {
ProposalStatus::Passed
} else {
ProposalStatus::Rejected
};
Ok(result)
}
/// 取消提案
pub fn cancel_proposal(&mut self, proposal_id: &str, canceller: &str) -> Result<(), String> {
let proposal = self.proposals.get_mut(proposal_id)
.ok_or_else(|| "Proposal not found".to_string())?;
// 只有创建者可以取消
if proposal.creator != canceller {
return Err("Only creator can cancel the proposal".to_string());
}
// 只能取消草案或进行中的提案
if !matches!(proposal.status, ProposalStatus::Draft | ProposalStatus::Active) {
return Err(format!("Cannot cancel proposal in status: {:?}", proposal.status));
}
proposal.status = ProposalStatus::Cancelled;
Ok(())
}
/// 获取提案
pub fn get_proposal(&self, proposal_id: &str) -> Option<&Proposal> {
self.proposals.get(proposal_id)
}
/// 获取所有提案
pub fn get_all_proposals(&self) -> Vec<&Proposal> {
self.proposals.values().collect()
}
/// 获取投票记录
pub fn get_votes(&self, proposal_id: &str) -> Option<&Vec<VoteRecord>> {
self.votes.get(proposal_id)
}
/// 获取账户的投票历史
pub fn get_voter_history(&self, voter: &str) -> Vec<VoteRecord> {
self.votes.values()
.flatten()
.filter(|v| v.voter == voter)
.cloned()
.collect()
}
/// 获取投票权信息
pub fn get_voting_rights(&self, account: &str) -> Option<&VotingRights> {
self.voting_rights.get(account)
}
/// 获取账户的代理授权
pub fn get_proxy_authorizations(&self, principal: &str) -> Vec<&ProxyAuthorization> {
self.proxy_authorizations.iter()
.filter(|auth| auth.principal == principal && !auth.revoked)
.collect()
}
/// 获取当前时间戳
fn current_timestamp() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
}
}
impl Default for VotingSystem {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_proposal() {
let mut system = VotingSystem::new();
let security_id = [1u8; 32];
let current_time = VotingSystem::current_timestamp();
let result = system.create_proposal(
"Test Proposal".to_string(),
"This is a test".to_string(),
ProposalType::Other,
"creator1".to_string(),
security_id,
current_time + 100,
current_time + 1000,
50,
66,
);
assert!(result.is_ok());
let proposal_id = result.unwrap();
let proposal = system.get_proposal(&proposal_id).unwrap();
assert_eq!(proposal.title, "Test Proposal");
assert_eq!(proposal.status, ProposalStatus::Draft);
}
#[test]
fn test_voting() {
let mut system = VotingSystem::new();
let security_id = [1u8; 32];
// 设置投票权
system.set_voting_rights("voter1".to_string(), 1000, 1);
system.set_voting_rights("voter2".to_string(), 500, 2);
// 创建提案
let current_time = VotingSystem::current_timestamp();
let proposal_id = system.create_proposal(
"Test Proposal".to_string(),
"Test".to_string(),
ProposalType::Other,
"creator1".to_string(),
security_id,
current_time,
current_time + 1000,
50,
66,
).unwrap();
// 激活提案
system.activate_proposal(&proposal_id).unwrap();
// 投票
system.cast_vote(&proposal_id, "voter1", VoteOption::For, None).unwrap();
system.cast_vote(&proposal_id, "voter2", VoteOption::Against, None).unwrap();
// 检查投票记录
let votes = system.get_votes(&proposal_id).unwrap();
assert_eq!(votes.len(), 2);
assert_eq!(votes[0].voting_power, 1000); // 1000 * 1
assert_eq!(votes[1].voting_power, 1000); // 500 * 2
}
#[test]
fn test_voting_result() {
let mut system = VotingSystem::new();
let security_id = [1u8; 32];
system.set_voting_rights("voter1".to_string(), 1000, 1);
system.set_voting_rights("voter2".to_string(), 500, 1);
system.set_voting_rights("voter3".to_string(), 300, 1);
let current_time = VotingSystem::current_timestamp();
let proposal_id = system.create_proposal(
"Test".to_string(),
"Test".to_string(),
ProposalType::Other,
"creator1".to_string(),
security_id,
current_time,
current_time + 1000,
50,
66,
).unwrap();
system.activate_proposal(&proposal_id).unwrap();
// 投票1000赞成500反对300弃权
system.cast_vote(&proposal_id, "voter1", VoteOption::For, None).unwrap();
system.cast_vote(&proposal_id, "voter2", VoteOption::Against, None).unwrap();
system.cast_vote(&proposal_id, "voter3", VoteOption::Abstain, None).unwrap();
let result = system.calculate_result(&proposal_id).unwrap();
assert_eq!(result.votes_for, 1000);
assert_eq!(result.votes_against, 500);
assert_eq!(result.votes_abstain, 300);
assert_eq!(result.total_voting_power, 1800);
assert_eq!(result.turnout_percentage, 100); // 1800/1800
assert_eq!(result.approval_percentage, 66); // 1000/(1000+500)
assert!(result.quorum_reached);
assert!(result.passed);
}
#[test]
fn test_proxy_voting() {
let mut system = VotingSystem::new();
let security_id = [1u8; 32];
system.set_voting_rights("principal1".to_string(), 1000, 1);
let current_time = VotingSystem::current_timestamp();
let proposal_id = system.create_proposal(
"Test".to_string(),
"Test".to_string(),
ProposalType::Other,
"creator1".to_string(),
security_id,
current_time,
current_time + 1000,
50,
50,
).unwrap();
// 授权代理
system.authorize_proxy(
"principal1".to_string(),
"proxy1".to_string(),
Some(proposal_id.clone()),
current_time,
current_time + 2000,
).unwrap();
system.activate_proposal(&proposal_id).unwrap();
// 代理投票
system.cast_vote(&proposal_id, "proxy1", VoteOption::For, Some("principal1")).unwrap();
let votes = system.get_votes(&proposal_id).unwrap();
assert_eq!(votes.len(), 1);
assert_eq!(votes[0].voter, "principal1");
assert!(votes[0].is_proxy);
assert_eq!(votes[0].proxy.as_deref(), Some("proxy1"));
}
#[test]
fn test_restrict_voting() {
let mut system = VotingSystem::new();
let security_id = [1u8; 32];
system.set_voting_rights("voter1".to_string(), 1000, 1);
let current_time = VotingSystem::current_timestamp();
let proposal_id = system.create_proposal(
"Test".to_string(),
"Test".to_string(),
ProposalType::Other,
"creator1".to_string(),
security_id,
current_time,
current_time + 1000,
50,
50,
).unwrap();
system.activate_proposal(&proposal_id).unwrap();
// 限制投票权
system.restrict_voting("voter1", "Compliance issue".to_string()).unwrap();
// 尝试投票应该失败
let result = system.cast_vote(&proposal_id, "voter1", VoteOption::For, None);
assert!(result.is_err());
// 恢复投票权
system.unrestrict_voting("voter1").unwrap();
// 现在投票应该成功
let result = system.cast_vote(&proposal_id, "voter1", VoteOption::For, None);
assert!(result.is_ok());
}
}