Skip to content

Commit

Permalink
feat: declare post compilation builtin validation (#296)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware authored Jul 2, 2024
1 parent d7802ae commit d9b06e3
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ blockifier= { workspace = true , features = ["testing"] }
cairo-lang-starknet-classes.workspace = true
cairo-vm.workspace = true
hyper.workspace = true
lazy_static.workspace = true
papyrus_config.workspace = true
papyrus_rpc.workspace = true
reqwest.workspace = true
Expand Down
4 changes: 3 additions & 1 deletion crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub enum GatewayError {
CompilationError(#[from] 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)]
Expand All @@ -38,6 +38,8 @@ pub enum GatewayError {
StatefulTransactionValidatorError(#[from] StatefulTransactionValidatorError),
#[error(transparent)]
StatelessTransactionValidatorError(#[from] StatelessTransactionValidatorError),
#[error("{builtins:?} is not a subsquence of {supported_builtins:?}")]
UnsupportedBuiltins { builtins: Vec<String>, supported_builtins: Vec<String> },
}

impl IntoResponse for GatewayError {
Expand Down
37 changes: 36 additions & 1 deletion crates/gateway/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ 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 lazy_static::lazy_static;
use starknet_api::core::CompiledClassHash;
use starknet_api::rpc_transaction::{RPCDeclareTransaction, RPCTransaction};
use starknet_api::transaction::TransactionHash;
Expand All @@ -25,7 +29,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::{external_tx_to_thin_tx, get_sender_address};
use crate::utils::{external_tx_to_thin_tx, get_sender_address, is_subsequence};

#[cfg(test)]
#[path = "gateway_test.rs"]
Expand Down Expand Up @@ -161,6 +165,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()));
Expand Down Expand Up @@ -199,3 +204,33 @@ impl ComponentRunner for Gateway {
Ok(())
}
}

// TODO(Arni): Add to a config.
// TODO(Arni): Use the Builtin enum from Starknet-api, and explicitly tag each builtin as supported
// or unsupported so that the compiler would alert us on new builtins.
lazy_static! {
static ref SUPPORTED_BUILTINS: Vec<String> = {
// The OS expects this order for the builtins.
const SUPPORTED_BUILTIN_NAMES: [&str; 7] =
["pedersen", "range_check", "ecdsa", "bitwise", "ec_op", "poseidon", "segment_arena"];
SUPPORTED_BUILTIN_NAMES.iter().map(|builtin| builtin.to_string()).collect::<Vec<String>>()
};
}

// 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());

for entry_point in entry_points_iterator {
let builtins = &entry_point.builtins;
if !is_subsequence(builtins, &SUPPORTED_BUILTINS) {
return Err(GatewayError::UnsupportedBuiltins {
builtins: builtins.clone(),
supported_builtins: SUPPORTED_BUILTINS.to_vec(),
});
}
}
Ok(())
}
21 changes: 21 additions & 0 deletions crates/gateway/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ use starknet_mempool_types::mempool_types::ThinTransaction;

use crate::errors::StatefulTransactionValidatorResult;

#[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 {
Expand Down Expand Up @@ -155,3 +159,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<T: Eq>(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()
}
78 changes: 78 additions & 0 deletions crates/gateway/src/utils_test.rs
Original file line number Diff line number Diff line change
@@ -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_2(
&["b"],
&["a", "b", "c"],
true
)]
#[case::subsequence_3(
&["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);
}

0 comments on commit d9b06e3

Please sign in to comment.