diff --git a/crates/blockifier/src/transaction/test_utils.rs b/crates/blockifier/src/transaction/test_utils.rs index 2203da52218..41d0c334ebd 100644 --- a/crates/blockifier/src/transaction/test_utils.rs +++ b/crates/blockifier/src/transaction/test_utils.rs @@ -15,13 +15,7 @@ use starknet_api::transaction::fields::{ TransactionSignature, ValidResourceBounds, }; -use starknet_api::transaction::{ - InvokeTransactionV0, - InvokeTransactionV1, - InvokeTransactionV3, - TransactionHash, - TransactionVersion, -}; +use starknet_api::transaction::TransactionVersion; use starknet_api::{calldata, declare_tx_args, deploy_account_tx_args, felt, invoke_tx_args}; use starknet_types_core::felt::Felt; use strum::IntoEnumIterator; @@ -52,7 +46,7 @@ use crate::transaction::account_transaction::AccountTransaction; use crate::transaction::constants; use crate::transaction::objects::{FeeType, TransactionExecutionInfo, TransactionExecutionResult}; use crate::transaction::transaction_types::TransactionType; -use crate::transaction::transactions::{ExecutableTransaction, InvokeTransaction}; +use crate::transaction::transactions::ExecutableTransaction; // Corresponding constants to the ones in faulty_account. pub const VALID: u64 = 0; @@ -64,25 +58,6 @@ pub const GET_BLOCK_NUMBER: u64 = 5; pub const GET_BLOCK_TIMESTAMP: u64 = 6; pub const GET_SEQUENCER_ADDRESS: u64 = 7; -macro_rules! impl_from_versioned_tx { - ($(($specified_tx_type:ty, $enum_variant:ident)),*) => { - $(impl From<$specified_tx_type> for InvokeTransaction { - fn from(tx: $specified_tx_type) -> Self { - Self::new( - starknet_api::transaction::InvokeTransaction::$enum_variant(tx), - TransactionHash::default(), - ) - } - })* - }; -} - -impl_from_versioned_tx!( - (InvokeTransactionV0, V0), - (InvokeTransactionV1, V1), - (InvokeTransactionV3, V3) -); - /// Test fixtures. #[fixture] diff --git a/crates/blockifier/src/transaction/transactions.rs b/crates/blockifier/src/transaction/transactions.rs index ba6ca80adfd..69853c23a4b 100644 --- a/crates/blockifier/src/transaction/transactions.rs +++ b/crates/blockifier/src/transaction/transactions.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; -use starknet_api::contract_class::{ClassInfo, ContractClass, EntryPointType}; -use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; +use starknet_api::contract_class::EntryPointType; +use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress}; use starknet_api::executable_transaction::{ DeclareTransaction as ExecutableDeclareTx, DeployAccountTransaction as ExecutableDeployAccountTx, @@ -12,16 +12,10 @@ use starknet_api::executable_transaction::{ use starknet_api::transaction::fields::{ AccountDeploymentData, Calldata, - ContractAddressSalt, Fee, TransactionSignature, }; -use starknet_api::transaction::{ - DeclareTransactionV2, - DeclareTransactionV3, - TransactionHash, - TransactionVersion, -}; +use starknet_api::transaction::{DeclareTransactionV2, DeclareTransactionV3, TransactionVersion}; use crate::abi::abi_utils::selector_from_name; use crate::context::{BlockContext, TransactionContext}; @@ -36,7 +30,6 @@ use crate::execution::execution_utils::execute_deployment; use crate::state::cached_state::TransactionalState; use crate::state::errors::StateError; use crate::state::state_api::{State, UpdatableState}; -use crate::transaction::account_transaction::is_cairo1; use crate::transaction::constants; use crate::transaction::errors::TransactionExecutionError; use crate::transaction::objects::{ @@ -54,14 +47,6 @@ use crate::transaction::objects::{ #[path = "transactions_test.rs"] mod test; -macro_rules! implement_inner_tx_getter_calls { - ($(($field:ident, $field_type:ty)),*) => { - $(pub fn $field(&self) -> $field_type { - self.tx.$field().clone() - })* - }; -} - #[derive(Clone, Copy, Debug)] pub struct ExecutionFlags { pub charge_fee: bool, @@ -133,438 +118,6 @@ pub trait ValidatableTransaction { ) -> TransactionExecutionResult>; } -#[derive(Clone, Debug)] -pub struct DeclareTransaction { - pub tx: starknet_api::transaction::DeclareTransaction, - pub tx_hash: TransactionHash, - // Indicates the presence of the only_query bit in the version. - only_query: bool, - pub class_info: ClassInfo, -} - -impl TryFrom for DeclareTransaction { - type Error = TransactionExecutionError; - - fn try_from( - declare_tx: starknet_api::executable_transaction::DeclareTransaction, - ) -> Result { - Self::new_from_executable_tx(declare_tx, false) - } -} - -impl DeclareTransaction { - fn create( - declare_tx: starknet_api::transaction::DeclareTransaction, - tx_hash: TransactionHash, - class_info: ClassInfo, - only_query: bool, - ) -> TransactionExecutionResult { - let declare_version = declare_tx.version(); - // Verify contract class version. - // TODO(Noa): Avoid the unnecessary conversion. - if !is_cairo1(&class_info.contract_class().try_into()?) { - if declare_version > TransactionVersion::ONE { - Err(TransactionExecutionError::ContractClassVersionMismatch { - declare_version, - cairo_version: 0, - })? - } - } else if declare_version <= TransactionVersion::ONE { - Err(TransactionExecutionError::ContractClassVersionMismatch { - declare_version, - cairo_version: 1, - })? - } - Ok(Self { tx: declare_tx, tx_hash, class_info, only_query }) - } - - pub fn new( - declare_tx: starknet_api::transaction::DeclareTransaction, - tx_hash: TransactionHash, - class_info: ClassInfo, - ) -> TransactionExecutionResult { - Self::create(declare_tx, tx_hash, class_info, false) - } - - pub fn new_for_query( - declare_tx: starknet_api::transaction::DeclareTransaction, - tx_hash: TransactionHash, - class_info: ClassInfo, - ) -> TransactionExecutionResult { - Self::create(declare_tx, tx_hash, class_info, true) - } - - fn new_from_executable_tx( - declare_tx: starknet_api::executable_transaction::DeclareTransaction, - only_query: bool, - ) -> Result { - let starknet_api::executable_transaction::DeclareTransaction { tx, tx_hash, class_info } = - declare_tx; - - Self::create(tx, tx_hash, class_info, only_query) - } - - implement_inner_tx_getter_calls!( - (class_hash, ClassHash), - (nonce, Nonce), - (sender_address, ContractAddress), - (signature, TransactionSignature), - (version, TransactionVersion) - ); - - pub fn tx(&self) -> &starknet_api::transaction::DeclareTransaction { - &self.tx - } - - pub fn tx_hash(&self) -> TransactionHash { - self.tx_hash - } - - pub fn contract_class(&self) -> ContractClass { - self.class_info.contract_class() - } - - pub fn only_query(&self) -> bool { - self.only_query - } - - fn try_declare( - &self, - state: &mut S, - class_hash: ClassHash, - compiled_class_hash: Option, - ) -> TransactionExecutionResult<()> { - match state.get_compiled_contract_class(class_hash) { - Err(StateError::UndeclaredClassHash(_)) => { - // Class is undeclared; declare it. - state.set_contract_class(class_hash, self.contract_class().try_into()?)?; - if let Some(compiled_class_hash) = compiled_class_hash { - state.set_compiled_class_hash(class_hash, compiled_class_hash)?; - } - Ok(()) - } - Err(error) => Err(error)?, - Ok(_) => { - // Class is already declared, cannot redeclare. - Err(TransactionExecutionError::DeclareTransactionError { class_hash }) - } - } - } -} - -impl Executable for DeclareTransaction { - fn run_execute( - &self, - state: &mut S, - _resources: &mut ExecutionResources, - context: &mut EntryPointExecutionContext, - _remaining_gas: &mut u64, - ) -> TransactionExecutionResult> { - let class_hash = self.class_hash(); - match &self.tx { - starknet_api::transaction::DeclareTransaction::V0(_) - | starknet_api::transaction::DeclareTransaction::V1(_) => { - if context.tx_context.block_context.versioned_constants.disable_cairo0_redeclaration - { - self.try_declare(state, class_hash, None)? - } else { - // We allow redeclaration of the class for backward compatibility. - // In the past, we allowed redeclaration of Cairo 0 contracts since there was - // no class commitment (so no need to check if the class is already declared). - state.set_contract_class(class_hash, self.contract_class().try_into()?)?; - } - } - starknet_api::transaction::DeclareTransaction::V2(DeclareTransactionV2 { - compiled_class_hash, - .. - }) - | starknet_api::transaction::DeclareTransaction::V3(DeclareTransactionV3 { - compiled_class_hash, - .. - }) => self.try_declare(state, class_hash, Some(*compiled_class_hash))?, - } - Ok(None) - } -} - -impl TransactionInfoCreator for DeclareTransaction { - fn create_tx_info(&self) -> TransactionInfo { - // TODO(Nir, 01/11/2023): Consider to move this (from all get_tx_info methods). - let common_fields = CommonAccountFields { - transaction_hash: self.tx_hash(), - version: self.version(), - signature: self.signature(), - nonce: self.nonce(), - sender_address: self.sender_address(), - only_query: self.only_query, - }; - - match &self.tx { - starknet_api::transaction::DeclareTransaction::V0(tx) - | starknet_api::transaction::DeclareTransaction::V1(tx) => { - TransactionInfo::Deprecated(DeprecatedTransactionInfo { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::DeclareTransaction::V2(tx) => { - TransactionInfo::Deprecated(DeprecatedTransactionInfo { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::DeclareTransaction::V3(tx) => { - TransactionInfo::Current(CurrentTransactionInfo { - common_fields, - resource_bounds: tx.resource_bounds, - tip: tx.tip, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - fee_data_availability_mode: tx.fee_data_availability_mode, - paymaster_data: tx.paymaster_data.clone(), - account_deployment_data: tx.account_deployment_data.clone(), - }) - } - } - } -} -#[derive(Debug, Clone)] -pub struct DeployAccountTransaction { - pub tx: starknet_api::executable_transaction::DeployAccountTransaction, - // Indicates the presence of the only_query bit in the version. - pub only_query: bool, -} - -impl DeployAccountTransaction { - pub fn new( - deploy_account_tx: starknet_api::transaction::DeployAccountTransaction, - tx_hash: TransactionHash, - contract_address: ContractAddress, - ) -> Self { - Self { - tx: starknet_api::executable_transaction::DeployAccountTransaction { - tx: deploy_account_tx, - tx_hash, - contract_address, - }, - only_query: false, - } - } - - pub fn new_for_query( - deploy_account_tx: starknet_api::transaction::DeployAccountTransaction, - tx_hash: TransactionHash, - contract_address: ContractAddress, - ) -> Self { - Self { - tx: starknet_api::executable_transaction::DeployAccountTransaction { - tx: deploy_account_tx, - tx_hash, - contract_address, - }, - only_query: true, - } - } - - implement_inner_tx_getter_calls!( - (class_hash, ClassHash), - (constructor_calldata, Calldata), - (contract_address, ContractAddress), - (contract_address_salt, ContractAddressSalt), - (nonce, Nonce), - (signature, TransactionSignature), - (tx_hash, TransactionHash), - (version, TransactionVersion) - ); - - pub fn tx(&self) -> &starknet_api::transaction::DeployAccountTransaction { - self.tx.tx() - } -} - -impl Executable for DeployAccountTransaction { - fn run_execute( - &self, - state: &mut S, - resources: &mut ExecutionResources, - context: &mut EntryPointExecutionContext, - remaining_gas: &mut u64, - ) -> TransactionExecutionResult> { - let class_hash = self.class_hash(); - let ctor_context = ConstructorContext { - class_hash, - code_address: None, - storage_address: self.contract_address(), - caller_address: ContractAddress::default(), - }; - let call_info = execute_deployment( - state, - resources, - context, - ctor_context, - self.constructor_calldata(), - remaining_gas, - )?; - - Ok(Some(call_info)) - } -} - -impl TransactionInfoCreator for DeployAccountTransaction { - fn create_tx_info(&self) -> TransactionInfo { - let common_fields = CommonAccountFields { - transaction_hash: self.tx_hash(), - version: self.version(), - signature: self.signature(), - nonce: self.nonce(), - sender_address: self.contract_address(), - only_query: self.only_query, - }; - - match &self.tx() { - starknet_api::transaction::DeployAccountTransaction::V1(tx) => { - TransactionInfo::Deprecated(DeprecatedTransactionInfo { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::DeployAccountTransaction::V3(tx) => { - TransactionInfo::Current(CurrentTransactionInfo { - common_fields, - resource_bounds: tx.resource_bounds, - tip: tx.tip, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - fee_data_availability_mode: tx.fee_data_availability_mode, - paymaster_data: tx.paymaster_data.clone(), - account_deployment_data: AccountDeploymentData::default(), - }) - } - } - } -} - -#[derive(Debug, Clone)] -pub struct InvokeTransaction { - pub tx: starknet_api::executable_transaction::InvokeTransaction, - // Indicates the presence of the only_query bit in the version. - pub only_query: bool, -} - -impl InvokeTransaction { - pub fn new( - invoke_tx: starknet_api::transaction::InvokeTransaction, - tx_hash: TransactionHash, - ) -> Self { - Self { - tx: starknet_api::executable_transaction::InvokeTransaction { tx: invoke_tx, tx_hash }, - only_query: false, - } - } - - pub fn new_for_query( - invoke_tx: starknet_api::transaction::InvokeTransaction, - tx_hash: TransactionHash, - ) -> Self { - Self { - tx: starknet_api::executable_transaction::InvokeTransaction { tx: invoke_tx, tx_hash }, - only_query: true, - } - } - - implement_inner_tx_getter_calls!( - (calldata, Calldata), - (nonce, Nonce), - (signature, TransactionSignature), - (sender_address, ContractAddress), - (tx_hash, TransactionHash), - (version, TransactionVersion) - ); - - pub fn tx(&self) -> &starknet_api::transaction::InvokeTransaction { - self.tx.tx() - } -} - -impl Executable for InvokeTransaction { - fn run_execute( - &self, - state: &mut S, - resources: &mut ExecutionResources, - context: &mut EntryPointExecutionContext, - remaining_gas: &mut u64, - ) -> TransactionExecutionResult> { - let entry_point_selector = match &self.tx.tx { - starknet_api::transaction::InvokeTransaction::V0(tx) => tx.entry_point_selector, - starknet_api::transaction::InvokeTransaction::V1(_) - | starknet_api::transaction::InvokeTransaction::V3(_) => { - selector_from_name(constants::EXECUTE_ENTRY_POINT_NAME) - } - }; - let storage_address = context.tx_context.tx_info.sender_address(); - let class_hash = state.get_class_hash_at(storage_address)?; - let execute_call = CallEntryPoint { - entry_point_type: EntryPointType::External, - entry_point_selector, - calldata: self.calldata(), - class_hash: None, - code_address: None, - storage_address, - caller_address: ContractAddress::default(), - call_type: CallType::Call, - initial_gas: *remaining_gas, - }; - - let call_info = execute_call - .non_reverting_execute(state, resources, context, remaining_gas) - .map_err(|error| TransactionExecutionError::ExecutionError { - error, - class_hash, - storage_address, - selector: entry_point_selector, - })?; - - Ok(Some(call_info)) - } -} - -impl TransactionInfoCreator for InvokeTransaction { - fn create_tx_info(&self) -> TransactionInfo { - let common_fields = CommonAccountFields { - transaction_hash: self.tx_hash(), - version: self.version(), - signature: self.signature(), - nonce: self.nonce(), - sender_address: self.sender_address(), - only_query: self.only_query, - }; - - match &self.tx() { - starknet_api::transaction::InvokeTransaction::V0(tx) => { - TransactionInfo::Deprecated(DeprecatedTransactionInfo { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::InvokeTransaction::V1(tx) => { - TransactionInfo::Deprecated(DeprecatedTransactionInfo { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::InvokeTransaction::V3(tx) => { - TransactionInfo::Current(CurrentTransactionInfo { - common_fields, - resource_bounds: tx.resource_bounds, - tip: tx.tip, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - fee_data_availability_mode: tx.fee_data_availability_mode, - paymaster_data: tx.paymaster_data.clone(), - account_deployment_data: tx.account_deployment_data.clone(), - }) - } - } - } -} - impl HasRelatedFeeType for L1HandlerTransaction { fn version(&self) -> TransactionVersion { self.tx.version diff --git a/crates/starknet_api/src/contract_class.rs b/crates/starknet_api/src/contract_class.rs index 72f4bde1616..263bdd6a1c3 100644 --- a/crates/starknet_api/src/contract_class.rs +++ b/crates/starknet_api/src/contract_class.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::core::{CompiledClassHash, WORD_WIDTH}; use crate::deprecated_contract_class::ContractClass as DeprecatedContractClass; +use crate::transaction::TransactionVersion; use crate::StarknetApiError; #[derive( @@ -38,6 +39,29 @@ impl ContractClass { } } } + + pub fn validate_class_version_matches_tx_version( + &self, + declare_version: TransactionVersion, + ) -> Result<(), StarknetApiError> { + match self { + ContractClass::V0(class) => { + class.validate_class_version_matches_tx_version(declare_version)?; + } + // TODO(AvivG): Add validation for V1 when 'impl CasmContractClass' is available. + ContractClass::V1(_) => { + if !(declare_version == TransactionVersion::TWO + || declare_version == TransactionVersion::THREE) + { + Err(StarknetApiError::ContractClassVersionMismatch { + declare_version, + cairo_version: 1, + })? + } + } + } + Ok(()) + } } /// All relevant information about a declared contract class, including the compiled contract class /// and other parameters derived from the original declare transaction required for billing. @@ -96,4 +120,13 @@ impl ClassInfo { }) } } + + pub fn validate_class_version_matches_tx_version( + &self, + declare_version: TransactionVersion, + ) -> Result<(), StarknetApiError> { + let class = &self.contract_class.clone(); + class.validate_class_version_matches_tx_version(declare_version)?; + Ok(()) + } } diff --git a/crates/starknet_api/src/deprecated_contract_class.rs b/crates/starknet_api/src/deprecated_contract_class.rs index 233ceb64ea1..af66c892119 100644 --- a/crates/starknet_api/src/deprecated_contract_class.rs +++ b/crates/starknet_api/src/deprecated_contract_class.rs @@ -11,6 +11,7 @@ use crate::contract_class::EntryPointType; use crate::core::EntryPointSelector; use crate::hash::StarkHash; use crate::serde_utils::deserialize_optional_contract_class_abi_entry_vector; +use crate::transaction::TransactionVersion; use crate::StarknetApiError; /// A deprecated contract class. @@ -30,6 +31,21 @@ impl ContractClass { pub fn bytecode_length(&self) -> usize { self.program.data.as_array().expect("The program data must be an array.").len() } + + pub fn validate_class_version_matches_tx_version( + &self, + declare_version: TransactionVersion, + ) -> Result<(), StarknetApiError> { + if !(declare_version == TransactionVersion::ZERO + || declare_version == TransactionVersion::ONE) + { + Err(StarknetApiError::ContractClassVersionMismatch { + declare_version, + cairo_version: 0, + })? + } + Ok(()) + } } /// A [ContractClass](`crate::deprecated_contract_class::ContractClass`) abi entry. diff --git a/crates/starknet_api/src/executable_transaction.rs b/crates/starknet_api/src/executable_transaction.rs index dac7c337b7c..7ffdb1e2720 100644 --- a/crates/starknet_api/src/executable_transaction.rs +++ b/crates/starknet_api/src/executable_transaction.rs @@ -182,6 +182,7 @@ impl DeclareTransaction { class_info: ClassInfo, chain_id: &ChainId, ) -> Result { + class_info.validate_class_version_matches_tx_version(declare_tx.version())?; let tx_hash = declare_tx.calculate_transaction_hash(chain_id, &declare_tx.version())?; Ok(Self { tx: declare_tx, tx_hash, class_info }) } diff --git a/crates/starknet_api/src/lib.rs b/crates/starknet_api/src/lib.rs index 4ba0366232d..76bf008cbb0 100644 --- a/crates/starknet_api/src/lib.rs +++ b/crates/starknet_api/src/lib.rs @@ -26,6 +26,8 @@ use std::num::ParseIntError; use serde_utils::InnerDeserializationError; +use crate::transaction::TransactionVersion; + /// The error type returned by StarknetApi. // Note: if you need `Eq` see InnerDeserializationError's docstring. #[derive(thiserror::Error, Clone, Debug, PartialEq)] @@ -57,6 +59,11 @@ pub enum StarknetApiError { contract_class_version: u8, sierra_program_length: usize, }, + #[error( + "Declare transaction version {} must have a contract class of Cairo \ + version {cairo_version:?}.", **declare_version + )] + ContractClassVersionMismatch { declare_version: TransactionVersion, cairo_version: u64 }, } pub type StarknetApiResult = Result;