diff --git a/crates/gateway/src/errors.rs b/crates/gateway/src/errors.rs index 1c301dde0..3a08a1683 100644 --- a/crates/gateway/src/errors.rs +++ b/crates/gateway/src/errors.rs @@ -1,3 +1,5 @@ +use starknet_api::transaction::{Resource, ResourceBounds}; + use thiserror::Error; #[derive(Debug, Error)] @@ -22,6 +24,12 @@ pub enum GatewayConfigError { #[derive(Debug, Error)] #[cfg_attr(test, derive(PartialEq))] -pub enum TransactionValidatorError {} +pub enum TransactionValidatorError { + #[error("Expected a positive amount of {resource:?}. Got {resource_bounds:?}.")] + ZeroFee { + resource: Resource, + resource_bounds: ResourceBounds, + }, +} pub type TransactionValidatorResult = Result; diff --git a/crates/gateway/src/starknet_api_test_utils.rs b/crates/gateway/src/starknet_api_test_utils.rs index d437b8f77..74526f9b9 100644 --- a/crates/gateway/src/starknet_api_test_utils.rs +++ b/crates/gateway/src/starknet_api_test_utils.rs @@ -6,10 +6,12 @@ use starknet_api::external_transaction::{ use starknet_api::transaction::{ResourceBounds, ResourceBoundsMapping, TransactionVersion}; // Utils. -pub fn create_external_declare_tx_for_testing() -> ExternalTransaction { +pub fn create_external_declare_tx_for_testing( + resource_bounds: ResourceBoundsMapping, +) -> ExternalTransaction { ExternalTransaction::Declare(ExternalDeclareTransaction::V3( ExternalDeclareTransactionV3 { - resource_bounds: zero_resource_bounds_mapping(), + resource_bounds, contract_class: Default::default(), tip: Default::default(), signature: Default::default(), @@ -26,10 +28,12 @@ pub fn create_external_declare_tx_for_testing() -> ExternalTransaction { )) } -pub fn create_external_deploy_account_tx_for_testing() -> ExternalTransaction { +pub fn create_external_deploy_account_tx_for_testing( + resource_bounds: ResourceBoundsMapping, +) -> ExternalTransaction { ExternalTransaction::DeployAccount(ExternalDeployAccountTransaction::V3( ExternalDeployAccountTransactionV3 { - resource_bounds: zero_resource_bounds_mapping(), + resource_bounds, tip: Default::default(), contract_address_salt: Default::default(), class_hash: Default::default(), @@ -45,9 +49,11 @@ pub fn create_external_deploy_account_tx_for_testing() -> ExternalTransaction { )) } -pub fn create_external_invoke_tx_for_testing() -> ExternalTransaction { +pub fn create_external_invoke_tx_for_testing( + resource_bounds: ResourceBoundsMapping, +) -> ExternalTransaction { ExternalTransaction::Invoke(ExternalInvokeTransaction::V3(ExternalInvokeTransactionV3 { - resource_bounds: zero_resource_bounds_mapping(), + resource_bounds, tip: Default::default(), signature: Default::default(), nonce: Default::default(), @@ -75,3 +81,20 @@ pub fn zero_resource_bounds_mapping() -> ResourceBoundsMapping { ]) .expect("Resource bounds mapping has unexpected structure.") } + +pub fn non_zero_resource_bounds_mapping() -> ResourceBoundsMapping { + ResourceBoundsMapping::try_from(vec![ + ( + starknet_api::transaction::Resource::L1Gas, + ResourceBounds { + max_amount: 1, + max_price_per_unit: 1, + }, + ), + ( + starknet_api::transaction::Resource::L2Gas, + ResourceBounds::default(), + ), + ]) + .expect("Resource bounds mapping has unexpected structure.") +} diff --git a/crates/gateway/src/transaction_validator.rs b/crates/gateway/src/transaction_validator.rs index bcbf8f354..828b4b9d5 100644 --- a/crates/gateway/src/transaction_validator.rs +++ b/crates/gateway/src/transaction_validator.rs @@ -1,24 +1,63 @@ -use starknet_api::external_transaction::ExternalTransaction; +use starknet_api::external_transaction::{ + ExternalDeclareTransaction, ExternalDeployAccountTransaction, ExternalInvokeTransaction, + ExternalTransaction, +}; +use starknet_api::transaction::Resource; -use crate::errors::TransactionValidatorResult; +use crate::errors::{TransactionValidatorError, TransactionValidatorResult}; #[cfg(test)] #[path = "transaction_validator_test.rs"] mod transaction_validator_test; -pub struct TransactionValidatorConfig {} +pub struct TransactionValidatorConfig { + pub fee_resource: Resource, +} + +impl Default for TransactionValidatorConfig { + fn default() -> Self { + Self { + fee_resource: Resource::L1Gas, + } + } +} pub struct TransactionValidator { pub config: TransactionValidatorConfig, } impl TransactionValidator { - pub fn validate(&self, _tx: ExternalTransaction) -> TransactionValidatorResult<()> { + 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 fee non zero. // TODO(Arni, 4/4/2024): Validate tx signature and calldata are not too long. + self.validate_fee(&tx)?; + + Ok(()) + } + + fn validate_fee(&self, tx: &ExternalTransaction) -> TransactionValidatorResult<()> { + let resource = self.config.fee_resource; + let resource_bounds_mapping = match tx { + ExternalTransaction::Declare(tx) => match tx { + ExternalDeclareTransaction::V3(tx) => &tx.resource_bounds, + }, + ExternalTransaction::DeployAccount(tx) => match tx { + ExternalDeployAccountTransaction::V3(tx) => &tx.resource_bounds, + }, + ExternalTransaction::Invoke(tx) => match tx { + ExternalInvokeTransaction::V3(tx) => &tx.resource_bounds, + }, + }; + let resource_bounds = resource_bounds_mapping.0[&resource]; + if resource_bounds.max_amount == 0 || resource_bounds.max_price_per_unit == 0 { + return Err(TransactionValidatorError::ZeroFee { + resource, + resource_bounds, + }); + } + Ok(()) } } diff --git a/crates/gateway/src/transaction_validator_test.rs b/crates/gateway/src/transaction_validator_test.rs index b891fb1fe..bf66b88c4 100644 --- a/crates/gateway/src/transaction_validator_test.rs +++ b/crates/gateway/src/transaction_validator_test.rs @@ -1,31 +1,43 @@ use rstest::rstest; use starknet_api::external_transaction::ExternalTransaction; +use starknet_api::transaction::{Resource, ResourceBounds}; use crate::starknet_api_test_utils::{ create_external_declare_tx_for_testing, create_external_deploy_account_tx_for_testing, - create_external_invoke_tx_for_testing, + create_external_invoke_tx_for_testing, non_zero_resource_bounds_mapping, + zero_resource_bounds_mapping, }; use crate::transaction_validator::{ - TransactionValidator, TransactionValidatorConfig, TransactionValidatorResult, + TransactionValidator, TransactionValidatorConfig, TransactionValidatorError, + TransactionValidatorResult, }; -const VALIDATOR_CONFIG_FOR_TESTING: TransactionValidatorConfig = TransactionValidatorConfig {}; +const VALIDATOR_CONFIG_FOR_TESTING: TransactionValidatorConfig = TransactionValidatorConfig { + fee_resource: Resource::L1Gas, +}; #[rstest] +#[case::invalid_resource_bounds( + VALIDATOR_CONFIG_FOR_TESTING, + create_external_invoke_tx_for_testing(zero_resource_bounds_mapping()), + Err(TransactionValidatorError::ZeroFee{ + resource: Resource::L1Gas, resource_bounds: ResourceBounds::default() + }) +)] #[case::valid_declare_tx( VALIDATOR_CONFIG_FOR_TESTING, - create_external_declare_tx_for_testing(), + create_external_declare_tx_for_testing(non_zero_resource_bounds_mapping()), Ok(()) )] #[case::valid_deploy_account_tx( VALIDATOR_CONFIG_FOR_TESTING, - create_external_deploy_account_tx_for_testing(), + create_external_deploy_account_tx_for_testing(non_zero_resource_bounds_mapping(),), Ok(()) )] #[case::valid_invoke_tx( VALIDATOR_CONFIG_FOR_TESTING, - create_external_invoke_tx_for_testing(), + create_external_invoke_tx_for_testing(non_zero_resource_bounds_mapping()), Ok(()) )] fn test_transaction_validator(