diff --git a/crates/class-hash/src/lib.rs b/crates/class-hash/src/lib.rs index 2400fe0097..c5e81e4f8f 100644 --- a/crates/class-hash/src/lib.rs +++ b/crates/class-hash/src/lib.rs @@ -58,6 +58,12 @@ use anyhow::{Context, Error, Result}; use pathfinder_common::class_definition::EntryPointType::*; +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedClassDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::{felt_bytes, ClassHash}; use pathfinder_crypto::hash::{HashChain, PoseidonHasher}; use pathfinder_crypto::Felt; @@ -80,22 +86,47 @@ impl ComputedClassHash { } } -/// Computes the starknet class hash for given class definition JSON blob. +/// Consumes an opaque serialized class definition and outputs the computed +/// class hash as well as the definition reinterpreted as either a serialized +/// Cairo or Sierra definition. /// /// This function first parses the JSON blob to decide if it's a Cairo or Sierra /// class definition and then calls the appropriate function to compute the /// class hash with the parsed definition. -pub fn compute_class_hash(contract_definition_dump: &[u8]) -> Result { - let contract_definition = parse_contract_definition(contract_definition_dump) +pub fn compute_class_hash( + serialized_definition: SerializedOpaqueClassDefinition, +) -> Result<(ComputedClassHash, SerializedClassDefinition)> { + let contract_definition = parse_contract_definition(&serialized_definition) .context("Failed to parse contract definition")?; match contract_definition { json::ContractDefinition::Sierra(definition) => compute_sierra_class_hash(definition) .map(ComputedClassHash::Sierra) - .context("Compute class hash"), + .context("Compute class hash") + .map(|hash| { + ( + hash, + // It is safe to reinterpret the serialized definition as a Sierra definition + // since the parsing step succeeded and confirmed it is a + // Sierra definition. + SerializedClassDefinition::Sierra(SerializedSierraDefinition::from_bytes( + serialized_definition.into_bytes(), + )), + ) + }), json::ContractDefinition::Cairo(definition) => compute_cairo_class_hash(definition.into()) .map(ComputedClassHash::Cairo) - .context("Compute class hash"), + .context("Compute class hash") + .map(|hash| { + ( + hash, + // It is safe to reinterpret the serialized definition as a Cairo definition + // since the parsing step succeeded and confirmed it is a Cairo definition. + SerializedClassDefinition::Cairo(SerializedCairoDefinition::from_bytes( + serialized_definition.into_bytes(), + )), + ) + }), } } @@ -132,14 +163,16 @@ pub fn compute_cairo_hinted_class_hash( /// /// Due to an issue in serde_json we can't use an untagged enum and simply /// derive a Deserialize implementation: -pub fn parse_contract_definition( - contract_definition_dump: &[u8], +fn parse_contract_definition( + serialized_definition: &SerializedOpaqueClassDefinition, ) -> serde_json::Result> { - serde_json::from_slice::>(contract_definition_dump) + serde_json::from_slice::>(serialized_definition.as_bytes()) .map(json::ContractDefinition::Sierra) .or_else(|_| { - serde_json::from_slice::>(contract_definition_dump) - .map(json::ContractDefinition::Cairo) + serde_json::from_slice::>( + serialized_definition.as_bytes(), + ) + .map(json::ContractDefinition::Cairo) }) } @@ -799,17 +832,22 @@ pub mod json { #[cfg(test)] mod test_vectors { + use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use pathfinder_common::macro_prelude::*; use starknet_gateway_test_fixtures::class_definitions::*; use super::super::{compute_class_hash, ComputedClassHash}; + fn hash(data: &[u8]) -> ComputedClassHash { + compute_class_hash(SerializedOpaqueClassDefinition::from_slice(data)) + .unwrap() + .0 + } + #[tokio::test] async fn first() { - let hash = compute_class_hash(INTEGRATION_TEST).unwrap(); - assert_eq!( - hash, + hash(INTEGRATION_TEST), ComputedClassHash::Cairo(class_hash!( "0x031da92cf5f54bcb81b447e219e2b791b23f3052d12b6c9abd04ff2e5626576" )) @@ -818,10 +856,8 @@ pub mod json { #[test] fn second() { - let hash = super::super::compute_class_hash(CONTRACT_DEFINITION).unwrap(); - assert_eq!( - hash, + hash(CONTRACT_DEFINITION), ComputedClassHash::Cairo(class_hash!( "0x50b2148c0d782914e0b12a1a32abe5e398930b7e914f82c65cb7afce0a0ab9b" )) @@ -830,10 +866,8 @@ pub mod json { #[tokio::test] async fn genesis_contract() { - let hash = compute_class_hash(GOERLI_GENESIS).unwrap(); - assert_eq!( - hash, + hash(GOERLI_GENESIS), ComputedClassHash::Cairo(class_hash!( "0x10455c752b86932ce552f2b0fe81a880746649b9aee7e0d842bf3f52378f9f8" )) @@ -851,10 +885,11 @@ pub mod json { // Known contract which triggered a hash mismatch failure. let extract = tokio::task::spawn_blocking(move || -> anyhow::Result<_> { - let hash = compute_class_hash(CAIRO_0_8_NEW_ATTRIBUTES)?; - Ok(hash) + compute_class_hash(SerializedOpaqueClassDefinition::from_slice( + CAIRO_0_8_NEW_ATTRIBUTES, + )) }); - let calculated_hash = extract.await.unwrap().unwrap(); + let (calculated_hash, _) = extract.await.unwrap().unwrap(); assert_eq!(calculated_hash, expected); } @@ -863,10 +898,8 @@ pub mod json { async fn cairo_0_10() { // Contract whose class triggered a deserialization issue because of the new // `compiler_version` property. - let hash = compute_class_hash(CAIRO_0_10_COMPILER_VERSION).unwrap(); - assert_eq!( - hash, + hash(CAIRO_0_10_COMPILER_VERSION), ComputedClassHash::Cairo(class_hash!( "0xa69700a89b1fa3648adff91c438b79c75f7dcb0f4798938a144cce221639d6" )) @@ -878,10 +911,8 @@ pub mod json { // Contract who's class contains `compiler_version` property as well as // `cairo_type` with tuple values. These tuple values require a // space to be injected in order to achieve the correct hash. - let hash = compute_class_hash(CAIRO_0_10_TUPLES_INTEGRATION).unwrap(); - assert_eq!( - hash, + hash(CAIRO_0_10_TUPLES_INTEGRATION), ComputedClassHash::Cairo(class_hash!( "0x542460935cea188d21e752d8459d82d60497866aaad21f873cbb61621d34f7f" )) @@ -893,10 +924,8 @@ pub mod json { // Contract who's class contains `compiler_version` property as well as // `cairo_type` with tuple values. These tuple values require a // space to be injected in order to achieve the correct hash. - let hash = compute_class_hash(CAIRO_0_10_TUPLES_GOERLI).unwrap(); - assert_eq!( - hash, + hash(CAIRO_0_10_TUPLES_GOERLI), ComputedClassHash::Cairo(class_hash!( "0x66af14b94491ba4e2aea1117acf0a3155c53d92fdfd9c1f1dcac90dc2d30157" )) @@ -905,10 +934,8 @@ pub mod json { #[tokio::test] async fn cairo_0_11_sierra() { - let hash = compute_class_hash(CAIRO_0_11_SIERRA).unwrap(); - assert_eq!( - hash, + hash(CAIRO_0_11_SIERRA), ComputedClassHash::Sierra(class_hash!( "0x4e70b19333ae94bd958625f7b61ce9eec631653597e68645e13780061b2136c" )) @@ -917,14 +944,17 @@ pub mod json { #[tokio::test] async fn cairo_0_11_with_decimal_entry_point_offset() { - let hash = compute_class_hash(CAIRO_0_11_WITH_DECIMAL_ENTRY_POINT_OFFSET).unwrap(); + let (hash, _) = compute_class_hash(SerializedOpaqueClassDefinition::from_slice( + CAIRO_0_11_WITH_DECIMAL_ENTRY_POINT_OFFSET, + )) + .unwrap(); assert_eq!( hash, ComputedClassHash::Cairo(class_hash!( "0x0484c163658bcce5f9916f486171ac60143a92897533aa7ff7ac800b16c63311" )) - ) + ); } } diff --git a/crates/common/src/casm_class.rs b/crates/common/src/casm_class.rs index 2385410edf..dc2fabecdf 100644 --- a/crates/common/src/casm_class.rs +++ b/crates/common/src/casm_class.rs @@ -90,10 +90,10 @@ pub enum NestedIntList { Node(Vec), } -impl TryFrom<&str> for CasmContractClass { - type Error = serde_json::Error; - - fn try_from(value: &str) -> Result { - serde_json::from_str(value) +impl CasmContractClass { + pub fn try_from_serialized_definition( + definition: &crate::class_definition::SerializedCasmDefinition, + ) -> Result { + serde_json::from_slice(definition.as_bytes()) } } diff --git a/crates/common/src/class_definition.rs b/crates/common/src/class_definition.rs index 056eb4e6b9..f47400a9c0 100644 --- a/crates/common/src/class_definition.rs +++ b/crates/common/src/class_definition.rs @@ -12,6 +12,28 @@ use crate::{ByteCodeOffset, EntryPoint}; pub const CLASS_DEFINITION_MAX_ALLOWED_SIZE: u64 = 4 * 1024 * 1024; +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Dummy)] +pub struct SerializedSierraDefinition(Vec); + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Dummy)] +pub struct SerializedCasmDefinition(Vec); + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Dummy)] +pub struct SerializedCairoDefinition(Vec); + +/// Carries the definition of a serialized contract class, either Sierra or +/// Cairo. The caller does not care which class definition it is. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Dummy)] +pub struct SerializedOpaqueClassDefinition(Vec); + +/// Carries the definition of a serialized contract class, either Sierra or +/// Cairo. +#[derive(Clone, Debug)] +pub enum SerializedClassDefinition { + Sierra(SerializedSierraDefinition), + Cairo(SerializedCairoDefinition), +} + #[derive(Debug, Deserialize, Dummy)] pub enum ClassDefinition<'a> { Sierra(Sierra<'a>), @@ -190,3 +212,89 @@ pub struct SelectorAndFunctionIndex { pub selector: EntryPoint, pub function_idx: u64, } + +impl SerializedSierraDefinition { + pub fn from_bytes(bytes: Vec) -> Self { + Self(bytes) + } + + pub fn from_slice(bytes: &[u8]) -> Self { + Self(bytes.to_vec()) + } + + pub fn into_bytes(self) -> Vec { + self.0 + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl SerializedCasmDefinition { + pub fn from_bytes(bytes: Vec) -> Self { + Self(bytes) + } + + pub fn from_slice(bytes: &[u8]) -> Self { + Self(bytes.to_vec()) + } + + pub fn into_bytes(self) -> Vec { + self.0 + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl SerializedCairoDefinition { + pub fn from_bytes(bytes: Vec) -> Self { + Self(bytes) + } + + pub fn from_slice(bytes: &[u8]) -> Self { + Self(bytes.to_vec()) + } + + pub fn into_bytes(self) -> Vec { + self.0 + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl SerializedOpaqueClassDefinition { + pub fn from_bytes(bytes: Vec) -> Self { + Self(bytes) + } + + pub fn from_slice(bytes: &[u8]) -> Self { + Self(bytes.to_vec()) + } + + pub fn into_bytes(self) -> Vec { + self.0 + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +/// We can use `From` because this is always safe. +impl From for SerializedOpaqueClassDefinition { + fn from(d: SerializedSierraDefinition) -> Self { + Self::from_bytes(d.into_bytes()) + } +} + +/// We can use `From` because this is always safe. +impl From for SerializedOpaqueClassDefinition { + fn from(d: SerializedCairoDefinition) -> Self { + Self::from_bytes(d.into_bytes()) + } +} diff --git a/crates/common/src/l2.rs b/crates/common/src/l2.rs index aebf4c8d8e..fdc2a448fd 100644 --- a/crates/common/src/l2.rs +++ b/crates/common/src/l2.rs @@ -3,6 +3,7 @@ use std::sync::{Arc, RwLock}; use fake::Dummy; +use crate::class_definition::{SerializedCasmDefinition, SerializedSierraDefinition}; use crate::event::Event; use crate::receipt::Receipt; use crate::state_update::StateUpdateData; @@ -97,8 +98,8 @@ pub struct ConsensusFinalizedBlockHeader { pub struct DeclaredClass { pub sierra_hash: SierraHash, pub casm_hash_v2: CasmHash, - pub sierra_def: Vec, - pub casm_def: Vec, + pub sierra_def: SerializedSierraDefinition, + pub casm_def: SerializedCasmDefinition, } impl From for L2BlockToCommit { diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index ac59c9e80a..feea965c9f 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -1,7 +1,8 @@ use std::io::Write; use anyhow::Context; -use pathfinder_common::{class_definition, felt, CasmHash}; +use pathfinder_common::class_definition::{self, SerializedCasmDefinition}; +use pathfinder_common::{felt, CasmHash}; use pathfinder_crypto::Felt; /// Resource limits for the compiler child process. @@ -102,10 +103,10 @@ impl std::fmt::Display for BlockifierLibfuncs { /// non-Unix platforms no resource limits are applied; the child process still /// runs but without any resource constraints. pub fn compile_sierra_to_casm( - sierra_definition: &[u8], + sierra_definition: &class_definition::SerializedSierraDefinition, resource_limits: ResourceLimits, blockifier_libfuncs: BlockifierLibfuncs, -) -> anyhow::Result> { +) -> anyhow::Result { let mut pathfinder_cmd = pathfinder_exe() .context("reading pathfinder executable path") .map(std::process::Command::new)?; @@ -126,7 +127,7 @@ pub fn compile_sierra_to_casm( { let mut ch_stdin = child.stdin.take().context("opening child stdin")?; ch_stdin - .write_all(sierra_definition) + .write_all(sierra_definition.as_bytes()) .context("writing Sierra definition to child stdin")?; ch_stdin.flush().context("flushing child stdin")?; } @@ -144,7 +145,7 @@ pub fn compile_sierra_to_casm( ); } - Ok(output.stdout) + Ok(SerializedCasmDefinition::from_bytes(output.stdout)) } /// Compile a Sierra class definition into CASM using an isolated child process. @@ -159,11 +160,15 @@ pub fn compile_sierra_to_casm_deser( sierra_definition: class_definition::Sierra<'_>, resource_limits: ResourceLimits, blockifier_libfuncs: BlockifierLibfuncs, -) -> anyhow::Result> { +) -> anyhow::Result { serde_json::to_vec(&sierra_definition) .context("serializing Sierra definition") .map(|sierra_definition| { - compile_sierra_to_casm(&sierra_definition, resource_limits, blockifier_libfuncs) + compile_sierra_to_casm( + &class_definition::SerializedSierraDefinition::from_bytes(sierra_definition), + resource_limits, + blockifier_libfuncs, + ) })? } @@ -305,14 +310,14 @@ mod spawn { /// which is not recommended. Use [`compile_sierra_to_casm`] to compile in an /// isolated child process with resource limits. pub fn compile_sierra_to_casm_impl( - sierra_definition: &[u8], + sierra_definition: &class_definition::SerializedSierraDefinition, blockifier_libfuncs: BlockifierLibfuncs, -) -> anyhow::Result> { +) -> anyhow::Result { // The class representation expected by the compiler doesn't match the // representation used by the feeder gateway for Sierra classes, so we have to // convert the JSON to something that can be parsed into the expected input // format for the compiler. - serde_json::from_slice::>(sierra_definition) + serde_json::from_slice::>(sierra_definition.as_bytes()) .context("Parsing Sierra class") .map(|sierra_class| compile_sierra_to_casm_deser_impl(sierra_class, blockifier_libfuncs))? .context("Compiling Sierra to CASM") @@ -326,7 +331,7 @@ pub fn compile_sierra_to_casm_impl( pub fn compile_sierra_to_casm_deser_impl( sierra_definition: class_definition::Sierra<'_>, blockifier_libfuncs: BlockifierLibfuncs, -) -> anyhow::Result> { +) -> anyhow::Result { let version = parse_sierra_version(&sierra_definition.sierra_program) .context("Parsing Sierra version")?; @@ -379,8 +384,8 @@ fn parse_sierra_version(program: &[Felt]) -> anyhow::Result { /// Parse CASM class definition and return _Blake2_ CASM class hash. /// /// Uses the _latest_ compiler for the parsing and starknet_api for the hashing. -pub fn casm_class_hash_v2(casm_definition: &[u8]) -> anyhow::Result { - v2::casm_class_hash_v2(casm_definition) +pub fn casm_class_hash_v2(casm_definition: &SerializedCasmDefinition) -> anyhow::Result { + v2::casm_class_hash_v2(casm_definition.as_bytes()) } mod v1_0_0_alpha6 { @@ -405,17 +410,15 @@ mod v1_0_0_alpha6 { serde_json::from_value::(json) } - pub(super) fn compile(definition: class_definition::Sierra<'_>) -> anyhow::Result> { + pub(super) fn compile( + definition: class_definition::Sierra<'_>, + ) -> anyhow::Result { let sierra_class: ContractClass = pathfinder_to_starknet_contract_class(definition) .context("Converting to Sierra class")?; validate_compatible_sierra_version( &sierra_class, ListSelector::ListName( - // Keeping the "experimental" list for backwards - // compatibility. Also, the names in - // BlockifierLibfuncs would have to be mapped to - // (audited|testnet|experimental)_v0.1.0 . casm_compiler_v1_0_0_alpha6::allowed_libfuncs::DEFAULT_EXPERIMENTAL_LIBFUNCS_LIST .to_string(), ), @@ -424,9 +427,9 @@ mod v1_0_0_alpha6 { let casm_class = CasmContractClass::from_contract_class(sierra_class, true) .context("Compiling to CASM")?; - let casm_definition = serde_json::to_vec(&casm_class)?; - - Ok(casm_definition) + Ok(super::SerializedCasmDefinition::from_bytes( + serde_json::to_vec(&casm_class)?, + )) } } @@ -452,17 +455,15 @@ mod v1_0_0_rc0 { serde_json::from_value::(json) } - pub(super) fn compile(definition: class_definition::Sierra<'_>) -> anyhow::Result> { + pub(super) fn compile( + definition: class_definition::Sierra<'_>, + ) -> anyhow::Result { let sierra_class: ContractClass = pathfinder_to_starknet_contract_class(definition) .context("Converting to Sierra class")?; validate_compatible_sierra_version( &sierra_class, ListSelector::ListName( - // Keeping the "experimental" list for backwards - // compatibility. Also, the names in - // BlockifierLibfuncs would have to be mapped to - // (audited|testnet|experimental)_v0.1.0 . casm_compiler_v1_0_0_rc0::allowed_libfuncs::DEFAULT_EXPERIMENTAL_LIBFUNCS_LIST .to_string(), ), @@ -471,9 +472,9 @@ mod v1_0_0_rc0 { let casm_class = CasmContractClass::from_contract_class(sierra_class, true) .context("Compiling to CASM")?; - let casm_definition = serde_json::to_vec(&casm_class)?; - - Ok(casm_definition) + Ok(super::SerializedCasmDefinition::from_bytes( + serde_json::to_vec(&casm_class)?, + )) } } @@ -499,17 +500,15 @@ mod v1_1_1 { serde_json::from_value::(json) } - pub(super) fn compile(definition: class_definition::Sierra<'_>) -> anyhow::Result> { + pub(super) fn compile( + definition: class_definition::Sierra<'_>, + ) -> anyhow::Result { let sierra_class: ContractClass = pathfinder_to_starknet_contract_class(definition) .context("Converting to Sierra class")?; validate_compatible_sierra_version( &sierra_class, ListSelector::ListName( - // Keeping the "experimental" list for backwards - // compatibility. Also, the names in - // BlockifierLibfuncs would have to be mapped to - // (audited|testnet|experimental)_v0.1.0 . casm_compiler_v1_0_0_rc0::allowed_libfuncs::DEFAULT_EXPERIMENTAL_LIBFUNCS_LIST .to_string(), ), @@ -518,9 +517,9 @@ mod v1_1_1 { let casm_class = CasmContractClass::from_contract_class(sierra_class, true) .context("Compiling to CASM")?; - let casm_definition = serde_json::to_vec(&casm_class)?; - - Ok(casm_definition) + Ok(super::SerializedCasmDefinition::from_bytes( + serde_json::to_vec(&casm_class)?, + )) } } @@ -547,7 +546,7 @@ mod v2 { pub(super) fn compile( definition: crate::class_definition::Sierra<'_>, blockifier_libfuncs: BlockifierLibfuncs, - ) -> anyhow::Result> { + ) -> anyhow::Result { let sierra_class: ContractClass = pathfinder_to_starknet_contract_class(definition) .context("Converting to Sierra class")?; @@ -571,9 +570,9 @@ mod v2 { usize::MAX, ) .context("Compiling to CASM")?; - let casm_definition = serde_json::to_vec(&casm_class)?; - - Ok(casm_definition) + Ok(super::SerializedCasmDefinition::from_bytes( + serde_json::to_vec(&casm_class)?, + )) } pub(super) fn casm_class_hash_v2(casm_definition: &[u8]) -> anyhow::Result { @@ -644,8 +643,13 @@ mod tests { #[test] fn test_compile_ser() { - compile_sierra_to_casm_impl(CAIRO_1_0_0_ALPHA5_SIERRA, BlockifierLibfuncs::default()) - .unwrap(); + compile_sierra_to_casm_impl( + &class_definition::SerializedSierraDefinition::from_slice( + CAIRO_1_0_0_ALPHA5_SIERRA, + ), + BlockifierLibfuncs::default(), + ) + .unwrap(); } } @@ -669,8 +673,11 @@ mod tests { #[test] fn test_compile_ser() { - compile_sierra_to_casm_impl(CAIRO_1_0_0_RC0_SIERRA, BlockifierLibfuncs::default()) - .unwrap(); + compile_sierra_to_casm_impl( + &class_definition::SerializedSierraDefinition::from_slice(CAIRO_1_0_0_RC0_SIERRA), + BlockifierLibfuncs::default(), + ) + .unwrap(); } } @@ -697,15 +704,23 @@ mod tests { #[test] fn test_compile_ser() { - compile_sierra_to_casm_impl(CAIRO_1_1_0_RC0_SIERRA, BlockifierLibfuncs::default()) - .unwrap(); + compile_sierra_to_casm_impl( + &class_definition::SerializedSierraDefinition::from_slice(CAIRO_1_1_0_RC0_SIERRA), + BlockifierLibfuncs::default(), + ) + .unwrap(); } #[test] fn regression_stack_overflow() { // This class caused a stack-overflow in v2 compilers <= v2.0.1 - compile_sierra_to_casm_impl(CAIRO_2_0_0_STACK_OVERFLOW, BlockifierLibfuncs::default()) - .unwrap(); + compile_sierra_to_casm_impl( + &class_definition::SerializedSierraDefinition::from_slice( + CAIRO_2_0_0_STACK_OVERFLOW, + ), + BlockifierLibfuncs::default(), + ) + .unwrap(); } } } diff --git a/crates/executor/src/class.rs b/crates/executor/src/class.rs index ec39d4ebb8..eed4e254c8 100644 --- a/crates/executor/src/class.rs +++ b/crates/executor/src/class.rs @@ -1,23 +1,23 @@ use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; +use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, +}; pub fn parse_deprecated_class_definition( - definition: Vec, + definition: SerializedOpaqueClassDefinition, ) -> anyhow::Result { - let definition = String::from_utf8(definition)?; - let class: starknet_api::deprecated_contract_class::ContractClass = - serde_json::from_str(&definition)?; + serde_json::from_slice(definition.as_bytes())?; Ok(starknet_api::contract_class::ContractClass::V0(class)) } pub fn parse_casm_definition( - casm_definition: Vec, + casm_definition: SerializedCasmDefinition, sierra_version: starknet_api::contract_class::SierraVersion, ) -> anyhow::Result { - let casm_definition = String::from_utf8(casm_definition)?; - - let class: CasmContractClass = serde_json::from_str(&casm_definition)?; + let class: CasmContractClass = serde_json::from_slice(casm_definition.as_bytes())?; Ok(starknet_api::contract_class::ContractClass::V1(( class, diff --git a/crates/executor/src/state_reader.rs b/crates/executor/src/state_reader.rs index 976d440621..6b06736096 100644 --- a/crates/executor/src/state_reader.rs +++ b/crates/executor/src/state_reader.rs @@ -4,6 +4,7 @@ use blockifier::execution::contract_class::RunnableCompiledClass; use blockifier::state::errors::StateError; use blockifier::state::state_api::StateReader; use cached::Cached; +use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use pathfinder_common::{BlockNumber, ClassHash, StorageAddress, StorageValue}; use pathfinder_crypto::Felt; use starknet_api::contract_class::compiled_class_hash::{HashVersion, HashableCompiledClass}; @@ -121,7 +122,7 @@ impl PathfinderStateReader { Some(casm_definition) => { // There's a CASM definition in storage, so this is a Sierra class. Extract // class version from program. - let sierra_version = self.sierra_version_from_class(&class_definition)?; + let sierra_version = self.try_sierra_version_from_class(&class_definition)?; #[cfg(feature = "cairo-native")] let runnable_class = if self.native_execution_force_use_for_incompatible_classes @@ -160,15 +161,16 @@ impl PathfinderStateReader { } None => { // No CASM definition means this is a legacy Cairo 0 class. - let class_definition = String::from_utf8(class_definition).map_err(|error| { - StateError::StateReadError(format!( - "Class definition is not valid UTF-8: {error}" - )) - })?; + let class_definition = + std::str::from_utf8(class_definition.as_bytes()).map_err(|error| { + StateError::StateReadError(format!( + "Class definition is not valid UTF-8: {error}" + )) + })?; let class = blockifier::execution::contract_class::CompiledClassV0::try_from_json_string( - &class_definition, + class_definition, ) .map_err(StateError::ProgramError)?; @@ -177,14 +179,14 @@ impl PathfinderStateReader { } } - fn sierra_version_from_class( + fn try_sierra_version_from_class( &self, - class_definition: &[u8], + class_definition: &SerializedOpaqueClassDefinition, ) -> Result { use cairo_vm::types::errors::program_errors::ProgramError; let sierra_class: pathfinder_common::class_definition::Sierra<'_> = - serde_json::from_slice(class_definition) + serde_json::from_slice(class_definition.as_bytes()) .map_err(|error| StateError::ProgramError(ProgramError::Parse(error)))?; SierraVersion::extract_from_program(&sierra_class.sierra_program).map_err(Into::into) } @@ -192,13 +194,13 @@ impl PathfinderStateReader { fn sierra_class_as_casm( sierra_version: SierraVersion, - casm_definition: Vec, + casm_definition: pathfinder_common::class_definition::SerializedCasmDefinition, ) -> Result { - let casm_definition = String::from_utf8(casm_definition).map_err(|error| { + let casm_definition = std::str::from_utf8(casm_definition.as_bytes()).map_err(|error| { StateError::StateReadError(format!("CASM definition is not valid UTF-8: {error}")) })?; let casm_class = blockifier::execution::contract_class::CompiledClassV1::try_from_json_string( - &casm_definition, + casm_definition, sierra_version, ) .map_err(StateError::ProgramError)?; diff --git a/crates/executor/src/state_reader/native.rs b/crates/executor/src/state_reader/native.rs index 1dcf273aea..91f16f4296 100644 --- a/crates/executor/src/state_reader/native.rs +++ b/crates/executor/src/state_reader/native.rs @@ -6,6 +6,10 @@ use blockifier::state::errors::StateError; use cached::{Cached, SizedCache}; use cairo_native::executor::AotContractExecutor; use cairo_vm::types::errors::program_errors::ProgramError; +use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, +}; use pathfinder_common::ClassHash; use starknet_api::contract_class::SierraVersion; use tokio_util::sync::CancellationToken; @@ -13,8 +17,8 @@ use tokio_util::sync::CancellationToken; struct CompilerInput { class_hash: ClassHash, sierra_version: SierraVersion, - class_definition: Vec, - casm_definition: Vec, + class_definition: SerializedOpaqueClassDefinition, + casm_definition: SerializedCasmDefinition, } enum CacheItem { @@ -53,8 +57,8 @@ impl NativeClassCache { &self, class_hash: ClassHash, sierra_version: SierraVersion, - class_definition: Vec, - casm_definition: Vec, + class_definition: SerializedOpaqueClassDefinition, + casm_definition: SerializedCasmDefinition, ) -> Option { let mut locked = self.cache.lock().unwrap(); @@ -137,8 +141,9 @@ fn sierra_class_as_native( input: CompilerInput, optimization_level: cairo_native::OptLevel, ) -> Result { - let mut sierra_definition: serde_json::Value = serde_json::from_slice(&input.class_definition) - .map_err(|e| StateError::ProgramError(ProgramError::Parse(e)))?; + let mut sierra_definition: serde_json::Value = + serde_json::from_slice(input.class_definition.as_bytes()) + .map_err(|e| StateError::ProgramError(ProgramError::Parse(e)))?; let sierra_abi_str = sierra_definition .get("abi") .ok_or_else(|| StateError::StateReadError("Sierra ABI is missing".to_owned()))? @@ -183,12 +188,13 @@ fn sierra_class_as_native( .map_err(|e| StateError::StateReadError(format!("Error compiling native class: {e:?}")))? .map_err(|e| StateError::StateReadError(format!("Error compiling native class: {e}")))?; - let casm_definition = String::from_utf8(input.casm_definition).map_err(|error| { - StateError::StateReadError(format!("Class definition is not valid UTF-8: {error}")) - })?; + let casm_definition = + std::str::from_utf8(input.casm_definition.as_bytes()).map_err(|error| { + StateError::StateReadError(format!("Class definition is not valid UTF-8: {error}")) + })?; let casm_class = blockifier::execution::contract_class::CompiledClassV1::try_from_json_string( - &casm_definition, + casm_definition, input.sierra_version, ) .map_err(StateError::ProgramError)?; diff --git a/crates/executor/src/state_reader/storage_adapter.rs b/crates/executor/src/state_reader/storage_adapter.rs index 61fbb94988..9c6d19c388 100644 --- a/crates/executor/src/state_reader/storage_adapter.rs +++ b/crates/executor/src/state_reader/storage_adapter.rs @@ -1,5 +1,9 @@ use blockifier::blockifier::config::TransactionExecutorConfig; use blockifier::state::errors::StateError; +use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, +}; use pathfinder_common::{ BlockHash, BlockId, @@ -16,15 +20,19 @@ pub mod concurrent; pub mod rc; // Keep clippy happy -type ClassDefinitionAtWithBlockNumber = Option<(BlockNumber, Vec)>; -type ClassDefinitionWithBlockNumber = Option<(Option, Vec)>; +type ClassDefinitionAtWithBlockNumber = Option<(BlockNumber, SerializedOpaqueClassDefinition)>; +type ClassDefinitionWithBlockNumber = + Option<(Option, SerializedOpaqueClassDefinition)>; pub trait StorageAdapter { fn transaction_executor_config(&self) -> TransactionExecutorConfig; fn block_hash(&self, block: BlockId) -> anyhow::Result>; - fn casm_definition(&self, class_hash: ClassHash) -> Result>, StateError>; + fn casm_definition( + &self, + class_hash: ClassHash, + ) -> Result, StateError>; fn class_definition_with_block_number( &self, @@ -35,7 +43,7 @@ pub trait StorageAdapter { &self, block_id: BlockId, class_hash: ClassHash, - ) -> Result>, StateError>; + ) -> Result, StateError>; fn class_definition_at_with_block_number( &self, diff --git a/crates/executor/src/state_reader/storage_adapter/concurrent.rs b/crates/executor/src/state_reader/storage_adapter/concurrent.rs index 4cf3e49291..d9321d442f 100644 --- a/crates/executor/src/state_reader/storage_adapter/concurrent.rs +++ b/crates/executor/src/state_reader/storage_adapter/concurrent.rs @@ -2,10 +2,10 @@ use std::sync::mpsc::{self, Receiver, Sender, SyncSender}; use blockifier::blockifier::config::{ConcurrencyConfig, TransactionExecutorConfig}; use blockifier::state::errors::StateError; +use pathfinder_common::class_definition::SerializedCasmDefinition; use pathfinder_common::{ BlockHash, BlockId, - BlockNumber, CasmHash, ClassHash, ContractAddress, @@ -32,7 +32,10 @@ pub struct ConcurrentStorageAdapter { enum Command { BlockHash(BlockId, SyncSender>>), - CasmDefinition(ClassHash, SyncSender>, StateError>>), + CasmDefinition( + ClassHash, + SyncSender, StateError>>, + ), ClassDefinitionWithBlockNumber( ClassHash, SyncSender>, @@ -40,7 +43,7 @@ enum Command { CasmDefinitionAt( BlockId, ClassHash, - SyncSender>, StateError>>, + SyncSender, StateError>>, ), ClassDefinitionAtWithBlockNumber( BlockId, @@ -126,7 +129,10 @@ impl StorageAdapter for ConcurrentStorageAdapter { rx.recv().expect("Channel not to be closed") } - fn casm_definition(&self, class_hash: ClassHash) -> Result>, StateError> { + fn casm_definition( + &self, + class_hash: ClassHash, + ) -> Result, StateError> { if let Some(casm_def) = decided::casm_definition(&self.decided_blocks, class_hash) { return Ok(Some(casm_def)); } @@ -142,7 +148,7 @@ impl StorageAdapter for ConcurrentStorageAdapter { fn class_definition_with_block_number( &self, class_hash: ClassHash, - ) -> Result, Vec)>, StateError> { + ) -> Result { if let Some(block_number_and_class_def) = decided::class_definition_with_block_number(&self.decided_blocks, class_hash) { @@ -161,7 +167,7 @@ impl StorageAdapter for ConcurrentStorageAdapter { &self, block_id: BlockId, class_hash: ClassHash, - ) -> Result>, StateError> { + ) -> Result, StateError> { if let Some(casm_def) = decided::casm_definition_at(&self.decided_blocks, block_id, class_hash) { @@ -405,6 +411,10 @@ mod decided { use std::collections::BTreeMap; use std::sync::RwLockReadGuard; + use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, + }; use pathfinder_common::state_update::ContractUpdate; use pathfinder_common::{ BlockId, @@ -460,7 +470,7 @@ mod decided { pub fn casm_definition( decided_blocks: &DecidedBlocks, class_hash: ClassHash, - ) -> Option> { + ) -> Option { let decided_blocks = decided_blocks.read().unwrap(); find_class(decided_blocks.values(), class_hash).map(|(_, c)| c.casm_def.clone()) } @@ -468,17 +478,17 @@ mod decided { pub fn class_definition_with_block_number( decided_blocks: &DecidedBlocks, class_hash: ClassHash, - ) -> Option<(Option, Vec)> { + ) -> Option<(Option, SerializedOpaqueClassDefinition)> { let decided_blocks = decided_blocks.read().unwrap(); find_class(decided_blocks.values(), class_hash) - .map(|(b, c)| (Some(b), c.sierra_def.clone())) + .map(|(b, c)| (Some(b), c.sierra_def.clone().into())) } pub fn casm_definition_at( decided_blocks: &DecidedBlocks, block_id: BlockId, class_hash: ClassHash, - ) -> Option> { + ) -> Option { let guard = decided_blocks.read().unwrap(); let it = rev_blocks_by_id(&guard, block_id); find_class(it, class_hash).map(|(_, c)| c.casm_def.clone()) @@ -491,7 +501,7 @@ mod decided { ) -> ClassDefinitionAtWithBlockNumber { let guard = decided_blocks.read().unwrap(); let it = rev_blocks_by_id(&guard, block_id); - find_class(it, class_hash).map(|(b, c)| (b, c.sierra_def.clone())) + find_class(it, class_hash).map(|(b, c)| (b, c.sierra_def.clone().into())) } pub fn storage_value( @@ -563,6 +573,10 @@ mod decided { use std::collections::{BTreeMap, HashMap}; use std::sync::{Arc, RwLock}; + use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::state_update::{ ContractClassUpdate, ContractUpdate, @@ -591,8 +605,8 @@ mod decided { declared_classes: vec![DeclaredClass { sierra_hash: SierraHash::ZERO, casm_hash_v2: CasmHash::ZERO, - sierra_def: vec![0], - casm_def: vec![1], + sierra_def: SerializedSierraDefinition::from_slice(&[0]), + casm_def: SerializedCasmDefinition::from_slice(&[1]), }], state_update: StateUpdateData { contract_updates: HashMap::from([ @@ -664,7 +678,10 @@ mod decided { #[test] fn success() { - assert_eq!(casm_definition(&two(), ClassHash::ZERO), Some(vec![1])); + assert_eq!( + casm_definition(&two(), ClassHash::ZERO), + Some(SerializedCasmDefinition::from_slice(&[1])) + ); } } @@ -685,7 +702,10 @@ mod decided { fn success() { assert_eq!( class_definition_with_block_number(&two(), ClassHash::ZERO), - Some((Some(BlockNumber::GENESIS), vec![0])) + Some(( + Some(BlockNumber::GENESIS), + SerializedOpaqueClassDefinition::from_slice(&[0]) + )) ); } } @@ -712,7 +732,7 @@ mod decided { BlockId::Number(BlockNumber::GENESIS), ClassHash::ZERO, ), - Some(vec![1]) + Some(SerializedCasmDefinition::from_slice(&[1])) ); } @@ -726,7 +746,7 @@ mod decided { BlockId::Number(BlockNumber::GENESIS + 1), ClassHash::ZERO, ), - Some(vec![1]) + Some(SerializedCasmDefinition::from_slice(&[1])) ); } @@ -754,7 +774,7 @@ mod decided { fn latest_instant_match() { assert_eq!( casm_definition_at(&one(), BlockId::Latest, ClassHash::ZERO,), - Some(vec![1]) + Some(SerializedCasmDefinition::from_slice(&[1])) ); } @@ -762,7 +782,7 @@ mod decided { fn latest_skips_blocks_that_dont_match() { assert_eq!( casm_definition_at(&two(), BlockId::Latest, ClassHash::ZERO,), - Some(vec![1]) + Some(SerializedCasmDefinition::from_slice(&[1])) ); } } @@ -789,7 +809,10 @@ mod decided { BlockId::Number(BlockNumber::GENESIS), ClassHash::ZERO, ), - Some((BlockNumber::GENESIS, vec![0])) + Some(( + BlockNumber::GENESIS, + SerializedOpaqueClassDefinition::from_slice(&[0]) + )) ); } @@ -803,7 +826,10 @@ mod decided { BlockId::Number(BlockNumber::GENESIS + 1), ClassHash::ZERO, ), - Some((BlockNumber::GENESIS, vec![0])) + Some(( + BlockNumber::GENESIS, + SerializedOpaqueClassDefinition::from_slice(&[0]) + )) ); } @@ -831,7 +857,10 @@ mod decided { fn latest_instant_match() { assert_eq!( class_definition_at_with_block_number(&one(), BlockId::Latest, ClassHash::ZERO), - Some((BlockNumber::GENESIS, vec![0])) + Some(( + BlockNumber::GENESIS, + SerializedOpaqueClassDefinition::from_slice(&[0]) + )) ); } @@ -839,7 +868,10 @@ mod decided { fn latest_skips_blocks_that_dont_match() { assert_eq!( class_definition_at_with_block_number(&two(), BlockId::Latest, ClassHash::ZERO), - Some((BlockNumber::GENESIS, vec![0])) + Some(( + BlockNumber::GENESIS, + SerializedOpaqueClassDefinition::from_slice(&[0]) + )) ); } } diff --git a/crates/executor/src/state_reader/storage_adapter/rc.rs b/crates/executor/src/state_reader/storage_adapter/rc.rs index fb97925c26..e929e4070f 100644 --- a/crates/executor/src/state_reader/storage_adapter/rc.rs +++ b/crates/executor/src/state_reader/storage_adapter/rc.rs @@ -2,10 +2,16 @@ use std::rc::Rc; use blockifier::blockifier::config::TransactionExecutorConfig; use blockifier::state::errors::StateError; +use pathfinder_common::class_definition::SerializedCasmDefinition; use pathfinder_common::{BlockHash, BlockId}; use pathfinder_storage::Transaction; -use crate::state_reader::storage_adapter::{map_anyhow_to_state_err, StorageAdapter}; +use crate::state_reader::storage_adapter::{ + map_anyhow_to_state_err, + ClassDefinitionAtWithBlockNumber, + ClassDefinitionWithBlockNumber, + StorageAdapter, +}; #[derive(Clone)] pub struct RcStorageAdapter<'tx> { @@ -32,7 +38,7 @@ impl<'tx> StorageAdapter for RcStorageAdapter<'tx> { fn casm_definition( &self, class_hash: pathfinder_common::ClassHash, - ) -> Result>, StateError> { + ) -> Result, StateError> { self.db_tx .casm_definition(class_hash) .map_err(map_anyhow_to_state_err) @@ -41,7 +47,7 @@ impl<'tx> StorageAdapter for RcStorageAdapter<'tx> { fn class_definition_with_block_number( &self, class_hash: pathfinder_common::ClassHash, - ) -> Result, Vec)>, StateError> { + ) -> Result { self.db_tx .class_definition_with_block_number(class_hash) .map_err(map_anyhow_to_state_err) @@ -51,7 +57,7 @@ impl<'tx> StorageAdapter for RcStorageAdapter<'tx> { &self, block_id: BlockId, class_hash: pathfinder_common::ClassHash, - ) -> Result>, StateError> { + ) -> Result, StateError> { self.db_tx .casm_definition_at(block_id, class_hash) .map_err(map_anyhow_to_state_err) @@ -61,7 +67,7 @@ impl<'tx> StorageAdapter for RcStorageAdapter<'tx> { &self, block_id: BlockId, class_hash: pathfinder_common::ClassHash, - ) -> Result)>, StateError> { + ) -> Result { self.db_tx .class_definition_at_with_block_number(block_id, class_hash) .map_err(map_anyhow_to_state_err) diff --git a/crates/feeder-gateway/src/main.rs b/crates/feeder-gateway/src/main.rs index 26f19f9845..8e8aa9255c 100644 --- a/crates/feeder-gateway/src/main.rs +++ b/crates/feeder-gateway/src/main.rs @@ -37,6 +37,10 @@ use pathfinder_block_commitments::{ calculate_receipt_commitment, calculate_transaction_commitment, }; +use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, +}; use pathfinder_common::integration_testing::debug_create_port_marker_file; use pathfinder_common::prelude::*; use pathfinder_common::state_update::ContractClassUpdate; @@ -505,7 +509,7 @@ async fn serve(cli: Cli, storage_rx: Receiver>) -> anyh match class { Ok(class) => { - let response = warp::http::Response::builder().header("content-type", "application/json").body(class).unwrap(); + let response = warp::http::Response::builder().header("content-type", "application/json").body(class.into_bytes()).unwrap(); Result::<_, Infallible>::Ok(response) }, Err(_) => { @@ -546,7 +550,7 @@ async fn serve(cli: Cli, storage_rx: Receiver>) -> anyh match compiled_class { Ok(compiled_class) => { - let response = warp::http::Response::builder().header("content-type", "application/json").body(compiled_class).unwrap(); + let response = warp::http::Response::builder().header("content-type", "application/json").body(compiled_class.into_bytes()).unwrap(); Result::<_, Infallible>::Ok(response) }, Err(_) => { @@ -823,7 +827,7 @@ fn resolve_state_update( fn resolve_class( tx: &pathfinder_storage::Transaction<'_>, class_hash: ClassHash, -) -> anyhow::Result> { +) -> anyhow::Result { let definition = tx .class_definition(class_hash) .context("Reading class definition from database")? @@ -836,7 +840,7 @@ fn resolve_class( fn resolve_compiled_class( tx: &pathfinder_storage::Transaction<'_>, compiled_class_hash: ClassHash, -) -> anyhow::Result> { +) -> anyhow::Result { let definition = tx .casm_definition(compiled_class_hash) .context("Reading compiled class definition from database")? diff --git a/crates/gateway-client/src/lib.rs b/crates/gateway-client/src/lib.rs index e334785b1b..da01846337 100644 --- a/crates/gateway-client/src/lib.rs +++ b/crates/gateway-client/src/lib.rs @@ -5,6 +5,10 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; use anyhow::Context; +use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, +}; use pathfinder_common::prelude::*; use reqwest::Url; use starknet_gateway_types::error::SequencerError; @@ -72,7 +76,7 @@ pub trait GatewayApi: Sync { &self, class_hash: ClassHash, block: BlockId, - ) -> Result { + ) -> Result { unimplemented!(); } @@ -80,7 +84,7 @@ pub trait GatewayApi: Sync { &self, class_hash: ClassHash, block: BlockId, - ) -> Result { + ) -> Result { unimplemented!(); } @@ -175,7 +179,7 @@ impl GatewayApi for Arc { &self, class_hash: ClassHash, block: BlockId, - ) -> Result { + ) -> Result { self.as_ref().class_by_hash(class_hash, block).await } @@ -183,7 +187,7 @@ impl GatewayApi for Arc { &self, class_hash: ClassHash, block: BlockId, - ) -> Result { + ) -> Result { self.as_ref().casm_by_hash(class_hash, block).await } @@ -556,14 +560,16 @@ impl GatewayApi for Client { &self, class_hash: ClassHash, block: BlockId, - ) -> Result { - self.feeder_gateway_request() + ) -> Result { + let bytes = self + .feeder_gateway_request() .get_class_by_hash() .class_hash(class_hash) .block(block) .retry(self.retry) .get_as_bytes() - .await + .await?; + Ok(SerializedOpaqueClassDefinition::from_bytes(bytes.to_vec())) } /// Gets CASM for a particular class hash. @@ -572,14 +578,16 @@ impl GatewayApi for Client { &self, class_hash: ClassHash, block: BlockId, - ) -> Result { - self.feeder_gateway_request() + ) -> Result { + let bytes = self + .feeder_gateway_request() .get_compiled_class_by_class_hash() .class_hash(class_hash) .block(block) .retry(self.retry) .get_as_bytes() - .await + .await?; + Ok(SerializedCasmDefinition::from_bytes(bytes.to_vec())) } /// Gets transaction status by transaction hash. diff --git a/crates/p2p/src/sync/client/conv.rs b/crates/p2p/src/sync/client/conv.rs index eda381860c..a5ea6d2a9f 100644 --- a/crates/p2p/src/sync/client/conv.rs +++ b/crates/p2p/src/sync/client/conv.rs @@ -25,6 +25,8 @@ use pathfinder_common::class_definition::{ Cairo, SelectorAndFunctionIndex, SelectorAndOffset, + SerializedCairoDefinition, + SerializedSierraDefinition, Sierra, }; use pathfinder_common::event::Event; @@ -885,10 +887,7 @@ impl TryFromDto for L1DataAvailabilit } } -#[derive(Debug)] -pub struct CairoDefinition(pub Vec); - -impl TryFromDto for CairoDefinition { +impl TryFromDto for SerializedCairoDefinition { fn try_from_dto(dto: p2p_proto::class::Cairo0Class) -> anyhow::Result { #[derive(Debug, Serialize)] struct SelectorAndOffset { @@ -955,13 +954,11 @@ impl TryFromDto for CairoDefinition { }; let class_def = serde_json::to_vec(&class_def).context("serialize cairo class definition")?; - Ok(Self(class_def)) + Ok(Self::from_bytes(class_def)) } } -pub struct SierraDefinition(pub Vec); - -impl TryFromDto for SierraDefinition { +impl TryFromDto for SerializedSierraDefinition { fn try_from_dto(dto: p2p_proto::class::Cairo1Class) -> anyhow::Result { #[derive(Debug, Serialize)] pub struct SelectorAndFunctionIndex { @@ -1020,7 +1017,7 @@ impl TryFromDto for SierraDefinition { let sierra = serde_json::to_vec(&sierra).context("serialize sierra class definition")?; - Ok(Self(sierra)) + Ok(Self::from_bytes(sierra)) } } diff --git a/crates/p2p/src/sync/client/peer_agnostic.rs b/crates/p2p/src/sync/client/peer_agnostic.rs index 060d2ec347..8a2023b998 100644 --- a/crates/p2p/src/sync/client/peer_agnostic.rs +++ b/crates/p2p/src/sync/client/peer_agnostic.rs @@ -26,6 +26,7 @@ use p2p_proto::sync::transaction::{ TransactionsRequest, TransactionsResponse, }; +use pathfinder_common::class_definition::{SerializedCairoDefinition, SerializedSierraDefinition}; use pathfinder_common::event::Event; use pathfinder_common::prelude::*; use pathfinder_common::state_update::{ContractClassUpdate, StateUpdateData}; @@ -50,7 +51,7 @@ use traits::{ }; use crate::peer_data::PeerData; -use crate::sync::client::conv::{CairoDefinition, FromDto, SierraDefinition, TryFromDto}; +use crate::sync::client::conv::{FromDto, TryFromDto}; use crate::sync::client::types::{ ClassDefinition, ClassDefinitionsError, @@ -461,12 +462,12 @@ impl BlockClient for Client { async fn class_definitions_for_block( self, - block: BlockNumber, + block_number: BlockNumber, declared_classes_count: u64, ) -> Result)>, ClassDefinitionsError> { let request = ClassesRequest { iteration: Iteration { - start: BlockNumberOrHash::Number(block.get()), + start: BlockNumberOrHash::Number(block_number.get()), direction: Direction::Forward, limit: 1, step: 1.into(), @@ -495,11 +496,11 @@ impl BlockClient for Client { domain: _, class_hash, })) => { - let definition = CairoDefinition::try_from_dto(class) + let definition = SerializedCairoDefinition::try_from_dto(class) .map_err(|_| ClassDefinitionsError::CairoDefinitionError(peer))?; class_definitions.push(ClassDefinition::Cairo { - block_number: block, - definition: definition.0, + block_number, + definition, hash: ClassHash(class_hash.0), }); } @@ -508,11 +509,11 @@ impl BlockClient for Client { domain: _, class_hash, })) => { - let definition = SierraDefinition::try_from_dto(class) + let sierra_definition = SerializedSierraDefinition::try_from_dto(class) .map_err(|_| ClassDefinitionsError::SierraDefinitionError(peer))?; class_definitions.push(ClassDefinition::Sierra { - block_number: block, - sierra_definition: definition.0, + block_number, + sierra_definition, hash: SierraHash(class_hash.0), }); } @@ -1261,7 +1262,7 @@ mod class_definition_stream { domain: _, class_hash, })) => { - let Ok(CairoDefinition(definition)) = CairoDefinition::try_from_dto(class) else { + let Ok(definition) = SerializedCairoDefinition::try_from_dto(class) else { // TODO punish the peer tracing::debug!(%peer, "Cairo definition failed to parse"); return None; @@ -1278,7 +1279,7 @@ mod class_definition_stream { domain: _, class_hash, })) => { - let Ok(SierraDefinition(definition)) = SierraDefinition::try_from_dto(class) else { + let Ok(sierra_definition) = SerializedSierraDefinition::try_from_dto(class) else { // TODO punish the peer tracing::debug!(%peer, "Sierra definition failed to parse"); return None; @@ -1286,7 +1287,7 @@ mod class_definition_stream { Some(ClassDefinition::Sierra { block_number, - sierra_definition: definition, + sierra_definition, hash: SierraHash(class_hash.0), }) } diff --git a/crates/p2p/src/sync/client/peer_agnostic/fixtures.rs b/crates/p2p/src/sync/client/peer_agnostic/fixtures.rs index f70148f8a6..df08ff8798 100644 --- a/crates/p2p/src/sync/client/peer_agnostic/fixtures.rs +++ b/crates/p2p/src/sync/client/peer_agnostic/fixtures.rs @@ -17,6 +17,7 @@ use p2p_proto::sync::state::{ StateDiffsResponse, }; use p2p_proto::sync::transaction::{TransactionWithReceipt, TransactionsResponse}; +use pathfinder_common::class_definition::{SerializedCairoDefinition, SerializedSierraDefinition}; use pathfinder_common::event::Event; use pathfinder_common::prelude::*; use pathfinder_common::state_update::{ContractClassUpdate, ContractUpdate, StateUpdateData}; @@ -33,7 +34,7 @@ use rand::seq::SliceRandom; use tokio::sync::Mutex; use super::ClassDefinition; -use crate::sync::client::conv::{CairoDefinition, SierraDefinition, ToDto, TryFromDto}; +use crate::sync::client::conv::{ToDto, TryFromDto}; use crate::sync::client::peer_agnostic::Receipt; #[derive(Clone, PartialEq, TaggedDebug)] @@ -352,7 +353,7 @@ pub fn class(tag: i32, block_number: u64) -> ClassDefinition { }) => { Tagged::get(format!("class {tag}"), || ClassDefinition::Cairo { block_number, - definition: CairoDefinition::try_from_dto(class).unwrap().0, + definition: SerializedCairoDefinition::try_from_dto(class).unwrap(), hash: ClassHash(class_hash.0), }) .unwrap() @@ -365,7 +366,7 @@ pub fn class(tag: i32, block_number: u64) -> ClassDefinition { }) => { Tagged::get(format!("class {tag}"), || ClassDefinition::Sierra { block_number, - sierra_definition: SierraDefinition::try_from_dto(class).unwrap().0, + sierra_definition: SerializedSierraDefinition::try_from_dto(class).unwrap(), hash: SierraHash(class_hash.0), }) .unwrap() diff --git a/crates/p2p/src/sync/client/types.rs b/crates/p2p/src/sync/client/types.rs index 0aea681134..2a4b15c022 100644 --- a/crates/p2p/src/sync/client/types.rs +++ b/crates/p2p/src/sync/client/types.rs @@ -1,6 +1,11 @@ use anyhow::Context; use fake::Dummy; use libp2p::PeerId; +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::event::Event; use pathfinder_common::prelude::*; use pathfinder_common::receipt::{ExecutionResources, ExecutionStatus, L2ToL1Message}; @@ -14,24 +19,24 @@ use crate::sync::client::conv::TryFromDto; pub enum ClassDefinition { Cairo { block_number: BlockNumber, - definition: Vec, + definition: SerializedCairoDefinition, hash: ClassHash, }, Sierra { block_number: BlockNumber, - sierra_definition: Vec, + sierra_definition: SerializedSierraDefinition, hash: SierraHash, }, } impl ClassDefinition { /// Return Cairo or Sierra class definition depending on the variant. - pub fn class_definition(&self) -> Vec { + pub fn class_definition(&self) -> SerializedOpaqueClassDefinition { match self { - Self::Cairo { definition, .. } => definition.clone(), + Self::Cairo { definition, .. } => definition.clone().into(), Self::Sierra { sierra_definition, .. - } => sierra_definition.clone(), + } => sierra_definition.clone().into(), } } } diff --git a/crates/pathfinder/examples/compute_class_hash.rs b/crates/pathfinder/examples/compute_class_hash.rs index 18d2948d31..6d3cb5eda5 100644 --- a/crates/pathfinder/examples/compute_class_hash.rs +++ b/crates/pathfinder/examples/compute_class_hash.rs @@ -10,11 +10,10 @@ fn main() -> Result<(), Box> { } let mut s = Vec::new(); std::io::stdin().read_to_end(&mut s).unwrap(); - let s = s; - let class_hash = match pathfinder_class_hash::compute_class_hash(&s)? { - pathfinder_class_hash::ComputedClassHash::Cairo(h) => h.0, - pathfinder_class_hash::ComputedClassHash::Sierra(h) => h.0, - }; - println!("{class_hash:x}"); + let definition = + pathfinder_common::class_definition::SerializedOpaqueClassDefinition::from_bytes(s); + let (hash, _) = pathfinder_class_hash::compute_class_hash(definition)?; + let class_hash = hash.hash(); + println!("{:x}", class_hash.0); Ok(()) } diff --git a/crates/pathfinder/examples/recompute_casm_class_hashes.rs b/crates/pathfinder/examples/recompute_casm_class_hashes.rs index 7810482bef..3836d1731b 100644 --- a/crates/pathfinder/examples/recompute_casm_class_hashes.rs +++ b/crates/pathfinder/examples/recompute_casm_class_hashes.rs @@ -44,7 +44,12 @@ fn main() -> Result<(), Box> { let definition = zstd::decode_all(definition.as_slice()) .map_err(|e| rusqlite::types::FromSqlError::Other(e.into())) .unwrap(); - let computed_hash = pathfinder_compiler::casm_class_hash_v2(&definition).unwrap(); + let computed_hash = pathfinder_compiler::casm_class_hash_v2( + &pathfinder_common::class_definition::SerializedCasmDefinition::from_bytes( + definition, + ), + ) + .unwrap(); println!( "Computed CASM hash for class {:?}: {:x?}", class_hash, computed_hash.0 diff --git a/crates/pathfinder/src/bin/pathfinder/main.rs b/crates/pathfinder/src/bin/pathfinder/main.rs index 8a3261a567..2680f30bf8 100644 --- a/crates/pathfinder/src/bin/pathfinder/main.rs +++ b/crates/pathfinder/src/bin/pathfinder/main.rs @@ -552,10 +552,12 @@ fn compile_main(config: CompileConfig) -> anyhow::Result<()> { use std::io::{Read, Write}; const SIERRA_DEFINITION_SIZE_ESTIMATE: usize = 400 * 1024; // 400 KiB - let mut sierra_definition = Vec::with_capacity(SIERRA_DEFINITION_SIZE_ESTIMATE); + let mut sierra_bytes = Vec::with_capacity(SIERRA_DEFINITION_SIZE_ESTIMATE); std::io::stdin() - .read_to_end(&mut sierra_definition) + .read_to_end(&mut sierra_bytes) .context("reading Sierra from stdin")?; + let sierra_definition = + pathfinder_common::class_definition::SerializedSierraDefinition::from_bytes(sierra_bytes); let casm = pathfinder_compiler::compile_sierra_to_casm_impl( &sierra_definition, @@ -564,7 +566,7 @@ fn compile_main(config: CompileConfig) -> anyhow::Result<()> { .context("compiling Sierra to CASM")?; std::io::stdout() - .write_all(&casm) + .write_all(casm.as_bytes()) .context("writing CASM to stdout") } diff --git a/crates/pathfinder/src/devnet/class.rs b/crates/pathfinder/src/devnet/class.rs index b83f367edf..1e80ae5ce7 100644 --- a/crates/pathfinder/src/devnet/class.rs +++ b/crates/pathfinder/src/devnet/class.rs @@ -1,6 +1,10 @@ use pathfinder_class_hash::compute_sierra_class_hash; use pathfinder_class_hash::json::SierraContractDefinition; -use pathfinder_common::class_definition::Sierra; +use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedSierraDefinition, + Sierra, +}; use pathfinder_common::{state_update, CasmHash, SierraHash}; use pathfinder_compiler::{ casm_class_hash_v2, @@ -57,11 +61,11 @@ pub struct PrepocessedSierra { // Deserialized into a format compatible with p2p, and hence the validator (for execution) pub cairo1_class_p2p: p2p_proto::class::Cairo1Class, // Re-serialized into a storage-compatible format - pub sierra_class_ser: Vec, + pub sierra_class_ser: SerializedSierraDefinition, // Casm hash v2 pub casm_hash_v2: CasmHash, // Casm - compiled from sierra - pub casm: Vec, + pub casm: SerializedCasmDefinition, } /// Preprocess a Sierra class definition. Class hash is computed if not @@ -101,7 +105,8 @@ pub fn preprocess_sierra( }; // Re-serialize into a storage-compatible format - let sierra_class_ser = serde_json::to_vec(&sierra_class_def).unwrap(); + let sierra_class_ser = + SerializedSierraDefinition::from_bytes(serde_json::to_vec(&sierra_class_def).unwrap()); let cairo1_class_p2p = sierra_def_to_p2p_cairo1(&sierra_class_def); let casm = compile_sierra_to_casm_deser( sierra_class_def, diff --git a/crates/pathfinder/src/p2p_network/sync/sync_handlers.rs b/crates/pathfinder/src/p2p_network/sync/sync_handlers.rs index 7665425d3b..48eca3a189 100644 --- a/crates/pathfinder/src/p2p_network/sync/sync_handlers.rs +++ b/crates/pathfinder/src/p2p_network/sync/sync_handlers.rs @@ -19,7 +19,12 @@ use p2p_proto::sync::transaction::{ TransactionsRequest, TransactionsResponse, }; -use pathfinder_common::{class_definition, BlockHash, BlockNumber, SignedBlockHeader}; +use pathfinder_common::class_definition::{ + self, + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, +}; +use pathfinder_common::{BlockHash, BlockNumber, SignedBlockHeader}; use pathfinder_storage::{Storage, Transaction}; use tokio::sync::mpsc; @@ -146,8 +151,11 @@ fn get_header( #[derive(Debug, Clone)] enum ClassDefinition { - Cairo(Vec), - Sierra { sierra: Vec, _casm: Vec }, + Cairo(SerializedOpaqueClassDefinition), + Sierra { + sierra: SerializedOpaqueClassDefinition, + _casm: SerializedCasmDefinition, + }, } fn get_classes_for_block( @@ -166,7 +174,7 @@ fn get_classes_for_block( Ok(match casm_definition { Some(_casm) => ClassDefinition::Sierra { sierra: definition, - _casm: Vec::new(), // TODO casm + _casm: SerializedCasmDefinition::from_slice(&[]), // TODO casm }, None => ClassDefinition::Cairo(definition), }) @@ -184,7 +192,7 @@ fn get_classes_for_block( let class: Class = match class_definition { ClassDefinition::Cairo(definition) => { let cairo_class = - serde_json::from_slice::>(&definition)?; + serde_json::from_slice::>(definition.as_bytes())?; Class::Cairo0 { class: cairo_class.to_dto(), domain: 0, // TODO @@ -195,7 +203,8 @@ fn get_classes_for_block( sierra, _casm: _, // TODO } => { - let sierra_class = serde_json::from_slice::>(&sierra)?; + let sierra_class = + serde_json::from_slice::>(sierra.as_bytes())?; Class::Cairo1 { class: sierra_class.to_dto(), diff --git a/crates/pathfinder/src/p2p_network/sync/sync_handlers/tests.rs b/crates/pathfinder/src/p2p_network/sync/sync_handlers/tests.rs index 4d1e1adf2e..66b2b449bb 100644 --- a/crates/pathfinder/src/p2p_network/sync/sync_handlers/tests.rs +++ b/crates/pathfinder/src/p2p_network/sync/sync_handlers/tests.rs @@ -104,7 +104,7 @@ mod prop { use futures::channel::mpsc; use futures::StreamExt; - use p2p::sync::client::conv::{CairoDefinition, SierraDefinition, TryFromDto}; + use p2p::sync::client::conv::TryFromDto; use p2p::sync::client::types::Receipt; use p2p_proto::class::Class; use p2p_proto::common::BlockNumberOrHash; @@ -124,6 +124,10 @@ mod prop { TransactionsRequest, TransactionsResponse, }; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::event::Event; use pathfinder_common::prelude::*; use pathfinder_common::state_update::SystemContractUpdate; @@ -353,11 +357,10 @@ mod prop { responses.into_iter().for_each(|response| match response { ClassesResponse::Class(Class::Cairo0 { class, domain: _, class_hash: _ }) => { - actual_cairo.push(CairoDefinition::try_from_dto(class).unwrap().0); + actual_cairo.push(SerializedCairoDefinition::try_from_dto(class).unwrap()); }, ClassesResponse::Class(Class::Cairo1 { class, domain: _, class_hash: _ }) => { - let SierraDefinition(sierra) = SierraDefinition::try_from_dto(class).unwrap(); - actual_sierra.push(sierra); + actual_sierra.push(SerializedSierraDefinition::try_from_dto(class).unwrap()); }, _ => panic!("unexpected response"), }); diff --git a/crates/pathfinder/src/state/sync.rs b/crates/pathfinder/src/state/sync.rs index 6fa927a271..ed53f6ed90 100644 --- a/crates/pathfinder/src/state/sync.rs +++ b/crates/pathfinder/src/state/sync.rs @@ -11,6 +11,11 @@ use std::time::Duration; use anyhow::Context; use pathfinder_block_commitments as block_hash; +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::prelude::*; use pathfinder_common::state_update::StateUpdateData; use pathfinder_common::{ @@ -78,14 +83,14 @@ pub enum SyncEvent { Reorg(BlockNumber), /// A new unique L2 Cairo 0.x class was found. CairoClass { - definition: Vec, + definition: SerializedCairoDefinition, hash: ClassHash, }, /// A new unique L2 Cairo 1.x class was found. SierraClass { - sierra_definition: Vec, + sierra_definition: SerializedSierraDefinition, sierra_hash: SierraHash, - casm_definition: Vec, + casm_definition: SerializedCasmDefinition, casm_hash: CasmHash, casm_hash_v2: CasmHash, }, @@ -1629,6 +1634,12 @@ Blockchain history must include the reorg tail and its parent block to perform a mod tests { use std::sync::Arc; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::event::Event; use pathfinder_common::felt_bytes; use pathfinder_common::macro_prelude::*; @@ -2302,7 +2313,7 @@ mod tests { let (event_tx, event_rx) = tokio::sync::mpsc::channel(1); let class_hash = class_hash_bytes!(b"class hash"); - let expected_definition = b"cairo class definition".to_vec(); + let expected_definition = SerializedCairoDefinition::from_slice(b"cairo class definition"); event_tx .send(SyncEvent::CairoClass { @@ -2331,7 +2342,10 @@ mod tests { let tx = connection.transaction().unwrap(); let definition = tx.class_definition(class_hash).unwrap().unwrap(); - assert_eq!(definition, expected_definition); + assert_eq!( + definition, + SerializedOpaqueClassDefinition::from(expected_definition) + ); } #[tokio::test(flavor = "multi_thread")] @@ -2346,13 +2360,14 @@ mod tests { let (event_tx, event_rx) = tokio::sync::mpsc::channel(1); let class_hash = felt_bytes!(b"class hash"); - let expected_definition = b"sierra class definition".to_vec(); + let expected_definition = + SerializedSierraDefinition::from_slice(b"sierra class definition"); event_tx .send(SyncEvent::SierraClass { sierra_definition: expected_definition.clone(), sierra_hash: SierraHash(class_hash), - casm_definition: b"casm definition".to_vec(), + casm_definition: SerializedCasmDefinition::from_slice(b"casm definition"), casm_hash: casm_hash_bytes!(b"casm hash"), casm_hash_v2: casm_hash_bytes!(b"casm hash blake"), }) @@ -2378,7 +2393,10 @@ mod tests { let tx = connection.transaction().unwrap(); let definition = tx.class_definition(ClassHash(class_hash)).unwrap().unwrap(); - assert_eq!(definition, expected_definition); + assert_eq!( + definition, + SerializedOpaqueClassDefinition::from(expected_definition) + ); let casm_hash_v2 = tx.casm_hash_v2(ClassHash(class_hash)).unwrap().unwrap(); assert_eq!(casm_hash_v2, casm_hash_bytes!(b"casm hash blake")); @@ -3303,8 +3321,8 @@ Blockchain history must include the reorg tail and its parent block to perform a let reorg_regression_data = ReorgRegressionData::new(); let removed_class_hash = SierraHash(reorg_regression_data.removed_class_hash.0); - let sierra_definition = b"sierra definition".to_vec(); - let casm_definition = b"casm definition".to_vec(); + let sierra_definition = SerializedSierraDefinition::from_slice(b"sierra definition"); + let casm_definition = SerializedCasmDefinition::from_slice(b"casm definition"); let casm_hash = casm_hash_bytes!(b"casm hash"); let casm_hash_v2 = casm_hash_bytes!(b"casm hash blake"); diff --git a/crates/pathfinder/src/state/sync/class.rs b/crates/pathfinder/src/state/sync/class.rs index 47aa2c4911..f26b35a18e 100644 --- a/crates/pathfinder/src/state/sync/class.rs +++ b/crates/pathfinder/src/state/sync/class.rs @@ -1,16 +1,22 @@ use anyhow::Context; +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedClassDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::{CasmHash, ClassHash, SierraHash}; use starknet_gateway_client::{BlockId, GatewayApi}; pub enum DownloadedClass { Cairo { - definition: Vec, + definition: SerializedCairoDefinition, hash: ClassHash, }, Sierra { - sierra_definition: Vec, + sierra_definition: SerializedSierraDefinition, sierra_hash: SierraHash, - casm_definition: Vec, + casm_definition: SerializedCasmDefinition, casm_hash_v2: CasmHash, }, } @@ -27,27 +33,25 @@ pub async fn download_class( let definition = sequencer .class_by_hash(class_hash, BlockId::Latest) .await - .with_context(|| format!("Downloading class {}", class_hash.0))? - .to_vec(); + .with_context(|| format!("Downloading class {}", class_hash.0))?; let (tx, rx) = tokio::sync::oneshot::channel(); rayon::spawn(move || { - let computed_hash = compute_class_hash(&definition).context("Computing class hash"); - let _ = tx.send((computed_hash, definition)); + let result = compute_class_hash(definition).context("Computing class hash"); + let _ = tx.send(result); }); - let (hash, definition) = rx.await.context("Panic on rayon thread")?; - let hash = hash?; + let (hash, serialized_class) = rx.await.context("Panic on rayon thread")??; use pathfinder_class_hash::ComputedClassHash; - match hash { - ComputedClassHash::Cairo(hash) => { + match (hash, serialized_class) { + (ComputedClassHash::Cairo(hash), SerializedClassDefinition::Cairo(definition)) => { if class_hash != hash { tracing::warn!(expected=%class_hash, computed=%hash, "Cairo 0 class hash mismatch"); } Ok(DownloadedClass::Cairo { definition, hash }) } - ComputedClassHash::Sierra(hash) => { + (ComputedClassHash::Sierra(hash), SerializedClassDefinition::Sierra(sierra_definition)) => { anyhow::ensure!( class_hash == hash, "Class hash mismatch, {} instead of {}", @@ -66,24 +70,23 @@ pub async fn download_class( let (sierra_definition, casm_definition) = if fetch_casm_from_fgw { ( - definition, + sierra_definition, sequencer .casm_by_hash(class_hash, BlockId::Latest) .await - .with_context(|| format!("Downloading CASM {}", class_hash.0))? - .to_vec(), + .with_context(|| format!("Downloading CASM {}", class_hash.0))?, ) } else { let (send, recv) = tokio::sync::oneshot::channel(); rayon::spawn(move || { let _span = span.entered(); let compile_result = pathfinder_compiler::compile_sierra_to_casm( - &definition, + &sierra_definition, compiler_resource_limit, blockifier_libfuncs, ) .context("Compiling Sierra class"); - let _ = send.send((compile_result, definition)); + let _ = send.send((compile_result, sierra_definition)); }); let (casm_definition, sierra_definition) = recv.await.expect("Panic on rayon thread"); @@ -96,7 +99,6 @@ pub async fn download_class( .casm_by_hash(class_hash, BlockId::Latest) .await .with_context(|| format!("Downloading CASM {}", class_hash.0))? - .to_vec() } }; (sierra_definition, casm_definition) @@ -131,5 +133,6 @@ pub async fn download_class( casm_hash_v2, }) } + _ => unreachable!("compute_class_hash returns matching hash and class variants"), } } diff --git a/crates/pathfinder/src/state/sync/l2.rs b/crates/pathfinder/src/state/sync/l2.rs index 14b4f6bccf..27171f420e 100644 --- a/crates/pathfinder/src/state/sync/l2.rs +++ b/crates/pathfinder/src/state/sync/l2.rs @@ -1603,6 +1603,7 @@ mod tests { use std::sync::LazyLock; use assert_matches::assert_matches; + use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_common::Chain; @@ -1854,12 +1855,15 @@ mod tests { }) } - static CONTRACT0_DEF: LazyLock = - LazyLock::new(|| format!("{DEF0}0{DEF1}").into()); - static CONTRACT0_DEF_V2: LazyLock = - LazyLock::new(|| format!("{DEF0}0 v2{DEF1}").into()); - static CONTRACT1_DEF: LazyLock = - LazyLock::new(|| format!("{DEF0}1{DEF1}").into()); + static CONTRACT0_DEF: LazyLock = LazyLock::new(|| { + SerializedOpaqueClassDefinition::from_bytes(format!("{DEF0}0{DEF1}").into_bytes()) + }); + static CONTRACT0_DEF_V2: LazyLock = LazyLock::new(|| { + SerializedOpaqueClassDefinition::from_bytes(format!("{DEF0}0 v2{DEF1}").into_bytes()) + }); + static CONTRACT1_DEF: LazyLock = LazyLock::new(|| { + SerializedOpaqueClassDefinition::from_bytes(format!("{DEF0}1{DEF1}").into_bytes()) + }); static BLOCK0: LazyLock = LazyLock::new(|| reply::Block { block_hash: BLOCK0_HASH, @@ -2135,7 +2139,7 @@ mod tests { mock: &mut MockGatewayApi, seq: &mut mockall::Sequence, class_hash: ClassHash, - returned_result: Result, + returned_result: Result, ) { mock.expect_class_by_hash() .withf(move |x, _| x == &class_hash) @@ -2148,7 +2152,7 @@ mod tests { fn expect_class_by_hash_no_sequence( mock: &mut MockGatewayApi, class_hash: ClassHash, - returned_result: Result, + returned_result: Result, ) { mock.expect_class_by_hash() .withf(move |x, _| x == &class_hash) @@ -2159,7 +2163,7 @@ mod tests { fn expect_class_by_hash_no_sequence_at_most_once( mock: &mut MockGatewayApi, class_hash: ClassHash, - returned_result: Result, + returned_result: Result, ) { mock.expect_class_by_hash() .withf(move |x, _| x == &class_hash) diff --git a/crates/pathfinder/src/state/sync/repair.rs b/crates/pathfinder/src/state/sync/repair.rs index 0a5d8f2833..a7ae76fabf 100644 --- a/crates/pathfinder/src/state/sync/repair.rs +++ b/crates/pathfinder/src/state/sync/repair.rs @@ -153,6 +153,10 @@ fn store_repaired_class( mod tests { use std::collections::HashSet; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedOpaqueClassDefinition, + }; use pathfinder_common::state_update::StateUpdateData; use pathfinder_common::BlockNumber; use pathfinder_storage::StorageBuilder; @@ -192,7 +196,7 @@ mod tests { async fn cairo_repair() { let storage = storage(); let hash = ClassHash(pathfinder_crypto::Felt::from_hex_str("0xdeadbeef").unwrap()); - let definition = b"cairo class definition bytes".to_vec(); + let definition = SerializedCairoDefinition::from_slice(b"cairo class definition bytes"); insert_placeholder(&storage, hash); @@ -214,7 +218,10 @@ mod tests { let mut db = storage.connection().unwrap(); let tx = db.transaction().unwrap(); let stored = tx.class_definition(hash).unwrap(); - assert_eq!(stored, Some(definition)); + assert_eq!( + stored, + Some(SerializedOpaqueClassDefinition::from(definition)) + ); } /// Cairo 0 classes can have a mismatch between the declared hash and the @@ -225,7 +232,7 @@ mod tests { let storage = storage(); let declared = ClassHash(pathfinder_crypto::Felt::from_hex_str("0xaaaa").unwrap()); let computed = ClassHash(pathfinder_crypto::Felt::from_hex_str("0xbbbb").unwrap()); - let definition = b"cairo class definition bytes".to_vec(); + let definition = SerializedCairoDefinition::from_slice(b"cairo class definition bytes"); insert_placeholder(&storage, declared); @@ -248,7 +255,10 @@ mod tests { let mut db = storage.connection().unwrap(); let tx = db.transaction().unwrap(); // Stored under the declared hash, not the computed one. - assert_eq!(tx.class_definition(declared).unwrap(), Some(definition)); + assert_eq!( + tx.class_definition(declared).unwrap(), + Some(SerializedOpaqueClassDefinition::from(definition)) + ); assert_eq!(tx.class_definition(computed).unwrap(), None); } @@ -258,7 +268,7 @@ mod tests { let storage = storage(); let good = ClassHash(pathfinder_crypto::Felt::from_hex_str("0x1111").unwrap()); let bad = ClassHash(pathfinder_crypto::Felt::from_hex_str("0x2222").unwrap()); - let definition = b"good class definition".to_vec(); + let definition = SerializedCairoDefinition::from_slice(b"good class definition"); // Insert both in one state update to avoid block number conflicts. { @@ -292,7 +302,10 @@ mod tests { let mut db = storage.connection().unwrap(); let tx = db.transaction().unwrap(); - assert_eq!(tx.class_definition(good).unwrap(), Some(definition)); + assert_eq!( + tx.class_definition(good).unwrap(), + Some(SerializedOpaqueClassDefinition::from(definition)) + ); // bad was not repaired — definition IS NULL, so it still appears in // the missing list. let still_missing = tx.class_hashes_with_missing_definitions().unwrap(); diff --git a/crates/pathfinder/src/sync.rs b/crates/pathfinder/src/sync.rs index 8bcaa576b8..9cd497043c 100644 --- a/crates/pathfinder/src/sync.rs +++ b/crates/pathfinder/src/sync.rs @@ -318,6 +318,12 @@ mod tests { calculate_transaction_commitment, compute_final_hash, }; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::event::Event; use pathfinder_common::prelude::*; use pathfinder_common::receipt::Receipt; @@ -613,7 +619,11 @@ mod tests { ); pretty_assertions_sorted::assert_eq!( cairo_defs, - expected.cairo_defs.into_iter().collect::>(), + expected + .cairo_defs + .into_iter() + .map(|(h, d)| (h, SerializedOpaqueClassDefinition::from(d))) + .collect::>(), "block {}", block_number ); @@ -623,7 +633,15 @@ mod tests { .sierra_defs .into_iter() // All sierra fixtures are not compile-able - .map(|(h, s, _, _)| (h, (s, starknet_gateway_test_fixtures::class_definitions::CAIRO_1_1_0_BALANCE_CASM_JSON.to_vec()))) + .map(|(h, s, _, _)| ( + h, + ( + SerializedOpaqueClassDefinition::from(s), + SerializedCasmDefinition::from_slice( + starknet_gateway_test_fixtures::class_definitions::CAIRO_1_1_0_BALANCE_CASM_JSON + ), + ) + )) .collect::>(), "block {}", block_number @@ -1073,8 +1091,8 @@ mod tests { &self, _: ClassHash, _: BlockId, - ) -> Result { - Ok(bytes::Bytes::from_static( + ) -> Result { + Ok(SerializedCasmDefinition::from_slice( starknet_gateway_test_fixtures::class_definitions::CAIRO_1_1_0_BALANCE_CASM_JSON, )) } diff --git a/crates/pathfinder/src/sync/checkpoint.rs b/crates/pathfinder/src/sync/checkpoint.rs index 2f99e17541..05eee92dcf 100644 --- a/crates/pathfinder/src/sync/checkpoint.rs +++ b/crates/pathfinder/src/sync/checkpoint.rs @@ -1370,6 +1370,12 @@ mod tests { use fake::{Dummy, Fake, Faker}; use futures::{stream, SinkExt}; use p2p::libp2p::PeerId; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::event::Event; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; @@ -1402,8 +1408,8 @@ mod tests { &self, _: ClassHash, _: BlockId, - ) -> Result { - Ok(bytes::Bytes::from_static(CASM2)) + ) -> Result { + Ok(SerializedCasmDefinition::from_slice(CASM2)) } } @@ -1441,7 +1447,7 @@ mod tests { struct Setup { pub streamed_classes: Vec, anyhow::Error>>, pub declared_classes: DeclaredClasses, - pub expected_defs: HashMap>, + pub expected_defs: HashMap, pub storage: Storage, } @@ -1477,19 +1483,20 @@ mod tests { fake_block(1, state_update1), ]; - blocks[1].cairo_defs = vec![(cairo_hash, CAIRO.to_vec())]; + blocks[1].cairo_defs = + vec![(cairo_hash, SerializedCairoDefinition::from_slice(CAIRO))]; blocks[1].sierra_defs = vec![ // Does not compile ( sierra0_hash, - SIERRA0.to_vec(), + SerializedSierraDefinition::from_slice(SIERRA0), Default::default(), Default::default(), ), // Compiles just fine ( sierra2_hash, - SIERRA2.to_vec(), + SerializedSierraDefinition::from_slice(SIERRA2), Default::default(), Default::default(), ), @@ -1498,17 +1505,17 @@ mod tests { let streamed_classes = vec![ Ok(PeerData::for_tests(ClassDefinition::Cairo { block_number: BlockNumber::GENESIS + 1, - definition: CAIRO.to_vec(), + definition: SerializedCairoDefinition::from_slice(CAIRO), hash: cairo_hash, })), Ok(PeerData::for_tests(ClassDefinition::Sierra { block_number: BlockNumber::GENESIS + 1, - sierra_definition: SIERRA0.to_vec(), + sierra_definition: SerializedSierraDefinition::from_slice(SIERRA0), hash: sierra0_hash, })), Ok(PeerData::for_tests(ClassDefinition::Sierra { block_number: BlockNumber::GENESIS + 1, - sierra_definition: SIERRA2.to_vec(), + sierra_definition: SerializedSierraDefinition::from_slice(SIERRA2), hash: sierra2_hash, })), ]; @@ -1529,9 +1536,18 @@ mod tests { ]); let expected_defs = [ - (cairo_hash, CAIRO.to_vec()), - (ClassHash(sierra0_hash.0), SIERRA0.to_vec()), - (ClassHash(sierra2_hash.0), SIERRA2.to_vec()), + ( + cairo_hash, + SerializedOpaqueClassDefinition::from_slice(CAIRO), + ), + ( + ClassHash(sierra0_hash.0), + SerializedOpaqueClassDefinition::from_slice(SIERRA0), + ), + ( + ClassHash(sierra2_hash.0), + SerializedOpaqueClassDefinition::from_slice(SIERRA2), + ), ] .into(); @@ -1576,12 +1592,13 @@ mod tests { db.casm_definition(ClassHash(SIERRA0_HASH.0)) .unwrap() .unwrap(), - CASM2 + SerializedCasmDefinition::from_slice(CASM2) ); assert!(serde_json::from_slice::( - &db.casm_definition(ClassHash(SIERRA2_HASH.0)) + db.casm_definition(ClassHash(SIERRA2_HASH.0)) .unwrap() .unwrap() + .as_bytes() ) .unwrap()["compiler_version"] .is_string()); diff --git a/crates/pathfinder/src/sync/class_definitions.rs b/crates/pathfinder/src/sync/class_definitions.rs index 494c1fa44a..962ee8335f 100644 --- a/crates/pathfinder/src/sync/class_definitions.rs +++ b/crates/pathfinder/src/sync/class_definitions.rs @@ -10,7 +10,14 @@ use p2p::sync::client::types::ClassDefinition as P2PClassDefinition; use p2p::PeerData; use p2p_proto::transaction; use pathfinder_class_hash::from_parts::{compute_cairo_class_hash, compute_sierra_class_hash}; -use pathfinder_common::class_definition::{Cairo, ClassDefinition as GwClassDefinition, Sierra}; +use pathfinder_common::class_definition::{ + Cairo, + ClassDefinition as GwClassDefinition, + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedSierraDefinition, + Sierra, +}; use pathfinder_common::state_update::DeclaredClasses; use pathfinder_common::{BlockNumber, CasmHash, ClassHash, SierraHash}; use pathfinder_storage::{Storage, Transaction}; @@ -37,8 +44,8 @@ pub struct ClassWithLayout { #[derive(Debug)] pub(super) enum ClassDefinition { - Cairo(Vec), - Sierra(Vec), + Cairo(SerializedCairoDefinition), + Sierra(SerializedSierraDefinition), } #[derive(Debug)] @@ -57,10 +64,10 @@ pub struct CompiledClass { #[derive(Debug)] pub enum CompiledClassDefinition { - Cairo(Vec), + Cairo(SerializedCairoDefinition), Sierra { - sierra_definition: Vec, - casm_definition: Vec, + sierra_definition: SerializedSierraDefinition, + casm_definition: SerializedCasmDefinition, casm_hash_v2: CasmHash, }, } @@ -143,7 +150,7 @@ fn verify_layout_impl( hash, } => { let layout = GwClassDefinition::Cairo( - serde_json::from_slice::>(&definition).map_err(|error| { + serde_json::from_slice::>(definition.as_bytes()).map_err(|error| { tracing::debug!(%peer, %block_number, %error, "Bad class layout"); SyncError::BadClassLayout(*peer) })?, @@ -161,10 +168,12 @@ fn verify_layout_impl( hash, } => { let layout = GwClassDefinition::Sierra( - serde_json::from_slice::>(&sierra_definition).map_err(|error| { - tracing::debug!(%peer, %block_number, %error, "Bad class layout"); - SyncError::BadClassLayout(*peer) - })?, + serde_json::from_slice::>(sierra_definition.as_bytes()).map_err( + |error| { + tracing::debug!(%peer, %block_number, %error, "Bad class layout"); + SyncError::BadClassLayout(*peer) + }, + )?, ); Ok(ClassWithLayout { block_number, @@ -523,8 +532,7 @@ fn compile_or_fetch_impl( .map_err(|error| { tracing::debug!(%block_number, class_hash=%hash, %error, "Fetching casm from feeder gateway failed"); SyncError::FetchingCasmFailed - })? - .to_vec(), + })?, }; let casm_hash_v2 = pathfinder_casm_hashes::get_precomputed_casm_v2_hash(&hash); diff --git a/crates/rpc/src/executor.rs b/crates/rpc/src/executor.rs index b09062e455..bb311e5e6b 100644 --- a/crates/rpc/src/executor.rs +++ b/crates/rpc/src/executor.rs @@ -1,4 +1,8 @@ use anyhow::Context; +use pathfinder_common::class_definition::{ + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::transaction::TransactionVariant; use pathfinder_common::{BlockNumber, ChainId, StarknetVersion}; use pathfinder_executor::types::to_starknet_api_transaction; @@ -91,8 +95,9 @@ pub(crate) fn map_broadcasted_transaction( .serialize_to_json() .context("Serializing Cairo class to JSON")?; - let contract_class = - pathfinder_executor::parse_deprecated_class_definition(contract_class_json)?; + let contract_class = pathfinder_executor::parse_deprecated_class_definition( + SerializedOpaqueClassDefinition::from_bytes(contract_class_json), + )?; Some(ClassInfo::new( &contract_class, @@ -107,8 +112,9 @@ pub(crate) fn map_broadcasted_transaction( .serialize_to_json() .context("Serializing Cairo class to JSON")?; - let contract_class = - pathfinder_executor::parse_deprecated_class_definition(contract_class_json)?; + let contract_class = pathfinder_executor::parse_deprecated_class_definition( + SerializedOpaqueClassDefinition::from_bytes(contract_class_json), + )?; Some(ClassInfo::new( &contract_class, @@ -120,8 +126,10 @@ pub(crate) fn map_broadcasted_transaction( BroadcastedTransaction::Declare(BroadcastedDeclareTransaction::V2(tx)) => { let sierra_version = SierraVersion::extract_from_program(&tx.contract_class.sierra_program)?; - let sierra_definition = serde_json::to_vec(&tx.contract_class) - .context("Serializing Sierra class definition")?; + let sierra_definition = SerializedSierraDefinition::from_bytes( + serde_json::to_vec(&tx.contract_class) + .context("Serializing Sierra class definition")?, + ); let casm_contract_definition = pathfinder_compiler::compile_sierra_to_casm( &sierra_definition, compiler_resource_limits, @@ -144,8 +152,10 @@ pub(crate) fn map_broadcasted_transaction( BroadcastedTransaction::Declare(BroadcastedDeclareTransaction::V3(tx)) => { let sierra_version = SierraVersion::extract_from_program(&tx.contract_class.sierra_program)?; - let sierra_definition = serde_json::to_vec(&tx.contract_class) - .context("Serializing Sierra class definition")?; + let sierra_definition = SerializedSierraDefinition::from_bytes( + serde_json::to_vec(&tx.contract_class) + .context("Serializing Sierra class definition")?, + ); let casm_contract_definition = pathfinder_compiler::compile_sierra_to_casm( &sierra_definition, compiler_resource_limits, @@ -282,7 +292,7 @@ pub fn compose_executor_transaction( .class_definition(tx.class_hash)? .context("Fetching class definition")?; let class_definition: crate::types::class::sierra::SierraContractClass = - serde_json::from_str(&String::from_utf8(class_definition)?) + serde_json::from_slice(class_definition.as_bytes()) .context("Deserializing class definition")?; let sierra_version = SierraVersion::extract_from_program(&class_definition.sierra_program)?; @@ -304,7 +314,7 @@ pub fn compose_executor_transaction( .class_definition(tx.class_hash)? .context("Fetching class definition")?; let class_definition: crate::types::class::sierra::SierraContractClass = - serde_json::from_str(&String::from_utf8(class_definition)?) + serde_json::from_str(&String::from_utf8(class_definition.into_bytes())?) .context("Deserializing class definition")?; let sierra_version = SierraVersion::extract_from_program(&class_definition.sierra_program)?; diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index acf3523318..85f2669b34 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -283,6 +283,11 @@ impl crate::dto::DeserializeForVersion for SubscriptionId { pub mod test_utils { use std::collections::HashMap; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::event::Event; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; @@ -398,11 +403,13 @@ pub mod test_utils { let contract1_update2 = HashMap::from([(storage_addr, storage_value_bytes!(b"storage value 2"))]); - let class0_definition = - starknet_gateway_test_fixtures::class_definitions::CONTRACT_DEFINITION.to_vec(); + let class0_definition = SerializedCairoDefinition::from_slice( + starknet_gateway_test_fixtures::class_definitions::CONTRACT_DEFINITION, + ); let class1_definition = &class0_definition; - let sierra_class_definition = - starknet_gateway_test_fixtures::class_definitions::CAIRO_0_11_SIERRA.to_vec(); + let sierra_class_definition = SerializedSierraDefinition::from_slice( + starknet_gateway_test_fixtures::class_definitions::CAIRO_0_11_SIERRA, + ); db_txn .insert_cairo_class_definition(class0_hash, &class0_definition) @@ -414,7 +421,7 @@ pub mod test_utils { .insert_sierra_class_definition( &sierra_class, &sierra_class_definition, - &[], + &SerializedCasmDefinition::from_slice(&[]), &sierra_casm_hash_v2, ) .unwrap(); @@ -913,13 +920,21 @@ pub mod test_utils { starknet_gateway_test_fixtures::class_definitions::CONTRACT_DEFINITION; for cairo in state_update_copy.declared_cairo_classes { - tx.insert_cairo_class_definition(cairo, class_definition) - .unwrap(); + tx.insert_cairo_class_definition( + cairo, + &SerializedCairoDefinition::from_slice(class_definition), + ) + .unwrap(); } for (sierra, casm) in state_update_copy.declared_sierra_classes { - tx.insert_sierra_class_definition(&sierra, b"sierra def", b"casm def", &casm) - .unwrap(); + tx.insert_sierra_class_definition( + &sierra, + &SerializedSierraDefinition::from_slice(b"sierra def"), + &SerializedCasmDefinition::from_slice(b"casm def"), + &casm, + ) + .unwrap(); } tx.commit().unwrap(); @@ -1269,21 +1284,37 @@ pub mod test_utils { starknet_gateway_test_fixtures::class_definitions::CONTRACT_DEFINITION; for cairo in pre_latest_state_update.declared_cairo_classes { - tx.insert_cairo_class_definition(cairo, class_definition) - .unwrap(); + tx.insert_cairo_class_definition( + cairo, + &SerializedCairoDefinition::from_slice(class_definition), + ) + .unwrap(); } for (sierra, casm) in pre_latest_state_update.declared_sierra_classes { - tx.insert_sierra_class_definition(&sierra, b"sierra def", b"casm def", &casm) - .unwrap(); + tx.insert_sierra_class_definition( + &sierra, + &SerializedSierraDefinition::from_slice(b"sierra def"), + &SerializedCasmDefinition::from_slice(b"casm def"), + &casm, + ) + .unwrap(); } for cairo in pre_confirmed_state_update_copy.declared_cairo_classes { - tx.insert_cairo_class_definition(cairo, class_definition) - .unwrap(); + tx.insert_cairo_class_definition( + cairo, + &SerializedCairoDefinition::from_slice(class_definition), + ) + .unwrap(); } for (sierra, casm) in pre_confirmed_state_update_copy.declared_sierra_classes { - tx.insert_sierra_class_definition(&sierra, b"sierra def", b"casm def", &casm) - .unwrap(); + tx.insert_sierra_class_definition( + &sierra, + &SerializedSierraDefinition::from_slice(b"sierra def"), + &SerializedCasmDefinition::from_slice(b"casm def"), + &casm, + ) + .unwrap(); } tx.commit().unwrap(); diff --git a/crates/rpc/src/method/add_declare_transaction.rs b/crates/rpc/src/method/add_declare_transaction.rs index 47f2ba9133..e5adf2b648 100644 --- a/crates/rpc/src/method/add_declare_transaction.rs +++ b/crates/rpc/src/method/add_declare_transaction.rs @@ -464,6 +464,7 @@ impl crate::dto::SerializeForVersion for Output { mod tests { use std::sync::LazyLock; + use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_common::transaction::{DataAvailabilityMode, ResourceBound, ResourceBounds}; @@ -485,10 +486,12 @@ mod tests { use crate::types::ContractClass; pub static CONTRACT_CLASS: LazyLock = LazyLock::new(|| { - ContractClass::from_definition_bytes(CONTRACT_DEFINITION) - .unwrap() - .as_cairo() - .unwrap() + ContractClass::try_from_serialized_definition(&SerializedOpaqueClassDefinition::from_slice( + CONTRACT_DEFINITION, + )) + .unwrap() + .as_cairo() + .unwrap() }); pub static CONTRACT_CLASS_WITH_INVALID_PRIME: LazyLock = @@ -502,24 +505,30 @@ mod tests { .get_mut("prime") .unwrap() = serde_json::json!("0x1"); let definition = serde_json::to_vec(&definition).unwrap(); - ContractClass::from_definition_bytes(&definition) - .unwrap() - .as_cairo() - .unwrap() + ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(&definition), + ) + .unwrap() + .as_cairo() + .unwrap() }); pub static SIERRA_CLASS: LazyLock = LazyLock::new(|| { - ContractClass::from_definition_bytes(CAIRO_2_0_0_STACK_OVERFLOW) - .unwrap() - .as_sierra() - .unwrap() + ContractClass::try_from_serialized_definition(&SerializedOpaqueClassDefinition::from_slice( + CAIRO_2_0_0_STACK_OVERFLOW, + )) + .unwrap() + .as_sierra() + .unwrap() }); pub static INTEGRATION_SIERRA_CLASS: LazyLock = LazyLock::new(|| { - ContractClass::from_definition_bytes(include_bytes!( + ContractClass::try_from_serialized_definition(&SerializedOpaqueClassDefinition::from_slice( + include_bytes!( "../../fixtures/contracts/\ integration_class_0x5ae9d09292a50ed48c5930904c880dab56e85b825022a7d689cfc9e65e01ee7.\ json" + ), )) .unwrap() .as_sierra() diff --git a/crates/rpc/src/method/call.rs b/crates/rpc/src/method/call.rs index bd40a3da4b..186eb2a8a1 100644 --- a/crates/rpc/src/method/call.rs +++ b/crates/rpc/src/method/call.rs @@ -256,6 +256,11 @@ mod tests { mod in_memory { use assert_matches::assert_matches; + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::felt; use pathfinder_common::prelude::*; use starknet_gateway_test_fixtures::class_definitions::{ @@ -292,8 +297,11 @@ mod tests { let block1_number = BlockNumber::GENESIS + 1; let block1_hash = BlockHash(felt!("0xb01")); - tx.insert_cairo_class_definition(CONTRACT_DEFINITION_CLASS_HASH, CONTRACT_DEFINITION) - .unwrap(); + tx.insert_cairo_class_definition( + CONTRACT_DEFINITION_CLASS_HASH, + &SerializedCairoDefinition::from_slice(CONTRACT_DEFINITION), + ) + .unwrap(); let header = BlockHeader::builder() .number(block1_number) @@ -435,8 +443,8 @@ mod tests { let tx = connection.transaction().unwrap(); tx.insert_sierra_class_definition( &sierra_hash, - sierra_definition, - casm_definition, + &SerializedSierraDefinition::from_slice(sierra_definition), + &SerializedCasmDefinition::from_slice(casm_definition), &casm_hash_v2, ) .unwrap(); @@ -541,8 +549,8 @@ mod tests { tx.insert_sierra_class_definition( &sierra_hash, - sierra_definition, - casm_definition, + &SerializedSierraDefinition::from_slice(sierra_definition), + &SerializedCasmDefinition::from_slice(casm_definition), &casm_hash_v2, ) .unwrap(); @@ -701,15 +709,15 @@ mod tests { tx.insert_sierra_class_definition( &sierra_hash, - sierra_definition, - casm_definition, + &SerializedSierraDefinition::from_slice(sierra_definition), + &SerializedCasmDefinition::from_slice(casm_definition), &casm_hash_v2, ) .unwrap(); tx.insert_sierra_class_definition( &caller_sierra_hash, - &caller_sierra_definition, - caller_casm_definition, + &SerializedSierraDefinition::from_bytes(caller_sierra_definition), + &SerializedCasmDefinition::from_slice(caller_casm_definition), &caller_casm_hash_v2, ) .unwrap(); diff --git a/crates/rpc/src/method/estimate_fee.rs b/crates/rpc/src/method/estimate_fee.rs index 601a60d87d..6344a5b075 100644 --- a/crates/rpc/src/method/estimate_fee.rs +++ b/crates/rpc/src/method/estimate_fee.rs @@ -232,6 +232,7 @@ impl crate::dto::SerializeForVersion for Output { #[cfg(test)] mod tests { use assert_matches::assert_matches; + use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_common::transaction::{DataAvailabilityMode, ResourceBound, ResourceBounds}; @@ -265,11 +266,12 @@ mod tests { let casm_hash = casm_hash!("0x069032ff71f77284e1a0864a573007108ca5cc08089416af50f03260f5d6d4d8"); - let contract_class: SierraContractClass = - ContractClass::from_definition_bytes(sierra_definition) - .unwrap() - .as_sierra() - .unwrap(); + let contract_class: SierraContractClass = ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(sierra_definition), + ) + .unwrap() + .as_sierra() + .unwrap(); assert_eq!(contract_class.class_hash().unwrap().hash(), sierra_hash); @@ -604,11 +606,12 @@ mod tests { let casm_hash = casm_hash!("0x02F58B23F7D98FF076AE59C08125AAFFD6DECCF1A7E97378D1A303B1A4223989"); - let contract_class: SierraContractClass = - ContractClass::from_definition_bytes(sierra_definition) - .unwrap() - .as_sierra() - .unwrap(); + let contract_class: SierraContractClass = ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(sierra_definition), + ) + .unwrap() + .as_sierra() + .unwrap(); self::assert_eq!(contract_class.class_hash().unwrap().hash(), sierra_hash); @@ -640,11 +643,12 @@ mod tests { let casm_hash = casm_hash!("0x138cd11c6de707426665bd8b0425d7411bb8dc5cbee15867025007a933b3379"); - let contract_class: SierraContractClass = - ContractClass::from_definition_bytes(sierra_definition) - .unwrap() - .as_sierra() - .unwrap(); + let contract_class: SierraContractClass = ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(sierra_definition), + ) + .unwrap() + .as_sierra() + .unwrap(); self::assert_eq!(contract_class.class_hash().unwrap().hash(), sierra_hash); diff --git a/crates/rpc/src/method/estimate_message_fee.rs b/crates/rpc/src/method/estimate_message_fee.rs index 774b618a97..9e3bb15e55 100644 --- a/crates/rpc/src/method/estimate_message_fee.rs +++ b/crates/rpc/src/method/estimate_message_fee.rs @@ -269,6 +269,10 @@ impl From for ApplicationError { #[cfg(test)] mod tests { use assert_matches::assert_matches; + use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_common::L1DataAvailabilityMode; @@ -309,8 +313,8 @@ mod tests { class_hash!("0x032908a85d43275f8509ba5f2acae88811b293463a3521dc05ab06d534b40848"); tx.insert_sierra_class_definition( &SierraHash(class_hash.0), - sierra_json, - casm_json, + &SerializedSierraDefinition::from_slice(sierra_json), + &SerializedCasmDefinition::from_slice(casm_json), &casm_hash_bytes!(b"casm hash blake"), ) .expect("insert class"); diff --git a/crates/rpc/src/method/get_class.rs b/crates/rpc/src/method/get_class.rs index c71596fe7e..2a5179e2f0 100644 --- a/crates/rpc/src/method/get_class.rs +++ b/crates/rpc/src/method/get_class.rs @@ -85,7 +85,7 @@ pub async fn get_class( return Err(Error::ClassHashNotFound); }; - let class = ContractClass::from_definition_bytes(&definition) + let class = ContractClass::try_from_serialized_definition(&definition) .context("Parsing class definition")? .into(); diff --git a/crates/rpc/src/method/get_class_at.rs b/crates/rpc/src/method/get_class_at.rs index 7b84e5a99d..b20d1d6e52 100644 --- a/crates/rpc/src/method/get_class_at.rs +++ b/crates/rpc/src/method/get_class_at.rs @@ -98,7 +98,7 @@ pub async fn get_class_at( .context("Fetching class definition")? .context("Class definition missing from database")?; - let class = ContractClass::from_definition_bytes(&definition) + let class = ContractClass::try_from_serialized_definition(&definition) .context("Parsing class definition")?; Ok(class) diff --git a/crates/rpc/src/method/get_compiled_casm.rs b/crates/rpc/src/method/get_compiled_casm.rs index d93a0dca5d..6791d1646e 100644 --- a/crates/rpc/src/method/get_compiled_casm.rs +++ b/crates/rpc/src/method/get_compiled_casm.rs @@ -90,13 +90,11 @@ pub async fn get_compiled_casm(context: RpcContext, input: Input) -> Result BroadcastedTransaction { - let contract_class = - crate::types::ContractClass::from_definition_bytes(SIERRA_DEFINITION) - .unwrap() - .as_sierra() - .unwrap(); + let contract_class = crate::types::ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(SIERRA_DEFINITION), + ) + .unwrap() + .as_sierra() + .unwrap(); assert_eq!(contract_class.class_hash().unwrap().hash(), SIERRA_HASH); @@ -954,11 +958,12 @@ pub(crate) mod tests { ) -> (BroadcastedTransaction, ClassHash) { let contract_definition = include_bytes!("../../fixtures/contracts/libfuncs_coverage.json"); - let contract_class = - crate::types::ContractClass::from_definition_bytes(contract_definition) - .unwrap() - .as_sierra() - .unwrap(); + let contract_class = crate::types::ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(contract_definition), + ) + .unwrap() + .as_sierra() + .unwrap(); let contract_hash = contract_class.class_hash().unwrap().hash(); let declare_tx = BroadcastedTransaction::Declare( BroadcastedDeclareTransaction::V3(BroadcastedDeclareTransactionV3 { diff --git a/crates/rpc/src/method/trace_block_transactions.rs b/crates/rpc/src/method/trace_block_transactions.rs index d505ebb941..071a5dffaf 100644 --- a/crates/rpc/src/method/trace_block_transactions.rs +++ b/crates/rpc/src/method/trace_block_transactions.rs @@ -823,6 +823,10 @@ impl From for TraceBlockTransactionsError { #[cfg(test)] pub(crate) mod tests { use assert_matches::assert_matches; + use pathfinder_common::class_definition::{ + SerializedCasmDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_common::receipt::Receipt; @@ -874,8 +878,8 @@ pub(crate) mod tests { tx.insert_sierra_class_definition( &SierraHash(fixtures::SIERRA_HASH.0), - fixtures::SIERRA_DEFINITION, - fixtures::CASM_DEFINITION, + &SerializedSierraDefinition::from_slice(fixtures::SIERRA_DEFINITION), + &SerializedCasmDefinition::from_slice(fixtures::CASM_DEFINITION), &casm_hash_bytes!(b"casm hash blake"), )?; @@ -1216,8 +1220,8 @@ pub(crate) mod tests { tx.insert_sierra_class_definition( &SierraHash(fixtures::SIERRA_HASH.0), - fixtures::SIERRA_DEFINITION, - fixtures::CASM_DEFINITION, + &SerializedSierraDefinition::from_slice(fixtures::SIERRA_DEFINITION), + &SerializedCasmDefinition::from_slice(fixtures::CASM_DEFINITION), &casm_hash_bytes!(b"casm hash blake"), )?; @@ -1362,8 +1366,8 @@ pub(crate) mod tests { tx.insert_sierra_class_definition( &SierraHash(fixtures::SIERRA_HASH.0), - fixtures::SIERRA_DEFINITION, - fixtures::CASM_DEFINITION, + &SerializedSierraDefinition::from_slice(fixtures::SIERRA_DEFINITION), + &SerializedCasmDefinition::from_slice(fixtures::CASM_DEFINITION), &casm_hash_bytes!(b"casm hash blake"), )?; diff --git a/crates/rpc/src/test_setup.rs b/crates/rpc/src/test_setup.rs index 644922c031..f91d19046c 100644 --- a/crates/rpc/src/test_setup.rs +++ b/crates/rpc/src/test_setup.rs @@ -1,3 +1,8 @@ +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_storage::Storage; @@ -42,8 +47,8 @@ pub async fn test_storage StateUpdate>( casm_hash!("0x0224b815fab6827eb21993e02e45e532e5476af6536dcf1f7085989ba9dc5bf0"); tx.insert_sierra_class_definition( &openzeppelin_account_sierra_hash, - openzeppelin_account_class_definition, - openzeppelin_account_casm_definition, + &SerializedSierraDefinition::from_slice(openzeppelin_account_class_definition), + &SerializedCasmDefinition::from_slice(openzeppelin_account_casm_definition), &casm_hash_bytes!(b"casm hash blake"), ) .unwrap(); @@ -53,16 +58,22 @@ pub async fn test_storage StateUpdate>( include_bytes!("../fixtures/contracts/universal_deployer.json"); let universal_deployer_class_hash = class_hash!("0x06f38fb91ddbf325a0625533576bb6f6eafd9341868a9ec3faa4b01ce6c4f4dc"); - tx.insert_cairo_class_definition(universal_deployer_class_hash, universal_deployer_definition) - .unwrap(); + tx.insert_cairo_class_definition( + universal_deployer_class_hash, + &SerializedCairoDefinition::from_slice(universal_deployer_definition), + ) + .unwrap(); // Declare ERC20 fee token contract class let erc20_class_hash = starknet_gateway_test_fixtures::class_definitions::ERC20_CONTRACT_DEFINITION_CLASS_HASH; let erc20_class_definition = starknet_gateway_test_fixtures::class_definitions::ERC20_CONTRACT_DEFINITION; - tx.insert_cairo_class_definition(erc20_class_hash, erc20_class_definition) - .unwrap(); + tx.insert_cairo_class_definition( + erc20_class_hash, + &SerializedCairoDefinition::from_slice(erc20_class_definition), + ) + .unwrap(); let header = BlockHeader::child_builder(&genesis) .timestamp(BlockTimestamp::new_or_panic(1)) diff --git a/crates/rpc/src/types/class.rs b/crates/rpc/src/types/class.rs index 3f07676e36..255f06d244 100644 --- a/crates/rpc/src/types/class.rs +++ b/crates/rpc/src/types/class.rs @@ -1,5 +1,6 @@ use anyhow::Context; use base64::prelude::*; +use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; #[derive(Clone, Debug, PartialEq, Eq)] pub enum ContractClass { @@ -19,8 +20,12 @@ impl ContractClass { /// /// Note that this function does not validate the class definition in any /// way, so this is only ever to be called for trusted data from storage. - pub fn from_definition_bytes(data: &[u8]) -> anyhow::Result { - let mut json = serde_json::from_slice::(data).context("Parsing json")?; + pub fn try_from_serialized_definition( + serialized_definition: &SerializedOpaqueClassDefinition, + ) -> anyhow::Result { + let mut json = + serde_json::from_slice::(serialized_definition.as_bytes()) + .context("Parsing json")?; let json_obj = json .as_object_mut() .context("Class definition is not a json object")?; @@ -228,8 +233,14 @@ pub mod cairo { impl CairoContractClass { pub fn class_hash(&self) -> anyhow::Result { let serialized = self.serialize_to_json()?; + let definition = + pathfinder_common::class_definition::SerializedOpaqueClassDefinition::from_bytes( + serialized, + ); - compute_class_hash(&serialized).context("Compute class hash") + compute_class_hash(definition) + .map(|(hash, _)| hash) + .context("Compute class hash") } pub fn serialize_to_json(&self) -> anyhow::Result> { @@ -687,7 +698,11 @@ pub mod sierra { impl SierraContractClass { pub fn class_hash(&self) -> anyhow::Result { let definition = serde_json::to_vec(self)?; - compute_class_hash(&definition) + let definition = + pathfinder_common::class_definition::SerializedOpaqueClassDefinition::from_bytes( + definition, + ); + compute_class_hash(definition).map(|(hash, _)| hash) } } @@ -779,6 +794,7 @@ mod tests { mod declare_class_hash { use pathfinder_class_hash::compute_class_hash; + use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use starknet_gateway_test_fixtures::class_definitions::{ CAIRO_0_11_SIERRA, CONTRACT_DEFINITION, @@ -788,22 +804,35 @@ mod tests { #[test] fn compute_sierra_class_hash() { - let class_hash = compute_class_hash(CAIRO_0_11_SIERRA).unwrap(); + let (class_hash, _) = compute_class_hash(SerializedOpaqueClassDefinition::from_slice( + CAIRO_0_11_SIERRA, + )) + .unwrap(); - let class = ContractClass::from_definition_bytes(CAIRO_0_11_SIERRA).unwrap(); + let class = ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(CAIRO_0_11_SIERRA), + ) + .unwrap(); assert_eq!(class.class_hash().unwrap(), class_hash); } #[test] fn compute_cairo_class_hash() { - let class_hash = compute_class_hash(CONTRACT_DEFINITION).unwrap(); + let (class_hash, _) = compute_class_hash(SerializedOpaqueClassDefinition::from_slice( + CONTRACT_DEFINITION, + )) + .unwrap(); - let class = ContractClass::from_definition_bytes(CONTRACT_DEFINITION).unwrap(); + let class = ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(CONTRACT_DEFINITION), + ) + .unwrap(); assert_eq!(class.class_hash().unwrap(), class_hash); } } mod contract_class_serialization { + use pathfinder_common::class_definition::SerializedOpaqueClassDefinition; use pathfinder_executor::parse_deprecated_class_definition; use super::super::cairo::CairoContractClass; @@ -830,14 +859,20 @@ mod tests { let serialized_definition = contract_class.serialize_to_json().unwrap(); - parse_deprecated_class_definition(serialized_definition).unwrap(); + parse_deprecated_class_definition(SerializedOpaqueClassDefinition::from_bytes( + serialized_definition, + )) + .unwrap(); } #[test] fn parse_deprecated_class_definition_with_debug_info() { let definition = include_bytes!("../../fixtures/contracts/cairo0_open_zeppelin_class.json"); - let class = ContractClass::from_definition_bytes(definition).unwrap(); + let class = ContractClass::try_from_serialized_definition( + &SerializedOpaqueClassDefinition::from_slice(definition), + ) + .unwrap(); // this step involves parsing the full program including debug info class.as_cairo().unwrap().serialize_to_json().unwrap(); diff --git a/crates/storage/src/connection/block.rs b/crates/storage/src/connection/block.rs index 57f157ddab..c124cd8622 100644 --- a/crates/storage/src/connection/block.rs +++ b/crates/storage/src/connection/block.rs @@ -649,6 +649,7 @@ fn parse_row_as_header(row: &rusqlite::Row<'_>) -> rusqlite::Result #[cfg(test)] mod tests { + use pathfinder_common::class_definition::SerializedCairoDefinition; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_common::L1DataAvailabilityMode; @@ -793,7 +794,8 @@ mod tests { // Add a class to test that purging a block unsets its block number; let cairo_hash = class_hash!("0x1234"); - tx.insert_cairo_class_definition(cairo_hash, &[]).unwrap(); + tx.insert_cairo_class_definition(cairo_hash, &SerializedCairoDefinition::from_slice(&[])) + .unwrap(); tx.insert_state_update( latest.number, &StateUpdate::default().with_declared_cairo_class(cairo_hash), diff --git a/crates/storage/src/connection/class.rs b/crates/storage/src/connection/class.rs index 330e6af533..9997e8f03c 100644 --- a/crates/storage/src/connection/class.rs +++ b/crates/storage/src/connection/class.rs @@ -1,4 +1,10 @@ use anyhow::Context; +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::{ BlockId, BlockNumber, @@ -14,17 +20,17 @@ impl Transaction<'_> { pub fn insert_sierra_class_definition( &self, sierra_hash: &SierraHash, - sierra_definition: &[u8], - casm_definition: &[u8], + sierra_definition: &SerializedSierraDefinition, + casm_definition: &SerializedCasmDefinition, // Blake2 hash of the compiled class definition casm_hash_v2: &CasmHash, ) -> anyhow::Result<()> { let mut compressor = zstd::bulk::Compressor::new(10).context("Creating zstd compressor")?; - let sierra_definition = compressor - .compress(sierra_definition) + let compressed_sierra_definition = compressor + .compress(sierra_definition.as_bytes()) .context("Compressing sierra definition")?; - let casm_definition = compressor - .compress(casm_definition) + let compressed_casm_definition = compressor + .compress(casm_definition.as_bytes()) .context("Compressing casm definition")?; self.inner() @@ -32,7 +38,7 @@ impl Transaction<'_> { "INSERT INTO class_definitions (hash, definition) VALUES (?, ?) ON CONFLICT(hash) DO UPDATE SET definition = excluded.definition WHERE class_definitions.definition IS NULL", - params![sierra_hash, &sierra_definition], + params![sierra_hash, &compressed_sierra_definition], ) .context("Inserting sierra definition")?; @@ -45,7 +51,7 @@ impl Transaction<'_> { ", named_params! { ":hash": sierra_hash, - ":definition": &casm_definition, + ":definition": &compressed_casm_definition, }, ) .context("Inserting CASM definition")?; @@ -70,23 +76,23 @@ impl Transaction<'_> { pub fn update_sierra_class_definition( &self, sierra_hash: &SierraHash, - sierra_definition: &[u8], - casm_definition: &[u8], + sierra_definition: &SerializedSierraDefinition, + casm_definition: &SerializedCasmDefinition, casm_hash_v2: &CasmHash, ) -> anyhow::Result<()> { let mut compressor = zstd::bulk::Compressor::new(10).context("Creating zstd compressor")?; - let sierra_definition = compressor - .compress(sierra_definition) + let compressed_sierra_definition = compressor + .compress(sierra_definition.as_bytes()) .context("Compressing sierra definition")?; - let casm_definition = compressor - .compress(casm_definition) + let compressed_casm_definition = compressor + .compress(casm_definition.as_bytes()) .context("Compressing casm definition")?; self.inner() .execute( r"UPDATE class_definitions SET definition=:definition WHERE hash=:hash", named_params! { - ":definition": &sierra_definition, + ":definition": &compressed_sierra_definition, ":hash": sierra_hash }, ) @@ -96,7 +102,7 @@ impl Transaction<'_> { .execute( r"INSERT OR REPLACE INTO casm_definitions(hash, definition) VALUES(:hash, :definition)", named_params! { - ":definition": &casm_definition, + ":definition": &compressed_casm_definition, ":hash": sierra_hash, }, ) @@ -118,11 +124,11 @@ impl Transaction<'_> { pub fn insert_cairo_class_definition( &self, cairo_hash: ClassHash, - definition: &[u8], + definition: &SerializedCairoDefinition, ) -> anyhow::Result<()> { let mut compressor = zstd::bulk::Compressor::new(10).context("Creating zstd compressor")?; - let definition = compressor - .compress(definition) + let compressed_definition = compressor + .compress(definition.as_bytes()) .context("Compressing cairo definition")?; self.inner() @@ -130,7 +136,7 @@ impl Transaction<'_> { r"INSERT INTO class_definitions (hash, definition) VALUES (?, ?) ON CONFLICT(hash) DO UPDATE SET definition = excluded.definition WHERE class_definitions.definition IS NULL", - params![&cairo_hash, &definition], + params![&cairo_hash, &compressed_definition], ) .context("Inserting cairo definition")?; @@ -140,17 +146,17 @@ impl Transaction<'_> { pub fn update_cairo_class_definition( &self, cairo_hash: ClassHash, - definition: &[u8], + definition: &SerializedCairoDefinition, ) -> anyhow::Result<()> { let mut compressor = zstd::bulk::Compressor::new(10).context("Creating zstd compressor")?; - let definition = compressor - .compress(definition) + let compressed_definition = compressor + .compress(definition.as_bytes()) .context("Compressing cairo definition")?; self.inner() .execute( r"UPDATE class_definitions SET definition=? WHERE hash=?", - params![&definition, &cairo_hash], + params![&compressed_definition, &cairo_hash], ) .context("Updating cairo definition")?; @@ -174,7 +180,10 @@ impl Transaction<'_> { } /// Returns the uncompressed class definition. - pub fn class_definition(&self, class_hash: ClassHash) -> anyhow::Result>> { + pub fn class_definition( + &self, + class_hash: ClassHash, + ) -> anyhow::Result> { self.class_definition_with_block_number(class_hash) .map(|option| option.map(|(_block_number, definition)| definition)) } @@ -184,7 +193,7 @@ impl Transaction<'_> { pub fn class_definition_with_block_number( &self, class_hash: ClassHash, - ) -> anyhow::Result, Vec)>> { + ) -> anyhow::Result, SerializedOpaqueClassDefinition)>> { let from_row = |row: &rusqlite::Row<'_>| { let definition = row.get_blob(0).map(|x| x.to_vec())?; let block_number = row.get_optional_block_number(1)?; @@ -206,21 +215,13 @@ impl Transaction<'_> { let definition = zstd::decode_all(definition.as_slice()).context("Decompressing class definition")?; - Ok(Some((block_number, definition))) + Ok(Some(( + block_number, + SerializedOpaqueClassDefinition::from_bytes(definition), + ))) } - /// Returns the compressed class definition if it has been declared at - /// `block_id`. - pub fn compressed_class_definition_at( - &self, - block_id: BlockId, - class_hash: ClassHash, - ) -> anyhow::Result>> { - self.compressed_class_definition_at_with_block_number(block_id, class_hash) - .map(|option| option.map(|(_block_number, definition)| definition)) - } - - pub fn compressed_class_definition_at_with_block_number( + fn compressed_class_definition_at_with_block_number( &self, block_id: BlockId, class_hash: ClassHash, @@ -271,9 +272,9 @@ impl Transaction<'_> { &self, block_id: BlockId, class_hash: ClassHash, - ) -> anyhow::Result>> { + ) -> anyhow::Result> { self.class_definition_at_with_block_number(block_id, class_hash) - .map(|option| option.map(|(_block_number, definition)| definition)) + .map(|option| option.map(|(_, definition)| definition)) } /// Returns the uncompressed class definition if it has been declared at @@ -282,7 +283,7 @@ impl Transaction<'_> { &self, block_id: BlockId, class_hash: ClassHash, - ) -> anyhow::Result)>> { + ) -> anyhow::Result> { let definition = self.compressed_class_definition_at_with_block_number(block_id, class_hash)?; let Some((block_number, definition)) = definition else { @@ -290,12 +291,16 @@ impl Transaction<'_> { }; let definition = zstd::decode_all(definition.as_slice()).context("Decompressing class definition")?; + let definition = SerializedOpaqueClassDefinition::from_bytes(definition); Ok(Some((block_number, definition))) } /// Returns the uncompressed compiled class definition. - pub fn casm_definition(&self, class_hash: ClassHash) -> anyhow::Result>> { + pub fn casm_definition( + &self, + class_hash: ClassHash, + ) -> anyhow::Result> { // Don't reuse the "_with_block_number" impl here since the suffixed one // requires a join that this one doesn't. let mut stmt = self @@ -314,7 +319,7 @@ impl Transaction<'_> { let definition = zstd::decode_all(definition.as_slice()) .context("Decompressing compiled class definition")?; - Ok(Some(definition)) + Ok(Some(SerializedCasmDefinition::from_bytes(definition))) } /// Returns the uncompressed compiled class definition, as well as the block @@ -322,7 +327,7 @@ impl Transaction<'_> { pub fn casm_definition_with_block_number( &self, class_hash: ClassHash, - ) -> anyhow::Result, Vec)>> { + ) -> anyhow::Result, SerializedCasmDefinition)>> { let from_row = |row: &rusqlite::Row<'_>| { let definition = row.get_blob(0).map(|x| x.to_vec())?; let block_number = row.get_optional_block_number(1)?; @@ -353,7 +358,10 @@ impl Transaction<'_> { let definition = zstd::decode_all(definition.as_slice()) .context("Decompressing compiled class definition")?; - Ok(Some((block_number, definition))) + Ok(Some(( + block_number, + SerializedCasmDefinition::from_bytes(definition), + ))) } /// Returns the uncompressed compiled class definition if it has been @@ -362,9 +370,9 @@ impl Transaction<'_> { &self, block_id: BlockId, class_hash: ClassHash, - ) -> anyhow::Result>> { + ) -> anyhow::Result> { self.casm_definition_at_with_block_number(block_id, class_hash) - .map(|option| option.map(|(_block_number, definition)| definition)) + .map(|option| option.map(|(_, definition)| definition)) } /// Returns the uncompressed compiled class definition if it has been @@ -374,14 +382,14 @@ impl Transaction<'_> { &self, block_id: BlockId, class_hash: ClassHash, - ) -> anyhow::Result, Vec)>> { + ) -> anyhow::Result, SerializedCasmDefinition)>> { let from_row = |row: &rusqlite::Row<'_>| { - let definition = row.get_blob(0).map(|x| x.to_vec())?; + let compressed_definition = row.get_blob(0).map(|x| x.to_vec())?; let block_number = row.get_optional_block_number(1)?; - Ok((block_number, definition)) + Ok((block_number, compressed_definition)) }; - let definition = match block_id { + let compressed_definition = match block_id { BlockId::Latest => { let mut stmt = self.inner().prepare_cached( r"SELECT @@ -432,13 +440,16 @@ impl Transaction<'_> { .optional() .context("Querying for compiled class definition")?; - let Some((block_number, definition)) = definition else { + let Some((block_number, compressed_definition)) = compressed_definition else { return Ok(None); }; - let definition = zstd::decode_all(definition.as_slice()) + let definition = zstd::decode_all(compressed_definition.as_slice()) .context("Decompressing compiled class definition")?; - Ok(Some((block_number, definition))) + Ok(Some(( + block_number, + SerializedCasmDefinition::from_bytes(definition), + ))) } /// Returns the compiled class hash for a class. @@ -628,10 +639,14 @@ mod tests { insert_placeholder(&tx, hash); let definition = b"example cairo program"; - tx.insert_cairo_class_definition(hash, definition).unwrap(); + tx.insert_cairo_class_definition(hash, &SerializedCairoDefinition::from_slice(definition)) + .unwrap(); let result = tx.class_definition(hash).unwrap(); - assert_eq!(result, Some(definition.to_vec())); + assert_eq!( + result, + Some(SerializedOpaqueClassDefinition::from_slice(definition)) + ); } #[test] @@ -652,17 +667,25 @@ mod tests { tx.insert_sierra_class_definition( &sierra_hash, - sierra_definition, - casm_definition, + &SerializedSierraDefinition::from_slice(sierra_definition), + &SerializedCasmDefinition::from_slice(casm_definition), &casm_hash_v2, ) .unwrap(); let result = tx.class_definition(class_hash).unwrap(); - assert_eq!(result, Some(sierra_definition.to_vec())); + assert_eq!( + result, + Some(SerializedOpaqueClassDefinition::from_slice( + sierra_definition + )) + ); let result = tx.casm_definition(class_hash).unwrap(); - assert_eq!(result, Some(casm_definition.to_vec())); + assert_eq!( + result, + Some(SerializedCasmDefinition::from_slice(casm_definition)) + ); } #[test] @@ -677,13 +700,22 @@ mod tests { let definition_a = b"definition A"; let definition_b = b"definition B"; - tx.insert_cairo_class_definition(hash, definition_a) - .unwrap(); - tx.insert_cairo_class_definition(hash, definition_b) - .unwrap(); + tx.insert_cairo_class_definition( + hash, + &SerializedCairoDefinition::from_slice(definition_a), + ) + .unwrap(); + tx.insert_cairo_class_definition( + hash, + &SerializedCairoDefinition::from_slice(definition_b), + ) + .unwrap(); let result = tx.class_definition(hash).unwrap(); - assert_eq!(result, Some(definition_a.to_vec())); + assert_eq!( + result, + Some(SerializedOpaqueClassDefinition::from_slice(definition_a)) + ); } fn setup_class(transaction: &Transaction<'_>) -> (ClassHash, &'static [u8], serde_json::Value) { @@ -692,7 +724,7 @@ mod tests { let definition = br#"{"abi":{"see":"above"},"program":{"huge":"hash"},"entry_points_by_type":{"this might be a":"hash"}}"#; transaction - .insert_cairo_class_definition(hash, definition) + .insert_cairo_class_definition(hash, &SerializedCairoDefinition::from_slice(definition)) .unwrap(); ( @@ -731,12 +763,18 @@ mod tests { let cairo_hash = class_hash_bytes!(b"cairo hash"); let cairo_definition = b"example cairo program"; - tx.insert_cairo_class_definition(cairo_hash, cairo_definition) - .unwrap(); + tx.insert_cairo_class_definition( + cairo_hash, + &SerializedCairoDefinition::from_slice(cairo_definition), + ) + .unwrap(); let definition = tx.class_definition(cairo_hash).unwrap().unwrap(); - assert_eq!(definition, cairo_definition); + assert_eq!( + definition, + SerializedOpaqueClassDefinition::from_slice(cairo_definition) + ); } #[test] @@ -754,8 +792,8 @@ mod tests { tx.insert_sierra_class_definition( &sierra_hash, - sierra_definition, - casm_definition, + &SerializedSierraDefinition::from_slice(sierra_definition), + &SerializedCasmDefinition::from_slice(casm_definition), &casm_hash_v2, ) .unwrap(); @@ -764,13 +802,19 @@ mod tests { .casm_definition(ClassHash(sierra_hash.0)) .unwrap() .unwrap(); - assert_eq!(definition, casm_definition); + assert_eq!( + definition, + SerializedCasmDefinition::from_slice(casm_definition) + ); let definition = tx .class_definition(ClassHash(sierra_hash.0)) .unwrap() .unwrap(); - assert_eq!(definition, sierra_definition); + assert_eq!( + definition, + SerializedOpaqueClassDefinition::from_slice(sierra_definition) + ); let retrieved_casm_hash_v2 = tx.casm_hash_v2(ClassHash(sierra_hash.0)).unwrap().unwrap(); assert_eq!(retrieved_casm_hash_v2, casm_hash_v2); diff --git a/crates/storage/src/connection/state_update.rs b/crates/storage/src/connection/state_update.rs index 0b94def871..8389fcaf72 100644 --- a/crates/storage/src/connection/state_update.rs +++ b/crates/storage/src/connection/state_update.rs @@ -1075,6 +1075,11 @@ impl Transaction<'_> { #[cfg(test)] mod tests { + use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedSierraDefinition, + }; use pathfinder_common::macro_prelude::*; use pathfinder_common::BlockHeader; @@ -1103,7 +1108,8 @@ mod tests { let state_update = StateUpdate::default().with_declared_cairo_class(target_class); - tx.insert_cairo_class_definition(target_class, &[]).unwrap(); + tx.insert_cairo_class_definition(target_class, &SerializedCairoDefinition::from_slice(&[])) + .unwrap(); tx.insert_block_header(&header_0).unwrap(); tx.insert_block_header(&header_1).unwrap(); tx.insert_state_update(header_0.number, &state_update) @@ -1157,10 +1163,16 @@ mod tests { let diff_3 = StateUpdate::default(); let diff_4 = StateUpdate::default(); - tx.insert_cairo_class_definition(original_class, definition) - .unwrap(); - tx.insert_cairo_class_definition(replaced_class, definition) - .unwrap(); + tx.insert_cairo_class_definition( + original_class, + &SerializedCairoDefinition::from_slice(definition), + ) + .unwrap(); + tx.insert_cairo_class_definition( + replaced_class, + &SerializedCairoDefinition::from_slice(definition), + ) + .unwrap(); tx.insert_block_header(&header_0).unwrap(); tx.insert_block_header(&header_1).unwrap(); @@ -1242,15 +1254,21 @@ mod tests { // Submit the class definitions since this occurs out of band of the header and // state diff. - tx.insert_cairo_class_definition(CAIRO_HASH, b"cairo definition") - .unwrap(); - tx.insert_cairo_class_definition(CAIRO_HASH2, b"cairo definition 2") - .unwrap(); + tx.insert_cairo_class_definition( + CAIRO_HASH, + &SerializedCairoDefinition::from_slice(b"cairo definition"), + ) + .unwrap(); + tx.insert_cairo_class_definition( + CAIRO_HASH2, + &SerializedCairoDefinition::from_slice(b"cairo definition 2"), + ) + .unwrap(); tx.insert_sierra_class_definition( &SIERRA_HASH, - b"sierra definition", - b"casm definition", + &SerializedSierraDefinition::from_slice(b"sierra definition"), + &SerializedCasmDefinition::from_slice(b"casm definition"), &CASM_HASH_V2, ) .unwrap(); @@ -1323,7 +1341,10 @@ mod tests { .casm_definition_at(BlockId::Latest, ClassHash(SIERRA_HASH.0)) .unwrap() .unwrap(); - assert_eq!(definition, b"casm definition"); + assert_eq!( + definition, + SerializedCasmDefinition::from_slice(b"casm definition") + ); // non-existent state update let non_existent = tx.state_update((header.number + 1).into()).unwrap(); diff --git a/crates/storage/src/fake.rs b/crates/storage/src/fake.rs index 145e3cce15..83e5eb10b4 100644 --- a/crates/storage/src/fake.rs +++ b/crates/storage/src/fake.rs @@ -4,6 +4,12 @@ use std::ops::RangeInclusive; use fake::{Fake, Faker}; use pathfinder_class_hash::compute_class_hash; +use pathfinder_common::class_definition::{ + SerializedCairoDefinition, + SerializedCasmDefinition, + SerializedOpaqueClassDefinition, + SerializedSierraDefinition, +}; use pathfinder_common::event::Event; use pathfinder_common::prelude::*; use pathfinder_common::receipt::Receipt; @@ -35,9 +41,14 @@ pub struct Block { /// [`fill`] by setting it to `None`. pub state_update: Option, // Cairo 0 definitions - pub cairo_defs: Vec<(ClassHash, Vec)>, + pub cairo_defs: Vec<(ClassHash, SerializedCairoDefinition)>, // Sierra + Casm definitions + Casm Blake2 hash - pub sierra_defs: Vec<(SierraHash, Vec, Vec, CasmHash)>, + pub sierra_defs: Vec<( + SierraHash, + SerializedSierraDefinition, + SerializedCasmDefinition, + CasmHash, + )>, } pub type BlockHashFn = Box BlockHash>; @@ -209,11 +220,8 @@ pub fn fill(storage: &Storage, blocks: &[Block], update_tries: Option, _>(rng), ) .unwrap(); - (compute_class_hash(&def).unwrap().hash(), def) + let def = SerializedOpaqueClassDefinition::from_bytes(def); + let (hash, def) = compute_class_hash(def).unwrap(); + let SerializedClassDefinition::Cairo(def) = def else { + panic!("Expected a Cairo class definition"); + }; + (hash.hash(), def) }) .collect::>(); let sierra_defs = (0..num_sierra_classes) @@ -344,11 +357,20 @@ pub mod generate { &Faker.fake_with_rng::, _>(rng), ) .unwrap(); + let def = SerializedOpaqueClassDefinition::from_bytes(def); + let (hash, sierra_def) = compute_class_hash(def).unwrap(); + let SerializedClassDefinition::Sierra(sierra_def) = sierra_def else { + panic!("Expected a Sierra class definition"); + }; + let hash = SierraHash(hash.hash().0); + let casm_def = SerializedCasmDefinition::from_bytes( + Faker.fake_with_rng::(rng).into_bytes(), + ); ( - SierraHash(compute_class_hash(&def).unwrap().hash().0), + hash, ( - def, - Faker.fake_with_rng::(rng).into_bytes(), + sierra_def, + casm_def, Faker.fake_with_rng::(rng), ), ) diff --git a/crates/storage/src/schema/revision_0076.rs b/crates/storage/src/schema/revision_0076.rs index 0548c38291..06d8e496e6 100644 --- a/crates/storage/src/schema/revision_0076.rs +++ b/crates/storage/src/schema/revision_0076.rs @@ -51,8 +51,12 @@ pub(crate) fn migrate(tx: &rusqlite::Transaction<'_>) -> anyhow::Result<()> { let definition = zstd::decode_all(definition.as_slice()) .map_err(|e| rusqlite::types::FromSqlError::Other(e.into())) .unwrap(); - let computed_hash = - pathfinder_compiler::casm_class_hash_v2(&definition).unwrap(); + let computed_hash = pathfinder_compiler::casm_class_hash_v2( + &pathfinder_common::class_definition::SerializedCasmDefinition::from_bytes( + definition, + ), + ) + .unwrap(); (class_hash, computed_hash) } } diff --git a/crates/validator/src/lib.rs b/crates/validator/src/lib.rs index 6084e14466..9d0d6b350e 100644 --- a/crates/validator/src/lib.rs +++ b/crates/validator/src/lib.rs @@ -12,7 +12,11 @@ use p2p_proto::sync::transaction::{DeclareV3WithoutClass, TransactionVariant as use p2p_proto::transaction::DeclareV3WithClass; use pathfinder_class_hash::compute_sierra_class_hash; use pathfinder_class_hash::json::SierraContractDefinition; -use pathfinder_common::class_definition::{SelectorAndFunctionIndex, SierraEntryPoints}; +use pathfinder_common::class_definition::{ + SelectorAndFunctionIndex, + SerializedSierraDefinition, + SierraEntryPoints, +}; use pathfinder_common::event::Event; use pathfinder_common::receipt::Receipt; use pathfinder_common::state_update::StateUpdateData; @@ -1127,7 +1131,7 @@ fn class_info( .collect(), }, }; - let sierra_def = serde_json::to_vec(&definition)?; + let sierra_def = SerializedSierraDefinition::from_bytes(serde_json::to_vec(&definition)?); let casm_def = pathfinder_compiler::compile_sierra_to_casm( &sierra_def, compiler_resource_limits,