Skip to content

Commit ad4469b

Browse files
apollo_http_server: use allow new tx flag
1 parent 1467bf7 commit ad4469b

File tree

5 files changed

+75
-13
lines changed

5 files changed

+75
-13
lines changed

crates/apollo_http_server/src/errors.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub enum HttpServerError {
2929
DecompressionError(#[from] CompressionError),
3030
#[error(transparent)]
3131
DeserializationError(#[from] serde_json::Error),
32+
#[error("Server is rejecting new transactions.")]
33+
DisabledError(),
3234
#[error(transparent)]
3335
GatewayClientError(#[from] Box<GatewayClientError>),
3436
}
@@ -41,6 +43,7 @@ impl IntoResponse for HttpServerError {
4143
}
4244
HttpServerError::DecompressionError(e) => compression_error_into_response(e),
4345
HttpServerError::DeserializationError(e) => serde_error_into_response(e),
46+
HttpServerError::DisabledError() => disabled_error_into_response(),
4447
HttpServerError::GatewayClientError(e) => gw_client_err_into_response(*e),
4548
}
4649
}
@@ -74,6 +77,19 @@ fn serde_error_into_response(err: serde_json::Error) -> Response {
7477
(response_code, response_body).into_response()
7578
}
7679

80+
fn disabled_error_into_response() -> Response {
81+
debug!("Server is configured to reject transactions.");
82+
let (response_code, starknet_error) = (
83+
StatusCode::SERVICE_UNAVAILABLE,
84+
StarknetError {
85+
code: StarknetErrorCode::UnknownErrorCode("Server unavailable.".to_string()),
86+
message: "Server unavailable.".to_string(),
87+
},
88+
);
89+
let response_body = serialize_error(&starknet_error);
90+
(response_code, response_body).into_response()
91+
}
92+
7793
fn gw_client_err_into_response(err: GatewayClientError) -> Response {
7894
let (response_code, deprecated_gateway_error) = match err {
7995
GatewayClientError::ClientError(e) => (

crates/apollo_http_server/src/http_server.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ async fn add_rpc_tx(
117117
Json(tx): Json<RpcTransaction>,
118118
) -> HttpServerResult<Json<GatewayOutput>> {
119119
debug!("ADD_TX_START: Http server received a new transaction.");
120+
121+
let dynamic_config = app_state.config_manager_client.get_http_server_dynamic_config().await?;
122+
check_new_transactions_are_allowed(dynamic_config.accept_new_txs)?;
123+
120124
ADDED_TRANSACTIONS_TOTAL.increment(1);
121125
add_tx_inner(app_state, headers, tx).await
122126
}
@@ -128,11 +132,12 @@ async fn add_tx(
128132
headers: HeaderMap,
129133
tx: String,
130134
) -> HttpServerResult<Json<GatewayOutput>> {
131-
ADDED_TRANSACTIONS_TOTAL.increment(1);
132135
debug!("ADD_TX_START: Http server received a new transaction.");
133136

134137
let dynamic_config = app_state.config_manager_client.get_http_server_dynamic_config().await?;
138+
check_new_transactions_are_allowed(dynamic_config.accept_new_txs)?;
135139

140+
ADDED_TRANSACTIONS_TOTAL.increment(1);
136141
let tx: DeprecatedGatewayTransactionV3 = match serde_json::from_str(&tx) {
137142
Ok(value) => value,
138143
Err(e) => {
@@ -154,6 +159,13 @@ async fn add_tx(
154159
add_tx_inner(app_state, headers, rpc_tx).await
155160
}
156161

162+
fn check_new_transactions_are_allowed(accept_new_txs: bool) -> HttpServerResult<()> {
163+
match accept_new_txs {
164+
true => Ok(()),
165+
false => Err(HttpServerError::DisabledError()),
166+
}
167+
}
168+
157169
#[allow(clippy::result_large_err)]
158170
fn validate_supported_tx_version_str(tx: &str) -> HttpServerResult<()> {
159171
// 1. Remove all whitespace

crates/apollo_http_server/src/http_server_test.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ const DEPRECATED_GATEWAY_DEPLOY_ACCOUNT_TX_RESPONSE_JSON_PATH: &str =
4545

4646
const EXPECTED_TX_HASH: TransactionHash = TransactionHash(Felt::ONE);
4747

48+
// TODO(Tsabary): find a better way to allocate different ports to different tests.
49+
4850
// The http_server is oblivious to the GateWayOutput type, so we always return invoke.
49-
pub fn default_gateway_output() -> GatewayOutput {
51+
fn default_gateway_output() -> GatewayOutput {
5052
GatewayOutput::Invoke(InvokeGatewayOutput::new(EXPECTED_TX_HASH))
5153
}
5254

@@ -88,6 +90,28 @@ async fn to_bytes(res: Response) -> Bytes {
8890
res.into_body().collect().await.unwrap().to_bytes()
8991
}
9092

93+
// TODO(Tsabary): there's a discrepancy with the used `StatusCode` crates: reqwest and hyper. Some
94+
// tests expect the former and some the other, based on the used test utils. Better sort this out
95+
// and make consistent across.
96+
97+
// Uses add_tx_http_client with index 0.
98+
/// Test that an HTTP server with a `allow_new_txs = false` config rejects new transactions.
99+
#[rstest]
100+
#[tokio::test]
101+
async fn allow_new_txs() {
102+
let tx = rpc_invoke_tx();
103+
104+
let mock_gateway_client = MockGatewayClient::new();
105+
let mock_config_manager_client = get_mock_config_manager_client(false);
106+
107+
let http_client = add_tx_http_client(mock_config_manager_client, mock_gateway_client, 0).await;
108+
109+
// Send a transaction to the server.
110+
let response = http_client.add_tx(tx.clone()).await;
111+
let status = response.status();
112+
assert_eq!(status, reqwest::StatusCode::SERVICE_UNAVAILABLE, "{status:?}");
113+
}
114+
91115
#[tokio::test]
92116
async fn error_into_response() {
93117
let error = HttpServerError::DeserializationError(
@@ -106,6 +130,7 @@ async fn error_into_response() {
106130
);
107131
}
108132

133+
// Uses add_tx_http_client with indices 1,2.
109134
#[traced_test]
110135
#[rstest]
111136
#[case::add_deprecated_gateway_tx(0, deprecated_gateway_invoke_tx())]
@@ -127,7 +152,7 @@ async fn record_region_test(#[case] index: u16, #[case] tx: impl GatewayTransact
127152
.times(1)
128153
.return_const(Ok(GatewayOutput::Invoke(InvokeGatewayOutput::new(tx_hash_2))));
129154

130-
let mock_config_manager_client = get_mock_config_manager_client();
155+
let mock_config_manager_client = get_mock_config_manager_client(true);
131156
// TODO(Yael): avoid the hardcoded node offset index, consider dynamic allocation.
132157
let http_client =
133158
add_tx_http_client(mock_config_manager_client, mock_gateway_client, 1 + index).await;
@@ -146,6 +171,7 @@ async fn record_region_test(#[case] index: u16, #[case] tx: impl GatewayTransact
146171
));
147172
}
148173

174+
// Uses add_tx_http_client with indices 3,4.
149175
#[traced_test]
150176
#[rstest]
151177
#[case::add_deprecated_gateway_tx(0, deprecated_gateway_invoke_tx())]
@@ -162,7 +188,7 @@ async fn record_region_gateway_failing_tx(#[case] index: u16, #[case] tx: impl G
162188
)),
163189
));
164190

165-
let mock_config_manager_client = get_mock_config_manager_client();
191+
let mock_config_manager_client = get_mock_config_manager_client(true);
166192
let http_client =
167193
add_tx_http_client(mock_config_manager_client, mock_gateway_client, 3 + index).await;
168194

@@ -171,6 +197,7 @@ async fn record_region_gateway_failing_tx(#[case] index: u16, #[case] tx: impl G
171197
assert!(!logs_contain("Recorded transaction transaction_hash="));
172198
}
173199

200+
// Uses add_tx_http_client with indices 5,6,7.
174201
#[rstest]
175202
#[case::add_deprecated_gateway_invoke(0, deprecated_gateway_invoke_tx())]
176203
#[case::add_deprecated_gateway_deploy_account(1, deprecated_gateway_deploy_account_tx())]
@@ -212,7 +239,7 @@ async fn test_response(#[case] index: u16, #[case] tx: impl GatewayTransaction)
212239
expected_internal_err,
213240
));
214241

215-
let mock_config_manager_client = get_mock_config_manager_client();
242+
let mock_config_manager_client = get_mock_config_manager_client(true);
216243
let http_client =
217244
add_tx_http_client(mock_config_manager_client, mock_gateway_client, 5 + index).await;
218245

@@ -231,6 +258,7 @@ async fn test_response(#[case] index: u16, #[case] tx: impl GatewayTransaction)
231258
assert_eq!(error_str, expected_gateway_client_err_str);
232259
}
233260

261+
// Uses add_tx_http_client with indices 9,10,11.
234262
#[rstest]
235263
#[case::missing_version(
236264
0,
@@ -281,7 +309,7 @@ async fn test_unsupported_tx_version(
281309
}
282310

283311
let mock_gateway_client = MockGatewayClient::new();
284-
let mock_config_manager_client = get_mock_config_manager_client();
312+
let mock_config_manager_client = get_mock_config_manager_client(true);
285313
let http_client =
286314
add_tx_http_client(mock_config_manager_client, mock_gateway_client, 9 + index).await;
287315

@@ -291,6 +319,7 @@ async fn test_unsupported_tx_version(
291319
assert_eq!(starknet_error, expected_err);
292320
}
293321

322+
// Uses add_tx_http_client with index 13.
294323
#[tokio::test]
295324
async fn sanitizing_error_message() {
296325
// Set the tx version to be a problematic text.
@@ -302,7 +331,7 @@ async fn sanitizing_error_message() {
302331
tx_object.insert("version".to_string(), Value::String(malicious_version.to_string())).unwrap();
303332

304333
let mock_gateway_client = MockGatewayClient::new();
305-
let mock_config_manager_client = get_mock_config_manager_client();
334+
let mock_config_manager_client = get_mock_config_manager_client(true);
306335
let http_client = add_tx_http_client(mock_config_manager_client, mock_gateway_client, 13).await;
307336

308337
let serialized_err =

crates/apollo_http_server/src/metrics_test.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn success_gateway_client_output() -> GatewayOutput {
3333
GatewayOutput::Invoke(InvokeGatewayOutput::new(TransactionHash::default()))
3434
}
3535

36+
// Uses add_tx_http_client with indices 14,15.
3637
#[rstest]
3738
#[case::add_deprecated_gateway_tx(0, deprecated_gateway_invoke_tx())]
3839
#[case::add_rpc_tx(1, rpc_invoke_tx())]
@@ -55,7 +56,7 @@ async fn add_tx_metrics_test(#[case] index: u16, #[case] tx: impl GatewayTransac
5556
)))
5657
});
5758

58-
let mock_config_manager_client = get_mock_config_manager_client();
59+
let mock_config_manager_client = get_mock_config_manager_client(true);
5960

6061
// Initialize the metrics directly instead of spawning a monitoring endpoint task.
6162
let recorder = PrometheusBuilder::new().build_recorder();
@@ -80,6 +81,7 @@ async fn add_tx_metrics_test(#[case] index: u16, #[case] tx: impl GatewayTransac
8081
ADDED_TRANSACTIONS_FAILURE.assert_eq::<usize>(&metrics, FAILURE_TXS_TO_SEND);
8182
}
8283

84+
// Uses add_tx_http_client with index 16.
8385
#[tokio::test]
8486
async fn add_tx_serde_failure_metrics_test() {
8587
let mut mock_gateway_client = MockGatewayClient::new();
@@ -89,7 +91,7 @@ async fn add_tx_serde_failure_metrics_test() {
8991
.times(1)
9092
.return_once(move |_| Ok(success_gateway_client_output()));
9193

92-
let mock_config_manager_client = get_mock_config_manager_client();
94+
let mock_config_manager_client = get_mock_config_manager_client(true);
9395

9496
// Initialize the metrics directly instead of spawning a monitoring endpoint task.
9597
let recorder = PrometheusBuilder::new().build_recorder();

crates/apollo_http_server/src/test_utils.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ impl GatewayTransaction for TransactionSerialization {
153153
}
154154
}
155155

156+
// Creates a client for testing the http server functionality. Must be invoked with different
157+
// `port_index` values to support concurrent execution, otherwise leading to `Address already in use
158+
// (os error 98)` errors.
156159
pub async fn add_tx_http_client(
157160
mock_config_manager_client: MockConfigManagerClient,
158161
mock_gateway_client: MockGatewayClient,
@@ -183,12 +186,12 @@ pub fn deprecated_gateway_declare_tx() -> DeprecatedGatewayTransactionV3 {
183186
DeprecatedGatewayTransactionV3::from(declare_tx())
184187
}
185188

186-
// A mock config manager client returning the default http server dynamic config for an unlimited
187-
// number of requests.
188-
pub fn get_mock_config_manager_client() -> MockConfigManagerClient {
189+
// A mock config manager client returning the an http server dynamic config that accepts/rejects
190+
// transactions for an unlimited number of requests.
191+
pub fn get_mock_config_manager_client(accept_new_txs: bool) -> MockConfigManagerClient {
189192
let mut mock_config_manager_client = MockConfigManagerClient::new();
190193
mock_config_manager_client
191194
.expect_get_http_server_dynamic_config()
192-
.returning(move || Ok(HttpServerDynamicConfig::default()));
195+
.returning(move || Ok(HttpServerDynamicConfig { accept_new_txs, ..Default::default() }));
193196
mock_config_manager_client
194197
}

0 commit comments

Comments
 (0)