From c590bbb8d7eab9aa13e4642ea1b61e49a768e9a0 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 10 Oct 2024 20:39:44 -0400 Subject: [PATCH] apply diffs --- Cargo.lock | 23 +++++++ Cargo.toml | 1 + types/Cargo.toml | 1 + types/src/hard_fork.rs | 140 ++++++++++++++++++++++++++++++++--------- 4 files changed, 134 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fea18d0..1b05efad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -933,6 +933,7 @@ dependencies = [ "proptest-derive", "serde", "serde_json", + "strum", "thiserror", ] @@ -2673,6 +2674,28 @@ dependencies = [ "spin", ] +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index fa348ccd..6c322fbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ rayon = { version = "1.10.0", default-features = false } serde_bytes = { version = "0.11.15", default-features = false } serde_json = { version = "1.0.128", default-features = false } serde = { version = "1.0.210", default-features = false } +strum = { version = "0.26.3", default-features = false } thiserror = { version = "1.0.63", default-features = false } thread_local = { version = "1.1.8", default-features = false } tokio-util = { version = "0.7.12", default-features = false } diff --git a/types/Cargo.toml b/types/Cargo.toml index 1c762902..8ac6b25f 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -27,6 +27,7 @@ curve25519-dalek = { workspace = true } monero-serai = { workspace = true } hex = { workspace = true, features = ["serde", "alloc"], optional = true } serde = { workspace = true, features = ["derive"], optional = true } +strum = { workspace = true, features = ["derive"] } thiserror = { workspace = true } proptest = { workspace = true, optional = true } diff --git a/types/src/hard_fork.rs b/types/src/hard_fork.rs index 8b2cd78c..ad129537 100644 --- a/types/src/hard_fork.rs +++ b/types/src/hard_fork.rs @@ -1,6 +1,10 @@ //! The [`HardFork`] type. use std::time::Duration; +use strum::{ + AsRefStr, Display, EnumCount, EnumIs, EnumString, FromRepr, IntoStaticStr, VariantArray, +}; + use monero_serai::block::BlockHeader; /// Target block time for hf 1. @@ -27,7 +31,25 @@ pub enum HardForkError { } /// An identifier for every hard-fork Monero has had. -#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] +#[derive( + Default, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Copy, + Clone, + Hash, + EnumCount, + Display, + AsRefStr, + EnumIs, + EnumString, + FromRepr, + IntoStaticStr, + VariantArray, +)] #[cfg_attr(any(feature = "proptest"), derive(proptest_derive::Arbitrary))] #[repr(u8)] pub enum HardFork { @@ -47,58 +69,87 @@ pub enum HardFork { V13, V14, V15, - // remember to update from_vote! V16, } impl HardFork { + /// The current [`HardFork`]. + /// + /// ```rust + /// # use cuprate_types::HardFork; + /// assert_eq!(HardFork::CURRENT, HardFork::V16); + /// ``` + pub const CURRENT: Self = Self::VARIANTS[Self::COUNT - 1]; + /// Returns the hard-fork for a blocks [`BlockHeader::hardfork_version`] field. /// /// ref: /// /// # Errors - /// /// Will return [`Err`] if the version is not a valid [`HardFork`]. + /// + /// ```rust + /// # use cuprate_types::{HardFork, HardForkError}; + /// # use strum::VariantArray; + /// assert_eq!(HardFork::from_version(0), Err(HardForkError::HardForkUnknown)); + /// assert_eq!(HardFork::from_version(17), Err(HardForkError::HardForkUnknown)); + /// + /// for (version, hf) in HardFork::VARIANTS.iter().enumerate() { + /// // +1 because enumerate starts at 0, hf starts at 1. + /// assert_eq!(*hf, HardFork::from_version(version as u8 + 1).unwrap()); + /// } + /// ``` #[inline] pub const fn from_version(version: u8) -> Result { - Ok(match version { - 1 => Self::V1, - 2 => Self::V2, - 3 => Self::V3, - 4 => Self::V4, - 5 => Self::V5, - 6 => Self::V6, - 7 => Self::V7, - 8 => Self::V8, - 9 => Self::V9, - 10 => Self::V10, - 11 => Self::V11, - 12 => Self::V12, - 13 => Self::V13, - 14 => Self::V14, - 15 => Self::V15, - 16 => Self::V16, - _ => return Err(HardForkError::HardForkUnknown), - }) + #[expect( + clippy::cast_possible_truncation, + reason = "we check that `HardFork::COUNT` fits into a `u8`." + )] + const COUNT_AS_U8: u8 = { + const COUNT: usize = HardFork::COUNT; + assert!(COUNT <= u8::MAX as usize); + COUNT as u8 + }; + + if version == 0 || version > COUNT_AS_U8 { + Err(HardForkError::HardForkUnknown) + } else { + // INVARIANT: we've proved above that `version` is in a valid range. + Ok(Self::VARIANTS[(version - 1) as usize]) + } } /// Returns the hard-fork for a blocks [`BlockHeader::hardfork_signal`] (vote) field. /// /// + /// + /// ```rust + /// # use cuprate_types::{HardFork, HardForkError}; + /// # use strum::VariantArray; + /// // 0 is interpreted as 1. + /// assert_eq!(HardFork::from_vote(0), HardFork::V1); + /// // Unknown defaults to `CURRENT`. + /// assert_eq!(HardFork::from_vote(17), HardFork::V16); + /// + /// for (vote, hf) in HardFork::VARIANTS.iter().enumerate() { + /// // +1 because enumerate starts at 0, hf starts at 1. + /// assert_eq!(*hf, HardFork::from_vote(vote as u8 + 1)); + /// } + /// ``` #[inline] pub fn from_vote(vote: u8) -> Self { if vote == 0 { // A vote of 0 is interpreted as 1 as that's what Monero used to default to. - return Self::V1; + Self::V1 + } else { + // This must default to the latest hard-fork! + Self::from_version(vote).unwrap_or(Self::CURRENT) } - // This must default to the latest hard-fork! - Self::from_version(vote).unwrap_or(Self::V16) } /// Returns the [`HardFork`] version and vote from this block header. /// /// # Errors - /// /// Will return [`Err`] if the [`BlockHeader::hardfork_version`] is not a valid [`HardFork`]. #[inline] pub fn from_block_header(header: &BlockHeader) -> Result<(Self, Self), HardForkError> { @@ -109,22 +160,49 @@ impl HardFork { } /// Returns the raw hard-fork value, as it would appear in [`BlockHeader::hardfork_version`]. - pub const fn as_u8(&self) -> u8 { - *self as u8 + /// + /// ```rust + /// # use cuprate_types::{HardFork, HardForkError}; + /// # use strum::VariantArray; + /// for (i, hf) in HardFork::VARIANTS.iter().enumerate() { + /// // +1 because enumerate starts at 0, hf starts at 1. + /// assert_eq!(hf.as_u8(), i as u8 + 1); + /// } + /// ``` + pub const fn as_u8(self) -> u8 { + self as u8 } /// Returns the next hard-fork. - pub fn next_fork(&self) -> Option { - Self::from_version(*self as u8 + 1).ok() + pub fn next_fork(self) -> Option { + Self::from_version(self as u8 + 1).ok() } /// Returns the target block time for this hardfork. /// /// ref: - pub const fn block_time(&self) -> Duration { + pub const fn block_time(self) -> Duration { match self { Self::V1 => BLOCK_TIME_V1, _ => BLOCK_TIME_V2, } } + + /// Returns `true` if `self` is [`Self::CURRENT`]. + /// + /// ```rust + /// # use cuprate_types::HardFork; + /// # use strum::VariantArray; + /// + /// for hf in HardFork::VARIANTS.iter() { + /// if *hf == HardFork::CURRENT { + /// assert!(hf.is_current()); + /// } else { + /// assert!(!hf.is_current()); + /// } + /// } + /// ``` + pub const fn is_current(self) -> bool { + matches!(self, Self::CURRENT) + } }