Skip to content
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/apollo_deployments/resources/base_app_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"gateway_config.block_declare": false,
"gateway_config.stateful_tx_validator_config.max_allowed_nonce_gap": 50,
"gateway_config.stateful_tx_validator_config.max_nonce_for_validation_skip": "0x1",
"gateway_config.stateful_tx_validator_config.min_gas_price_percentage": 100,
"gateway_config.stateful_tx_validator_config.reject_future_declare_txs": true,
"gateway_config.stateless_tx_validator_config.max_calldata_length": 5000,
"gateway_config.stateless_tx_validator_config.max_contract_bytecode_size": 81920,
Expand Down
1 change: 1 addition & 0 deletions crates/apollo_gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ cairo-lang-starknet-classes.workspace = true
futures.workspace = true
lazy_static.workspace = true
mempool_test_utils.workspace = true
num-rational.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
Expand Down
11 changes: 11 additions & 0 deletions crates/apollo_gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ impl SerializeConfig for GatewayConfig {

#[derive(Clone, Debug, Serialize, Deserialize, Validate, PartialEq)]
pub struct StatelessTransactionValidatorConfig {
// TODO(Arni): Align the name of this field with the mempool config, and all other places where
// validation is skipped during the systems bootstrap phase.
// If true, validates that the resource bounds are not zero.
pub validate_non_zero_resource_bounds: bool,
// TODO(AlonH): Remove this field and use the one from the versioned constants.
Expand Down Expand Up @@ -170,6 +172,8 @@ pub struct StatefulTransactionValidatorConfig {
pub reject_future_declare_txs: bool,
pub max_nonce_for_validation_skip: Nonce,
pub versioned_constants_overrides: VersionedConstantsOverrides,
// Minimum gas price as percentage of threshold to accept transactions.
pub min_gas_price_percentage: u8, // E.g., 80 to require 80% of threshold.
}

impl Default for StatefulTransactionValidatorConfig {
Expand All @@ -178,6 +182,7 @@ impl Default for StatefulTransactionValidatorConfig {
max_allowed_nonce_gap: 50,
reject_future_declare_txs: true,
max_nonce_for_validation_skip: Nonce(Felt::ONE),
min_gas_price_percentage: 100,
versioned_constants_overrides: VersionedConstantsOverrides::default(),
}
}
Expand All @@ -204,6 +209,12 @@ impl SerializeConfig for StatefulTransactionValidatorConfig {
"If true, rejects declare transactions with future nonces.",
ParamPrivacyInput::Public,
),
ser_param(
"min_gas_price_percentage",
&self.min_gas_price_percentage,
"Minimum gas price as percentage of threshold to accept transactions.",
ParamPrivacyInput::Public,
),
]);
dump.append(&mut prepend_sub_config_name(
self.versioned_constants_overrides.dump(),
Expand Down
50 changes: 50 additions & 0 deletions crates/apollo_gateway/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ use apollo_proc_macros::sequencer_latency_histogram;
use apollo_state_sync_types::communication::SharedStateSyncClient;
use axum::async_trait;
use blockifier::context::ChainInfo;
use num_rational::Ratio;
use starknet_api::block::NonzeroGasPrice;
use starknet_api::executable_transaction::ValidateCompiledClassHashError;
use starknet_api::rpc_transaction::{
InternalRpcTransaction,
InternalRpcTransactionWithoutTxHash,
RpcTransaction,
};
use starknet_api::transaction::fields::ValidResourceBounds;
use tracing::{debug, error, info, instrument, warn, Span};

use crate::config::GatewayConfig;
Expand Down Expand Up @@ -203,6 +206,19 @@ impl ProcessTxBlockingTask {
let mut validator = self
.stateful_tx_validator
.instantiate_validator(self.state_reader_factory.as_ref(), &self.chain_info)?;

// Skip this validation during the systems bootstrap phase.
if self.stateless_tx_validator.config.validate_non_zero_resource_bounds {
// TODO(Arni): get next_l2_gas_price from the block header.
let previous_block_l2_gas_price =
validator.block_context().block_info().gas_prices.strk_gas_prices.l2_gas_price;
validate_tx_l2_gas_price_within_threshold(
executable_tx.resource_bounds(),
previous_block_l2_gas_price,
self.stateful_tx_validator.config.min_gas_price_percentage,
)?;
}

let address = executable_tx.contract_address();
let nonce = validator.get_nonce(address).map_err(|e| {
error!("Failed to get nonce for sender address {}: {}", address, e);
Expand All @@ -222,6 +238,40 @@ impl ProcessTxBlockingTask {
}
}

// TODO(Arni): Consider running this validation for all gas prices.
fn validate_tx_l2_gas_price_within_threshold(
tx_resource_bounds: ValidResourceBounds,
previous_block_l2_gas_price: NonzeroGasPrice,
min_gas_price_percentage: u8,
) -> GatewayResult<()> {
match tx_resource_bounds {
ValidResourceBounds::AllResources(tx_resource_bounds) => {
let tx_l2_gas_price = tx_resource_bounds.l2_gas.max_price_per_unit;
let gas_price_threshold_multiplier =
Ratio::new(min_gas_price_percentage.into(), 100_u128);
let threshold =
(gas_price_threshold_multiplier * previous_block_l2_gas_price.get().0).to_integer();
if tx_l2_gas_price.0 < threshold {
return Err(StarknetError {
// We didn't have this kind of an error.
code: StarknetErrorCode::UnknownErrorCode(
"StarknetErrorCode.GAS_PRICE_TOO_LOW".to_string(),
),
message: format!(
"Transaction L2 gas price {} is below the required threshold {}.",
tx_l2_gas_price, threshold
),
});
}
}
ValidResourceBounds::L1Gas(_) => {
// No validation required for legacy transactions.
}
}

Ok(())
}

fn convert_compiled_class_hash_error(error: ValidateCompiledClassHashError) -> StarknetError {
let ValidateCompiledClassHashError::CompiledClassHashMismatch {
computed_class_hash,
Expand Down
5 changes: 5 additions & 0 deletions crates/apollo_node/resources/config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,11 @@
"privacy": "Public",
"value": "0x1"
},
"gateway_config.stateful_tx_validator_config.min_gas_price_percentage": {
"description": "Minimum gas price as percentage of threshold to accept transactions.",
"privacy": "Public",
"value": 100
},
"gateway_config.stateful_tx_validator_config.reject_future_declare_txs": {
"description": "If true, rejects declare transactions with future nonces.",
"privacy": "Public",
Expand Down
4 changes: 4 additions & 0 deletions crates/blockifier/src/blockifier/stateful_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ impl<S: StateReader> StatefulValidator<S> {
Ok(())
}

pub fn block_context(&self) -> &BlockContext {
self.tx_executor.block_context.as_ref()
}

fn state(&mut self) -> &mut CachedState<S> {
self.tx_executor.block_state.as_mut().expect(BLOCK_STATE_ACCESS_ERR)
}
Expand Down
Loading