Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion crates/common/src/config/mux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ async fn fetch_lido_registry_keys(
}

async fn fetch_ssv_pubkeys(
api_url: Url,
mut api_url: Url,
chain: Chain,
node_operator_id: U256,
http_timeout: Duration,
Expand All @@ -386,6 +386,15 @@ async fn fetch_ssv_pubkeys(
let mut pubkeys: Vec<BlsPublicKey> = vec![];
let mut page = 1;

// Validate the URL - this appends a trailing slash if missing as efficiently as
// possible
if !api_url.path().ends_with('/') {
match api_url.path_segments_mut() {
Ok(mut segments) => segments.push(""), // Analogous to a trailing slash
Err(_) => bail!("SSV API URL is not a valid base URL"),
};
}

loop {
let route = format!(
"{chain_name}/validators/in_operator/{node_operator_id}?perPage={MAX_PER_PAGE}&page={page}",
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/config/pbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,5 +404,5 @@ pub async fn load_pbs_custom_config<T: DeserializeOwned>() -> Result<(PbsModuleC

/// Default URL for the SSV network API
fn default_ssv_api_url() -> Url {
Url::parse("https://api.ssv.network/api/v4").expect("default URL is valid")
Url::parse("https://api.ssv.network/api/v4/").expect("default URL is valid")
}
5 changes: 4 additions & 1 deletion tests/src/mock_ssv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ pub async fn create_mock_ssv_server(
force_timeout: Arc::new(RwLock::new(false)),
});
let router = axum::Router::new()
.route("/{chain_name}/validators/in_operator/{node_operator_id}", get(handle_validators))
.route(
"/api/v4/{chain_name}/validators/in_operator/{node_operator_id}",
get(handle_validators),
)
.route("/big_data", get(handle_big_data))
.with_state(state)
.into_make_service();
Expand Down
10 changes: 6 additions & 4 deletions tests/tests/pbs_mux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ async fn test_ssv_network_fetch() -> Result<()> {
// Start the mock server
let port = 30100;
let _server_handle = create_mock_ssv_server(port, None).await?;
let url = Url::parse(&format!("http://localhost:{port}/test_chain/validators/in_operator/1"))
.unwrap();
let url =
Url::parse(&format!("http://localhost:{port}/api/v4/test_chain/validators/in_operator/1"))
.unwrap();
let response =
fetch_ssv_pubkeys_from_url(url, Duration::from_secs(HTTP_TIMEOUT_SECONDS_DEFAULT)).await?;

Expand Down Expand Up @@ -100,8 +101,9 @@ async fn test_ssv_network_fetch_timeout() -> Result<()> {
force_timeout: Arc::new(RwLock::new(true)),
};
let server_handle = create_mock_ssv_server(port, Some(state)).await?;
let url = Url::parse(&format!("http://localhost:{port}/test_chain/validators/in_operator/1"))
.unwrap();
let url =
Url::parse(&format!("http://localhost:{port}/api/v4/test_chain/validators/in_operator/1"))
.unwrap();
let response = fetch_ssv_pubkeys_from_url(url, Duration::from_secs(TEST_HTTP_TIMEOUT)).await;

// The response should fail due to timeout
Expand Down
3 changes: 2 additions & 1 deletion tests/tests/pbs_mux_refresh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ async fn test_auto_refresh() -> Result<()> {

// Start the mock SSV API server
let ssv_api_port = pbs_port + 1;
let ssv_api_url = Url::parse(&format!("http://localhost:{ssv_api_port}"))?;
// Intentionally missing a trailing slash to ensure this is handled properly
let ssv_api_url = Url::parse(&format!("http://localhost:{ssv_api_port}/api/v4"))?;
let mock_ssv_state = SsvMockState {
validators: Arc::new(RwLock::new(vec![SSVValidator {
pubkey: existing_mux_pubkey.clone(),
Expand Down
Loading