Skip to content

Commit

Permalink
chore: define creator for executable transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Sep 8, 2024
1 parent 25c4fcd commit 865ec2d
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 58 deletions.
22 changes: 1 addition & 21 deletions crates/gateway/src/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ 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::rpc_transaction::RpcDeclareTransaction;
use starknet_sierra_compile::cairo_lang_compiler::CairoLangSierraToCasmCompiler;
use starknet_sierra_compile::command_line_compiler::CommandLineCompiler;
Expand Down Expand Up @@ -37,7 +36,7 @@ impl GatewayCompiler {
/// 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 process_declare_tx(
&self,
declare_tx: &RpcDeclareTransaction,
) -> GatewayResult<ClassInfo> {
Expand All @@ -47,8 +46,6 @@ impl GatewayCompiler {

let casm_contract_class = self.compile(cairo_lang_contract_class)?;

validate_compiled_class_hash(&casm_contract_class, &tx.compiled_class_hash)?;

Ok(ClassInfo {
casm_contract_class,
sierra_program_length: rpc_contract_class.sierra_program.len(),
Expand All @@ -73,20 +70,3 @@ impl GatewayCompiler {
}
}
}

/// Validates that the compiled class hash of the compiled contract class matches the supplied
/// compiled class hash.
fn validate_compiled_class_hash(
casm_contract_class: &CasmContractClass,
supplied_compiled_class_hash: &CompiledClassHash,
) -> GatewayResult<()> {
let compiled_class_hash = CompiledClassHash(casm_contract_class.compiled_class_hash());
if compiled_class_hash != *supplied_compiled_class_hash {
debug!(
"Compiled class hash mismatch. Supplied: {:?}, Hash result: {:?}",
supplied_compiled_class_hash, compiled_class_hash
);
return Err(GatewaySpecError::CompiledClassHashMismatch);
}
Ok(())
}
23 changes: 0 additions & 23 deletions crates/gateway/src/compilation_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,6 @@ fn declare_tx_v3() -> RpcDeclareTransactionV3 {
)
}

// TODO(Arni): Redesign this test once the compiler is passed with dependancy injection.
#[traced_test]
#[rstest]
fn test_compile_contract_class_compiled_class_hash_mismatch(
gateway_compiler: GatewayCompiler,
mut declare_tx_v3: RpcDeclareTransactionV3,
) {
let expected_hash = declare_tx_v3.compiled_class_hash;
let wrong_supplied_hash = CompiledClassHash::default();
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();
assert_eq!(err, GatewaySpecError::CompiledClassHashMismatch);
assert!(logs_contain(
format!(
"Compiled class hash mismatch. Supplied: {:?}, Hash result: {:?}",
wrong_supplied_hash, expected_hash
)
.as_str()
));
}

// TODO(Arni): Redesign this test once the compiler is passed with dependancy injection.
#[traced_test]
#[rstest]
Expand Down
2 changes: 2 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub type GatewayResult<T> = Result<T, GatewaySpecError>;
impl IntoResponse for GatewaySpecError {
fn into_response(self) -> Response {
let as_rpc = self.into_rpc();
// TODO(Arni): Fix the status code. The status code should be a HTTP status code - not a
// Json RPC error code. status code.
let status =
StatusCode::from_u16(u16::try_from(as_rpc.code).expect("Expecting a valid u16"))
.expect("Expecting a valid error code");
Expand Down
37 changes: 28 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_contract_and_build_executable_tx;

#[cfg(test)]
#[path = "gateway_test.rs"]
Expand Down Expand Up @@ -130,25 +130,44 @@ 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 directly as the mempool input.
let copy_of_rpc_tx = tx.clone();
let executable_tx = compile_contract_and_build_executable_tx(
tx,
&gateway_compiler,
&stateful_tx_validator.config.chain_info.chain_id,
)?;

// Perfom post compilation validations.
if let Transaction::Declare(executable_declare_tx) = &executable_tx {
if !executable_declare_tx.validate_compiled_class_hash() {
return Err(GatewaySpecError::CompiledClassHashMismatch);
}
}

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
27 changes: 24 additions & 3 deletions crates/gateway/src/gateway_test.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
use std::sync::Arc;

use assert_matches::assert_matches;
use axum::body::{Bytes, HttpBody};
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use blockifier::context::ChainInfo;
use blockifier::test_utils::CairoVersion;
use mempool_test_utils::starknet_api_test_utils::{create_executable_tx, invoke_tx};
use mempool_test_utils::starknet_api_test_utils::{create_executable_tx, declare_tx, invoke_tx};
use mockall::predicate::eq;
use starknet_api::core::ContractAddress;
use starknet_api::rpc_transaction::RpcTransaction;
use starknet_api::core::{CompiledClassHash, ContractAddress};
use starknet_api::rpc_transaction::{RpcDeclareTransaction, RpcTransaction};
use starknet_api::transaction::{TransactionHash, ValidResourceBounds};
use starknet_mempool_types::communication::MockMempoolClient;
use starknet_mempool_types::mempool_types::{Account, AccountState, MempoolInput};
use starknet_sierra_compile::config::SierraToCasmCompilationConfig;

use crate::compilation::GatewayCompiler;
use crate::config::{StatefulTransactionValidatorConfig, StatelessTransactionValidatorConfig};
use crate::errors::GatewaySpecError;
use crate::gateway::{add_tx, AppState, SharedMempoolClient};
use crate::state_reader_test_utils::{local_test_state_reader_factory, TestStateReaderFactory};
use crate::stateful_transaction_validator::StatefulTransactionValidator;
Expand Down Expand Up @@ -93,6 +95,25 @@ async fn to_bytes(res: Response) -> Bytes {
res.into_body().collect().await.unwrap().to_bytes()
}

// Gateway spec errors tests.
// TODO(Arni): Add tests for all the error cases. Check the response (use `into_response` on the
// result of `add_tx`).

#[tokio::test]
async fn test_compiled_class_hash_mismatch() {
let mut declare_tx =
assert_matches!(declare_tx(), RpcTransaction::Declare(RpcDeclareTransaction::V3(tx)) => tx);
declare_tx.compiled_class_hash = CompiledClassHash::default();
let tx = RpcTransaction::Declare(RpcDeclareTransaction::V3(declare_tx));

let mock_mempool_client = MockMempoolClient::new();
let state_reader_factory = local_test_state_reader_factory(CairoVersion::Cairo1, false);
let app_state = app_state(Arc::new(mock_mempool_client), state_reader_factory);

let err = add_tx(State(app_state), tx.into()).await.unwrap_err();
assert_matches!(err, GatewaySpecError::CompiledClassHashMismatch);
}

fn calculate_hash(rpc_tx: &RpcTransaction) -> TransactionHash {
let optional_class_info = match &rpc_tx {
RpcTransaction::Declare(_declare_tx) => {
Expand Down
77 changes: 75 additions & 2 deletions crates/gateway/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ use blockifier::transaction::transactions::{
InvokeTransaction as BlockifierInvokeTransaction,
};
use starknet_api::core::{calculate_contract_address, ChainId, ClassHash, ContractAddress};
use starknet_api::executable_transaction::{
DeclareTransaction as ExecutableDeclareTransaction,
DeployAccountTransaction as ExecutableDeployAccountTransaction,
InvokeTransaction as ExecutableInvokeTransaction,
Transaction as ExecutableTransaction,
};
use starknet_api::rpc_transaction::{
RpcDeclareTransaction,
RpcDeployAccountTransaction,
Expand All @@ -21,10 +27,77 @@ use starknet_api::transaction::{
TransactionHasher,
ValidResourceBounds,
};
use tracing::error;
use tracing::{debug, error};

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

/// Converts an RPC transaction to an executable transaction.
/// Note, for declare transaction this step is heavy, as it requires compilation of Sierra to
/// executable contract class.
pub fn compile_contract_and_build_executable_tx(
rpc_tx: RpcTransaction,
gateway_compiler: &GatewayCompiler,
chain_id: &ChainId,
) -> GatewayResult<ExecutableTransaction> {
Ok(match rpc_tx {
RpcTransaction::Declare(rpc_declare_tx) => {
let executable_declare_tx = compile_contract_and_build_executable_declare_tx(
rpc_declare_tx,
gateway_compiler,
chain_id,
)?;
ExecutableTransaction::Declare(executable_declare_tx)
}
RpcTransaction::DeployAccount(rpc_deploy_account_tx) => {
let executable_deploy_account_tx =
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(),
}
})?;
ExecutableTransaction::DeployAccount(executable_deploy_account_tx)
}
RpcTransaction::Invoke(rpc_invoke_tx) => {
let executable_invoke_tx = 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() }
})?;
ExecutableTransaction::Invoke(executable_invoke_tx)
}
})
}

use crate::errors::{GatewaySpecError, StatefulTransactionValidatorResult};
fn compile_contract_and_build_executable_declare_tx(
rpc_tx: RpcDeclareTransaction,
gateway_compiler: &GatewayCompiler,
chain_id: &ChainId,
) -> GatewayResult<ExecutableDeclareTransaction> {
let class_info = gateway_compiler.process_declare_tx(&rpc_tx)?;
let declare_tx: starknet_api::transaction::DeclareTransaction = rpc_tx.into();
let executable_declare_tx =
ExecutableDeclareTransaction::create(declare_tx, class_info, chain_id).map_err(|err| {
debug!("Failed to create executable declare transaction {:?}", err);
GatewaySpecError::UnexpectedError { data: "Internal server error.".to_owned() }
})?;

Ok(executable_declare_tx)
}

// TODO(Arni): Remove this function.
pub fn rpc_tx_to_account_tx(
rpc_tx: &RpcTransaction,
// FIXME(yael 15/4/24): calculate class_info inside the function once compilation code is ready
Expand Down

0 comments on commit 865ec2d

Please sign in to comment.