From d0d405325a13f7bc4a74b00a625b32a90644e38c Mon Sep 17 00:00:00 2001 From: Arni Hod Date: Wed, 26 Jun 2024 14:39:07 +0300 Subject: [PATCH] feat: declare post compilation builtin validation --- crates/gateway/src/errors.rs | 4 +- crates/gateway/src/gateway.rs | 32 ++++++++++++- crates/gateway/src/utils.rs | 21 +++++++++ crates/gateway/src/utils_test.rs | 78 ++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 crates/gateway/src/utils_test.rs diff --git a/crates/gateway/src/errors.rs b/crates/gateway/src/errors.rs index 3b65f783..d3effbbc 100644 --- a/crates/gateway/src/errors.rs +++ b/crates/gateway/src/errors.rs @@ -22,7 +22,7 @@ pub enum GatewayError { CompilationError(#[from] starknet_sierra_compile::compile::CompilationUtilError), #[error( "The supplied compiled class hash {supplied:?} does not match the hash of the Casm class \ - compiled from the supplied Sierra {hash_result:?}." + compiled from the supplied Sierra {hash_result:?}" )] CompiledClassHashMismatch { supplied: CompiledClassHash, hash_result: CompiledClassHash }, #[error(transparent)] @@ -37,6 +37,8 @@ pub enum GatewayError { StatefulTransactionValidatorError(#[from] StatefulTransactionValidatorError), #[error(transparent)] StatelessTransactionValidatorError(#[from] StatelessTransactionValidatorError), + #[error("{builtins:?} is not a subsquence of {supported_builtins:?}")] + SupportedBuiltins { builtins: Vec, supported_builtins: Vec }, } impl IntoResponse for GatewayError { diff --git a/crates/gateway/src/gateway.rs b/crates/gateway/src/gateway.rs index e0646ef3..b8df34a4 100644 --- a/crates/gateway/src/gateway.rs +++ b/crates/gateway/src/gateway.rs @@ -8,6 +8,9 @@ use axum::routing::{get, post}; use axum::{Json, Router}; use blockifier::execution::contract_class::{ClassInfo, ContractClass, ContractClassV1}; use blockifier::execution::execution_utils::felt_to_stark_felt; +use cairo_lang_starknet_classes::casm_contract_class::{ + CasmContractClass, CasmContractEntryPoints, +}; use starknet_api::core::CompiledClassHash; use starknet_api::rpc_transaction::{RPCDeclareTransaction, RPCTransaction}; use starknet_api::transaction::TransactionHash; @@ -23,7 +26,7 @@ use crate::starknet_api_test_utils::get_sender_address; use crate::state_reader::StateReaderFactory; use crate::stateful_transaction_validator::StatefulTransactionValidator; use crate::stateless_transaction_validator::StatelessTransactionValidator; -use crate::utils::external_tx_to_thin_tx; +use crate::utils::{external_tx_to_thin_tx, is_subsequence}; #[cfg(test)] #[path = "gateway_test.rs"] @@ -159,6 +162,7 @@ pub fn compile_contract_class(declare_tx: &RPCDeclareTransaction) -> GatewayResu return Err(GatewayError::CompilationError(CompilationUtilError::CompilationPanic)); } }; + validate_casm_class(&casm_contract_class)?; let hash_result = CompiledClassHash(felt_to_stark_felt(&casm_contract_class.compiled_class_hash())); @@ -188,3 +192,29 @@ pub fn create_gateway( let state_reader_factory = Arc::new(RpcStateReaderFactory { config: rpc_state_reader_config }); Gateway::new(config, state_reader_factory, client) } + +// TODO(Arni): Add to a config. +fn get_supported_builtins() -> Vec { + let builtins = + ["pedersen", "range_check", "ecdsa", "bitwise", "ec_op", "poseidon", "segment_arena"]; + builtins.iter().map(|builtin| builtin.to_string()).collect() +} + +// TODO(Arni): Add test. +fn validate_casm_class(contract_class: &CasmContractClass) -> Result<(), GatewayError> { + let CasmContractEntryPoints { external, l1_handler, constructor } = + &contract_class.entry_points_by_type; + let entry_points_iterator = external.iter().chain(l1_handler.iter()).chain(constructor.iter()); + + let supported_builtins = &get_supported_builtins(); + for entry_point in entry_points_iterator { + let builtins = &entry_point.builtins; + if !is_subsequence(builtins, supported_builtins) { + return Err(GatewayError::SupportedBuiltins { + builtins: builtins.clone(), + supported_builtins: supported_builtins.clone(), + }); + } + } + Ok(()) +} diff --git a/crates/gateway/src/utils.rs b/crates/gateway/src/utils.rs index 2e438f63..396c3c01 100644 --- a/crates/gateway/src/utils.rs +++ b/crates/gateway/src/utils.rs @@ -18,6 +18,10 @@ use starknet_mempool_types::mempool_types::ThinTransaction; use crate::errors::StatefulTransactionValidatorResult; use crate::starknet_api_test_utils::get_sender_address; +#[cfg(test)] +#[path = "utils_test.rs"] +mod utils_test; + macro_rules! implement_ref_getters { ($(($member_name:ident, $member_type:ty));* $(;)?) => { $(fn $member_name(&self) -> &$member_type { @@ -145,3 +149,20 @@ pub fn get_tx_hash(tx: &AccountTransaction) -> TransactionHash { AccountTransaction::Invoke(tx) => tx.tx_hash, } } + +/// Checks whether 'subsequence' is a subsequence of 'sequence'. +pub fn is_subsequence(subsequence: &[T], sequence: &[T]) -> bool { + let mut offset = 0; + + for item in sequence { + if offset == subsequence.len() { + return true; + } + + if item == &subsequence[offset] { + offset += 1; + } + } + + offset == subsequence.len() +} diff --git a/crates/gateway/src/utils_test.rs b/crates/gateway/src/utils_test.rs new file mode 100644 index 00000000..1135f8ea --- /dev/null +++ b/crates/gateway/src/utils_test.rs @@ -0,0 +1,78 @@ +use pretty_assertions::assert_eq; +use rstest::rstest; + +use crate::utils::is_subsequence; + +#[rstest] +#[case::empty( + &[], + &[], + true +)] +#[case::empty_subsequence( + &[], + &["a", "b"], + true +)] +#[case::empty_sequence( + &["a"], + &[], + false +)] +#[case::subsequence_1( + &["a"], + &["a", "b", "c"], + true +)] +#[case::subsequence_1( + &["b"], + &["a", "b", "c"], + true +)] +#[case::subsequence_1( + &["c"], + &["a", "b", "c"], + true +)] +#[case::subsequence_4( + &["a", "b"], + &["a", "b", "c"], + true +)] +#[case::subsequence_5( + &["a", "c"], + &["a", "b", "c"], + true +)] +#[case::subsequence_6( + &["b", "c"], + &["a", "b", "c"], + true +)] +#[case::subsequence_7( + &["a", "b", "c"], + &["a", "b", "c"], + true +)] +#[case::out_of_order_1( + &["b", "a"], + &["a", "b", "c"], + false +)] +#[case::out_of_order_2( + &["b", "a", "c"], + &["a", "b", "c"], + false +)] +#[case::unrelated( + &["a", "b", "d"], + &["a", "b", "c"], + false +)] +fn test_is_subsequence( + #[case] subsequence: &[&str], + #[case] sequence: &[&str], + #[case] expected_result: bool, +) { + assert_eq!(is_subsequence(subsequence, sequence), expected_result); +}