Skip to main content

tetcore_runtime/
governance.rs

1use 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}