Skip to content

Commit 1d8ef3a

Browse files
committed
Fixed v2->v1 fallback retrying
1 parent 653b232 commit 1d8ef3a

3 files changed

Lines changed: 84 additions & 10 deletions

File tree

crates/pbs/src/mev_boost/submit_block.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ async fn submit_block_with_timeout(
9898
let mut remaining_timeout_ms = timeout_ms;
9999
let mut retry = 0;
100100
let mut backoff = Duration::from_millis(250);
101+
let mut request_api_version = api_version;
101102

102103
loop {
103104
let start_request = Instant::now();
@@ -108,12 +109,21 @@ async fn submit_block_with_timeout(
108109
headers.clone(),
109110
remaining_timeout_ms,
110111
retry,
111-
&api_version,
112+
&request_api_version,
112113
fork_name,
113114
)
114115
.await
115116
{
116-
Ok(response) => return Ok(response),
117+
Ok(response) => {
118+
// If the original request was for v2 but we had to fall back to v1, return a v2
119+
// response
120+
if request_api_version == BuilderApiVersion::V1 &&
121+
api_version != request_api_version
122+
{
123+
return Ok(None);
124+
}
125+
return Ok(response);
126+
}
117127

118128
Err(err) if err.should_retry() => {
119129
tokio::time::sleep(backoff).await;
@@ -127,12 +137,15 @@ async fn submit_block_with_timeout(
127137
}
128138
}
129139

130-
Err(err) if err.is_not_found() && matches!(api_version, BuilderApiVersion::V2) => {
140+
Err(err)
141+
if err.is_not_found() && matches!(request_api_version, BuilderApiVersion::V2) =>
142+
{
131143
warn!(
132144
relay_id = relay.id.as_ref(),
133145
"relay does not support v2 endpoint, retrying with v1"
134146
);
135147
url = relay.submit_block_url(BuilderApiVersion::V1)?;
148+
request_api_version = BuilderApiVersion::V1;
136149
}
137150

138151
Err(err) => return Err(err),

tests/src/mock_relay.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ pub struct MockRelayState {
4646
pub chain: Chain,
4747
pub signer: BlsSecretKey,
4848
large_body: bool,
49+
supports_submit_block_v2: bool,
50+
use_not_found_for_submit_block: bool,
4951
received_get_header: Arc<AtomicU64>,
5052
received_get_status: Arc<AtomicU64>,
5153
received_register_validator: Arc<AtomicU64>,
@@ -69,6 +71,12 @@ impl MockRelayState {
6971
pub fn large_body(&self) -> bool {
7072
self.large_body
7173
}
74+
pub fn supports_submit_block_v2(&self) -> bool {
75+
self.supports_submit_block_v2
76+
}
77+
pub fn use_not_found_for_submit_block(&self) -> bool {
78+
self.use_not_found_for_submit_block
79+
}
7280
pub fn set_response_override(&self, status: StatusCode) {
7381
*self.response_override.write().unwrap() = Some(status);
7482
}
@@ -80,6 +88,8 @@ impl MockRelayState {
8088
chain,
8189
signer,
8290
large_body: false,
91+
supports_submit_block_v2: true,
92+
use_not_found_for_submit_block: false,
8393
received_get_header: Default::default(),
8494
received_get_status: Default::default(),
8595
received_register_validator: Default::default(),
@@ -91,6 +101,14 @@ impl MockRelayState {
91101
pub fn with_large_body(self) -> Self {
92102
Self { large_body: true, ..self }
93103
}
104+
105+
pub fn with_no_submit_block_v2(self) -> Self {
106+
Self { supports_submit_block_v2: false, ..self }
107+
}
108+
109+
pub fn with_not_found_for_submit_block(self) -> Self {
110+
Self { use_not_found_for_submit_block: true, ..self }
111+
}
94112
}
95113

96114
pub fn mock_relay_app_router(state: Arc<MockRelayState>) -> Router {
@@ -100,7 +118,11 @@ pub fn mock_relay_app_router(state: Arc<MockRelayState>) -> Router {
100118
.route(REGISTER_VALIDATOR_PATH, post(handle_register_validator))
101119
.route(SUBMIT_BLOCK_PATH, post(handle_submit_block_v1));
102120

103-
let v2_builder_routes = Router::new().route(SUBMIT_BLOCK_PATH, post(handle_submit_block_v2));
121+
let v2_builder_routes = if state.supports_submit_block_v2 {
122+
Router::new().route(SUBMIT_BLOCK_PATH, post(handle_submit_block_v2))
123+
} else {
124+
Router::new()
125+
};
104126

105127
let builder_router_v1 = Router::new().nest(BUILDER_V1_API_PATH, v1_builder_routes);
106128
let builder_router_v2 = Router::new().nest(BUILDER_V2_API_PATH, v2_builder_routes);
@@ -165,6 +187,9 @@ async fn handle_submit_block_v1(
165187
State(state): State<Arc<MockRelayState>>,
166188
Json(submit_block): Json<SignedBlindedBeaconBlock>,
167189
) -> Response {
190+
if state.use_not_found_for_submit_block() {
191+
return StatusCode::NOT_FOUND.into_response();
192+
}
168193
state.received_submit_block.fetch_add(1, Ordering::Relaxed);
169194
if state.large_body() {
170195
(StatusCode::OK, Json(vec![1u8; 1 + MAX_SIZE_SUBMIT_BLOCK_RESPONSE])).into_response()
@@ -192,6 +217,9 @@ async fn handle_submit_block_v1(
192217
}
193218
}
194219
async fn handle_submit_block_v2(State(state): State<Arc<MockRelayState>>) -> Response {
220+
if state.use_not_found_for_submit_block() {
221+
return StatusCode::NOT_FOUND.into_response();
222+
}
195223
state.received_submit_block.fetch_add(1, Ordering::Relaxed);
196224
(StatusCode::ACCEPTED, "").into_response()
197225
}

tests/tests/pbs_post_blinded_blocks.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::{sync::Arc, time::Duration};
22

3+
use alloy::providers::mock;
34
use cb_common::{
45
pbs::{BuilderApiVersion, GetPayloadInfo, SubmitBlindedBlockResponse},
56
signer::random_secret,
67
types::Chain,
78
};
89
use cb_pbs::{DefaultBuilderApi, PbsService, PbsState};
910
use cb_tests::{
10-
mock_relay::{MockRelayState, start_mock_relay_service},
11+
mock_relay::{self, MockRelayState, start_mock_relay_service},
1112
mock_validator::{MockValidator, load_test_signed_blinded_block},
1213
utils::{generate_mock_relay, get_pbs_static_config, setup_test_env, to_pbs_config},
1314
};
@@ -17,7 +18,7 @@ use tracing::info;
1718

1819
#[tokio::test]
1920
async fn test_submit_block_v1() -> Result<()> {
20-
let res = submit_block_impl(3800, &BuilderApiVersion::V1).await?;
21+
let res = submit_block_impl(3800, &BuilderApiVersion::V1, false, false).await?;
2122
assert_eq!(res.status(), StatusCode::OK);
2223

2324
let signed_blinded_block = load_test_signed_blinded_block();
@@ -32,12 +33,31 @@ async fn test_submit_block_v1() -> Result<()> {
3233

3334
#[tokio::test]
3435
async fn test_submit_block_v2() -> Result<()> {
35-
let res = submit_block_impl(3850, &BuilderApiVersion::V2).await?;
36+
let res = submit_block_impl(3802, &BuilderApiVersion::V2, false, false).await?;
3637
assert_eq!(res.status(), StatusCode::ACCEPTED);
3738
assert_eq!(res.bytes().await?.len(), 0);
3839
Ok(())
3940
}
4041

42+
// Test that when submitting a block using v2 to a relay that does not support
43+
// v2, PBS falls back to v1 and successfully submits the block.
44+
#[tokio::test]
45+
async fn test_submit_block_v2_without_relay_support() -> Result<()> {
46+
let res = submit_block_impl(3804, &BuilderApiVersion::V2, true, false).await?;
47+
assert_eq!(res.status(), StatusCode::ACCEPTED);
48+
assert_eq!(res.bytes().await?.len(), 0);
49+
Ok(())
50+
}
51+
52+
// Test that when submitting a block using v2 to a relay that returns 404s
53+
// for both v1 and v2, PBS doesn't loop forever.
54+
#[tokio::test]
55+
async fn test_submit_block_on_broken_relay() -> Result<()> {
56+
let res = submit_block_impl(3806, &BuilderApiVersion::V2, true, true).await?;
57+
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
58+
Ok(())
59+
}
60+
4161
#[tokio::test]
4262
async fn test_submit_block_too_large() -> Result<()> {
4363
setup_test_env();
@@ -68,7 +88,12 @@ async fn test_submit_block_too_large() -> Result<()> {
6888
Ok(())
6989
}
7090

71-
async fn submit_block_impl(pbs_port: u16, api_version: &BuilderApiVersion) -> Result<Response> {
91+
async fn submit_block_impl(
92+
pbs_port: u16,
93+
api_version: &BuilderApiVersion,
94+
remove_v2_support: bool,
95+
force_404s: bool,
96+
) -> Result<Response> {
7297
setup_test_env();
7398
let signer = random_secret();
7499
let pubkey = signer.public_key();
@@ -77,7 +102,14 @@ async fn submit_block_impl(pbs_port: u16, api_version: &BuilderApiVersion) -> Re
77102

78103
// Run a mock relay
79104
let relays = vec![generate_mock_relay(pbs_port + 1, pubkey)?];
80-
let mock_state = Arc::new(MockRelayState::new(chain, signer));
105+
let mut mock_relay_state = MockRelayState::new(chain, signer);
106+
if remove_v2_support {
107+
mock_relay_state = mock_relay_state.with_no_submit_block_v2();
108+
}
109+
if force_404s {
110+
mock_relay_state = mock_relay_state.with_not_found_for_submit_block();
111+
}
112+
let mock_state = Arc::new(mock_relay_state);
81113
tokio::spawn(start_mock_relay_service(mock_state.clone(), pbs_port + 1));
82114

83115
// Run the PBS service
@@ -99,6 +131,7 @@ async fn submit_block_impl(pbs_port: u16, api_version: &BuilderApiVersion) -> Re
99131
mock_validator.do_submit_block_v2(Some(signed_blinded_block)).await?
100132
}
101133
};
102-
assert_eq!(mock_state.received_submit_block(), 1);
134+
let expected_count = if force_404s { 0 } else { 1 };
135+
assert_eq!(mock_state.received_submit_block(), expected_count);
103136
Ok(res)
104137
}

0 commit comments

Comments
 (0)