1use std::{
7 collections::VecDeque,
8 fmt::{Display, Formatter},
9};
10
11pub use cuprate_types::{HardFork, HardForkError};
12
13#[cfg(test)]
14mod tests;
15
16pub const NUMB_OF_HARD_FORKS: usize = 16;
17
18pub fn check_block_version_vote(
22 hf: &HardFork,
23 version: &HardFork,
24 vote: &HardFork,
25) -> Result<(), HardForkError> {
26 if hf != version {
28 return Err(HardForkError::VersionIncorrect);
29 }
30 if hf > vote {
31 return Err(HardForkError::VoteTooLow);
32 }
33
34 Ok(())
35}
36
37#[derive(Debug, Clone, Copy, Eq, PartialEq)]
39pub struct HFInfo {
40 height: usize,
41 threshold: usize,
42}
43impl HFInfo {
44 pub const fn new(height: usize, threshold: usize) -> Self {
45 Self { height, threshold }
46 }
47}
48
49#[derive(Debug, Clone, Copy, Eq, PartialEq)]
51pub struct HFsInfo([HFInfo; NUMB_OF_HARD_FORKS]);
52
53impl HFsInfo {
54 pub const fn info_for_hf(&self, hf: &HardFork) -> HFInfo {
55 self.0[*hf as usize - 1]
56 }
57
58 pub const fn new(hfs: [HFInfo; NUMB_OF_HARD_FORKS]) -> Self {
59 Self(hfs)
60 }
61
62 pub const fn main_net() -> Self {
66 Self([
67 HFInfo::new(0, 0),
68 HFInfo::new(1009827, 0),
69 HFInfo::new(1141317, 0),
70 HFInfo::new(1220516, 0),
71 HFInfo::new(1288616, 0),
72 HFInfo::new(1400000, 0),
73 HFInfo::new(1546000, 0),
74 HFInfo::new(1685555, 0),
75 HFInfo::new(1686275, 0),
76 HFInfo::new(1788000, 0),
77 HFInfo::new(1788720, 0),
78 HFInfo::new(1978433, 0),
79 HFInfo::new(2210000, 0),
80 HFInfo::new(2210720, 0),
81 HFInfo::new(2688888, 0),
82 HFInfo::new(2689608, 0),
83 ])
84 }
85
86 pub const fn test_net() -> Self {
90 Self([
91 HFInfo::new(0, 0),
92 HFInfo::new(624634, 0),
93 HFInfo::new(800500, 0),
94 HFInfo::new(801219, 0),
95 HFInfo::new(802660, 0),
96 HFInfo::new(971400, 0),
97 HFInfo::new(1057027, 0),
98 HFInfo::new(1057058, 0),
99 HFInfo::new(1057778, 0),
100 HFInfo::new(1154318, 0),
101 HFInfo::new(1155038, 0),
102 HFInfo::new(1308737, 0),
103 HFInfo::new(1543939, 0),
104 HFInfo::new(1544659, 0),
105 HFInfo::new(1982800, 0),
106 HFInfo::new(1983520, 0),
107 ])
108 }
109
110 pub const fn stage_net() -> Self {
114 Self([
115 HFInfo::new(0, 0),
116 HFInfo::new(32000, 0),
117 HFInfo::new(33000, 0),
118 HFInfo::new(34000, 0),
119 HFInfo::new(35000, 0),
120 HFInfo::new(36000, 0),
121 HFInfo::new(37000, 0),
122 HFInfo::new(176456, 0),
123 HFInfo::new(177176, 0),
124 HFInfo::new(269000, 0),
125 HFInfo::new(269720, 0),
126 HFInfo::new(454721, 0),
127 HFInfo::new(675405, 0),
128 HFInfo::new(676125, 0),
129 HFInfo::new(1151000, 0),
130 HFInfo::new(1151720, 0),
131 ])
132 }
133}
134
135#[derive(Debug, Clone, Eq, PartialEq)]
137pub struct HFVotes {
138 votes: [usize; NUMB_OF_HARD_FORKS],
139 vote_list: VecDeque<HardFork>,
140 window_size: usize,
141}
142
143impl Display for HFVotes {
144 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
145 f.debug_struct("HFVotes")
146 .field("total", &self.total_votes())
147 .field("V1", &self.votes_for_hf(&HardFork::V1))
148 .field("V2", &self.votes_for_hf(&HardFork::V2))
149 .field("V3", &self.votes_for_hf(&HardFork::V3))
150 .field("V4", &self.votes_for_hf(&HardFork::V4))
151 .field("V5", &self.votes_for_hf(&HardFork::V5))
152 .field("V6", &self.votes_for_hf(&HardFork::V6))
153 .field("V7", &self.votes_for_hf(&HardFork::V7))
154 .field("V8", &self.votes_for_hf(&HardFork::V8))
155 .field("V9", &self.votes_for_hf(&HardFork::V9))
156 .field("V10", &self.votes_for_hf(&HardFork::V10))
157 .field("V11", &self.votes_for_hf(&HardFork::V11))
158 .field("V12", &self.votes_for_hf(&HardFork::V12))
159 .field("V13", &self.votes_for_hf(&HardFork::V13))
160 .field("V14", &self.votes_for_hf(&HardFork::V14))
161 .field("V15", &self.votes_for_hf(&HardFork::V15))
162 .field("V16", &self.votes_for_hf(&HardFork::V16))
163 .finish()
164 }
165}
166
167impl HFVotes {
168 pub fn new(window_size: usize) -> Self {
169 Self {
170 votes: [0; NUMB_OF_HARD_FORKS],
171 vote_list: VecDeque::with_capacity(window_size),
172 window_size,
173 }
174 }
175
176 pub fn add_vote_for_hf(&mut self, hf: &HardFork) {
178 self.vote_list.push_back(*hf);
179 self.votes[*hf as usize - 1] += 1;
180 if self.vote_list.len() > self.window_size {
181 let hf = self.vote_list.pop_front().unwrap();
182 self.votes[hf as usize - 1] -= 1;
183 }
184 }
185
186 pub fn reverse_blocks(&mut self, numb_blocks: usize, old_block_votes: Self) {
196 assert!(old_block_votes.vote_list.len() <= numb_blocks);
197
198 for hf in self.vote_list.drain(self.vote_list.len() - numb_blocks..) {
199 self.votes[hf as usize - 1] -= 1;
200 }
201
202 for old_vote in old_block_votes.vote_list.into_iter().rev() {
203 self.vote_list.push_front(old_vote);
204 self.votes[old_vote as usize - 1] += 1;
205 }
206 }
207
208 pub fn votes_for_hf(&self, hf: &HardFork) -> usize {
212 self.votes[*hf as usize - 1..].iter().sum()
213 }
214
215 pub fn total_votes(&self) -> usize {
217 self.vote_list.len()
218 }
219
220 pub fn current_fork(
225 &self,
226 current_hf: &HardFork,
227 current_height: usize,
228 window: usize,
229 hfs_info: &HFsInfo,
230 ) -> HardFork {
231 let mut current_hf = *current_hf;
232
233 while let Some(next_hf) = current_hf.next_fork() {
234 let hf_info = hfs_info.info_for_hf(&next_hf);
235 if current_height >= hf_info.height
236 && self.votes_for_hf(&next_hf) >= votes_needed(hf_info.threshold, window)
237 {
238 current_hf = next_hf;
239 } else {
240 return current_hf;
245 }
246 }
247 current_hf
248 }
249}
250
251pub const fn votes_needed(threshold: usize, window: usize) -> usize {
255 (threshold * window).div_ceil(100)
256}