Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add declare specific size related stateless tx validator #225

Merged
merged 1 commit into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
)
}
Loading