From 5f65a5e95916b0ccfa6b344d3aba33d25e5e1943 Mon Sep 17 00:00:00 2001 From: Dori Medini Date: Sun, 13 Oct 2024 20:02:22 +0300 Subject: [PATCH] feat(starknet_api): the StarknetVersion type is now an enum --- crates/papyrus_rpc/src/v0_8/api/test.rs | 6 +- crates/papyrus_storage/src/header_test.rs | 4 +- .../src/serialization/serializers.rs | 22 +++- crates/papyrus_test_utils/src/lib.rs | 22 +++- crates/starknet_api/src/block.rs | 102 ++++++++++++++++-- .../src/block_hash/block_hash_calculator.rs | 4 +- crates/starknet_api/src/block_test.rs | 31 +++--- crates/starknet_api/src/lib.rs | 2 + 8 files changed, 165 insertions(+), 28 deletions(-) diff --git a/crates/papyrus_rpc/src/v0_8/api/test.rs b/crates/papyrus_rpc/src/v0_8/api/test.rs index a535f5e3f94..84814d21f74 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 94297a9dc28..b2be22408e9 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 31f18161a5e..f092e69aa52 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 a2e0b654523..1113bc941e7 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 1c74cdb8590..2fb82b7f4ad 100644 --- a/crates/starknet_api/src/block.rs +++ b/crates/starknet_api/src/block.rs @@ -37,18 +37,97 @@ pub struct Block { } /// A version of the Starknet protocol used when creating a block. -#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub struct StarknetVersion(pub Vec); +#[cfg_attr(any(test, feature = "testing"), derive(strum_macros::EnumIter))] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub enum StarknetVersion { + V0_9_1, + V0_10_0, + V0_10_1, + V0_10_2, + V0_10_3, + V0_11_0, + V0_11_0_2, + V0_11_1, + V0_11_2, + V0_12_0, + V0_12_1, + V0_12_2, + V0_12_3, + V0_13_0, + V0_13_1, + V0_13_1_1, + V0_13_2, + V0_13_2_1, + #[default] + V0_13_3, +} -impl Default for StarknetVersion { - fn default() -> Self { - Self(vec![0, 0, 0]) +impl From<&StarknetVersion> for Vec { + fn from(value: &StarknetVersion) -> Self { + match value { + StarknetVersion::V0_9_1 => vec![0, 9, 1], + StarknetVersion::V0_10_0 => vec![0, 10, 0], + StarknetVersion::V0_10_1 => vec![0, 10, 1], + StarknetVersion::V0_10_2 => vec![0, 10, 2], + StarknetVersion::V0_10_3 => vec![0, 10, 3], + StarknetVersion::V0_11_0 => vec![0, 11, 0], + StarknetVersion::V0_11_0_2 => vec![0, 11, 0, 2], + StarknetVersion::V0_11_1 => vec![0, 11, 1], + StarknetVersion::V0_11_2 => vec![0, 11, 2], + StarknetVersion::V0_12_0 => vec![0, 12, 0], + StarknetVersion::V0_12_1 => vec![0, 12, 1], + StarknetVersion::V0_12_2 => vec![0, 12, 2], + StarknetVersion::V0_12_3 => vec![0, 12, 3], + StarknetVersion::V0_13_0 => vec![0, 13, 0], + StarknetVersion::V0_13_1 => vec![0, 13, 1], + StarknetVersion::V0_13_1_1 => vec![0, 13, 1, 1], + StarknetVersion::V0_13_2 => vec![0, 13, 2], + StarknetVersion::V0_13_2_1 => vec![0, 13, 2, 1], + StarknetVersion::V0_13_3 => vec![0, 13, 3], + } + } +} + +impl From for Vec { + fn from(value: StarknetVersion) -> Self { + Vec::::from(&value) + } +} + +impl TryFrom> for StarknetVersion { + type Error = StarknetApiError; + fn try_from(value: Vec) -> Result { + if value.len() != 3 && value.len() != 4 { + return Err(StarknetApiError::InvalidStarknetVersion(value)); + } + match (value[0], value[1], value[2], value.get(3)) { + (0, 9, 1, None) => Ok(Self::V0_9_1), + (0, 10, 0, None) => Ok(Self::V0_10_0), + (0, 10, 1, None) => Ok(Self::V0_10_1), + (0, 10, 2, None) => Ok(Self::V0_10_2), + (0, 10, 3, None) => Ok(Self::V0_10_3), + (0, 11, 0, None) => Ok(Self::V0_11_0), + (0, 11, 0, Some(2)) => Ok(Self::V0_11_0_2), + (0, 11, 1, None) => Ok(Self::V0_11_1), + (0, 11, 2, None) => Ok(Self::V0_11_2), + (0, 12, 0, None) => Ok(Self::V0_12_0), + (0, 12, 1, None) => Ok(Self::V0_12_1), + (0, 12, 2, None) => Ok(Self::V0_12_2), + (0, 12, 3, None) => Ok(Self::V0_12_3), + (0, 13, 0, None) => Ok(Self::V0_13_0), + (0, 13, 1, None) => Ok(Self::V0_13_1), + (0, 13, 1, Some(1)) => Ok(Self::V0_13_1_1), + (0, 13, 2, None) => Ok(Self::V0_13_2), + (0, 13, 2, Some(1)) => Ok(Self::V0_13_2_1), + (0, 13, 3, None) => Ok(Self::V0_13_3), + _ => Err(StarknetApiError::InvalidStarknetVersion(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 +136,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 fe5381d7f06..55b19ac487f 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 bb5b199acf7..175034e6acf 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 f4778496672..e789972a03b 100644 --- a/crates/starknet_api/src/lib.rs +++ b/crates/starknet_api/src/lib.rs @@ -44,6 +44,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, }