Skip to content

Commit bcc7a4f

Browse files
committed
Fetch keys from SSV network
1 parent 4c370ea commit bcc7a4f

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

crates/common/src/config/mux.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ pub enum MuxKeysLoader {
149149
pub enum NORegistry {
150150
#[serde(alias = "lido")]
151151
Lido,
152+
#[serde(alias = "ssv")]
153+
SSV,
152154
}
153155

154156
impl MuxKeysLoader {
@@ -176,6 +178,9 @@ impl MuxKeysLoader {
176178

177179
fetch_lido_registry_keys(rpc_url, chain, U256::from(*node_operator_id)).await
178180
}
181+
NORegistry::SSV => {
182+
fetch_ssv_registry_keys(chain, U256::from(*node_operator_id)).await
183+
}
179184
},
180185
}
181186
}
@@ -266,6 +271,71 @@ async fn fetch_lido_registry_keys(
266271
Ok(keys)
267272
}
268273

274+
async fn fetch_ssv_registry_keys(
275+
chain: Chain,
276+
node_operator_id: U256,
277+
) -> eyre::Result<Vec<BlsPublicKey>> {
278+
const MAX_PER_PAGE: usize = 100;
279+
280+
let chain_name = match chain {
281+
Chain::Mainnet => "mainnet",
282+
Chain::Holesky => "holesky",
283+
_ => bail!("SSV network is not supported for chain: {chain:?}"),
284+
};
285+
286+
let client = reqwest::Client::new();
287+
let mut pubkeys: Vec<BlsPublicKey> = vec![];
288+
let mut page = 1;
289+
290+
loop {
291+
let response = client
292+
.get(format!(
293+
"https://api.ssv.network/api/v4/{}/validators/in_operator/{}?perPage={}&page={}",
294+
chain_name, node_operator_id, MAX_PER_PAGE, page
295+
))
296+
.send()
297+
.await
298+
.map_err(|e| eyre::eyre!("Error sending request to SSV network: {e}"))?
299+
.json::<SSVResponse>()
300+
.await?;
301+
302+
pubkeys.extend(response.validators.iter().map(|v| v.pubkey).collect::<Vec<BlsPublicKey>>());
303+
page += 1;
304+
305+
if response.validators.is_empty() || response.validators.len() < MAX_PER_PAGE {
306+
ensure!(
307+
pubkeys.len() == response.pagination.total,
308+
"expected {} keys, got {}",
309+
response.pagination.total,
310+
pubkeys.len()
311+
);
312+
break;
313+
}
314+
}
315+
316+
let unique = pubkeys.iter().collect::<HashSet<_>>();
317+
ensure!(unique.len() == pubkeys.len(), "found duplicate keys in registry");
318+
319+
Ok(pubkeys)
320+
}
321+
322+
#[derive(Deserialize)]
323+
struct SSVResponse {
324+
validators: Vec<SSVValidator>,
325+
pagination: SSVPagination,
326+
}
327+
328+
#[derive(Deserialize)]
329+
struct SSVValidator {
330+
#[serde(rename = "public_key")]
331+
pubkey: BlsPublicKey,
332+
}
333+
334+
#[derive(Deserialize)]
335+
struct SSVPagination {
336+
total: usize,
337+
}
338+
269339
#[cfg(test)]
270340
mod tests {
271341
use alloy::{primitives::U256, providers::ProviderBuilder};

examples/configs/pbs_mux.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ id = "timing-mux"
1919
validator_pubkeys = [
2020
"0x80c7f782b2467c5898c5516a8b6595d75623960b4afc4f71ee07d40985d20e117ba35e7cd352a3e75fb85a8668a3b745",
2121
]
22-
loader = "./mux_keys.example.json"
22+
loader = "./tests/data/mux_keys.example.json"
2323
timeout_get_header_ms = 900
2424
late_in_slot_time_ms = 1500
2525

@@ -38,3 +38,11 @@ loader = { registry = "lido", node_operator_id = 8 }
3838
[[mux.relays]]
3939
id = "relay-3"
4040
url = "http://0x80c7f782b2467c5898c5516a8b6595d75623960b4afc4f71ee07d40985d20e117ba35e7cd352a3e75fb85a8668a3b745@fgh.xyz"
41+
42+
[[mux]]
43+
id = "ssv-mux"
44+
loader = { registry = "ssv", node_operator_id = 200 }
45+
46+
[[mux.relays]]
47+
id = "relay-4"
48+
url = "http://0x80c7f782b2467c5898c5516a8b6595d75623960b4afc4f71ee07d40985d20e117ba35e7cd352a3e75fb85a8668a3b745@fgh.xyz"

0 commit comments

Comments
 (0)