Skip to content

Commit a293bdb

Browse files
authored
fix: read body in chunks (#164)
* read body in chunks * fix test * add test
1 parent d0abdb4 commit a293bdb

File tree

15 files changed

+133
-56
lines changed

15 files changed

+133
-56
lines changed

Cargo.lock

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ ethereum_serde_utils = "0.7.0"
3939
# networking
4040
axum = { version = "0.7.5", features = ["macros"] }
4141
axum-extra = { version = "0.9.3", features = ["typed-header"] }
42-
reqwest = { version = "0.12.4", features = ["json"] }
42+
reqwest = { version = "0.12.4", features = ["json", "stream"] }
4343
headers = "0.4.0"
4444

4545
# async / threads

crates/common/src/pbs/error.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ use crate::error::BlstErrorWrapper;
88

99
#[derive(Debug, Error)]
1010
pub enum PbsError {
11-
#[error("axum error: {0}")]
11+
#[error("axum error: {0:?}")]
1212
AxumError(#[from] axum::Error),
1313

14-
#[error("reqwest error: {0}")]
14+
#[error("reqwest error: {0:?}")]
1515
Reqwest(#[from] reqwest::Error),
1616

17-
#[error("json decode error: {err}, raw: {raw}")]
17+
#[error("json decode error: {err:?}, raw: {raw}")]
1818
JsonDecode { err: serde_json::Error, raw: String },
1919

2020
#[error("relay response error. Code: {code}, err: {error_msg}")]
2121
RelayResponse { error_msg: String, code: u16 },
2222

23-
#[error("response size exceeds max size: max: {max} got: {got}")]
24-
PayloadTooLarge { max: usize, got: usize },
23+
#[error("response size exceeds max size: max: {max} raw: {raw}")]
24+
PayloadTooLarge { max: usize, raw: String },
2525

2626
#[error("failed validating relay response: {0}")]
2727
Validation(#[from] ValidationError),

crates/common/src/pbs/relay.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ use super::{
1616
};
1717
use crate::{config::RelayConfig, DEFAULT_REQUEST_TIMEOUT};
1818

19-
pub const MAX_SIZE: usize = 10 * 1024 * 1024;
20-
2119
/// A parsed entry of the relay url in the format: scheme://pubkey@host
2220
#[derive(Debug, Clone)]
2321
pub struct RelayEntry {

crates/pbs/src/constants.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
pub(crate) const STATUS_ENDPOINT_TAG: &str = "status";
2-
pub(crate) const REGISTER_VALIDATOR_ENDPOINT_TAG: &str = "register_validator";
3-
pub(crate) const SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG: &str = "submit_blinded_block";
4-
pub(crate) const GET_HEADER_ENDPOINT_TAG: &str = "get_header";
1+
pub const STATUS_ENDPOINT_TAG: &str = "status";
2+
pub const REGISTER_VALIDATOR_ENDPOINT_TAG: &str = "register_validator";
3+
pub const SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG: &str = "submit_blinded_block";
4+
pub const GET_HEADER_ENDPOINT_TAG: &str = "get_header";
55

66
/// For metrics recorded when a request times out
7-
pub(crate) const TIMEOUT_ERROR_CODE: u16 = 555;
8-
pub(crate) const TIMEOUT_ERROR_CODE_STR: &str = "555";
7+
pub const TIMEOUT_ERROR_CODE: u16 = 555;
8+
pub const TIMEOUT_ERROR_CODE_STR: &str = "555";
9+
10+
/// 20 MiB to cover edge cases for heavy blocks and also add a bit of slack for
11+
/// any Ethereum upgrades in the near future
12+
pub const MAX_SIZE_SUBMIT_BLOCK: usize = 20 * 1024 * 1024;
13+
14+
/// 10 KiB, headers are around 700 bytes + buffer for encoding
15+
pub const MAX_SIZE_GET_HEADER: usize = 10 * 1024;
16+
17+
pub const MAX_SIZE_DEFAULT: usize = 1024;

crates/pbs/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ mod mev_boost;
66
mod routes;
77
mod service;
88
mod state;
9+
mod utils;
910

1011
pub use api::*;
12+
pub use constants::*;
1113
pub use mev_boost::*;
1214
pub use service::PbsService;
1315
pub use state::{BuilderApiState, PbsState};

crates/pbs/src/mev_boost/get_header.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use cb_common::{
1111
pbs::{
1212
error::{PbsError, ValidationError},
1313
GetHeaderParams, GetHeaderResponse, RelayClient, SignedExecutionPayloadHeader,
14-
EMPTY_TX_ROOT_HASH, HEADER_SLOT_UUID_KEY, HEADER_START_TIME_UNIX_MS, MAX_SIZE,
14+
EMPTY_TX_ROOT_HASH, HEADER_SLOT_UUID_KEY, HEADER_START_TIME_UNIX_MS,
1515
},
1616
signature::verify_signed_message,
1717
types::Chain,
@@ -24,9 +24,12 @@ use tracing::{debug, error, warn, Instrument};
2424
use url::Url;
2525

2626
use crate::{
27-
constants::{GET_HEADER_ENDPOINT_TAG, TIMEOUT_ERROR_CODE, TIMEOUT_ERROR_CODE_STR},
27+
constants::{
28+
GET_HEADER_ENDPOINT_TAG, MAX_SIZE_GET_HEADER, TIMEOUT_ERROR_CODE, TIMEOUT_ERROR_CODE_STR,
29+
},
2830
metrics::{RELAY_HEADER_VALUE, RELAY_LAST_SLOT, RELAY_LATENCY, RELAY_STATUS_CODE},
2931
state::{BuilderApiState, PbsState},
32+
utils::read_chunked_body_with_max,
3033
};
3134

3235
/// Implements https://ethereum.github.io/builder-specs/#/Builder/getHeader
@@ -247,11 +250,7 @@ async fn send_one_get_header(
247250
let code = res.status();
248251
RELAY_STATUS_CODE.with_label_values(&[code.as_str(), GET_HEADER_ENDPOINT_TAG, &relay.id]).inc();
249252

250-
let response_bytes = res.bytes().await?;
251-
if response_bytes.len() > MAX_SIZE {
252-
return Err(PbsError::PayloadTooLarge { max: MAX_SIZE, got: response_bytes.len() });
253-
}
254-
253+
let response_bytes = read_chunked_body_with_max(res, MAX_SIZE_GET_HEADER).await?;
255254
if !code.is_success() {
256255
return Err(PbsError::RelayResponse {
257256
error_msg: String::from_utf8_lossy(&response_bytes).into_owned(),

crates/pbs/src/mev_boost/register_validator.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::time::{Duration, Instant};
33
use alloy::rpc::types::beacon::relay::ValidatorRegistration;
44
use axum::http::{HeaderMap, HeaderValue};
55
use cb_common::{
6-
pbs::{error::PbsError, RelayClient, HEADER_START_TIME_UNIX_MS, MAX_SIZE},
6+
pbs::{error::PbsError, RelayClient, HEADER_START_TIME_UNIX_MS},
77
utils::{get_user_agent_with_version, utcnow_ms},
88
};
99
use eyre::bail;
@@ -12,9 +12,10 @@ use reqwest::header::USER_AGENT;
1212
use tracing::{debug, error, Instrument};
1313

1414
use crate::{
15-
constants::{REGISTER_VALIDATOR_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR},
15+
constants::{MAX_SIZE_DEFAULT, REGISTER_VALIDATOR_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR},
1616
metrics::{RELAY_LATENCY, RELAY_STATUS_CODE},
1717
state::{BuilderApiState, PbsState},
18+
utils::read_chunked_body_with_max,
1819
};
1920

2021
/// Implements https://ethereum.github.io/builder-specs/#/Builder/registerValidator
@@ -103,11 +104,8 @@ async fn send_register_validator(
103104
.with_label_values(&[code.as_str(), REGISTER_VALIDATOR_ENDPOINT_TAG, &relay.id])
104105
.inc();
105106

106-
let response_bytes = res.bytes().await?;
107-
if response_bytes.len() > MAX_SIZE {
108-
return Err(PbsError::PayloadTooLarge { max: MAX_SIZE, got: response_bytes.len() });
109-
}
110107
if !code.is_success() {
108+
let response_bytes = read_chunked_body_with_max(res, MAX_SIZE_DEFAULT).await?;
111109
let err = PbsError::RelayResponse {
112110
error_msg: String::from_utf8_lossy(&response_bytes).into_owned(),
113111
code: code.as_u16(),

crates/pbs/src/mev_boost/status.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ use std::time::{Duration, Instant};
22

33
use axum::http::HeaderMap;
44
use cb_common::{
5-
pbs::{error::PbsError, RelayClient, MAX_SIZE},
5+
pbs::{error::PbsError, RelayClient},
66
utils::get_user_agent_with_version,
77
};
88
use futures::future::select_ok;
99
use reqwest::header::USER_AGENT;
1010
use tracing::{debug, error};
1111

1212
use crate::{
13-
constants::{STATUS_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR},
13+
constants::{MAX_SIZE_DEFAULT, STATUS_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR},
1414
metrics::{RELAY_LATENCY, RELAY_STATUS_CODE},
1515
state::{BuilderApiState, PbsState},
16+
utils::read_chunked_body_with_max,
1617
};
1718

1819
/// Implements https://ethereum.github.io/builder-specs/#/Builder/status
@@ -74,11 +75,8 @@ async fn send_relay_check(relay: &RelayClient, headers: HeaderMap) -> Result<(),
7475
let code = res.status();
7576
RELAY_STATUS_CODE.with_label_values(&[code.as_str(), STATUS_ENDPOINT_TAG, &relay.id]).inc();
7677

77-
let response_bytes = res.bytes().await?;
78-
if response_bytes.len() > MAX_SIZE {
79-
return Err(PbsError::PayloadTooLarge { max: MAX_SIZE, got: response_bytes.len() });
80-
}
8178
if !code.is_success() {
79+
let response_bytes = read_chunked_body_with_max(res, MAX_SIZE_DEFAULT).await?;
8280
let err = PbsError::RelayResponse {
8381
error_msg: String::from_utf8_lossy(&response_bytes).into_owned(),
8482
code: code.as_u16(),

crates/pbs/src/mev_boost/submit_block.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cb_common::{
55
pbs::{
66
error::{PbsError, ValidationError},
77
RelayClient, SignedBlindedBeaconBlock, SubmitBlindedBlockResponse, HEADER_SLOT_UUID_KEY,
8-
HEADER_START_TIME_UNIX_MS, MAX_SIZE,
8+
HEADER_START_TIME_UNIX_MS,
99
},
1010
utils::{get_user_agent_with_version, utcnow_ms},
1111
};
@@ -14,9 +14,10 @@ use reqwest::header::USER_AGENT;
1414
use tracing::{debug, warn};
1515

1616
use crate::{
17-
constants::{SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR},
17+
constants::{MAX_SIZE_SUBMIT_BLOCK, SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR},
1818
metrics::{RELAY_LATENCY, RELAY_STATUS_CODE},
1919
state::{BuilderApiState, PbsState},
20+
utils::read_chunked_body_with_max,
2021
};
2122

2223
/// Implements https://ethereum.github.io/builder-specs/#/Builder/submitBlindedBlock
@@ -94,18 +95,14 @@ async fn send_submit_block(
9495
.with_label_values(&[code.as_str(), SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG, &relay.id])
9596
.inc();
9697

97-
let response_bytes = res.bytes().await?;
98-
99-
if response_bytes.len() > MAX_SIZE {
100-
return Err(PbsError::PayloadTooLarge { max: MAX_SIZE, got: response_bytes.len() });
101-
}
98+
let response_bytes = read_chunked_body_with_max(res, MAX_SIZE_SUBMIT_BLOCK).await?;
10299
if !code.is_success() {
103100
let err = PbsError::RelayResponse {
104101
error_msg: String::from_utf8_lossy(&response_bytes).into_owned(),
105102
code: code.as_u16(),
106103
};
107104

108-
// we request payload to all relays, but some may have not received it
105+
// we requested the payload from all relays, but some may have not received it
109106
warn!(%err, "failed to get payload (this might be ok if other relays have it)");
110107
return Err(err);
111108
};

0 commit comments

Comments
 (0)