11use {
22 crate :: {
33 chain:: reader:: { BlockNumber , BlockStatus , EntropyReader } ,
4+ config:: Config ,
45 history:: History ,
56 state:: MonitoredHashChainState ,
67 } ,
2223 tokio:: sync:: RwLock ,
2324 url:: Url ,
2425} ;
25- pub use { chain_ids:: * , explorer:: * , index:: * , live:: * , metrics:: * , ready:: * , revelation:: * } ;
26+ pub use {
27+ chain_ids:: * , config:: * , explorer:: * , index:: * , live:: * , metrics:: * , ready:: * , revelation:: * ,
28+ } ;
2629
2730mod chain_ids;
31+ mod config;
2832mod explorer;
2933mod index;
3034mod live;
@@ -73,13 +77,16 @@ pub struct ApiState {
7377 pub metrics : Arc < ApiMetrics > ,
7478
7579 pub explorer_metrics : Arc < ExplorerMetrics > ,
80+
81+ pub config : Config ,
7682}
7783
7884impl ApiState {
7985 pub async fn new (
8086 chains : Arc < RwLock < HashMap < ChainId , ApiBlockChainState > > > ,
8187 metrics_registry : Arc < RwLock < Registry > > ,
8288 history : Arc < History > ,
89+ config : & Config ,
8390 ) -> ApiState {
8491 let metrics = ApiMetrics {
8592 http_requests : Family :: default ( ) ,
@@ -100,6 +107,7 @@ impl ApiState {
100107 explorer_metrics,
101108 history,
102109 metrics_registry,
110+ config : config. clone ( ) ,
103111 }
104112 }
105113}
@@ -211,6 +219,7 @@ pub fn routes(state: ApiState) -> Router<(), Body> {
211219 "/v1/chains/:chain_id/revelations/:sequence" ,
212220 get ( revelation) ,
213221 )
222+ . route ( "/v1/chains/configs" , get ( get_chain_configs) )
214223 . with_state ( state)
215224}
216225
@@ -230,9 +239,10 @@ mod test {
230239 crate :: {
231240 api:: {
232241 self , ApiBlockChainState , ApiState , BinaryEncoding , Blob , BlockchainState ,
233- GetRandomValueResponse ,
242+ ChainConfigSummary , GetRandomValueResponse ,
234243 } ,
235244 chain:: reader:: { mock:: MockEntropyReader , BlockStatus } ,
245+ config:: Config ,
236246 history:: History ,
237247 state:: { HashChainState , MonitoredHashChainState , PebbleHashChain } ,
238248 } ,
@@ -311,10 +321,40 @@ mod test {
311321 ApiBlockChainState :: Initialized ( avax_state) ,
312322 ) ;
313323
324+ // Create a minimal config for testing
325+ let config = Config {
326+ chains : HashMap :: new ( ) ,
327+ provider : crate :: config:: ProviderConfig {
328+ uri : "http://localhost:8080/" . to_string ( ) ,
329+ address : PROVIDER ,
330+ private_key : crate :: config:: SecretString {
331+ value : Some ( "0xabcd" . to_string ( ) ) ,
332+ file : None ,
333+ } ,
334+ secret : crate :: config:: SecretString {
335+ value : Some ( "abcd" . to_string ( ) ) ,
336+ file : None ,
337+ } ,
338+ chain_length : 100000 ,
339+ chain_sample_interval : 10 ,
340+ fee_manager : None ,
341+ } ,
342+ keeper : crate :: config:: KeeperConfig {
343+ private_key : crate :: config:: SecretString {
344+ value : Some ( "0xabcd" . to_string ( ) ) ,
345+ file : None ,
346+ } ,
347+ fee_manager_private_key : None ,
348+ other_keeper_addresses : vec ! [ ] ,
349+ replica_config : None ,
350+ } ,
351+ } ;
352+
314353 let api_state = ApiState :: new (
315354 Arc :: new ( RwLock :: new ( chains) ) ,
316355 metrics_registry,
317356 Arc :: new ( History :: new ( ) . await . unwrap ( ) ) ,
357+ & config,
318358 )
319359 . await ;
320360
@@ -534,4 +574,151 @@ mod test {
534574 )
535575 . await ;
536576 }
577+
578+ #[ tokio:: test]
579+ async fn test_chain_configs ( ) {
580+ let ( server, _, _) = test_server ( ) . await ;
581+
582+ // Test the chain configs endpoint
583+ let response = server. get ( "/v1/chains/configs" ) . await ;
584+ response. assert_status ( StatusCode :: OK ) ;
585+
586+ // Parse the response as JSON
587+ let configs: Vec < ChainConfigSummary > = response. json ( ) ;
588+
589+ // Verify the response structure - should be empty for test server
590+ assert_eq ! (
591+ configs. len( ) ,
592+ 0 ,
593+ "Should return empty configs for test server"
594+ ) ;
595+ }
596+
597+ #[ tokio:: test]
598+ async fn test_chain_configs_with_data ( ) {
599+ // Create a config with actual chain data
600+ let mut config_chains = HashMap :: new ( ) ;
601+ config_chains. insert (
602+ "ethereum" . to_string ( ) ,
603+ crate :: config:: EthereumConfig {
604+ geth_rpc_addr : "http://localhost:8545" . to_string ( ) ,
605+ contract_addr : Address :: from_low_u64_be ( 0x1234 ) ,
606+ reveal_delay_blocks : 1 ,
607+ confirmed_block_status : BlockStatus :: Latest ,
608+ backlog_range : 1000 ,
609+ legacy_tx : false ,
610+ gas_limit : 500000 ,
611+ priority_fee_multiplier_pct : 100 ,
612+ escalation_policy : crate :: config:: EscalationPolicyConfig :: default ( ) ,
613+ min_profit_pct : 0 ,
614+ target_profit_pct : 20 ,
615+ max_profit_pct : 100 ,
616+ min_keeper_balance : 100000000000000000 ,
617+ fee : 1500000000000000 ,
618+ sync_fee_only_on_register : true ,
619+ commitments : None ,
620+ max_num_hashes : None ,
621+ block_delays : vec ! [ 5 ] ,
622+ } ,
623+ ) ;
624+ config_chains. insert (
625+ "avalanche" . to_string ( ) ,
626+ crate :: config:: EthereumConfig {
627+ geth_rpc_addr : "http://localhost:9650" . to_string ( ) ,
628+ contract_addr : Address :: from_low_u64_be ( 0x5678 ) ,
629+ reveal_delay_blocks : 2 ,
630+ confirmed_block_status : BlockStatus :: Latest ,
631+ backlog_range : 1000 ,
632+ legacy_tx : false ,
633+ gas_limit : 600000 ,
634+ priority_fee_multiplier_pct : 100 ,
635+ escalation_policy : crate :: config:: EscalationPolicyConfig :: default ( ) ,
636+ min_profit_pct : 0 ,
637+ target_profit_pct : 20 ,
638+ max_profit_pct : 100 ,
639+ min_keeper_balance : 100000000000000000 ,
640+ fee : 2000000000000000 ,
641+ sync_fee_only_on_register : true ,
642+ commitments : None ,
643+ max_num_hashes : None ,
644+ block_delays : vec ! [ 5 ] ,
645+ } ,
646+ ) ;
647+
648+ let config = Config {
649+ chains : config_chains,
650+ provider : crate :: config:: ProviderConfig {
651+ uri : "http://localhost:8080/" . to_string ( ) ,
652+ address : PROVIDER ,
653+ private_key : crate :: config:: SecretString {
654+ value : Some ( "0xabcd" . to_string ( ) ) ,
655+ file : None ,
656+ } ,
657+ secret : crate :: config:: SecretString {
658+ value : Some ( "abcd" . to_string ( ) ) ,
659+ file : None ,
660+ } ,
661+ chain_length : 100000 ,
662+ chain_sample_interval : 10 ,
663+ fee_manager : None ,
664+ } ,
665+ keeper : crate :: config:: KeeperConfig {
666+ private_key : crate :: config:: SecretString {
667+ value : Some ( "0xabcd" . to_string ( ) ) ,
668+ file : None ,
669+ } ,
670+ fee_manager_private_key : None ,
671+ other_keeper_addresses : vec ! [ ] ,
672+ replica_config : None ,
673+ } ,
674+ } ;
675+
676+ let metrics_registry = Arc :: new ( RwLock :: new ( Registry :: default ( ) ) ) ;
677+ let api_state = ApiState :: new (
678+ Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
679+ metrics_registry,
680+ Arc :: new ( History :: new ( ) . await . unwrap ( ) ) ,
681+ & config,
682+ )
683+ . await ;
684+
685+ let app = api:: routes ( api_state) ;
686+ let server = TestServer :: new ( app) . unwrap ( ) ;
687+
688+ // Test the chain configs endpoint
689+ let response = server. get ( "/v1/chains/configs" ) . await ;
690+ response. assert_status ( StatusCode :: OK ) ;
691+
692+ // Parse the response as JSON
693+ let configs: Vec < ChainConfigSummary > = response. json ( ) ;
694+
695+ // Verify we have 2 chains
696+ assert_eq ! ( configs. len( ) , 2 , "Should return 2 chain configs" ) ;
697+
698+ // Find ethereum config
699+ let eth_config = configs
700+ . iter ( )
701+ . find ( |c| c. name == "ethereum" )
702+ . expect ( "Ethereum config not found" ) ;
703+ assert_eq ! (
704+ eth_config. contract_addr,
705+ "0x0000000000000000000000000000000000001234"
706+ ) ;
707+ assert_eq ! ( eth_config. reveal_delay_blocks, 1 ) ;
708+ assert_eq ! ( eth_config. gas_limit, 500000 ) ;
709+ assert_eq ! ( eth_config. fee, 1500000000000000 ) ;
710+
711+ // Find avalanche config
712+ let avax_config = configs
713+ . iter ( )
714+ . find ( |c| c. name == "avalanche" )
715+ . expect ( "Avalanche config not found" ) ;
716+ assert_eq ! (
717+ avax_config. contract_addr,
718+ "0x0000000000000000000000000000000000005678"
719+ ) ;
720+ assert_eq ! ( avax_config. reveal_delay_blocks, 2 ) ;
721+ assert_eq ! ( avax_config. gas_limit, 600000 ) ;
722+ assert_eq ! ( avax_config. fee, 2000000000000000 ) ;
723+ }
537724}
0 commit comments