diff --git a/crates/papyrus_rpc/src/v0_8/api/test.rs b/crates/papyrus_rpc/src/v0_8/api/test.rs index a535f5e3f9..84814d21f7 100644 --- a/crates/papyrus_rpc/src/v0_8/api/test.rs +++ b/crates/papyrus_rpc/src/v0_8/api/test.rs @@ -494,7 +494,7 @@ async fn get_block_w_full_transactions() { let block_hash = BlockHash(random::().into()); let sequencer_address = SequencerContractAddress(random::().into()); let timestamp = BlockTimestamp(random::()); - let starknet_version = StarknetVersion(vec![123]); + let starknet_version = StarknetVersion::V0_10_0; block.header.block_hash = block_hash; block.header.block_header_without_hash.sequencer = sequencer_address; block.header.block_header_without_hash.timestamp = timestamp; @@ -680,7 +680,7 @@ async fn get_block_w_full_transactions_and_receipts() { let block_hash = BlockHash(random::().into()); let sequencer_address = SequencerContractAddress(random::().into()); let timestamp = BlockTimestamp(random::()); - let starknet_version = StarknetVersion(vec![123]); + let starknet_version = StarknetVersion::V0_10_0; let block_number = block.header.block_header_without_hash.block_number; block.header.block_hash = block_hash; block.header.block_header_without_hash.sequencer = sequencer_address; @@ -882,7 +882,7 @@ async fn get_block_w_transaction_hashes() { let block_hash = BlockHash(random::().into()); let sequencer_address = SequencerContractAddress(random::().into()); let timestamp = BlockTimestamp(random::()); - let starknet_version = StarknetVersion(vec![123]); + let starknet_version = StarknetVersion::V0_10_0; block.header.block_hash = block_hash; block.header.block_header_without_hash.sequencer = sequencer_address; block.header.block_header_without_hash.timestamp = timestamp; diff --git a/crates/papyrus_storage/src/header_test.rs b/crates/papyrus_storage/src/header_test.rs index 94297a9dc2..b2be22408e 100644 --- a/crates/papyrus_storage/src/header_test.rs +++ b/crates/papyrus_storage/src/header_test.rs @@ -181,8 +181,8 @@ async fn starknet_version() { reader.begin_ro_txn().unwrap().get_starknet_version(BlockNumber(1)).unwrap(); assert!(non_existing_block_starknet_version.is_none()); - let second_version = StarknetVersion(vec![2]); - let yet_another_version = StarknetVersion(vec![3]); + let second_version = StarknetVersion::V0_9_1; + let yet_another_version = StarknetVersion::V0_12_0; writer .begin_rw_txn() diff --git a/crates/papyrus_storage/src/serialization/serializers.rs b/crates/papyrus_storage/src/serialization/serializers.rs index 31f18161a5..f092e69aa5 100644 --- a/crates/papyrus_storage/src/serialization/serializers.rs +++ b/crates/papyrus_storage/src/serialization/serializers.rs @@ -377,7 +377,27 @@ auto_storage_serde! { pub enum StructType { Struct = 0, } - pub struct StarknetVersion(pub Vec); + pub enum StarknetVersion { + V0_9_1 = 0, + V0_10_0 = 1, + V0_10_1 = 2, + V0_10_2 = 3, + V0_10_3 = 4, + V0_11_0 = 5, + V0_11_0_2 = 6, + V0_11_1 = 7, + V0_11_2 = 8, + V0_12_0 = 9, + V0_12_1 = 10, + V0_12_2 = 11, + V0_12_3 = 12, + V0_13_0 = 13, + V0_13_1 = 14, + V0_13_1_1 = 15, + V0_13_2 = 16, + V0_13_2_1 = 17, + V0_13_3 = 18, + } pub struct StateDiffCommitment(pub PoseidonHash); pub struct Tip(pub u64); pub struct TransactionCommitment(pub StarkHash); diff --git a/crates/papyrus_test_utils/src/lib.rs b/crates/papyrus_test_utils/src/lib.rs index a2e0b65452..1113bc941e 100644 --- a/crates/papyrus_test_utils/src/lib.rs +++ b/crates/papyrus_test_utils/src/lib.rs @@ -474,7 +474,27 @@ auto_impl_get_test_instance! { MulMod = 9, RangeCheck96 = 10, } - pub struct StarknetVersion(pub Vec); + pub enum StarknetVersion { + V0_9_1 = 0, + V0_10_0 = 1, + V0_10_1 = 2, + V0_10_2 = 3, + V0_10_3 = 4, + V0_11_0 = 5, + V0_11_0_2 = 6, + V0_11_1 = 7, + V0_11_2 = 8, + V0_12_0 = 9, + V0_12_1 = 10, + V0_12_2 = 11, + V0_12_3 = 12, + V0_13_0 = 13, + V0_13_1 = 14, + V0_13_1_1 = 15, + V0_13_2 = 16, + V0_13_2_1 = 17, + V0_13_3 = 18, + } pub struct Calldata(pub Arc>); pub struct ClassHash(pub StarkHash); pub struct CompiledClassHash(pub StarkHash); diff --git a/crates/starknet_api/src/block.rs b/crates/starknet_api/src/block.rs index 16df97778f..c0ea73291b 100644 --- a/crates/starknet_api/src/block.rs +++ b/crates/starknet_api/src/block.rs @@ -36,19 +36,85 @@ pub struct Block { pub body: BlockBody, } -/// A version of the Starknet protocol used when creating a block. -#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub struct StarknetVersion(pub Vec); +macro_rules! starknet_version_enum { + ( + $(($variant:ident, $major:literal, $minor:literal, $patch:literal $(, $fourth:literal)?)),+, + $latest:ident + ) => { + /// A version of the Starknet protocol used when creating a block. + #[cfg_attr(any(test, feature = "testing"), derive(strum_macros::EnumIter))] + #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] + pub enum StarknetVersion { + $($variant,)+ + } + + impl StarknetVersion { + pub const LATEST: Self = Self::$latest; + } + + impl From<&StarknetVersion> for Vec { + fn from(value: &StarknetVersion) -> Self { + match value { + $( + StarknetVersion::$variant => vec![$major, $minor, $patch $(, $fourth)?], + )+ + } + } + } + + impl TryFrom> for StarknetVersion { + type Error = StarknetApiError; + + fn try_from(value: Vec) -> Result { + match value[..] { + $( + [$major, $minor, $patch $(, $fourth)?] => Ok(Self::$variant), + )+ + _ => Err(StarknetApiError::InvalidStarknetVersion(value)), + } + } + } + } +} + +starknet_version_enum! { + (V0_9_1, 0, 9, 1), + (V0_10_0, 0, 10, 0), + (V0_10_1, 0, 10, 1), + (V0_10_2, 0, 10, 2), + (V0_10_3, 0, 10, 3), + (V0_11_0, 0, 11, 0), + (V0_11_0_2, 0, 11, 0, 2), + (V0_11_1, 0, 11, 1), + (V0_11_2, 0, 11, 2), + (V0_12_0, 0, 12, 0), + (V0_12_1, 0, 12, 1), + (V0_12_2, 0, 12, 2), + (V0_12_3, 0, 12, 3), + (V0_13_0, 0, 13, 0), + (V0_13_1, 0, 13, 1), + (V0_13_1_1, 0, 13, 1, 1), + (V0_13_2, 0, 13, 2), + (V0_13_2_1, 0, 13, 2, 1), + (V0_13_3, 0, 13, 3), + V0_13_3 +} impl Default for StarknetVersion { fn default() -> Self { - Self(vec![0, 0, 0]) + Self::LATEST + } +} + +impl From for Vec { + fn from(value: StarknetVersion) -> Self { + Vec::::from(&value) } } impl Display for StarknetVersion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0.iter().map(|x| x.to_string()).join(".")) + write!(f, "{}", Vec::::from(self).iter().map(|x| x.to_string()).join(".")) } } @@ -57,7 +123,16 @@ impl TryFrom for StarknetVersion { /// Parses a string separated by dots into a StarknetVersion. fn try_from(starknet_version: String) -> Result { - Ok(Self(starknet_version.split('.').map(|x| x.parse::()).try_collect()?)) + let version: Vec = + starknet_version.split('.').map(|x| x.parse::()).try_collect()?; + Ok(Self::try_from(version)?) + } +} + +impl TryFrom<&str> for StarknetVersion { + type Error = StarknetApiError; + fn try_from(starknet_version: &str) -> Result { + Self::try_from(starknet_version.to_string()) } } diff --git a/crates/starknet_api/src/block_hash/block_hash_calculator.rs b/crates/starknet_api/src/block_hash/block_hash_calculator.rs index fe5381d7f0..55b19ac487 100644 --- a/crates/starknet_api/src/block_hash/block_hash_calculator.rs +++ b/crates/starknet_api/src/block_hash/block_hash_calculator.rs @@ -54,8 +54,8 @@ pub enum BlockHashVersion { impl From for StarknetVersion { fn from(value: BlockHashVersion) -> Self { match value { - BlockHashVersion::VO_13_2 => StarknetVersion(vec![0, 13, 2]), - BlockHashVersion::VO_13_3 => StarknetVersion(vec![0, 13, 3]), + BlockHashVersion::VO_13_2 => StarknetVersion::V0_13_2, + BlockHashVersion::VO_13_3 => StarknetVersion::V0_13_3, } } } diff --git a/crates/starknet_api/src/block_test.rs b/crates/starknet_api/src/block_test.rs index bb5b199acf..175034e6ac 100644 --- a/crates/starknet_api/src/block_test.rs +++ b/crates/starknet_api/src/block_test.rs @@ -1,4 +1,5 @@ use serde_json::json; +use strum::IntoEnumIterator; use super::{verify_block_signature, StarknetVersion}; use crate::block::{BlockHash, BlockNumber, BlockSignature}; @@ -49,18 +50,24 @@ fn block_signature_verification() { } #[test] -fn test_vec_version() { - assert_eq!(StarknetVersion::default().to_string(), "0.0.0"); - - let version_123 = StarknetVersion::try_from("1.2.3".to_owned()).unwrap(); - assert_eq!(version_123, StarknetVersion(vec![1, 2, 3])); +fn test_version_serde() { + for version in StarknetVersion::iter() { + // To/from Vec. + assert_eq!(StarknetVersion::try_from(Vec::::from(&version)).unwrap(), version); + // To/from json. + assert_eq!(serde_json::from_value::(json!(version)).unwrap(), version); + } - let serialized_123 = json!(version_123); - assert_eq!(serialized_123, "1.2.3".to_owned()); - assert_eq!(serde_json::from_value::(serialized_123).unwrap(), version_123); + // Sanity check substring deserialization. + assert_eq!(StarknetVersion::try_from("0.13.1").unwrap(), StarknetVersion::V0_13_1); + assert_eq!(StarknetVersion::try_from("0.13.1.1").unwrap(), StarknetVersion::V0_13_1_1); +} - assert!(StarknetVersion(vec![0, 10, 0]) > StarknetVersion(vec![0, 2, 5])); - assert!(StarknetVersion(vec![0, 13, 1]) > StarknetVersion(vec![0, 12, 2])); - assert!(StarknetVersion(vec![0, 13, 0, 1]) > StarknetVersion(vec![0, 13, 0])); - assert!(StarknetVersion(vec![0, 13, 0]) > StarknetVersion(vec![0, 13])); +/// Order of version variants should match byte-vector lexicographic order. +#[test] +fn test_version_byte_vec_order() { + let versions = StarknetVersion::iter().collect::>(); + for i in 0..(versions.len() - 1) { + assert!(Vec::::from(versions[i]) <= Vec::::from(versions[i + 1])); + } } diff --git a/crates/starknet_api/src/lib.rs b/crates/starknet_api/src/lib.rs index 6d60c74751..98f9f42eda 100644 --- a/crates/starknet_api/src/lib.rs +++ b/crates/starknet_api/src/lib.rs @@ -45,6 +45,8 @@ pub enum StarknetApiError { /// Missing resource type / duplicated resource type. #[error("Missing resource type / duplicated resource type; got {0}.")] InvalidResourceMappingInitializer(String), + #[error("Invalid Starknet version: {0:?}")] + InvalidStarknetVersion(Vec), #[error("NonzeroGasPrice cannot be zero.")] ZeroGasPrice, }