Skip to content

Commit

Permalink
feat: add declare specific size related stateless tx validator
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Jun 16, 2024
1 parent 7aad112 commit 89636ba
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 13 deletions.
5 changes: 4 additions & 1 deletion crates/gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ pub struct StatelessTransactionValidatorConfig {
// If true, validates that the resource bounds are not zero.
pub validate_non_zero_l1_gas_fee: bool,
pub validate_non_zero_l2_gas_fee: bool,

pub max_calldata_length: usize,
pub max_signature_length: usize,

// Declare txs specific config.
pub max_bytecode_size: usize,
pub max_raw_class_size: usize,
}

impl SerializeConfig for StatelessTransactionValidatorConfig {
Expand Down
13 changes: 13 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ pub enum StatelessTransactionValidatorError {
(allowed length: {max_signature_length})."
)]
SignatureTooLong { signature_length: usize, max_signature_length: usize },
#[error(
"Cannot declare contract class with bytecode size of {bytecode_size}; max allowed size: \
{max_bytecode_size}."
)]
BytecodeSizeTooLarge { bytecode_size: usize, max_bytecode_size: usize },
#[error(
"Cannot declare contract class with size of {contract_class_object_size}; max allowed \
size: {max_contract_class_object_size}."
)]
ContractClassObjectSizeTooLarge {
contract_class_object_size: usize,
max_contract_class_object_size: usize,
},
}

pub type StatelessTransactionValidatorResult<T> = Result<T, StatelessTransactionValidatorError>;
Expand Down
8 changes: 6 additions & 2 deletions crates/gateway/src/starknet_api_test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use blockifier::test_utils::contracts::FeatureContract;
use blockifier::test_utils::{create_trivial_calldata, CairoVersion, NonceManager};
use serde_json::to_string_pretty;
use starknet_api::calldata;
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
use starknet_api::data_availability::DataAvailabilityMode;
use starknet_api::external_transaction::{
Expand All @@ -15,6 +14,7 @@ use starknet_api::transaction::{
AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, ResourceBounds, Tip,
TransactionSignature, TransactionVersion,
};
use starknet_api::{calldata, stark_felt};

use crate::{declare_tx_args, deploy_account_tx_args, invoke_tx_args};

Expand Down Expand Up @@ -47,7 +47,11 @@ pub fn external_tx_for_testing(
) -> ExternalTransaction {
match tx_type {
TransactionType::Declare => {
external_declare_tx(declare_tx_args!(resource_bounds, signature))
let contract_class = ContractClass {
sierra_program: vec![stark_felt!(1_u32); 3],
..ContractClass::default()
};
external_declare_tx(declare_tx_args!(resource_bounds, signature, contract_class))
}
TransactionType::DeployAccount => external_deploy_account_tx(
deploy_account_tx_args!(resource_bounds, constructor_calldata: calldata, signature),
Expand Down
44 changes: 40 additions & 4 deletions crates/gateway/src/stateless_transaction_validator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use starknet_api::external_transaction::{
ExternalDeployAccountTransaction, ExternalInvokeTransaction, ExternalTransaction,
ResourceBoundsMapping,
ExternalDeclareTransaction, ExternalDeployAccountTransaction, ExternalInvokeTransaction,
ExternalTransaction, ResourceBoundsMapping,
};
use starknet_api::transaction::Resource;

Expand All @@ -24,6 +24,9 @@ impl StatelessTransactionValidator {
self.validate_resource_bounds(tx)?;
self.validate_tx_size(tx)?;

if let ExternalTransaction::Declare(declare_tx) = tx {
self.validate_declare_tx(declare_tx)?;
}
Ok(())
}

Expand Down Expand Up @@ -95,9 +98,42 @@ impl StatelessTransactionValidator {

Ok(())
}
}

// Utilities.
fn validate_declare_tx(
&self,
declare_tx: &ExternalDeclareTransaction,
) -> StatelessTransactionValidatorResult<()> {
let contract_class = match declare_tx {
ExternalDeclareTransaction::V3(tx) => &tx.contract_class,
};
self.validate_class_length(contract_class)
}

fn validate_class_length(
&self,
contract_class: &starknet_api::external_transaction::ContractClass,
) -> StatelessTransactionValidatorResult<()> {
let bytecode_size = contract_class.sierra_program.len();
if bytecode_size > self.config.max_bytecode_size {
return Err(StatelessTransactionValidatorError::BytecodeSizeTooLarge {
bytecode_size,
max_bytecode_size: self.config.max_bytecode_size,
});
}

let contract_class_object_size = serde_json::to_string(&contract_class)
.expect("Unexpected error serializing contract class.")
.len();
if contract_class_object_size > self.config.max_raw_class_size {
return Err(StatelessTransactionValidatorError::ContractClassObjectSizeTooLarge {
contract_class_object_size,
max_contract_class_object_size: self.config.max_raw_class_size,
});
}

Ok(())
}
}

fn validate_resource_is_non_zero(
resource_bounds_mapping: &ResourceBoundsMapping,
Expand Down
64 changes: 58 additions & 6 deletions crates/gateway/src/stateless_transaction_validator_test.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use assert_matches::assert_matches;
use rstest::rstest;
use starknet_api::calldata;
use starknet_api::external_transaction::ResourceBoundsMapping;
use starknet_api::external_transaction::{ContractClass, ResourceBoundsMapping};
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Resource, ResourceBounds, TransactionSignature};
use starknet_api::{calldata, stark_felt};

use crate::config::StatelessTransactionValidatorConfig;
use crate::declare_tx_args;
use crate::starknet_api_test_utils::{
create_resource_bounds_mapping, external_tx_for_testing, zero_resource_bounds_mapping,
TransactionType, NON_EMPTY_RESOURCE_BOUNDS,
create_resource_bounds_mapping, external_declare_tx, external_tx_for_testing,
zero_resource_bounds_mapping, TransactionType, NON_EMPTY_RESOURCE_BOUNDS,
};
use crate::stateless_transaction_validator::{
StatelessTransactionValidator, StatelessTransactionValidatorConfig,
StatelessTransactionValidatorError,
StatelessTransactionValidator, StatelessTransactionValidatorError,
};

const DEFAULT_VALIDATOR_CONFIG_FOR_TESTING: StatelessTransactionValidatorConfig =
Expand All @@ -20,6 +21,8 @@ const DEFAULT_VALIDATOR_CONFIG_FOR_TESTING: StatelessTransactionValidatorConfig
validate_non_zero_l2_gas_fee: false,
max_calldata_length: 1,
max_signature_length: 1,
max_bytecode_size: 10000,
max_raw_class_size: 100000,
};

#[rstest]
Expand Down Expand Up @@ -180,3 +183,52 @@ fn test_signature_too_long(
}
);
}

