Skip to content

Commit 843f3b1

Browse files
committed
apollo_gateway: reject tx with low l2 gas price
1 parent a21723c commit 843f3b1

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_gateway/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ cairo-lang-starknet-classes.workspace = true
3131
futures.workspace = true
3232
lazy_static.workspace = true
3333
mempool_test_utils.workspace = true
34+
num-rational.workspace = true
3435
reqwest.workspace = true
3536
serde.workspace = true
3637
serde_json.workspace = true

crates/apollo_gateway/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ impl SerializeConfig for GatewayConfig {
4444

4545
#[derive(Clone, Debug, Serialize, Deserialize, Validate, PartialEq)]
4646
pub struct StatelessTransactionValidatorConfig {
47+
// TODO(Arni): Align the name of this field with the mempool config, and all other places where
48+
// validation is skipped during the systems bootstrap phase.
4749
// If true, validates that the resource bounds are not zero.
4850
pub validate_non_zero_resource_bounds: bool,
4951
// TODO(AlonH): Remove this field and use the one from the versioned constants.

crates/apollo_gateway/src/gateway.rs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ use apollo_proc_macros::sequencer_latency_histogram;
2626
use apollo_state_sync_types::communication::SharedStateSyncClient;
2727
use axum::async_trait;
2828
use blockifier::context::ChainInfo;
29+
use num_rational::Ratio;
30+
use starknet_api::block::NonzeroGasPrice;
2931
use starknet_api::executable_transaction::ValidateCompiledClassHashError;
3032
use starknet_api::rpc_transaction::{
3133
InternalRpcTransaction,
3234
InternalRpcTransactionWithoutTxHash,
3335
RpcTransaction,
3436
};
37+
use starknet_api::transaction::fields::ValidResourceBounds;
3538
use tracing::{debug, error, info, instrument, warn, Span};
3639

3740
use crate::config::GatewayConfig;
@@ -46,6 +49,10 @@ use crate::sync_state_reader::SyncStateReaderFactory;
4649
#[path = "gateway_test.rs"]
4750
pub mod gateway_test;
4851

52+
// TODO(Arni): Move to a config.
53+
// Minimum gas price as percentage of threshold to accept transactions.
54+
const MIN_GAS_PRICE_PRECENTAGE: u8 = 80; // E.g., 80 to require 80% of threshold.
55+
4956
#[derive(Clone)]
5057
pub struct Gateway {
5158
pub config: Arc<GatewayConfig>,
@@ -203,11 +210,18 @@ impl ProcessTxBlockingTask {
203210
let mut validator = self
204211
.stateful_tx_validator
205212
.instantiate_validator(self.state_reader_factory.as_ref(), &self.chain_info)?;
206-
// TODO(Arni): Use the _l2_gas_price reject transactions that do not pass the gas price
207-
// threshold.
208-
// TODO(Arni): get next_l2_gas_price from the block header.
209-
let _l2_gas_price =
210-
validator.block_context().block_info().gas_prices.strk_gas_prices.l2_gas_price;
213+
214+
// Skip this validation during the systems bootstrap phase.
215+
if self.stateless_tx_validator.config.validate_non_zero_resource_bounds {
216+
// TODO(Arni): get next_l2_gas_price from the block header.
217+
let previous_block_l2_gas_price =
218+
validator.block_context().block_info().gas_prices.strk_gas_prices.l2_gas_price;
219+
validate_tx_l2_gas_price_within_threshold(
220+
executable_tx.resource_bounds(),
221+
previous_block_l2_gas_price,
222+
)?;
223+
}
224+
211225
let address = executable_tx.contract_address();
212226
let nonce = validator.get_nonce(address).map_err(|e| {
213227
error!("Failed to get nonce for sender address {}: {}", address, e);
@@ -227,6 +241,39 @@ impl ProcessTxBlockingTask {
227241
}
228242
}
229243

244+
// TODO(Arni): Consider running this validation for all gas prices.
245+
fn validate_tx_l2_gas_price_within_threshold(
246+
tx_resource_bounds: ValidResourceBounds,
247+
previous_block_l2_gas_price: NonzeroGasPrice,
248+
) -> GatewayResult<()> {
249+
match tx_resource_bounds {
250+
ValidResourceBounds::AllResources(tx_resource_bounds) => {
251+
let tx_l2_gas_price = tx_resource_bounds.l2_gas.max_price_per_unit;
252+
let gas_price_threshold_multiplier =
253+
Ratio::new(MIN_GAS_PRICE_PRECENTAGE.into(), 100_u128);
254+
let threshold =
255+
(gas_price_threshold_multiplier * previous_block_l2_gas_price.get().0).to_integer();
256+
if tx_l2_gas_price.0 < threshold {
257+
return Err(StarknetError {
258+
// We didn't have this kind of an error.
259+
code: StarknetErrorCode::UnknownErrorCode(
260+
"StarknetErrorCode.GAS_PRICE_TOO_LOW".to_string(),
261+
),
262+
message: format!(
263+
"Transaction L2 gas price {} is below the required threshold {}.",
264+
tx_l2_gas_price, threshold
265+
),
266+
});
267+
}
268+
}
269+
ValidResourceBounds::L1Gas(_) => {
270+
// No validation required for legacy transactions.
271+
}
272+
}
273+
274+
Ok(())
275+
}
276+
230277
fn convert_compiled_class_hash_error(error: ValidateCompiledClassHashError) -> StarknetError {
231278
let ValidateCompiledClassHashError::CompiledClassHashMismatch {
232279
computed_class_hash,

0 commit comments

Comments
 (0)