From 539063c1e4f8adc6d175fe00a1c1eb875c68e1d0 Mon Sep 17 00:00:00 2001 From: Charpa Date: Mon, 23 Dec 2024 15:24:03 +0100 Subject: [PATCH] fix(gateway): use compressed sierra for declare --- CHANGELOG.md | 3 +- .../client/gateway/server/src/handler.rs | 6 +- .../class/src/into_starknet_types.rs | 9 +- crates/madara/primitives/class/src/lib.rs | 50 ++++++++++ .../gateway/src/user_transaction.rs | 94 +++++++++++-------- .../transactions/src/to_blockifier.rs | 2 +- 6 files changed, 118 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 859e0f7ef..d5a76f92d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Next release - refactor(gateway): remove query-only support from user transactions -- fix(gateway): serialize tip as hex in UserTransaction +- fix(gateway): serialize `tip` as hex in UserTransaction - fix(block_production): dynamic block closing now adds special address with prev block hash - fix(rpc): call, simulate, estimate rpcs executed on top of the block, not at the start of it - fix(compilation): crate-level compilation @@ -503,7 +503,6 @@ ## v0.4.0 -- fix(gateway): serialize `tip` as hex in UserTransaction - chore: release v0.4.0 - feat: better management of custom configurations for genesis assets - feat: use actual vm resource costs diff --git a/crates/madara/client/gateway/server/src/handler.rs b/crates/madara/client/gateway/server/src/handler.rs index c239c64c4..8317c6e3e 100644 --- a/crates/madara/client/gateway/server/src/handler.rs +++ b/crates/madara/client/gateway/server/src/handler.rs @@ -23,7 +23,7 @@ use mp_utils::service::ServiceContext; use serde::Serialize; use serde_json::json; use starknet_types_core::felt::Felt; -use starknet_types_rpc::TraceBlockTransactionsResult; +use starknet_types_rpc::{BroadcastedDeclareTxn, TraceBlockTransactionsResult}; use super::{ error::{GatewayError, OptionExt, ResultExt}, @@ -349,7 +349,9 @@ async fn declare_transaction( tx: UserDeclareTransaction, add_transaction_provider: Arc, ) -> Response { - match add_transaction_provider.add_declare_transaction(tx.into()).await { + let tx: BroadcastedDeclareTxn = tx.try_into().unwrap(); + + match add_transaction_provider.add_declare_transaction(tx).await { Ok(result) => create_json_response(hyper::StatusCode::OK, &result), Err(e) => create_json_response(hyper::StatusCode::OK, &e), } diff --git a/crates/madara/primitives/class/src/into_starknet_types.rs b/crates/madara/primitives/class/src/into_starknet_types.rs index 86b897be2..a331185dc 100644 --- a/crates/madara/primitives/class/src/into_starknet_types.rs +++ b/crates/madara/primitives/class/src/into_starknet_types.rs @@ -10,7 +10,7 @@ use crate::{ }; impl TryFrom> for ContractClass { - type Error = base64::DecodeError; + type Error = std::io::Error; fn try_from(contract_class: starknet_types_rpc::MaybeDeprecatedContractClass) -> Result { match contract_class { @@ -123,15 +123,16 @@ impl From for starknet_types_rpc::SierraEntryPoint { } impl TryFrom> for CompressedLegacyContractClass { - type Error = base64::DecodeError; + type Error = std::io::Error; fn try_from( compressed_legacy_contract_class: starknet_types_rpc::DeprecatedContractClass, ) -> Result { use base64::Engine; - let decoded_program = - base64::engine::general_purpose::STANDARD.decode(&compressed_legacy_contract_class.program)?; + let decoded_program = base64::engine::general_purpose::STANDARD + .decode(&compressed_legacy_contract_class.program) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; Ok(CompressedLegacyContractClass { program: decoded_program, diff --git a/crates/madara/primitives/class/src/lib.rs b/crates/madara/primitives/class/src/lib.rs index 4ed54ec31..2ff5239de 100644 --- a/crates/madara/primitives/class/src/lib.rs +++ b/crates/madara/primitives/class/src/lib.rs @@ -168,6 +168,56 @@ impl FlattenedSierraClass { } } +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct CompressedSierraClass { + /// The gzipped compressed program as a base64 string. + pub sierra_program: String, + pub contract_class_version: String, + pub entry_points_by_type: EntryPointsByType, + pub abi: String, +} + +impl TryFrom for CompressedSierraClass { + type Error = std::io::Error; + + fn try_from(flattened_sierra_class: FlattenedSierraClass) -> Result { + let mut base64_encoder = + base64::write::EncoderWriter::new(Vec::new(), &base64::engine::general_purpose::STANDARD); + let mut gzip_encoder = flate2::write::GzEncoder::new(&mut base64_encoder, flate2::Compression::default()); + serde_json::to_writer(&mut gzip_encoder, &flattened_sierra_class.sierra_program)?; + gzip_encoder.try_finish()?; + drop(gzip_encoder); + base64_encoder.finish().map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "base64 encoding error"))?; + let sierra_program = String::from_utf8(base64_encoder.into_inner()).unwrap(); + + Ok(Self { + sierra_program, + contract_class_version: flattened_sierra_class.contract_class_version, + entry_points_by_type: flattened_sierra_class.entry_points_by_type, + abi: flattened_sierra_class.abi, + }) + } +} + +impl TryFrom for FlattenedSierraClass { + type Error = std::io::Error; + + fn try_from(compressed_sierra_class: CompressedSierraClass) -> Result { + let string_reader = std::io::Cursor::new(compressed_sierra_class.sierra_program); + let base64_decoder = + base64::read::DecoderReader::new(string_reader, &base64::engine::general_purpose::STANDARD); + let gzip_decoder = flate2::read::GzDecoder::new(base64_decoder); + let sierra_program = serde_json::from_reader(gzip_decoder)?; + + Ok(Self { + sierra_program, + contract_class_version: compressed_sierra_class.contract_class_version, + entry_points_by_type: compressed_sierra_class.entry_points_by_type, + abi: compressed_sierra_class.abi, + }) + } +} + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "UPPERCASE")] pub struct EntryPointsByType { diff --git a/crates/madara/primitives/gateway/src/user_transaction.rs b/crates/madara/primitives/gateway/src/user_transaction.rs index dc72e1e17..b3285d309 100644 --- a/crates/madara/primitives/gateway/src/user_transaction.rs +++ b/crates/madara/primitives/gateway/src/user_transaction.rs @@ -31,7 +31,7 @@ //! - `ContractClassDecodeError`: When contract class decoding fails //! -use mp_class::{CompressedLegacyContractClass, FlattenedSierraClass}; +use mp_class::{CompressedLegacyContractClass, CompressedSierraClass, FlattenedSierraClass}; use mp_convert::hex_serde::U64AsHex; use mp_transactions::{DataAvailabilityMode, ResourceBoundsMapping}; use serde::{Deserialize, Serialize}; @@ -48,7 +48,7 @@ pub enum UserTransactionConversionError { #[error("User transaction can't be a query only transaction")] UnsupportedQueryTransaction, #[error("Error while decoding the contract class: {0}")] - ContractClassDecodeError(#[from] base64::DecodeError), + ContractClassDecodeError(#[from] std::io::Error), } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -60,12 +60,14 @@ pub enum UserTransaction { DeployAccount(UserDeployAccountTransaction), } -impl From for BroadcastedTxn { - fn from(transaction: UserTransaction) -> Self { +impl TryFrom for BroadcastedTxn { + type Error = UserTransactionConversionError; + + fn try_from(transaction: UserTransaction) -> Result { match transaction { - UserTransaction::Declare(tx) => BroadcastedTxn::Declare(tx.into()), - UserTransaction::InvokeFunction(tx) => BroadcastedTxn::Invoke(tx.into()), - UserTransaction::DeployAccount(tx) => BroadcastedTxn::DeployAccount(tx.into()), + UserTransaction::Declare(tx) => Ok(BroadcastedTxn::Declare(tx.try_into()?)), + UserTransaction::InvokeFunction(tx) => Ok(BroadcastedTxn::Invoke(tx.into())), + UserTransaction::DeployAccount(tx) => Ok(BroadcastedTxn::DeployAccount(tx.into())), } } } @@ -93,12 +95,14 @@ pub enum UserDeclareTransaction { V3(UserDeclareV3Transaction), } -impl From for BroadcastedDeclareTxn { - fn from(transaction: UserDeclareTransaction) -> Self { +impl TryFrom for BroadcastedDeclareTxn { + type Error = UserTransactionConversionError; + + fn try_from(transaction: UserDeclareTransaction) -> Result { match transaction { - UserDeclareTransaction::V1(tx) => BroadcastedDeclareTxn::V1(tx.into()), - UserDeclareTransaction::V2(tx) => BroadcastedDeclareTxn::V2(tx.into()), - UserDeclareTransaction::V3(tx) => BroadcastedDeclareTxn::V3(tx.into()), + UserDeclareTransaction::V1(tx) => Ok(BroadcastedDeclareTxn::V1(tx.into())), + UserDeclareTransaction::V2(tx) => Ok(BroadcastedDeclareTxn::V2(tx.try_into()?)), + UserDeclareTransaction::V3(tx) => Ok(BroadcastedDeclareTxn::V3(tx.try_into()?)), } } } @@ -109,8 +113,8 @@ impl TryFrom> for UserDeclareTransaction { fn try_from(transaction: BroadcastedDeclareTxn) -> Result { match transaction { BroadcastedDeclareTxn::V1(tx) => Ok(UserDeclareTransaction::V1(tx.try_into()?)), - BroadcastedDeclareTxn::V2(tx) => Ok(UserDeclareTransaction::V2(tx.into())), - BroadcastedDeclareTxn::V3(tx) => Ok(UserDeclareTransaction::V3(tx.into())), + BroadcastedDeclareTxn::V2(tx) => Ok(UserDeclareTransaction::V2(tx.try_into()?)), + BroadcastedDeclareTxn::V3(tx) => Ok(UserDeclareTransaction::V3(tx.try_into()?)), BroadcastedDeclareTxn::QueryV1(_) | BroadcastedDeclareTxn::QueryV2(_) | BroadcastedDeclareTxn::QueryV3(_) => Err(UserTransactionConversionError::UnsupportedQueryTransaction), @@ -140,7 +144,7 @@ impl From for BroadcastedDeclareTxnV1 { } impl TryFrom> for UserDeclareV1Transaction { - type Error = base64::DecodeError; + type Error = std::io::Error; fn try_from(transaction: BroadcastedDeclareTxnV1) -> Result { Ok(Self { @@ -155,7 +159,7 @@ impl TryFrom> for UserDeclareV1Transaction { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct UserDeclareV2Transaction { - pub contract_class: FlattenedSierraClass, + pub contract_class: CompressedSierraClass, pub compiled_class_hash: Felt, pub sender_address: Felt, pub max_fee: Felt, @@ -163,36 +167,44 @@ pub struct UserDeclareV2Transaction { pub nonce: Felt, } -impl From for BroadcastedDeclareTxnV2 { - fn from(transaction: UserDeclareV2Transaction) -> Self { - Self { +impl TryFrom for BroadcastedDeclareTxnV2 { + type Error = std::io::Error; + + fn try_from(transaction: UserDeclareV2Transaction) -> Result { + let flattened_sierra_class: FlattenedSierraClass = transaction.contract_class.try_into()?; + + Ok(Self { sender_address: transaction.sender_address, compiled_class_hash: transaction.compiled_class_hash, max_fee: transaction.max_fee, signature: transaction.signature, nonce: transaction.nonce, - contract_class: transaction.contract_class.into(), - } + contract_class: flattened_sierra_class.into(), + }) } } -impl From> for UserDeclareV2Transaction { - fn from(transaction: BroadcastedDeclareTxnV2) -> Self { - Self { +impl TryFrom> for UserDeclareV2Transaction { + type Error = std::io::Error; + + fn try_from(transaction: BroadcastedDeclareTxnV2) -> Result { + let flattened_sierra_class: FlattenedSierraClass = transaction.contract_class.into(); + + Ok(Self { sender_address: transaction.sender_address, compiled_class_hash: transaction.compiled_class_hash, max_fee: transaction.max_fee, signature: transaction.signature, nonce: transaction.nonce, - contract_class: transaction.contract_class.into(), - } + contract_class: flattened_sierra_class.try_into()?, + }) } } #[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct UserDeclareV3Transaction { - pub contract_class: FlattenedSierraClass, + pub contract_class: CompressedSierraClass, pub compiled_class_hash: Felt, pub sender_address: Felt, pub signature: Vec, @@ -206,9 +218,13 @@ pub struct UserDeclareV3Transaction { pub account_deployment_data: Vec, } -impl From for BroadcastedDeclareTxnV3 { - fn from(transaction: UserDeclareV3Transaction) -> Self { - Self { +impl TryFrom for BroadcastedDeclareTxnV3 { + type Error = std::io::Error; + + fn try_from(transaction: UserDeclareV3Transaction) -> Result { + let flattened_sierra_class: FlattenedSierraClass = transaction.contract_class.try_into()?; + + Ok(Self { sender_address: transaction.sender_address, compiled_class_hash: transaction.compiled_class_hash, signature: transaction.signature, @@ -217,16 +233,20 @@ impl From for BroadcastedDeclareTxnV3 { fee_data_availability_mode: transaction.fee_data_availability_mode.into(), resource_bounds: transaction.resource_bounds.into(), tip: transaction.tip, - contract_class: transaction.contract_class.into(), + contract_class: flattened_sierra_class.into(), paymaster_data: transaction.paymaster_data, account_deployment_data: transaction.account_deployment_data, - } + }) } } -impl From> for UserDeclareV3Transaction { - fn from(transaction: BroadcastedDeclareTxnV3) -> Self { - Self { +impl TryFrom> for UserDeclareV3Transaction { + type Error = std::io::Error; + + fn try_from(transaction: BroadcastedDeclareTxnV3) -> Result { + let flattened_sierra_class: FlattenedSierraClass = transaction.contract_class.into(); + + Ok(Self { sender_address: transaction.sender_address, compiled_class_hash: transaction.compiled_class_hash, signature: transaction.signature, @@ -235,10 +255,10 @@ impl From> for UserDeclareV3Transaction { fee_data_availability_mode: transaction.fee_data_availability_mode.into(), resource_bounds: transaction.resource_bounds.into(), tip: transaction.tip, - contract_class: transaction.contract_class.into(), + contract_class: flattened_sierra_class.try_into()?, paymaster_data: transaction.paymaster_data, account_deployment_data: transaction.account_deployment_data, - } + }) } } diff --git a/crates/madara/primitives/transactions/src/to_blockifier.rs b/crates/madara/primitives/transactions/src/to_blockifier.rs index 64d9b989b..3f52e5c39 100644 --- a/crates/madara/primitives/transactions/src/to_blockifier.rs +++ b/crates/madara/primitives/transactions/src/to_blockifier.rs @@ -177,7 +177,7 @@ pub enum ToBlockifierError { #[error("Compiled class hash mismatch: expected {expected}, actual {compilation}")] CompiledClassHashMismatch { expected: Felt, compilation: Felt }, #[error("Failed to convert base64 program to cairo program: {0}")] - Base64ToCairoError(#[from] base64::DecodeError), + Base64ToCairoError(#[from] std::io::Error), #[error("Missing class")] MissingClass, }