tetcore_runtime/
governance.rs1use crate::RuntimeError;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use tetcore_primitives::{Address, Hash32};
5
6#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
7pub enum ProposalState {
8 Pending,
9 Active,
10 Passed,
11 Rejected,
12 Executed,
13}
14
15#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct Proposal {
17 pub id: Hash32,
18 pub proposer: Address,
19 pub title: String,
20 pub description: String,
21 pub payload: Vec<u8>,
22 pub state: ProposalState,
23 pub votes_for: u128,
24 pub votes_against: u128,
25 pub voting_start: u64,
26 pub voting_end: u64,
27 pub executed: bool,
28}
29
30#[derive(Clone, Debug, Serialize, Deserialize)]
31pub struct Vote {
32 pub voter: Address,
33 pub proposal_id: Hash32,
34 pub support: bool,
35 pub weight: u128,
36}
37
38pub struct GovernanceModule {
39 proposals: HashMap<Hash32, Proposal>,
40 votes: HashMap<(Address, Hash32), Vote>,
41 proposal_counter: u64,
42}
43
44impl GovernanceModule {
45 pub fn new() -> Self {
46 Self {
47 proposals: HashMap::new(),
48 votes: HashMap::new(),
49 proposal_counter: 0,
50 }
51 }
52
53 pub fn create_proposal(
54 &mut self,
55 proposer: Address,
56 title: String,
57 description: String,
58 payload: Vec<u8>,
59 voting_end: u64,
60 ) -> Result<Hash32, RuntimeError> {
61 self.proposal_counter += 1;
62
63 let mut data = Vec::new();
64 data.extend_from_slice(&self.proposal_counter.to_le_bytes());
65 data.extend_from_slice(proposer.as_bytes());
66 data.extend_from_slice(&payload);
67
68 use sha2::{Digest, Sha256};
69 let hash = Sha256::digest(&data);
70 let mut id = [0u8; 32];
71 id.copy_from_slice(&hash[..32]);
72
73 let proposal = Proposal {
74 id: Hash32(id),
75 proposer,
76 title,
77 description,
78 payload,
79 state: ProposalState::Pending,
80 votes_for: 0,
81 votes_against: 0,
82 voting_start: 0,
83 voting_end,
84 executed: false,
85 };
86
87 self.proposals.insert(Hash32(id), proposal);
88
89 Ok(Hash32(id))
90 }
91
92 pub fn activate_proposal(
93 &mut self,
94 proposal_id: &Hash32,
95 current_height: u64,
96 ) -> Result<(), RuntimeError> {
97 let proposal = self
98 .proposals
99 .get_mut(proposal_id)
100 .ok_or(RuntimeError::InvalidState)?;
101
102 if proposal.state != ProposalState::Pending {
103 return Err(RuntimeError::InvalidState);
104 }
105
106 proposal.state = ProposalState::Active;
107 proposal.voting_start = current_height;
108
109 Ok(())
110 }
111
112 pub fn vote(
113 &mut self,
114 voter: Address,
115 proposal_id: &Hash32,
116 support: bool,
117 weight: u128,
118 ) -> Result<(), RuntimeError> {
119 let proposal = self
120 .proposals
121 .get_mut(proposal_id)
122 .ok_or(RuntimeError::InvalidState)?;
123
124 if proposal.state != ProposalState::Active {
125 return Err(RuntimeError::InvalidState);
126 }
127
128 if support {
129 proposal.votes_for += weight;
130 } else {
131 proposal.votes_against += weight;
132 }
133
134 let vote = Vote {
135 voter,
136 proposal_id: *proposal_id,
137 support,
138 weight,
139 };
140
141 self.votes.insert((voter, *proposal_id), vote);
142
143 Ok(())
144 }
145
146 pub fn finalize_proposal(
147 &mut self,
148 proposal_id: &Hash32,
149 ) -> Result<ProposalState, RuntimeError> {
150 let proposal = self
151 .proposals
152 .get_mut(proposal_id)
153 .ok_or(RuntimeError::InvalidState)?;
154
155 if proposal.votes_for > proposal.votes_against {
156 proposal.state = ProposalState::Passed;
157 } else {
158 proposal.state = ProposalState::Rejected;
159 }
160
161 Ok(proposal.state.clone())
162 }
163
164 pub fn execute_proposal(&mut self, proposal_id: &Hash32) -> Result<(), RuntimeError> {
165 let proposal = self
166 .proposals
167 .get_mut(proposal_id)
168 .ok_or(RuntimeError::InvalidState)?;
169
170 if proposal.state != ProposalState::Passed {
171 return Err(RuntimeError::InvalidState);
172 }
173
174 if proposal.executed {
175 return Err(RuntimeError::InvalidState);
176 }
177
178 proposal.state = ProposalState::Executed;
179 proposal.executed = true;
180
181 Ok(())
182 }
183
184 pub fn get_proposal(&self, proposal_id: &Hash32) -> Option<&Proposal> {
185 self.proposals.get(proposal_id)
186 }
187
188 pub fn all_proposals(&self) -> &HashMap<Hash32, Proposal> {
189 &self.proposals
190 }
191}
192
193impl Default for GovernanceModule {
194 fn default() -> Self {
195 Self::new()
196 }
197}