11use std:: {
22 collections:: { HashMap , HashSet } ,
3+ path:: { Path , PathBuf } ,
34 sync:: Arc ,
45} ;
56
67use alloy:: rpc:: types:: beacon:: BlsPublicKey ;
7- use eyre:: { bail, ensure, eyre} ;
8+ use eyre:: { bail, ensure, eyre, Context } ;
89use serde:: { Deserialize , Serialize } ;
910
10- use super :: { PbsConfig , RelayConfig } ;
11+ use super :: { load_optional_env_var , PbsConfig , RelayConfig , MUX_PATH_ENV } ;
1112use crate :: pbs:: { RelayClient , RelayEntry } ;
1213
13- #[ derive( Debug , Clone , Deserialize , Serialize ) ]
14+ #[ derive( Debug , Deserialize , Serialize ) ]
1415pub struct PbsMuxes {
1516 /// List of PBS multiplexers
1617 #[ serde( rename = "mux" ) ]
@@ -19,6 +20,7 @@ pub struct PbsMuxes {
1920
2021#[ derive( Debug , Clone ) ]
2122pub struct RuntimeMuxConfig {
23+ pub id : String ,
2224 pub config : Arc < PbsConfig > ,
2325 pub relays : Vec < RelayClient > ,
2426}
@@ -29,9 +31,18 @@ impl PbsMuxes {
2931 default_pbs : & PbsConfig ,
3032 default_relays : & [ RelayConfig ] ,
3133 ) -> eyre:: Result < HashMap < BlsPublicKey , RuntimeMuxConfig > > {
34+ let mut muxes = self . muxes ;
35+
36+ for mux in muxes. iter_mut ( ) {
37+ if let Some ( loader) = & mux. loader {
38+ let extra_keys = loader. load ( & mux. id ) ?;
39+ mux. validator_pubkeys . extend ( extra_keys) ;
40+ }
41+ }
42+
3243 // check that validator pubkeys are in disjoint sets
3344 let mut unique_pubkeys = HashSet :: new ( ) ;
34- for mux in self . muxes . iter ( ) {
45+ for mux in muxes. iter ( ) {
3546 for pubkey in mux. validator_pubkeys . iter ( ) {
3647 if !unique_pubkeys. insert ( pubkey) {
3748 bail ! ( "duplicate validator pubkey in muxes: {pubkey}" ) ;
@@ -41,11 +52,12 @@ impl PbsMuxes {
4152
4253 let mut configs = HashMap :: new ( ) ;
4354 // fill the configs using the default pbs config and relay entries
44- for mux in self . muxes {
45- ensure ! ( !mux. relays. is_empty( ) , "mux config must have at least one relay" ) ;
55+ for mux in muxes {
56+ ensure ! ( !mux. relays. is_empty( ) , "mux config {} must have at least one relay" , mux . id ) ;
4657 ensure ! (
4758 !mux. validator_pubkeys. is_empty( ) ,
48- "mux config must have at least one validator pubkey"
59+ "mux config {} must have at least one validator pubkey" ,
60+ mux. id
4961 ) ;
5062
5163 let mut relay_clients = Vec :: with_capacity ( mux. relays . len ( ) ) ;
@@ -89,7 +101,7 @@ impl PbsMuxes {
89101 } ;
90102 let config = Arc :: new ( config) ;
91103
92- let runtime_config = RuntimeMuxConfig { config, relays : relay_clients } ;
104+ let runtime_config = RuntimeMuxConfig { id : mux . id , config, relays : relay_clients } ;
93105 for pubkey in mux. validator_pubkeys . iter ( ) {
94106 configs. insert ( * pubkey, runtime_config. clone ( ) ) ;
95107 }
@@ -100,16 +112,36 @@ impl PbsMuxes {
100112}
101113
102114/// Configuration for the PBS Multiplexer
103- #[ derive( Debug , Clone , Deserialize , Serialize ) ]
115+ #[ derive( Debug , Deserialize , Serialize ) ]
104116pub struct MuxConfig {
117+ /// Identifier for this mux config
118+ pub id : String ,
105119 /// Relays to use for this mux config
106120 pub relays : Vec < PartialRelayConfig > ,
107121 /// Which validator pubkeys to match against this mux config
122+ #[ serde( default ) ]
108123 pub validator_pubkeys : Vec < BlsPublicKey > ,
124+ /// Loader for extra validator pubkeys
125+ pub loader : Option < MuxKeysLoader > ,
109126 pub timeout_get_header_ms : Option < u64 > ,
110127 pub late_in_slot_time_ms : Option < u64 > ,
111128}
112129
130+ impl MuxConfig {
131+ /// Returns the env, actual path, and internal path to use for the loader
132+ pub fn loader_env ( & self ) -> Option < ( String , String , String ) > {
133+ self . loader . as_ref ( ) . map ( |loader| match loader {
134+ MuxKeysLoader :: File ( path_buf) => {
135+ let path =
136+ path_buf. to_str ( ) . unwrap_or_else ( || panic ! ( "invalid path: {:?}" , path_buf) ) ;
137+ let internal_path = get_mux_path ( & self . id ) ;
138+
139+ ( get_mux_env ( & self . id ) , path. to_owned ( ) , internal_path)
140+ }
141+ } )
142+ }
143+ }
144+
113145#[ derive( Debug , Clone , Deserialize , Serialize ) ]
114146/// A relay config with all optional fields. See [`RelayConfig`] for the
115147/// description of the fields.
@@ -136,3 +168,39 @@ impl PartialRelayConfig {
136168 }
137169 }
138170}
171+
172+ #[ derive( Debug , Clone , Deserialize , Serialize ) ]
173+ #[ serde( untagged) ]
174+ pub enum MuxKeysLoader {
175+ /// A file containing a list of validator pubkeys
176+ File ( PathBuf ) ,
177+ }
178+
179+ impl MuxKeysLoader {
180+ pub fn load ( & self , mux_id : & str ) -> eyre:: Result < Vec < BlsPublicKey > > {
181+ match self {
182+ Self :: File ( config_path) => {
183+ // First try loading from env
184+ let path: PathBuf = load_optional_env_var ( & get_mux_env ( mux_id) )
185+ . map ( PathBuf :: from)
186+ . unwrap_or ( config_path. clone ( ) ) ;
187+ let file = load_file ( path) ?;
188+ serde_json:: from_str ( & file) . wrap_err ( "failed to parse mux keys file" )
189+ }
190+ }
191+ }
192+ }
193+
194+ fn load_file < P : AsRef < Path > + std:: fmt:: Debug > ( path : P ) -> eyre:: Result < String > {
195+ std:: fs:: read_to_string ( & path) . wrap_err ( format ! ( "Unable to find mux keys file: {path:?}" ) )
196+ }
197+
198+ /// A different env var for each mux
199+ fn get_mux_env ( mux_id : & str ) -> String {
200+ format ! ( "{MUX_PATH_ENV}_{mux_id}" )
201+ }
202+
203+ /// Path to the mux file
204+ fn get_mux_path ( mux_id : & str ) -> String {
205+ format ! ( "/{mux_id}-mux_keys.json" )
206+ }
0 commit comments