From 357b5ecfb29c146b7e6e86a8af7f694f0c0a6784 Mon Sep 17 00:00:00 2001 From: Arnon Hod Date: Tue, 5 Nov 2024 10:14:02 +0200 Subject: [PATCH] feat: add contract address in range validation (#1790) --- crates/gateway/src/errors.rs | 4 ++++ .../src/stateless_transaction_validator.rs | 11 +++++++++++ .../src/starknet_api_test_utils.rs | 14 ++++++++++---- crates/starknet_api/src/core.rs | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/crates/gateway/src/errors.rs b/crates/gateway/src/errors.rs index 5ffc49f2ce..cb346f7c04 100644 --- a/crates/gateway/src/errors.rs +++ b/crates/gateway/src/errors.rs @@ -3,6 +3,7 @@ use blockifier::state::errors::StateError; use serde_json::{Error as SerdeError, Value}; use starknet_api::block::GasPrice; use starknet_api::transaction::{Resource, ResourceBounds}; +use starknet_api::StarknetApiError; use starknet_gateway_types::errors::GatewaySpecError; use thiserror::Error; @@ -35,6 +36,8 @@ pub enum StatelessTransactionValidatorError { (allowed length: {max_signature_length})." )] SignatureTooLong { signature_length: usize, max_signature_length: usize }, + #[error(transparent)] + StarknetApiError(#[from] StarknetApiError), #[error( "Sierra versions older than {min_version} or newer than {max_version} are not supported. \ The Sierra version of the declared contract is {version}." @@ -57,6 +60,7 @@ impl From for GatewaySpecError { | StatelessTransactionValidatorError::EntryPointsNotUniquelySorted | StatelessTransactionValidatorError::InvalidSierraVersion(..) | StatelessTransactionValidatorError::SignatureTooLong { .. } + | StatelessTransactionValidatorError::StarknetApiError(..) | StatelessTransactionValidatorError::ZeroResourceBounds { .. } => { GatewaySpecError::ValidationFailure { data: e.to_string() } } diff --git a/crates/gateway/src/stateless_transaction_validator.rs b/crates/gateway/src/stateless_transaction_validator.rs index 2043465da7..ad5191fd1b 100644 --- a/crates/gateway/src/stateless_transaction_validator.rs +++ b/crates/gateway/src/stateless_transaction_validator.rs @@ -30,6 +30,7 @@ impl StatelessTransactionValidator { // TODO(Arni, 1/5/2024): Add a mechanism that validate the sender address is not blocked. // TODO(Arni, 1/5/2024): Validate transaction version. + Self::validate_contract_address(tx)?; self.validate_resource_bounds(tx)?; self.validate_tx_size(tx)?; @@ -55,6 +56,16 @@ impl StatelessTransactionValidator { Ok(()) } + fn validate_contract_address(tx: &RpcTransaction) -> StatelessTransactionValidatorResult<()> { + let sender_address = match tx { + RpcTransaction::Declare(RpcDeclareTransaction::V3(tx)) => tx.sender_address, + RpcTransaction::DeployAccount(_) => return Ok(()), + RpcTransaction::Invoke(RpcInvokeTransaction::V3(tx)) => tx.sender_address, + }; + + Ok(sender_address.validate()?) + } + fn validate_tx_size(&self, tx: &RpcTransaction) -> StatelessTransactionValidatorResult<()> { self.validate_tx_calldata_size(tx)?; self.validate_tx_signature_size(tx)?; diff --git a/crates/mempool_test_utils/src/starknet_api_test_utils.rs b/crates/mempool_test_utils/src/starknet_api_test_utils.rs index c78c41da4f..683de9e63f 100644 --- a/crates/mempool_test_utils/src/starknet_api_test_utils.rs +++ b/crates/mempool_test_utils/src/starknet_api_test_utils.rs @@ -85,17 +85,23 @@ pub fn rpc_tx_for_testing( ], ..Default::default() }; - rpc_declare_tx(declare_tx_args!(resource_bounds, signature, contract_class)) + rpc_declare_tx(declare_tx_args!( + signature, + sender_address: TEST_SENDER_ADDRESS.into(), + resource_bounds, + contract_class, + )) } TransactionType::DeployAccount => rpc_deploy_account_tx(deploy_account_tx_args!( + signature, resource_bounds: ValidResourceBounds::AllResources(resource_bounds), constructor_calldata: calldata, - signature )), TransactionType::Invoke => rpc_invoke_tx(invoke_tx_args!( signature, + sender_address: TEST_SENDER_ADDRESS.into(), + calldata, resource_bounds: ValidResourceBounds::AllResources(resource_bounds), - calldata )), } } @@ -456,7 +462,7 @@ impl Default for DeclareTxArgs { fn default() -> Self { Self { signature: TransactionSignature::default(), - sender_address: ContractAddress::default(), + sender_address: TEST_SENDER_ADDRESS.into(), version: TransactionVersion::THREE, resource_bounds: zero_resource_bounds_mapping(), tip: Tip::default(), diff --git a/crates/starknet_api/src/core.rs b/crates/starknet_api/src/core.rs index 3f35867b76..93f9dea540 100644 --- a/crates/starknet_api/src/core.rs +++ b/crates/starknet_api/src/core.rs @@ -90,6 +90,7 @@ impl ChainId { // The block hash table is stored in address 0x1, // this is a special address that is not used for contracts. pub const BLOCK_HASH_TABLE_ADDRESS: ContractAddress = ContractAddress(PatriciaKey(StarkHash::ONE)); + #[derive( Debug, Default, @@ -107,6 +108,21 @@ pub const BLOCK_HASH_TABLE_ADDRESS: ContractAddress = ContractAddress(PatriciaKe )] pub struct ContractAddress(pub PatriciaKey); +impl ContractAddress { + /// Validates the contract address is in the valid range for external access. + /// The lower bound is above the special saved addresses and the upper bound is congruent with + /// the storage var address upper bound. + pub fn validate(&self) -> Result<(), StarknetApiError> { + let value = self.0.0; + let l2_address_upper_bound = Felt::from(*L2_ADDRESS_UPPER_BOUND); + if (value > BLOCK_HASH_TABLE_ADDRESS.0.0) && (value < l2_address_upper_bound) { + return Ok(()); + } + + Err(StarknetApiError::OutOfRange { string: format!("[0x2, {})", l2_address_upper_bound) }) + } +} + impl From for Felt { fn from(contract_address: ContractAddress) -> Felt { **contract_address