#[test]
fn test_declare_bytecode_size_too_long() {
let config_max_bytecode_size = 10;
let tx_validator = StatelessTransactionValidator {
config: StatelessTransactionValidatorConfig {
max_bytecode_size: config_max_bytecode_size,
..DEFAULT_VALIDATOR_CONFIG_FOR_TESTING
},
};
let sierra_program_length = config_max_bytecode_size + 1;
let sierra_program = vec![stark_felt!(1_u128); sierra_program_length];
let contract_class = ContractClass { sierra_program, ..Default::default() };
let tx = external_declare_tx(declare_tx_args!(contract_class));

assert_matches!(
tx_validator.validate(&tx).unwrap_err(),
StatelessTransactionValidatorError::BytecodeSizeTooLarge {
bytecode_size,
max_bytecode_size
} if (
bytecode_size, max_bytecode_size
) == (sierra_program_length, config_max_bytecode_size)
)
}

#[test]
fn test_declare_contract_class_size_too_long() {
let config_max_raw_class_size = 100;
let tx_validator = StatelessTransactionValidator {
config: StatelessTransactionValidatorConfig {
max_raw_class_size: config_max_raw_class_size,
..DEFAULT_VALIDATOR_CONFIG_FOR_TESTING
},
};
let contract_class =
ContractClass { sierra_program: vec![stark_felt!(1_u128); 3], ..Default::default() };
let contract_class_length = serde_json::to_string(&contract_class).unwrap().len();
let tx = external_declare_tx(declare_tx_args!(contract_class));

assert_matches!(
tx_validator.validate(&tx).unwrap_err(),
StatelessTransactionValidatorError::ContractClassObjectSizeTooLarge {
contract_class_object_size, max_contract_class_object_size
} if (
contract_class_object_size, max_contract_class_object_size
) == (contract_class_length, config_max_raw_class_size)
)
}

0 comments on commit 89636ba

Please sign in to comment.