Skip to content

Commit

Permalink
refactor: create executable tx in the gateway process_tx
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Aug 29, 2024
1 parent c24efcc commit 191165f
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 18 deletions.
39 changes: 37 additions & 2 deletions crates/gateway/src/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::sync::Arc;
use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass;
use cairo_lang_starknet_classes::contract_class::ContractClass as CairoLangContractClass;
use starknet_api::contract_class::ClassInfo;
use starknet_api::core::CompiledClassHash;
use starknet_api::core::{ChainId, ClassHash, CompiledClassHash};
use starknet_api::executable_transaction::DeclareTransaction;
use starknet_api::rpc_transaction::RpcDeclareTransaction;
use starknet_api::transaction::{DeclareTransactionV3, TransactionHasher};
use starknet_sierra_compile::cairo_lang_compiler::CairoLangSierraToCasmCompiler;
use starknet_sierra_compile::config::SierraToCasmCompilationConfig;
use starknet_sierra_compile::utils::into_contract_class_for_compilation;
Expand All @@ -28,10 +30,11 @@ impl GatewayCompiler {
Self { sierra_to_casm_compiler: Arc::new(CairoLangSierraToCasmCompiler { config }) }
}

// TODO(Arni): Squash this function into `process_declare_tx`.
/// Formats the contract class for compilation, compiles it, and returns the compiled contract
/// class wrapped in a [`ClassInfo`].
/// Assumes the contract class is of a Sierra program which is compiled to Casm.
pub fn process_declare_tx(
pub(crate) fn class_info_from_declare_tx(
&self,
declare_tx: &RpcDeclareTransaction,
) -> GatewayResult<ClassInfo> {
Expand All @@ -50,6 +53,38 @@ impl GatewayCompiler {
})
}

/// Processes a declare transaction, compiling the contract class and returning the executable
/// declare transaction.
pub fn process_declare_tx(
&self,
rpc_tx: RpcDeclareTransaction,
chain_id: &ChainId,
) -> GatewayResult<DeclareTransaction> {
let class_info = self.class_info_from_declare_tx(&rpc_tx)?;
let RpcDeclareTransaction::V3(tx) = rpc_tx;
let declare_tx = starknet_api::transaction::DeclareTransaction::V3(DeclareTransactionV3 {
class_hash: ClassHash::default(), /* FIXME(yael 15/4/24): call the starknet-api
* function once ready */
resource_bounds: tx.resource_bounds.into(),
tip: tx.tip,
signature: tx.signature,
nonce: tx.nonce,
compiled_class_hash: tx.compiled_class_hash,
sender_address: tx.sender_address,
nonce_data_availability_mode: tx.nonce_data_availability_mode,
fee_data_availability_mode: tx.fee_data_availability_mode,
paymaster_data: tx.paymaster_data,
account_deployment_data: tx.account_deployment_data,
});
let tx_hash = declare_tx
.calculate_transaction_hash(chain_id, &declare_tx.version())
.map_err(|err| {
error!("Failed to calculate tx hash: {}", err);
GatewaySpecError::UnexpectedError { data: "Internal server error.".to_owned() }
})?;
Ok(DeclareTransaction { tx: declare_tx, tx_hash, class_info })
}

