@@ -7,10 +7,9 @@ use std::{
77} ;
88
99use alloy:: {
10- primitives:: { Address , U256 , address } ,
10+ primitives:: { Address , Bytes , U256 } ,
1111 providers:: ProviderBuilder ,
1212 rpc:: { client:: RpcClient , types:: beacon:: constants:: BLS_PUBLIC_KEY_BYTES_LEN } ,
13- sol,
1413 transports:: http:: Http ,
1514} ;
1615use eyre:: { Context , bail, ensure} ;
@@ -22,7 +21,7 @@ use url::Url;
2221use super :: { MUX_PATH_ENV , PbsConfig , RelayConfig , load_optional_env_var} ;
2322use crate :: {
2423 config:: { remove_duplicate_keys, safe_read_http_response} ,
25- interop:: ssv:: utils:: fetch_ssv_pubkeys_from_url ,
24+ interop:: { lido :: utils :: * , ssv:: utils:: * } ,
2625 pbs:: RelayClient ,
2726 types:: { BlsPublicKey , Chain } ,
2827 utils:: default_bool,
@@ -193,6 +192,8 @@ pub enum MuxKeysLoader {
193192 Registry {
194193 registry : NORegistry ,
195194 node_operator_id : u64 ,
195+ #[ serde( default ) ]
196+ lido_module_id : Option < u8 > ,
196197 #[ serde( default = "default_bool::<false>" ) ]
197198 enable_refreshing : bool ,
198199 } ,
@@ -239,30 +240,33 @@ impl MuxKeysLoader {
239240 . wrap_err ( "failed to fetch mux keys from HTTP endpoint" )
240241 }
241242
242- Self :: Registry { registry, node_operator_id, enable_refreshing : _ } => match registry {
243- NORegistry :: Lido => {
244- let Some ( rpc_url) = rpc_url else {
245- bail ! ( "Lido registry requires RPC URL to be set in the PBS config" ) ;
246- } ;
247-
248- fetch_lido_registry_keys (
249- rpc_url,
250- chain,
251- U256 :: from ( * node_operator_id) ,
252- http_timeout,
253- )
254- . await
255- }
256- NORegistry :: SSV => {
257- fetch_ssv_pubkeys (
258- ssv_api_url,
259- chain,
260- U256 :: from ( * node_operator_id) ,
261- http_timeout,
262- )
263- . await
243+ Self :: Registry { registry, node_operator_id, lido_module_id, enable_refreshing : _ } => {
244+ match registry {
245+ NORegistry :: Lido => {
246+ let Some ( rpc_url) = rpc_url else {
247+ bail ! ( "Lido registry requires RPC URL to be set in the PBS config" ) ;
248+ } ;
249+
250+ fetch_lido_registry_keys (
251+ rpc_url,
252+ chain,
253+ U256 :: from ( * node_operator_id) ,
254+ lido_module_id. unwrap_or ( 1 ) ,
255+ http_timeout,
256+ )
257+ . await
258+ }
259+ NORegistry :: SSV => {
260+ fetch_ssv_pubkeys (
261+ ssv_api_url,
262+ chain,
263+ U256 :: from ( * node_operator_id) ,
264+ http_timeout,
265+ )
266+ . await
267+ }
264268 }
265- } ,
269+ }
266270 } ?;
267271
268272 // Remove duplicates
@@ -285,63 +289,28 @@ fn get_mux_path(mux_id: &str) -> String {
285289 format ! ( "/{mux_id}-mux_keys.json" )
286290}
287291
288- sol ! {
289- #[ allow( missing_docs) ]
290- #[ sol( rpc) ]
291- LidoRegistry ,
292- "src/abi/LidoNORegistry.json"
293- }
294-
295- // Fetching Lido Curated Module
296- fn lido_registry_address ( chain : Chain ) -> eyre:: Result < Address > {
297- match chain {
298- Chain :: Mainnet => Ok ( address ! ( "55032650b14df07b85bF18A3a3eC8E0Af2e028d5" ) ) ,
299- Chain :: Holesky => Ok ( address ! ( "595F64Ddc3856a3b5Ff4f4CC1d1fb4B46cFd2bAC" ) ) ,
300- Chain :: Hoodi => Ok ( address ! ( "5cDbE1590c083b5A2A64427fAA63A7cfDB91FbB5" ) ) ,
301- Chain :: Sepolia => Ok ( address ! ( "33d6E15047E8644F8DDf5CD05d202dfE587DA6E3" ) ) ,
302- _ => bail ! ( "Lido registry not supported for chain: {chain:?}" ) ,
303- }
304- }
305-
306- async fn fetch_lido_registry_keys (
307- rpc_url : Url ,
308- chain : Chain ,
309- node_operator_id : U256 ,
310- http_timeout : Duration ,
311- ) -> eyre:: Result < Vec < BlsPublicKey > > {
312- debug ! ( ?chain, %node_operator_id, "loading operator keys from Lido registry" ) ;
313-
314- // Create an RPC provider with HTTP timeout support
315- let client = Client :: builder ( ) . timeout ( http_timeout) . build ( ) ?;
316- let http = Http :: with_client ( client, rpc_url) ;
317- let is_local = http. guess_local ( ) ;
318- let rpc_client = RpcClient :: new ( http, is_local) ;
319- let provider = ProviderBuilder :: new ( ) . connect_client ( rpc_client) ;
320-
321- let registry_address = lido_registry_address ( chain) ?;
322- let registry = LidoRegistry :: new ( registry_address, provider) ;
323-
324- let total_keys = registry. getTotalSigningKeyCount ( node_operator_id) . call ( ) . await ?. try_into ( ) ?;
325-
292+ async fn collect_registry_keys < F , Fut > (
293+ total_keys : u64 ,
294+ mut fetch_batch : F ,
295+ ) -> eyre:: Result < Vec < BlsPublicKey > >
296+ where
297+ F : FnMut ( u64 , u64 ) -> Fut ,
298+ Fut : std:: future:: Future < Output = eyre:: Result < Bytes > > ,
299+ {
326300 if total_keys == 0 {
327301 return Ok ( Vec :: new ( ) ) ;
328302 }
329-
330303 debug ! ( "fetching {total_keys} total keys" ) ;
331304
332305 const CALL_BATCH_SIZE : u64 = 250u64 ;
333306
334307 let mut keys = vec ! [ ] ;
335- let mut offset = 0 ;
308+ let mut offset: u64 = 0 ;
336309
337310 while offset < total_keys {
338311 let limit = CALL_BATCH_SIZE . min ( total_keys - offset) ;
339312
340- let pubkeys = registry
341- . getSigningKeys ( node_operator_id, U256 :: from ( offset) , U256 :: from ( limit) )
342- . call ( )
343- . await ?
344- . pubkeys ;
313+ let pubkeys = fetch_batch ( offset, limit) . await ?;
345314
346315 ensure ! (
347316 pubkeys. len( ) % BLS_PUBLIC_KEY_BYTES_LEN == 0 ,
@@ -368,6 +337,59 @@ async fn fetch_lido_registry_keys(
368337 Ok ( keys)
369338}
370339
340+ async fn fetch_lido_csm_registry_keys (
341+ registry_address : Address ,
342+ rpc_client : RpcClient ,
343+ node_operator_id : U256 ,
344+ ) -> eyre:: Result < Vec < BlsPublicKey > > {
345+ let provider = ProviderBuilder :: new ( ) . connect_client ( rpc_client) ;
346+ let registry = get_lido_csm_registry ( registry_address, provider) ;
347+ let total_keys = fetch_lido_csm_keys_total ( & registry, node_operator_id) . await ?;
348+
349+ collect_registry_keys ( total_keys, |offset, limit| {
350+ fetch_lido_csm_keys_batch ( & registry, node_operator_id, offset, limit)
351+ } )
352+ . await
353+ }
354+
355+ async fn fetch_lido_module_registry_keys (
356+ registry_address : Address ,
357+ rpc_client : RpcClient ,
358+ node_operator_id : U256 ,
359+ ) -> eyre:: Result < Vec < BlsPublicKey > > {
360+ let provider = ProviderBuilder :: new ( ) . connect_client ( rpc_client) ;
361+ let registry = get_lido_module_registry ( registry_address, provider) ;
362+ let total_keys: u64 = fetch_lido_module_keys_total ( & registry, node_operator_id) . await ?;
363+
364+ collect_registry_keys ( total_keys, |offset, limit| {
365+ fetch_lido_module_keys_batch ( & registry, node_operator_id, offset, limit)
366+ } )
367+ . await
368+ }
369+
370+ async fn fetch_lido_registry_keys (
371+ rpc_url : Url ,
372+ chain : Chain ,
373+ node_operator_id : U256 ,
374+ lido_module_id : u8 ,
375+ http_timeout : Duration ,
376+ ) -> eyre:: Result < Vec < BlsPublicKey > > {
377+ debug ! ( ?chain, %node_operator_id, ?lido_module_id, "loading operator keys from Lido registry" ) ;
378+
379+ // Create an RPC provider with HTTP timeout support
380+ let client = Client :: builder ( ) . timeout ( http_timeout) . build ( ) ?;
381+ let http = Http :: with_client ( client, rpc_url) ;
382+ let is_local = http. guess_local ( ) ;
383+ let rpc_client = RpcClient :: new ( http, is_local) ;
384+ let registry_address = lido_registry_address ( chain, lido_module_id) ?;
385+
386+ if is_csm_module ( chain, lido_module_id) {
387+ fetch_lido_csm_registry_keys ( registry_address, rpc_client, node_operator_id) . await
388+ } else {
389+ fetch_lido_module_registry_keys ( registry_address, rpc_client, node_operator_id) . await
390+ }
391+ }
392+
371393async fn fetch_ssv_pubkeys (
372394 mut api_url : Url ,
373395 chain : Chain ,
@@ -421,46 +443,3 @@ async fn fetch_ssv_pubkeys(
421443
422444 Ok ( pubkeys)
423445}
424-
425- #[ cfg( test) ]
426- mod tests {
427- use alloy:: { primitives:: U256 , providers:: ProviderBuilder } ;
428- use url:: Url ;
429-
430- use super :: * ;
431-
432- #[ tokio:: test]
433- async fn test_lido_registry_address ( ) -> eyre:: Result < ( ) > {
434- let url = Url :: parse ( "https://ethereum-rpc.publicnode.com" ) ?;
435- let provider = ProviderBuilder :: new ( ) . connect_http ( url) ;
436-
437- let registry =
438- LidoRegistry :: new ( address ! ( "55032650b14df07b85bF18A3a3eC8E0Af2e028d5" ) , provider) ;
439-
440- const LIMIT : usize = 3 ;
441- let node_operator_id = U256 :: from ( 1 ) ;
442-
443- let total_keys: u64 =
444- registry. getTotalSigningKeyCount ( node_operator_id) . call ( ) . await ?. try_into ( ) ?;
445-
446- assert ! ( total_keys > LIMIT as u64 ) ;
447-
448- let pubkeys = registry
449- . getSigningKeys ( node_operator_id, U256 :: ZERO , U256 :: from ( LIMIT ) )
450- . call ( )
451- . await ?
452- . pubkeys ;
453-
454- let mut vec = vec ! [ ] ;
455- for chunk in pubkeys. chunks ( BLS_PUBLIC_KEY_BYTES_LEN ) {
456- vec. push (
457- BlsPublicKey :: deserialize ( chunk)
458- . map_err ( |_| eyre:: eyre!( "invalid BLS public key" ) ) ?,
459- ) ;
460- }
461-
462- assert_eq ! ( vec. len( ) , LIMIT ) ;
463-
464- Ok ( ( ) )
465- }
466- }
0 commit comments