1
1
use {
2
2
crate :: {
3
3
chain:: reader:: { BlockNumber , BlockStatus , EntropyReader } ,
4
+ config:: Config ,
4
5
history:: History ,
5
6
state:: MonitoredHashChainState ,
6
7
} ,
22
23
tokio:: sync:: RwLock ,
23
24
url:: Url ,
24
25
} ;
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
+ } ;
26
29
27
30
mod chain_ids;
31
+ mod config;
28
32
mod explorer;
29
33
mod index;
30
34
mod live;
@@ -73,13 +77,16 @@ pub struct ApiState {
73
77
pub metrics : Arc < ApiMetrics > ,
74
78
75
79
pub explorer_metrics : Arc < ExplorerMetrics > ,
80
+
81
+ pub config : Config ,
76
82
}
77
83
78
84
impl ApiState {
79
85
pub async fn new (
80
86
chains : Arc < RwLock < HashMap < ChainId , ApiBlockChainState > > > ,
81
87
metrics_registry : Arc < RwLock < Registry > > ,
82
88
history : Arc < History > ,
89
+ config : & Config ,
83
90
) -> ApiState {
84
91
let metrics = ApiMetrics {
85
92
http_requests : Family :: default ( ) ,
@@ -100,6 +107,7 @@ impl ApiState {
100
107
explorer_metrics,
101
108
history,
102
109
metrics_registry,
110
+ config : config. clone ( ) ,
103
111
}
104
112
}
105
113
}
@@ -211,6 +219,7 @@ pub fn routes(state: ApiState) -> Router<(), Body> {
211
219
"/v1/chains/:chain_id/revelations/:sequence" ,
212
220
get ( revelation) ,
213
221
)
222
+ . route ( "/v1/chains/configs" , get ( get_chain_configs) )
214
223
. with_state ( state)
215
224
}
216
225
@@ -230,9 +239,10 @@ mod test {
230
239
crate :: {
231
240
api:: {
232
241
self , ApiBlockChainState , ApiState , BinaryEncoding , Blob , BlockchainState ,
233
- GetRandomValueResponse ,
242
+ ChainConfigSummary , GetRandomValueResponse ,
234
243
} ,
235
244
chain:: reader:: { mock:: MockEntropyReader , BlockStatus } ,
245
+ config:: Config ,
236
246
history:: History ,
237
247
state:: { HashChainState , MonitoredHashChainState , PebbleHashChain } ,
238
248
} ,
@@ -311,10 +321,40 @@ mod test {
311
321
ApiBlockChainState :: Initialized ( avax_state) ,
312
322
) ;
313
323
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
+
314
353
let api_state = ApiState :: new (
315
354
Arc :: new ( RwLock :: new ( chains) ) ,
316
355
metrics_registry,
317
356
Arc :: new ( History :: new ( ) . await . unwrap ( ) ) ,
357
+ & config,
318
358
)
319
359
. await ;
320
360
@@ -534,4 +574,212 @@ mod test {
534
574
)
535
575
. await ;
536
576
}
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
+ use crate :: api:: get_chain_configs;
600
+ use axum:: { routing:: get, Router } ;
601
+
602
+ // Create a config with actual chain data
603
+ let mut config_chains = HashMap :: new ( ) ;
604
+ config_chains. insert (
605
+ "ethereum" . to_string ( ) ,
606
+ crate :: config:: EthereumConfig {
607
+ geth_rpc_addr : "http://localhost:8545" . to_string ( ) ,
608
+ contract_addr : Address :: from_low_u64_be ( 0x1234 ) ,
609
+ reveal_delay_blocks : 1 ,
610
+ confirmed_block_status : BlockStatus :: Latest ,
611
+ backlog_range : 1000 ,
612
+ legacy_tx : false ,
613
+ gas_limit : 500000 ,
614
+ priority_fee_multiplier_pct : 100 ,
615
+ escalation_policy : crate :: config:: EscalationPolicyConfig :: default ( ) ,
616
+ min_profit_pct : 0 ,
617
+ target_profit_pct : 20 ,
618
+ max_profit_pct : 100 ,
619
+ min_keeper_balance : 100000000000000000 ,
620
+ fee : 1500000000000000 ,
621
+ sync_fee_only_on_register : true ,
622
+ commitments : None ,
623
+ max_num_hashes : None ,
624
+ block_delays : vec ! [ 5 ] ,
625
+ } ,
626
+ ) ;
627
+ config_chains. insert (
628
+ "avalanche" . to_string ( ) ,
629
+ crate :: config:: EthereumConfig {
630
+ geth_rpc_addr : "http://localhost:9650" . to_string ( ) ,
631
+ contract_addr : Address :: from_low_u64_be ( 0x5678 ) ,
632
+ reveal_delay_blocks : 2 ,
633
+ confirmed_block_status : BlockStatus :: Latest ,
634
+ backlog_range : 1000 ,
635
+ legacy_tx : false ,
636
+ gas_limit : 600000 ,
637
+ priority_fee_multiplier_pct : 100 ,
638
+ escalation_policy : crate :: config:: EscalationPolicyConfig :: default ( ) ,
639
+ min_profit_pct : 0 ,
640
+ target_profit_pct : 20 ,
641
+ max_profit_pct : 100 ,
642
+ min_keeper_balance : 100000000000000000 ,
643
+ fee : 2000000000000000 ,
644
+ sync_fee_only_on_register : true ,
645
+ commitments : None ,
646
+ max_num_hashes : None ,
647
+ block_delays : vec ! [ 5 ] ,
648
+ } ,
649
+ ) ;
650
+
651
+ let config = Config {
652
+ chains : config_chains,
653
+ provider : crate :: config:: ProviderConfig {
654
+ uri : "http://localhost:8080/" . to_string ( ) ,
655
+ address : PROVIDER ,
656
+ private_key : crate :: config:: SecretString {
657
+ value : Some ( "0xabcd" . to_string ( ) ) ,
658
+ file : None ,
659
+ } ,
660
+ secret : crate :: config:: SecretString {
661
+ value : Some ( "abcd" . to_string ( ) ) ,
662
+ file : None ,
663
+ } ,
664
+ chain_length : 100000 ,
665
+ chain_sample_interval : 10 ,
666
+ fee_manager : None ,
667
+ } ,
668
+ keeper : crate :: config:: KeeperConfig {
669
+ private_key : crate :: config:: SecretString {
670
+ value : Some ( "0xabcd" . to_string ( ) ) ,
671
+ file : None ,
672
+ } ,
673
+ fee_manager_private_key : None ,
674
+ other_keeper_addresses : vec ! [ ] ,
675
+ replica_config : None ,
676
+ } ,
677
+ } ;
678
+
679
+ // Create initialized blockchain states with network IDs
680
+ let eth_read = Arc :: new ( MockEntropyReader :: with_requests ( 10 , & [ ] ) ) ;
681
+ let avax_read = Arc :: new ( MockEntropyReader :: with_requests ( 10 , & [ ] ) ) ;
682
+
683
+ let eth_state = MonitoredHashChainState :: new (
684
+ ETH_CHAIN . clone ( ) ,
685
+ Default :: default ( ) ,
686
+ "ethereum" . into ( ) ,
687
+ PROVIDER ,
688
+ ) ;
689
+
690
+ let eth_blockchain_state = BlockchainState {
691
+ id : "ethereum" . into ( ) ,
692
+ network_id : 1 , // Ethereum mainnet
693
+ state : Arc :: new ( eth_state) ,
694
+ contract : eth_read. clone ( ) ,
695
+ provider_address : PROVIDER ,
696
+ reveal_delay_blocks : 1 ,
697
+ confirmed_block_status : BlockStatus :: Latest ,
698
+ } ;
699
+
700
+ let avax_state = MonitoredHashChainState :: new (
701
+ AVAX_CHAIN . clone ( ) ,
702
+ Default :: default ( ) ,
703
+ "avalanche" . into ( ) ,
704
+ PROVIDER ,
705
+ ) ;
706
+
707
+ let avax_blockchain_state = BlockchainState {
708
+ id : "avalanche" . into ( ) ,
709
+ network_id : 43114 , // Avalanche C-Chain
710
+ state : Arc :: new ( avax_state) ,
711
+ contract : avax_read. clone ( ) ,
712
+ provider_address : PROVIDER ,
713
+ reveal_delay_blocks : 2 ,
714
+ confirmed_block_status : BlockStatus :: Latest ,
715
+ } ;
716
+
717
+ // Create chains HashMap with initialized states
718
+ let mut chains = HashMap :: new ( ) ;
719
+ chains. insert (
720
+ "ethereum" . into ( ) ,
721
+ ApiBlockChainState :: Initialized ( eth_blockchain_state) ,
722
+ ) ;
723
+ chains. insert (
724
+ "avalanche" . into ( ) ,
725
+ ApiBlockChainState :: Initialized ( avax_blockchain_state) ,
726
+ ) ;
727
+
728
+ // Minimal ApiState for this endpoint
729
+ let api_state = ApiState {
730
+ chains : Arc :: new ( RwLock :: new ( chains) ) ,
731
+ history : Arc :: new ( History :: new ( ) . await . unwrap ( ) ) ,
732
+ metrics_registry : Arc :: new ( RwLock :: new ( Registry :: default ( ) ) ) ,
733
+ metrics : Arc :: new ( crate :: api:: ApiMetrics {
734
+ http_requests : prometheus_client:: metrics:: family:: Family :: default ( ) ,
735
+ } ) ,
736
+ explorer_metrics : Arc :: new (
737
+ crate :: api:: ExplorerMetrics :: new ( Arc :: new ( RwLock :: new ( Registry :: default ( ) ) ) ) . await ,
738
+ ) ,
739
+ config,
740
+ } ;
741
+
742
+ let app = Router :: new ( )
743
+ . route ( "/v1/chains/configs" , get ( get_chain_configs) )
744
+ . with_state ( api_state) ;
745
+ let server = TestServer :: new ( app) . unwrap ( ) ;
746
+
747
+ // Test the chain configs endpoint
748
+ let response = server. get ( "/v1/chains/configs" ) . await ;
749
+ response. assert_status ( StatusCode :: OK ) ;
750
+
751
+ // Parse the response as JSON
752
+ let configs: Vec < ChainConfigSummary > = response. json ( ) ;
753
+
754
+ // Verify we have 2 chains
755
+ assert_eq ! ( configs. len( ) , 2 , "Should return 2 chain configs" ) ;
756
+
757
+ // Find ethereum config
758
+ let eth_config = configs
759
+ . iter ( )
760
+ . find ( |c| c. name == "ethereum" )
761
+ . expect ( "Ethereum config not found" ) ;
762
+ assert_eq ! (
763
+ eth_config. contract_addr,
764
+ "0x0000000000000000000000000000000000001234"
765
+ ) ;
766
+ assert_eq ! ( eth_config. reveal_delay_blocks, 1 ) ;
767
+ assert_eq ! ( eth_config. gas_limit, 500000 ) ;
768
+ assert_eq ! ( eth_config. default_fee, 1500000000000000 ) ;
769
+ assert_eq ! ( eth_config. network_id, 1 ) ; // Ethereum mainnet
770
+
771
+ // Find avalanche config
772
+ let avax_config = configs
773
+ . iter ( )
774
+ . find ( |c| c. name == "avalanche" )
775
+ . expect ( "Avalanche config not found" ) ;
776
+ assert_eq ! (
777
+ avax_config. contract_addr,
778
+ "0x0000000000000000000000000000000000005678"
779
+ ) ;
780
+ assert_eq ! ( avax_config. reveal_delay_blocks, 2 ) ;
781
+ assert_eq ! ( avax_config. gas_limit, 600000 ) ;
782
+ assert_eq ! ( avax_config. default_fee, 2000000000000000 ) ;
783
+ assert_eq ! ( avax_config. network_id, 43114 ) ; // Avalanche C-Chain
784
+ }
537
785
}
0 commit comments