1use enum_iterator::{Sequence, last};
17
18#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Sequence)]
21pub enum ConsensusVersion {
22 V1 = 1,
24 V2 = 2,
26 V3 = 3,
28 V4 = 4,
30 V5 = 5,
32 V6 = 6,
34 V7 = 7,
36 V8 = 8,
38 V9 = 9,
40 V10 = 10,
42}
43
44impl ConsensusVersion {
45 pub fn latest() -> Self {
46 last::<ConsensusVersion>().expect("At least one ConsensusVersion should be defined.")
47 }
48}
49
50pub(crate) const NUM_CONSENSUS_VERSIONS: usize = 10;
52
53pub const CANARY_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
55 (ConsensusVersion::V1, 0),
56 (ConsensusVersion::V2, 2_900_000),
57 (ConsensusVersion::V3, 4_560_000),
58 (ConsensusVersion::V4, 5_730_000),
59 (ConsensusVersion::V5, 5_780_000),
60 (ConsensusVersion::V6, 6_240_000),
61 (ConsensusVersion::V7, 6_880_000),
62 (ConsensusVersion::V8, 7_565_000),
63 (ConsensusVersion::V9, 8_028_000),
64 (ConsensusVersion::V10, 8_600_000),
65];
66
67pub const MAINNET_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
69 (ConsensusVersion::V1, 0),
70 (ConsensusVersion::V2, 2_800_000),
71 (ConsensusVersion::V3, 4_900_000),
72 (ConsensusVersion::V4, 6_135_000),
73 (ConsensusVersion::V5, 7_060_000),
74 (ConsensusVersion::V6, 7_560_000),
75 (ConsensusVersion::V7, 7_570_000),
76 (ConsensusVersion::V8, 9_430_000),
77 (ConsensusVersion::V9, 10_272_000),
78 (ConsensusVersion::V10, 11_205_000),
79];
80
81pub const TESTNET_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
83 (ConsensusVersion::V1, 0),
84 (ConsensusVersion::V2, 2_950_000),
85 (ConsensusVersion::V3, 4_800_000),
86 (ConsensusVersion::V4, 6_625_000),
87 (ConsensusVersion::V5, 6_765_000),
88 (ConsensusVersion::V6, 7_600_000),
89 (ConsensusVersion::V7, 8_365_000),
90 (ConsensusVersion::V8, 9_173_000),
91 (ConsensusVersion::V9, 9_800_000),
92 (ConsensusVersion::V10, 10_525_000),
93];
94
95pub const TEST_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
97 (ConsensusVersion::V1, 0),
98 (ConsensusVersion::V2, 10),
99 (ConsensusVersion::V3, 11),
100 (ConsensusVersion::V4, 12),
101 (ConsensusVersion::V5, 13),
102 (ConsensusVersion::V6, 14),
103 (ConsensusVersion::V7, 15),
104 (ConsensusVersion::V8, 16),
105 (ConsensusVersion::V9, 17),
106 (ConsensusVersion::V10, 18),
107];
108
109#[cfg(any(test, feature = "test", feature = "test_consensus_heights"))]
110pub fn load_test_consensus_heights() -> [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] {
111 let verify_consensus_heights = |heights: &[(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS]| {
113 assert_eq!(heights[0].1, 0, "Genesis height must be 0.");
115 for window in heights.windows(2) {
117 if window[0] >= window[1] {
118 panic!("Heights must be strictly increasing, but found: {window:?}");
119 }
120 }
121 };
122
123 let mut test_consensus_heights = TEST_CONSENSUS_VERSION_HEIGHTS;
125
126 match std::env::var("CONSENSUS_VERSION_HEIGHTS") {
128 Ok(height_string) => {
129 let parsing_error = format!("Expected exactly {NUM_CONSENSUS_VERSIONS} ConsensusVersion heights.");
130 let parsed_test_consensus_heights: [u32; NUM_CONSENSUS_VERSIONS] = height_string
132 .replace(" ", "")
133 .split(",")
134 .map(|height| height.parse::<u32>().expect("Heights should be valid u32 values."))
135 .collect::<Vec<u32>>()
136 .try_into()
137 .expect(&parsing_error);
138 for (i, height) in parsed_test_consensus_heights.into_iter().enumerate() {
140 test_consensus_heights[i] = (TEST_CONSENSUS_VERSION_HEIGHTS[i].0, height);
141 }
142 verify_consensus_heights(&test_consensus_heights);
144 test_consensus_heights
145 }
146 Err(_) => {
147 verify_consensus_heights(&test_consensus_heights);
149 test_consensus_heights
150 }
151 }
152}
153
154#[macro_export]
161macro_rules! consensus_config_value {
162 ($network:ident, $constant:ident, $seek_height:expr) => {
163 $network::CONSENSUS_VERSION($seek_height).map_or(None, |seek_version| {
165 match $network::$constant.binary_search_by(|(version, _)| version.cmp(&seek_version)) {
167 Ok(index) => Some($network::$constant[index].1),
169 Err(index) => {
171 if index == 0 {
173 None
174 } else {
176 Some($network::$constant[index - 1].1)
177 }
178 }
179 }
180 })
181 };
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::{CanaryV0, MainnetV0, Network, TestnetV0};
188
189 fn consensus_constants_at_genesis<N: Network>() {
192 let height = N::_CONSENSUS_VERSION_HEIGHTS.first().unwrap().1;
193 assert_eq!(height, 0);
194 let consensus_version = N::_CONSENSUS_VERSION_HEIGHTS.first().unwrap().0;
195 assert_eq!(consensus_version, ConsensusVersion::V1);
196 assert_eq!(consensus_version as usize, 1);
197 }
198
199 fn consensus_versions<N: Network>() {
201 let mut previous_version = N::_CONSENSUS_VERSION_HEIGHTS.first().unwrap().0;
202 assert_eq!(previous_version as usize, 1);
204 for (version, _) in N::_CONSENSUS_VERSION_HEIGHTS.iter().skip(1) {
206 assert_eq!(*version as usize, previous_version as usize + 1);
207 previous_version = *version;
208 }
209 let mut previous_version = N::MAX_CERTIFICATES.first().unwrap().0;
211 for (version, _) in N::MAX_CERTIFICATES.iter().skip(1) {
212 assert!(*version > previous_version);
213 previous_version = *version;
214 }
215 let mut previous_version = N::TRANSACTION_SPEND_LIMIT.first().unwrap().0;
216 for (version, _) in N::TRANSACTION_SPEND_LIMIT.iter().skip(1) {
217 assert!(*version > previous_version);
218 previous_version = *version;
219 }
220 }
221
222 fn consensus_constants_increasing_heights<N: Network>() {
224 let mut previous_height = N::CONSENSUS_VERSION_HEIGHTS().first().unwrap().1;
225 for (version, height) in N::CONSENSUS_VERSION_HEIGHTS().iter().skip(1) {
226 assert!(*height > previous_height);
227 previous_height = *height;
228 assert_eq!(N::CONSENSUS_VERSION(*height).unwrap(), *version);
230 assert_eq!(N::CONSENSUS_HEIGHT(*version).unwrap(), *height);
232 }
233 }
234
235 fn consensus_constants_valid_heights<N: Network>() {
237 for (version, value) in N::MAX_CERTIFICATES.iter() {
238 let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
240 assert_eq!(consensus_config_value!(N, MAX_CERTIFICATES, height).unwrap(), *value);
242 }
243 for (version, value) in N::TRANSACTION_SPEND_LIMIT.iter() {
244 let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
246 assert_eq!(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, height).unwrap(), *value);
248 }
249 }
250
251 fn consensus_config_returns_some<N: Network>() {
253 for (_, height) in N::CONSENSUS_VERSION_HEIGHTS().iter() {
254 assert!(consensus_config_value!(N, MAX_CERTIFICATES, *height).is_some());
255 assert!(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, *height).is_some());
256 }
257 }
258
259 fn max_certificates_increasing<N: Network>() {
262 let mut previous_value = N::MAX_CERTIFICATES.first().unwrap().1;
263 for (_, value) in N::MAX_CERTIFICATES.iter().skip(1) {
264 assert!(*value >= previous_value);
265 previous_value = *value;
266 }
267 }
268
269 fn constants_equal_length<N1: Network, N2: Network, N3: Network>() {
271 let _ = [N1::CONSENSUS_VERSION_HEIGHTS, N2::CONSENSUS_VERSION_HEIGHTS, N3::CONSENSUS_VERSION_HEIGHTS];
273 let _ = [N1::MAX_CERTIFICATES, N2::MAX_CERTIFICATES, N3::MAX_CERTIFICATES];
274 let _ = [N1::TRANSACTION_SPEND_LIMIT, N2::TRANSACTION_SPEND_LIMIT, N3::TRANSACTION_SPEND_LIMIT];
275 }
276
277 #[test]
278 #[allow(clippy::assertions_on_constants)]
279 fn test_consensus_constants() {
280 consensus_constants_at_genesis::<MainnetV0>();
281 consensus_constants_at_genesis::<TestnetV0>();
282 consensus_constants_at_genesis::<CanaryV0>();
283
284 consensus_versions::<MainnetV0>();
285 consensus_versions::<TestnetV0>();
286 consensus_versions::<CanaryV0>();
287
288 consensus_constants_increasing_heights::<MainnetV0>();
289 consensus_constants_increasing_heights::<TestnetV0>();
290 consensus_constants_increasing_heights::<CanaryV0>();
291
292 consensus_constants_valid_heights::<MainnetV0>();
293 consensus_constants_valid_heights::<TestnetV0>();
294 consensus_constants_valid_heights::<CanaryV0>();
295
296 consensus_config_returns_some::<MainnetV0>();
297 consensus_config_returns_some::<TestnetV0>();
298 consensus_config_returns_some::<CanaryV0>();
299
300 max_certificates_increasing::<MainnetV0>();
301 max_certificates_increasing::<TestnetV0>();
302 max_certificates_increasing::<CanaryV0>();
303
304 constants_equal_length::<MainnetV0, TestnetV0, CanaryV0>();
305 }
306
307 #[test]
308 fn test_latest_consensus_version() {
309 assert_eq!(ConsensusVersion::latest(), ConsensusVersion::V10); }
312}