Skip to content

Commit

Permalink
feat: stateless validator add calldata len validator
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Apr 10, 2024
1 parent f675230 commit e5c3156
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 24 deletions.
8 changes: 8 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ pub enum TransactionValidatorError {
},
#[error("The resource bounds mapping is missing a resource {resource:?}.")]
MissingResource { resource: Resource },
#[error(
"Calldata length exceeded maximum: length {calldata_length}
(allowed length: {max_calldata_length})."
)]
CalldataTooLong {
calldata_length: usize,
max_calldata_length: usize,
},
}

pub type TransactionValidatorResult<T> = Result<T, TransactionValidatorError>;
8 changes: 5 additions & 3 deletions crates/gateway/src/starknet_api_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use starknet_api::external_transaction::{
ExternalDeployAccountTransactionV3, ExternalInvokeTransaction, ExternalInvokeTransactionV3,
ExternalTransaction,
};
use starknet_api::transaction::{ResourceBounds, ResourceBoundsMapping};
use starknet_api::transaction::{Calldata, ResourceBounds, ResourceBoundsMapping};

// Utils.
pub fn create_external_declare_tx_for_testing(
Expand All @@ -29,14 +29,15 @@ pub fn create_external_declare_tx_for_testing(

pub fn create_external_deploy_account_tx_for_testing(
resource_bounds: ResourceBoundsMapping,
constructor_calldata: Calldata,
) -> ExternalTransaction {
ExternalTransaction::DeployAccount(ExternalDeployAccountTransaction::V3(
ExternalDeployAccountTransactionV3 {
resource_bounds,
tip: Default::default(),
contract_address_salt: Default::default(),
class_hash: Default::default(),
constructor_calldata: Default::default(),
constructor_calldata,
nonce: Default::default(),
signature: Default::default(),
nonce_data_availability_mode: DataAvailabilityMode::L1,
Expand All @@ -48,14 +49,15 @@ pub fn create_external_deploy_account_tx_for_testing(

pub fn create_external_invoke_tx_for_testing(
resource_bounds: ResourceBoundsMapping,
calldata: Calldata,
) -> ExternalTransaction {
ExternalTransaction::Invoke(ExternalInvokeTransaction::V3(ExternalInvokeTransactionV3 {
resource_bounds,
tip: Default::default(),
signature: Default::default(),
nonce: Default::default(),
sender_address: Default::default(),
calldata: Default::default(),
calldata,
nonce_data_availability_mode: DataAvailabilityMode::L1,
fee_data_availability_mode: DataAvailabilityMode::L1,
paymaster_data: Default::default(),
Expand Down
40 changes: 39 additions & 1 deletion crates/gateway/src/transaction_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub struct TransactionValidatorConfig {
// If true, validates that the reousrce 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 struct TransactionValidator {
Expand All @@ -25,9 +27,9 @@ impl TransactionValidator {
pub fn validate(&self, tx: ExternalTransaction) -> TransactionValidatorResult<()> {
// TODO(Arni, 1/5/2024): Add a mechanism that validate the sender address is not blocked.
// TODO(Arni, 1/5/2024): Validate transaction version.
// TODO(Arni, 4/4/2024): Validate tx signature and calldata are not too long.

self.validate_fee(&tx)?;
self.validate_tx_size(&tx)?;

Ok(())
}
Expand Down Expand Up @@ -72,4 +74,40 @@ impl TransactionValidator {

Ok(())
}

fn validate_tx_size(&self, tx: &ExternalTransaction) -> TransactionValidatorResult<()> {
self.validate_tx_calldata_size(tx)?;

// TODO(Arni, 4/4/2024): Validate tx signature is not too long.

Ok(())
}

fn validate_tx_calldata_size(
&self,
tx: &ExternalTransaction,
) -> TransactionValidatorResult<()> {
let calldata = match tx {
ExternalTransaction::Declare(_) => {
// Declare transaction has no calldata.
return Ok(());
}
ExternalTransaction::DeployAccount(tx) => match tx {
ExternalDeployAccountTransaction::V3(tx) => &tx.constructor_calldata,
},
ExternalTransaction::Invoke(tx) => match tx {
ExternalInvokeTransaction::V3(tx) => &tx.calldata,
},
};

let calldata_length = calldata.0.len();
if calldata_length > self.config.max_calldata_length {
return Err(TransactionValidatorError::CalldataTooLong {
calldata_length,
max_calldata_length: self.config.max_calldata_length,
});
}

Ok(())
}
}
72 changes: 52 additions & 20 deletions crates/gateway/src/transaction_validator_test.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use rstest::rstest;

use starknet_api::calldata;
use starknet_api::external_transaction::ExternalTransaction;
use starknet_api::transaction::{Resource, ResourceBounds, ResourceBoundsMapping};
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Resource, ResourceBounds, ResourceBoundsMapping};

use crate::starknet_api_test_utils::{
create_external_declare_tx_for_testing, create_external_deploy_account_tx_for_testing,
Expand All @@ -16,65 +18,95 @@ use crate::transaction_validator::{
const VALIDATOR_CONFIG_FOR_TESTING: TransactionValidatorConfig = TransactionValidatorConfig {
validate_non_zero_l1_gas_fee: true,
validate_non_zero_l2_gas_fee: false,
max_calldata_length: 1,
};

const VALIDATOR_CONFIG_FOR_L2_GAS_TESTING: TransactionValidatorConfig =
TransactionValidatorConfig {
validate_non_zero_l1_gas_fee: false,
validate_non_zero_l2_gas_fee: true,
};

#[rstest]
#[case::ignore_resource_bounds(
TransactionValidatorConfig{
validate_non_zero_l1_gas_fee: false,
validate_non_zero_l2_gas_fee: false,
..VALIDATOR_CONFIG_FOR_TESTING
},
create_external_invoke_tx_for_testing(zero_resource_bounds_mapping()),
create_external_invoke_tx_for_testing(zero_resource_bounds_mapping(), calldata![]),
Ok(())
)]
#[case::missing_l1_gas_resource_bounds(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_invoke_tx_for_testing(ResourceBoundsMapping::default()),
TransactionValidatorConfig{
validate_non_zero_l1_gas_fee: true,
..VALIDATOR_CONFIG_FOR_TESTING
},
create_external_invoke_tx_for_testing(ResourceBoundsMapping::default(), calldata![]),
Err(TransactionValidatorError::MissingResource { resource: Resource::L1Gas })
)]
#[case::missing_l2_gas_resource_bounds(
VALIDATOR_CONFIG_FOR_L2_GAS_TESTING,
create_external_invoke_tx_for_testing(ResourceBoundsMapping::default()),
TransactionValidatorConfig{
validate_non_zero_l1_gas_fee: false,
validate_non_zero_l2_gas_fee: true,
..VALIDATOR_CONFIG_FOR_TESTING
},
create_external_invoke_tx_for_testing(ResourceBoundsMapping::default(), calldata![]),
Err(TransactionValidatorError::MissingResource { resource: Resource::L2Gas })
)]
#[case::zero_l1_gas_resource_bounds(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_invoke_tx_for_testing(zero_resource_bounds_mapping()),
TransactionValidatorConfig{
validate_non_zero_l1_gas_fee: true,
..VALIDATOR_CONFIG_FOR_TESTING
},
create_external_invoke_tx_for_testing(zero_resource_bounds_mapping(), calldata![]),
Err(TransactionValidatorError::ZeroFee{
resource: Resource::L1Gas, resource_bounds: ResourceBounds::default()
})
)]
#[case::zero_l2_gas_resource_bounds(
VALIDATOR_CONFIG_FOR_L2_GAS_TESTING,
create_external_invoke_tx_for_testing(non_zero_l1_resource_bounds_mapping()),
TransactionValidatorConfig{
validate_non_zero_l1_gas_fee: false,
validate_non_zero_l2_gas_fee: true,
..VALIDATOR_CONFIG_FOR_TESTING
},
create_external_invoke_tx_for_testing(non_zero_l1_resource_bounds_mapping(), calldata![]),
Err(TransactionValidatorError::ZeroFee{
resource: Resource::L2Gas, resource_bounds: ResourceBounds::default()
})
)]
#[case::deploy_account_calldata_too_long(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_deploy_account_tx_for_testing(
non_zero_l1_resource_bounds_mapping(),
calldata![StarkFelt::from_u128(1),StarkFelt::from_u128(2)],
),
Err(TransactionValidatorError::CalldataTooLong { calldata_length: 2, max_calldata_length: 1 })
)]
#[case::invoke_calldata_too_long(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_invoke_tx_for_testing(
non_zero_l1_resource_bounds_mapping(),
calldata![StarkFelt::from_u128(1),StarkFelt::from_u128(2)],
),
Err(TransactionValidatorError::CalldataTooLong { calldata_length: 2, max_calldata_length: 1 })
)]
#[case::valid_declare_tx(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_declare_tx_for_testing(non_zero_l1_resource_bounds_mapping()),
Ok(())
)]
#[case::valid_deploy_account_tx(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_deploy_account_tx_for_testing(non_zero_l1_resource_bounds_mapping(),),
create_external_deploy_account_tx_for_testing(non_zero_l1_resource_bounds_mapping(), calldata![]),
Ok(())
)]
#[case::valid_invoke_tx(
VALIDATOR_CONFIG_FOR_TESTING,
create_external_invoke_tx_for_testing(non_zero_l1_resource_bounds_mapping()),
create_external_invoke_tx_for_testing(non_zero_l1_resource_bounds_mapping(), calldata![]),
Ok(())
)]
#[case::valid_l2_gas_invoke_tx(
VALIDATOR_CONFIG_FOR_L2_GAS_TESTING,
create_external_invoke_tx_for_testing(non_zero_l2_resource_bounds_mapping()),
TransactionValidatorConfig{
validate_non_zero_l1_gas_fee: false,
validate_non_zero_l2_gas_fee: true,
..VALIDATOR_CONFIG_FOR_TESTING
},
create_external_invoke_tx_for_testing(non_zero_l2_resource_bounds_mapping(), calldata![]),
Ok(())
)]
fn test_transaction_validator(
Expand Down

0 comments on commit e5c3156

Please sign in to comment.