fn compile(
&self,
cairo_lang_contract_class: CairoLangContractClass,
Expand Down
11 changes: 6 additions & 5 deletions crates/gateway/src/compilation_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn test_compile_contract_class_compiled_class_hash_mismatch(
declare_tx_v3.compiled_class_hash = wrong_supplied_hash;
let declare_tx = RpcDeclareTransaction::V3(declare_tx_v3);

let err = gateway_compiler.process_declare_tx(&declare_tx).unwrap_err();
let err = gateway_compiler.class_info_from_declare_tx(&declare_tx).unwrap_err();
assert_eq!(err, GatewaySpecError::CompiledClassHashMismatch);
assert!(logs_contain(
format!(
Expand All @@ -62,7 +62,8 @@ fn test_compile_contract_class_bytecode_size_validation(declare_tx_v3: RpcDeclar
max_bytecode_size: 1,
});

let result = gateway_compiler.process_declare_tx(&RpcDeclareTransaction::V3(declare_tx_v3));
let result =
gateway_compiler.class_info_from_declare_tx(&RpcDeclareTransaction::V3(declare_tx_v3));
assert_matches!(result.unwrap_err(), GatewaySpecError::CompilationFailed);
let expected_compilation_error =
CompilationUtilError::CompilationError("Code size limit exceeded.".to_owned());
Expand All @@ -80,7 +81,7 @@ fn test_compile_contract_class_bad_sierra(
declare_tx_v3.contract_class.sierra_program[..100].to_vec();
let declare_tx = RpcDeclareTransaction::V3(declare_tx_v3);

let err = gateway_compiler.process_declare_tx(&declare_tx).unwrap_err();
let err = gateway_compiler.class_info_from_declare_tx(&declare_tx).unwrap_err();
assert_eq!(err, GatewaySpecError::CompilationFailed);

let expected_compilation_error =
Expand All @@ -89,7 +90,7 @@ fn test_compile_contract_class_bad_sierra(
}

#[rstest]
fn test_process_declare_tx_success(
fn test_class_info_from_declare_tx_tx_success(
gateway_compiler: GatewayCompiler,
declare_tx_v3: RpcDeclareTransactionV3,
) {
Expand All @@ -98,7 +99,7 @@ fn test_process_declare_tx_success(
let abi_length = contract_class.abi.len();
let declare_tx = RpcDeclareTransaction::V3(declare_tx_v3);

let class_info = gateway_compiler.process_declare_tx(&declare_tx).unwrap();
let class_info = gateway_compiler.class_info_from_declare_tx(&declare_tx).unwrap();
let compiled_class_hash =
CompiledClassHash(class_info.casm_contract_class.compiled_class_hash());
assert_eq!(compiled_class_hash, *test_contract_compiled_class_hash());
Expand Down
29 changes: 20 additions & 9 deletions crates/gateway/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use async_trait::async_trait;
use axum::extract::State;
use axum::routing::{get, post};
use axum::{Json, Router};
use blockifier::execution::contract_class::ClassInfo;
use starknet_api::executable_transaction::Transaction;
use starknet_api::rpc_transaction::RpcTransaction;
use starknet_api::transaction::TransactionHash;
Expand All @@ -23,6 +22,7 @@ use crate::rpc_state_reader::RpcStateReaderFactory;
use crate::state_reader::StateReaderFactory;
use crate::stateful_transaction_validator::StatefulTransactionValidator;
use crate::stateless_transaction_validator::StatelessTransactionValidator;
use crate::utils::compile_to_casm_and_convert_rpc_to_executable_tx;

#[cfg(test)]
#[path = "gateway_test.rs"]
Expand Down Expand Up @@ -130,25 +130,36 @@ fn process_tx(
// Perform stateless validations.
stateless_tx_validator.validate(&tx)?;

// Compile Sierra to Casm.
let optional_class_info = match &tx {
RpcTransaction::Declare(declare_tx) => Some(
ClassInfo::try_from(gateway_compiler.process_declare_tx(declare_tx)?).map_err(|e| {
// TODO(Arni): remove copy_of_rpc_tx and use executable_tx instead.
let copy_of_rpc_tx = tx.clone();
let executable_tx = compile_to_casm_and_convert_rpc_to_executable_tx(
tx,
&gateway_compiler,
&stateful_tx_validator.config.chain_info.chain_id,
)?;
let optional_class_info = match executable_tx {
starknet_api::executable_transaction::Transaction::Declare(tx) => {
Some(tx.class_info.try_into().map_err(|e| {
error!("Failed to convert Starknet API ClassInfo to Blockifier ClassInfo: {:?}", e);
GatewaySpecError::UnexpectedError { data: "Internal server error.".to_owned() }
})?,
),
})?)
}
_ => None,
};

let validator = stateful_tx_validator.instantiate_validator(state_reader_factory)?;
// TODO(Yael 31/7/24): refactor after IntrnalTransaction is ready, delete validate_info and
// compute all the info outside of run_validate.
let validate_info = stateful_tx_validator.run_validate(&tx, optional_class_info, validator)?;
let validate_info =
stateful_tx_validator.run_validate(&copy_of_rpc_tx, optional_class_info, validator)?;

// TODO(Arni): Add the Sierra and the Casm to the mempool input.
Ok(MempoolInput {
tx: Transaction::new_from_rpc_tx(tx, validate_info.tx_hash, validate_info.sender_address),
tx: Transaction::new_from_rpc_tx(
copy_of_rpc_tx,
validate_info.tx_hash,
validate_info.sender_address,
),
account: Account {
sender_address: validate_info.sender_address,
state: AccountState { nonce: validate_info.account_nonce },
Expand Down
52 changes: 52 additions & 0 deletions crates/gateway/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ use blockifier::transaction::transactions::{
InvokeTransaction as BlockifierInvokeTransaction,
};
use starknet_api::core::{calculate_contract_address, ChainId, ContractAddress};
use starknet_api::executable_transaction::{
DeployAccountTransaction as ExecutableDeployAccountTransaction,
InvokeTransaction as ExecutableInvokeTransaction,
Transaction as ExecutableTransaction,
};
use starknet_api::rpc_transaction::RpcTransaction;
use starknet_api::transaction::{
DeclareTransaction,
Expand All @@ -15,8 +20,55 @@ use starknet_api::transaction::{
};
use tracing::error;

use crate::compilation::GatewayCompiler;
use crate::errors::{GatewaySpecError, StatefulTransactionValidatorResult};

/// Converts an RPC transaction to an executable transaction.
/// This conversion is dependent on the chain ID.
/// Note, that for declare transaction this step is heavy, as it requires compilation of Sierra to
/// executable contract class.
pub fn compile_to_casm_and_convert_rpc_to_executable_tx(
rpc_tx: RpcTransaction,
gateway_compiler: &GatewayCompiler,
chain_id: &ChainId,
) -> Result<ExecutableTransaction, GatewaySpecError> {
Ok(match rpc_tx {
RpcTransaction::Declare(rpc_declare_tx) => ExecutableTransaction::Declare(
gateway_compiler.process_declare_tx(rpc_declare_tx, chain_id).map_err(|error| {
error!(
"Failed to convert RPC declare transaction to executable transaction: {}",
error
);
GatewaySpecError::UnexpectedError { data: "Internal server error".to_owned() }
})?,
),
RpcTransaction::DeployAccount(rpc_deploy_account_tx) => {
ExecutableTransaction::DeployAccount(
ExecutableDeployAccountTransaction::from_rpc_tx(rpc_deploy_account_tx, chain_id)
.map_err(|error| {
error!(
"Failed to convert RPC deploy account transaction to executable \
transaction: {}",
error
);
GatewaySpecError::UnexpectedError {
data: "Internal server error".to_owned(),
}
})?,
)
}
RpcTransaction::Invoke(rpc_invoke_tx) => ExecutableTransaction::Invoke(
ExecutableInvokeTransaction::from_rpc_tx(rpc_invoke_tx, chain_id).map_err(|error| {
error!(
"Failed to convert RPC invoke transaction to executable transaction: {}",
error
);
GatewaySpecError::UnexpectedError { data: "Internal server error".to_owned() }
})?,
),
})
}

// TODO(Arni): Remove this function. Replace with a function that take ownership of RpcTransaction.
pub fn rpc_tx_to_account_tx(
rpc_tx: &RpcTransaction,
Expand Down
58 changes: 56 additions & 2 deletions crates/starknet_api/src/executable_transaction.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
use serde::{Deserialize, Serialize};

use crate::contract_class::ClassInfo;
use crate::core::{ClassHash, ContractAddress, Nonce};
use crate::core::{calculate_contract_address, ChainId, ClassHash, ContractAddress, Nonce};
use crate::data_availability::DataAvailabilityMode;
use crate::rpc_transaction::RpcTransaction;
use crate::rpc_transaction::{RpcDeployAccountTransaction, RpcInvokeTransaction, RpcTransaction};
use crate::transaction::{
AccountDeploymentData,
Calldata,
ContractAddressSalt,
DeployAccountTransactionV3,
DeprecatedResourceBoundsMapping,
InvokeTransactionV3,
PaymasterData,
Tip,
TransactionHash,
TransactionHasher,
TransactionSignature,
TransactionVersion,
};
use crate::StarknetApiError;

macro_rules! implement_inner_tx_getter_calls {
($(($field:ident, $field_type:ty)),*) => {
Expand Down Expand Up @@ -157,6 +161,35 @@ impl DeployAccountTransaction {
pub fn tx(&self) -> &crate::transaction::DeployAccountTransaction {
&self.tx
}

pub fn from_rpc_tx(
rpc_tx: RpcDeployAccountTransaction,
chain_id: &ChainId,
) -> Result<Self, StarknetApiError> {
let RpcDeployAccountTransaction::V3(tx) = rpc_tx;
let deploy_account_tx =
crate::transaction::DeployAccountTransaction::V3(DeployAccountTransactionV3 {
resource_bounds: tx.resource_bounds.into(),
tip: tx.tip,
signature: tx.signature,
nonce: tx.nonce,
class_hash: tx.class_hash,
contract_address_salt: tx.contract_address_salt,
constructor_calldata: tx.constructor_calldata,
nonce_data_availability_mode: tx.nonce_data_availability_mode,
fee_data_availability_mode: tx.fee_data_availability_mode,
paymaster_data: tx.paymaster_data,
});
let contract_address = calculate_contract_address(
deploy_account_tx.contract_address_salt(),
deploy_account_tx.class_hash(),
&deploy_account_tx.constructor_calldata(),
ContractAddress::default(),
)?;
let tx_hash =
deploy_account_tx.calculate_transaction_hash(chain_id, &deploy_account_tx.version())?;
Ok(Self { tx: deploy_account_tx, tx_hash, contract_address })
}
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
Expand All @@ -178,4 +211,25 @@ impl InvokeTransaction {
pub fn tx(&self) -> &crate::transaction::InvokeTransaction {
&self.tx
}

pub fn from_rpc_tx(
rpc_tx: RpcInvokeTransaction,
chain_id: &ChainId,
) -> Result<Self, StarknetApiError> {
let RpcInvokeTransaction::V3(tx) = rpc_tx;
let invoke_tx = crate::transaction::InvokeTransaction::V3(InvokeTransactionV3 {
resource_bounds: tx.resource_bounds.into(),
tip: tx.tip,
signature: tx.signature,
nonce: tx.nonce,
sender_address: tx.sender_address,
calldata: tx.calldata,
nonce_data_availability_mode: tx.nonce_data_availability_mode,
fee_data_availability_mode: tx.fee_data_availability_mode,
paymaster_data: tx.paymaster_data,
account_deployment_data: tx.account_deployment_data,
});
let tx_hash = invoke_tx.calculate_transaction_hash(chain_id, &invoke_tx.version())?;
Ok(Self { tx: invoke_tx, tx_hash })
}
}

0 comments on commit 191165f

Please sign in to comment.