From dacda4c33e384edfeeba9e6c9af464ed0f91b67d Mon Sep 17 00:00:00 2001
From: Aviv Greenburg <aviv.g@starkware.co>
Date: Wed, 25 Sep 2024 15:41:23 +0300
Subject: [PATCH] chore(blockifier): impl traits Executable & TxInfoCreator for
 api_executabe_txs

---
 crates/blockifier/src/transaction/objects.rs  |   4 +
 .../src/transaction/transactions.rs           | 250 +++++++++++++++++-
 .../src/executable_transaction.rs             |  14 +-
 3 files changed, 266 insertions(+), 2 deletions(-)

diff --git a/crates/blockifier/src/transaction/objects.rs b/crates/blockifier/src/transaction/objects.rs
index f3a7ed7cf4..a3ad411891 100644
--- a/crates/blockifier/src/transaction/objects.rs
+++ b/crates/blockifier/src/transaction/objects.rs
@@ -233,3 +233,7 @@ pub enum FeeType {
 pub trait TransactionInfoCreator {
     fn create_tx_info(&self) -> TransactionInfo;
 }
+
+pub trait TransactionInfoCreatorInner {
+    fn create_tx_info(&self, only_query: bool) -> TransactionInfo;
+}
diff --git a/crates/blockifier/src/transaction/transactions.rs b/crates/blockifier/src/transaction/transactions.rs
index ad8b13df78..37210c8e64 100644
--- a/crates/blockifier/src/transaction/transactions.rs
+++ b/crates/blockifier/src/transaction/transactions.rs
@@ -3,7 +3,12 @@ use std::sync::Arc;
 use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
 use starknet_api::contract_class::{ContractClass, EntryPointType};
 use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
-use starknet_api::executable_transaction::L1HandlerTransaction;
+use starknet_api::executable_transaction::{
+    DeclareTransaction as ExecutableDeclareTx,
+    DeployAccountTransaction as ExecutableDeployAccountTx,
+    InvokeTransaction as ExecutableInvokeTx,
+    L1HandlerTransaction,
+};
 use starknet_api::transaction::{
     AccountDeploymentData,
     Calldata,
@@ -42,6 +47,7 @@ use crate::transaction::objects::{
     TransactionExecutionResult,
     TransactionInfo,
     TransactionInfoCreator,
+    TransactionInfoCreatorInner,
 };
 #[cfg(test)]
 #[path = "transactions_test.rs"]
@@ -620,3 +626,245 @@ impl TransactionInfoCreator for L1HandlerTransaction {
         })
     }
 }
+
+impl<S: State> Executable<S> for ExecutableDeclareTx {
+    fn run_execute(
+        &self,
+        state: &mut S,
+        _resources: &mut ExecutionResources,
+        context: &mut EntryPointExecutionContext,
+        _remaining_gas: &mut u64,
+    ) -> TransactionExecutionResult<Option<CallInfo>> {
+        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
+                {
+                    try_declare(self, 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,
+                ..
+            }) => try_declare(self, state, class_hash, Some(*compiled_class_hash))?,
+        }
+        Ok(None)
+    }
+}
+
+impl TransactionInfoCreatorInner for ExecutableDeclareTx {
+    fn create_tx_info(&self, only_query: bool) -> 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,
+        };
+
+        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(),
+                })
+            }
+        }
+    }
+}
+
+impl<S: State> Executable<S> for ExecutableDeployAccountTx {
+    fn run_execute(
+        &self,
+        state: &mut S,
+        resources: &mut ExecutionResources,
+        context: &mut EntryPointExecutionContext,
+        remaining_gas: &mut u64,
+    ) -> TransactionExecutionResult<Option<CallInfo>> {
+        let class_hash = self.class_hash();
+        let constructor_context = ConstructorContext {
+            class_hash,
+            code_address: None,
+            storage_address: self.contract_address(),
+            caller_address: ContractAddress::default(),
+        };
+        let call_info = execute_deployment(
+            state,
+            resources,
+            context,
+            constructor_context,
+            self.constructor_calldata(),
+            remaining_gas,
+        )?;
+
+        Ok(Some(call_info))
+    }
+}
+
+impl TransactionInfoCreatorInner for ExecutableDeployAccountTx {
+    fn create_tx_info(&self, only_query: bool) -> 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,
+        };
+
+        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(),
+                })
+            }
+        }
+    }
+}
+
+impl<S: State> Executable<S> for ExecutableInvokeTx {
+    fn run_execute(
+        &self,
+        state: &mut S,
+        resources: &mut ExecutionResources,
+        context: &mut EntryPointExecutionContext,
+        remaining_gas: &mut u64,
+    ) -> TransactionExecutionResult<Option<CallInfo>> {
+        let entry_point_selector = match &self.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 TransactionInfoCreatorInner for ExecutableInvokeTx {
+    fn create_tx_info(&self, only_query: bool) -> 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,
+        };
+
+        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(),
+                })
+            }
+        }
+    }
+}
+
+/// Tries declaring a contract class i.e. setting the contract class at the state at the given class
+/// hash.
+fn try_declare<S: State>(
+    tx: &ExecutableDeclareTx,
+    state: &mut S,
+    class_hash: ClassHash,
+    compiled_class_hash: Option<CompiledClassHash>,
+) -> TransactionExecutionResult<()> {
+    match state.get_compiled_contract_class(class_hash) {
+        Err(StateError::UndeclaredClassHash(_)) => {
+            // Class is undeclared; declare it.
+            state.set_contract_class(class_hash, tx.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 })
+        }
+    }
+}
diff --git a/crates/starknet_api/src/executable_transaction.rs b/crates/starknet_api/src/executable_transaction.rs
index 0553553267..383319fd8e 100644
--- a/crates/starknet_api/src/executable_transaction.rs
+++ b/crates/starknet_api/src/executable_transaction.rs
@@ -1,6 +1,6 @@
 use serde::{Deserialize, Serialize};
 
-use crate::contract_class::ClassInfo;
+use crate::contract_class::{ClassInfo, ContractClass};
 use crate::core::{calculate_contract_address, ChainId, ClassHash, ContractAddress, Nonce};
 use crate::data_availability::DataAvailabilityMode;
 use crate::rpc_transaction::{
@@ -197,6 +197,14 @@ pub struct DeclareTransaction {
 }
 
 impl DeclareTransaction {
+    implement_inner_tx_getter_calls!(
+        (class_hash, ClassHash),
+        (nonce, Nonce),
+        (sender_address, ContractAddress),
+        (signature, TransactionSignature),
+        (version, TransactionVersion)
+    );
+
     pub fn create(
         declare_tx: crate::transaction::DeclareTransaction,
         class_info: ClassInfo,
@@ -222,6 +230,10 @@ impl DeclareTransaction {
 
         compiled_class_hash == supplied_compiled_class_hash
     }
+
+    pub fn contract_class(&self) -> ContractClass {
+        self.class_info.contract_class.clone()
+    }
 }
 
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]