Skip to content

Commit

Permalink
Reject orders using too much gas
Browse files Browse the repository at this point in the history
  • Loading branch information
fleupold committed Mar 20, 2024
1 parent d12f0ca commit b0cb12e
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 0 deletions.
60 changes: 60 additions & 0 deletions crates/e2e/tests/e2e/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use {
order::{Hook, OrderCreation, OrderCreationAppData, OrderKind},
signature::{hashed_eip712_message, EcdsaSigningScheme, Signature},
},
reqwest::StatusCode,
secp256k1::SecretKey,
serde_json::json,
shared::ethrpc::Web3,
Expand All @@ -34,6 +35,65 @@ async fn local_node_partial_fills() {
run_test(partial_fills).await;
}

#[tokio::test]
#[ignore]
async fn local_node_gas_limit() {
run_test(gas_limit).await;
}

async fn gas_limit(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3).await;

let [solver] = onchain.make_solvers(to_wei(1)).await;
let [trader] = onchain.make_accounts(to_wei(1)).await;
let cow = onchain
.deploy_cow_weth_pool(to_wei(1_000_000), to_wei(1_000), to_wei(1_000))
.await;

// Fund trader accounts and approve relayer
cow.fund(trader.address(), to_wei(5)).await;
tx!(
trader.account(),
cow.approve(onchain.contracts().allowance, to_wei(5))
);

let services = Services::new(onchain.contracts()).await;
services.start_protocol(solver).await;

let order = OrderCreation {
sell_token: cow.address(),
sell_amount: to_wei(4),
buy_token: onchain.contracts().weth.address(),
buy_amount: to_wei(3),
valid_to: model::time::now_in_epoch_seconds() + 300,
kind: OrderKind::Sell,
app_data: OrderCreationAppData::Full {
full: json!({
"metadata": {
"hooks": {
"pre": [Hook {
target: trader.address(),
call_data: Default::default(),
gas_limit: 1_000_000,
}],
"post": [],
},
},
})
.to_string(),
},
..Default::default()
}
.sign(
EcdsaSigningScheme::Eip712,
&onchain.contracts().domain_separator,
SecretKeyRef::from(&SecretKey::from_slice(trader.private_key()).unwrap()),
);
let error = services.create_order(&order).await.unwrap_err();
assert_eq!(error.0, StatusCode::BAD_REQUEST);
assert!(error.1.contains("TooMuchGas"));
}

async fn allowance(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3).await;

Expand Down
1 change: 1 addition & 0 deletions crates/orderbook/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,7 @@ components:
ZeroAmount,
IncompatibleSigningScheme,
TooManyLimitOrders,
TooMuchGas,
UnsupportedBuyTokenDestination,
UnsupportedSellTokenSource,
UnsupportedOrderType,
Expand Down
4 changes: 4 additions & 0 deletions crates/orderbook/src/api/post_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ impl IntoWarpReply for ValidationErrorWrapper {
error("TooManyLimitOrders", "Too many limit orders"),
StatusCode::BAD_REQUEST,
),
ValidationError::TooMuchGas => with_status(
error("TooMuchGas", "Executing order requires too many gas units"),
StatusCode::BAD_REQUEST,
),

ValidationError::Other(err) => {
tracing::error!(?err, "ValidationErrorWrapper");
Expand Down
6 changes: 6 additions & 0 deletions crates/orderbook/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ pub struct Arguments {
/// Set the maximum size in bytes of order app data.
#[clap(long, env, default_value = "8192")]
pub app_data_size_limit: usize,

/// The maximum gas amount a single order can use for getting settled.
#[clap(long, env, default_value = "8000000")]
pub max_gas_per_order: u64,
}

impl std::fmt::Display for Arguments {
Expand Down Expand Up @@ -173,6 +177,7 @@ impl std::fmt::Display for Arguments {
hooks_contract_address,
app_data_size_limit,
db_url,
max_gas_per_order,
} = self;

write!(f, "{}", shared)?;
Expand Down Expand Up @@ -237,6 +242,7 @@ impl std::fmt::Display for Arguments {
&hooks_contract_address.map(|a| format!("{a:?}")),
)?;
writeln!(f, "app_data_size_limit: {}", app_data_size_limit)?;
writeln!(f, "max_gas_per_order: {}", max_gas_per_order)?;

Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions crates/orderbook/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ pub async fn run(args: Arguments) {
Arc::new(CachedCodeFetcher::new(Arc::new(web3.clone()))),
app_data_validator.clone(),
args.shared.market_orders_deprecation_date,
args.max_gas_per_order,
)
.with_verified_quotes(args.price_estimation.trade_simulator.is_some()),
);
Expand Down
26 changes: 26 additions & 0 deletions crates/shared/src/order_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ pub enum ValidationError {
ZeroAmount,
IncompatibleSigningScheme,
TooManyLimitOrders,
TooMuchGas,
Other(anyhow::Error),
}

Expand Down Expand Up @@ -255,6 +256,7 @@ pub struct OrderValidator {
app_data_validator: crate::app_data::Validator,
request_verified_quotes: bool,
market_orders_deprecation_date: Option<chrono::DateTime<chrono::Utc>>,
max_gas_per_order: u64,
}

#[derive(Debug, Eq, PartialEq, Default)]
Expand Down Expand Up @@ -326,6 +328,7 @@ impl OrderValidator {
code_fetcher: Arc<dyn CodeFetching>,
app_data_validator: crate::app_data::Validator,
market_orders_deprecation_date: Option<chrono::DateTime<chrono::Utc>>,
max_gas_per_order: u64,
) -> Self {
Self {
native_token,
Expand All @@ -343,6 +346,7 @@ impl OrderValidator {
app_data_validator,
request_verified_quotes: false,
market_orders_deprecation_date,
max_gas_per_order,
}
}

Expand Down Expand Up @@ -728,6 +732,14 @@ impl OrderValidating for OrderValidator {
}
};

if quote.as_ref().is_some_and(|quote| {
// Quoted gas does not include additional gas for hooks
quote.data.fee_parameters.gas_amount as u64 + quote_parameters.additional_gas
> self.max_gas_per_order
}) {
return Err(ValidationError::TooMuchGas);
}

let order = Order {
metadata: OrderMetadata {
owner,
Expand Down Expand Up @@ -1061,6 +1073,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let result = validator
.partial_validate(PreOrderData {
Expand Down Expand Up @@ -1208,6 +1221,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = || PreOrderData {
valid_to: time::now_in_epoch_seconds()
Expand Down Expand Up @@ -1296,6 +1310,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);

let creation = OrderCreation {
Expand Down Expand Up @@ -1500,6 +1515,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);

let creation = OrderCreation {
Expand Down Expand Up @@ -1571,6 +1587,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);

let creation = OrderCreation {
Expand Down Expand Up @@ -1627,6 +1644,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -1685,6 +1703,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -1742,6 +1761,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -1794,6 +1814,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -1848,6 +1869,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -1906,6 +1928,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -1958,6 +1981,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);
let order = OrderCreation {
valid_to: time::now_in_epoch_seconds() + 2,
Expand Down Expand Up @@ -2014,6 +2038,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);

let creation = OrderCreation {
Expand Down Expand Up @@ -2077,6 +2102,7 @@ mod tests {
Arc::new(MockCodeFetching::new()),
Default::default(),
None,
u64::MAX,
);

let order = OrderCreation {
Expand Down

0 comments on commit b0cb12e

Please sign in to comment.