Skip to content

Commit f9b847b

Browse files
authored
types: HardFork improvements (#309)
* apply diffs * review fixes
1 parent d43f12e commit f9b847b

File tree

4 files changed

+122
-31
lines changed

4 files changed

+122
-31
lines changed

Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ rayon = { version = "1.10.0", default-features = false }
7878
serde_bytes = { version = "0.11.15", default-features = false }
7979
serde_json = { version = "1.0.128", default-features = false }
8080
serde = { version = "1.0.210", default-features = false }
81+
strum = { version = "0.26.3", default-features = false }
8182
thiserror = { version = "1.0.63", default-features = false }
8283
thread_local = { version = "1.1.8", default-features = false }
8384
tokio-util = { version = "0.7.12", default-features = false }

types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ curve25519-dalek = { workspace = true }
2727
monero-serai = { workspace = true }
2828
hex = { workspace = true, features = ["serde", "alloc"], optional = true }
2929
serde = { workspace = true, features = ["derive"], optional = true }
30+
strum = { workspace = true, features = ["derive"] }
3031
thiserror = { workspace = true }
3132

3233
proptest = { workspace = true, optional = true }

types/src/hard_fork.rs

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//! The [`HardFork`] type.
22
use std::time::Duration;
33

4+
use strum::{
5+
AsRefStr, Display, EnumCount, EnumIs, EnumString, FromRepr, IntoStaticStr, VariantArray,
6+
};
7+
48
use monero_serai::block::BlockHeader;
59

610
/// Target block time for hf 1.
@@ -27,7 +31,25 @@ pub enum HardForkError {
2731
}
2832

2933
/// An identifier for every hard-fork Monero has had.
30-
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
34+
#[derive(
35+
Default,
36+
Debug,
37+
PartialEq,
38+
Eq,
39+
PartialOrd,
40+
Ord,
41+
Copy,
42+
Clone,
43+
Hash,
44+
EnumCount,
45+
Display,
46+
AsRefStr,
47+
EnumIs,
48+
EnumString,
49+
FromRepr,
50+
IntoStaticStr,
51+
VariantArray,
52+
)]
3153
#[cfg_attr(any(feature = "proptest"), derive(proptest_derive::Arbitrary))]
3254
#[repr(u8)]
3355
pub enum HardFork {
@@ -47,58 +69,75 @@ pub enum HardFork {
4769
V13,
4870
V14,
4971
V15,
50-
// remember to update from_vote!
5172
V16,
5273
}
5374

5475
impl HardFork {
76+
/// The latest [`HardFork`].
77+
///
78+
/// ```rust
79+
/// # use cuprate_types::HardFork;
80+
/// assert_eq!(HardFork::LATEST, HardFork::V16);
81+
/// ```
82+
pub const LATEST: Self = Self::VARIANTS[Self::COUNT - 1];
83+
5584
/// Returns the hard-fork for a blocks [`BlockHeader::hardfork_version`] field.
5685
///
5786
/// ref: <https://monero-book.cuprate.org/consensus_rules/hardforks.html#blocks-version-and-vote>
5887
///
5988
/// # Errors
60-
///
6189
/// Will return [`Err`] if the version is not a valid [`HardFork`].
90+
///
91+
/// ```rust
92+
/// # use cuprate_types::{HardFork, HardForkError};
93+
/// # use strum::VariantArray;
94+
/// assert_eq!(HardFork::from_version(0), Err(HardForkError::HardForkUnknown));
95+
/// assert_eq!(HardFork::from_version(17), Err(HardForkError::HardForkUnknown));
96+
///
97+
/// for (version, hf) in HardFork::VARIANTS.iter().enumerate() {
98+
/// // +1 because enumerate starts at 0, hf starts at 1.
99+
/// assert_eq!(*hf, HardFork::from_version(version as u8 + 1).unwrap());
100+
/// }
101+
/// ```
62102
#[inline]
63103
pub const fn from_version(version: u8) -> Result<Self, HardForkError> {
64-
Ok(match version {
65-
1 => Self::V1,
66-
2 => Self::V2,
67-
3 => Self::V3,
68-
4 => Self::V4,
69-
5 => Self::V5,
70-
6 => Self::V6,
71-
7 => Self::V7,
72-
8 => Self::V8,
73-
9 => Self::V9,
74-
10 => Self::V10,
75-
11 => Self::V11,
76-
12 => Self::V12,
77-
13 => Self::V13,
78-
14 => Self::V14,
79-
15 => Self::V15,
80-
16 => Self::V16,
81-
_ => return Err(HardForkError::HardForkUnknown),
82-
})
104+
match Self::from_repr(version) {
105+
Some(this) => Ok(this),
106+
None => Err(HardForkError::HardForkUnknown),
107+
}
83108
}
84109

85110
/// Returns the hard-fork for a blocks [`BlockHeader::hardfork_signal`] (vote) field.
86111
///
87112
/// <https://monero-book.cuprate.org/consensus_rules/hardforks.html#blocks-version-and-vote>
113+
///
114+
/// ```rust
115+
/// # use cuprate_types::{HardFork, HardForkError};
116+
/// # use strum::VariantArray;
117+
/// // 0 is interpreted as 1.
118+
/// assert_eq!(HardFork::from_vote(0), HardFork::V1);
119+
/// // Unknown defaults to `LATEST`.
120+
/// assert_eq!(HardFork::from_vote(17), HardFork::V16);
121+
///
122+
/// for (vote, hf) in HardFork::VARIANTS.iter().enumerate() {
123+
/// // +1 because enumerate starts at 0, hf starts at 1.
124+
/// assert_eq!(*hf, HardFork::from_vote(vote as u8 + 1));
125+
/// }
126+
/// ```
88127
#[inline]
89128
pub fn from_vote(vote: u8) -> Self {
90129
if vote == 0 {
91130
// A vote of 0 is interpreted as 1 as that's what Monero used to default to.
92-
return Self::V1;
131+
Self::V1
132+
} else {
133+
// This must default to the latest hard-fork!
134+
Self::from_version(vote).unwrap_or(Self::LATEST)
93135
}
94-
// This must default to the latest hard-fork!
95-
Self::from_version(vote).unwrap_or(Self::V16)
96136
}
97137

98138
/// Returns the [`HardFork`] version and vote from this block header.
99139
///
100140
/// # Errors
101-
///
102141
/// Will return [`Err`] if the [`BlockHeader::hardfork_version`] is not a valid [`HardFork`].
103142
#[inline]
104143
pub fn from_block_header(header: &BlockHeader) -> Result<(Self, Self), HardForkError> {
@@ -109,22 +148,49 @@ impl HardFork {
109148
}
110149

111150
/// Returns the raw hard-fork value, as it would appear in [`BlockHeader::hardfork_version`].
112-
pub const fn as_u8(&self) -> u8 {
113-
*self as u8
151+
///
152+
/// ```rust
153+
/// # use cuprate_types::{HardFork, HardForkError};
154+
/// # use strum::VariantArray;
155+
/// for (i, hf) in HardFork::VARIANTS.iter().enumerate() {
156+
/// // +1 because enumerate starts at 0, hf starts at 1.
157+
/// assert_eq!(hf.as_u8(), i as u8 + 1);
158+
/// }
159+
/// ```
160+
pub const fn as_u8(self) -> u8 {
161+
self as u8
114162
}
115163

116164
/// Returns the next hard-fork.
117-
pub fn next_fork(&self) -> Option<Self> {
118-
Self::from_version(*self as u8 + 1).ok()
165+
pub fn next_fork(self) -> Option<Self> {
166+
Self::from_version(self as u8 + 1).ok()
119167
}
120168

121169
/// Returns the target block time for this hardfork.
122170
///
123171
/// ref: <https://monero-book.cuprate.org/consensus_rules/blocks/difficulty.html#target-seconds>
124-
pub const fn block_time(&self) -> Duration {
172+
pub const fn block_time(self) -> Duration {
125173
match self {
126174
Self::V1 => BLOCK_TIME_V1,
127175
_ => BLOCK_TIME_V2,
128176
}
129177
}
178+
179+
/// Returns `true` if `self` is [`Self::LATEST`].
180+
///
181+
/// ```rust
182+
/// # use cuprate_types::HardFork;
183+
/// # use strum::VariantArray;
184+
///
185+
/// for hf in HardFork::VARIANTS.iter() {
186+
/// if *hf == HardFork::LATEST {
187+
/// assert!(hf.is_latest());
188+
/// } else {
189+
/// assert!(!hf.is_latest());
190+
/// }
191+
/// }
192+
/// ```
193+
pub const fn is_latest(self) -> bool {
194+
matches!(self, Self::LATEST)
195+
}
130196
}

0 commit comments

Comments
 (0)