@@ -489,7 +489,7 @@ impl ChainSection {
489489 }
490490
491491 for ( name, chain) in self . chains . iter_mut ( ) {
492- chain. validate ( ) ?;
492+ chain. validate ( name ) ?;
493493 if chain. cache_size <= reorg_threshold {
494494 return Err ( anyhow ! (
495495 "chain '{}': cache_size ({}) must be greater than reorg_threshold ({})" ,
@@ -701,6 +701,33 @@ impl Default for ChainSettings {
701701 }
702702}
703703
704+ impl ChainSettings {
705+ fn validate ( & self ) -> Result < ( ) > {
706+ anyhow:: ensure!(
707+ self . max_block_range_size > 0 ,
708+ "max_block_range_size must be > 0"
709+ ) ;
710+ anyhow:: ensure!(
711+ self . max_event_only_range > 0 ,
712+ "max_event_only_range must be > 0"
713+ ) ;
714+ anyhow:: ensure!( self . block_batch_size > 0 , "block_batch_size must be > 0" ) ;
715+ anyhow:: ensure!(
716+ self . block_ptr_batch_size > 0 ,
717+ "block_ptr_batch_size must be > 0"
718+ ) ;
719+ anyhow:: ensure!(
720+ self . block_ingestor_max_concurrent_json_rpc_calls > 0 ,
721+ "block_ingestor_max_concurrent_json_rpc_calls must be > 0"
722+ ) ;
723+ anyhow:: ensure!(
724+ self . get_logs_max_contracts > 0 ,
725+ "get_logs_max_contracts must be > 0"
726+ ) ;
727+ Ok ( ( ) )
728+ }
729+ }
730+
704731#[ derive( Clone , Debug , PartialEq , Deserialize , Serialize ) ]
705732pub struct Chain {
706733 pub shard : String ,
@@ -731,19 +758,23 @@ fn default_blockchain_kind() -> BlockchainKind {
731758}
732759
733760impl Chain {
734- fn validate ( & mut self ) -> Result < ( ) > {
761+ fn validate ( & mut self , name : & str ) -> Result < ( ) > {
735762 let mut labels = self . providers . iter ( ) . map ( |p| & p. label ) . collect_vec ( ) ;
736763 labels. sort ( ) ;
737764 labels. dedup ( ) ;
738765 if labels. len ( ) != self . providers . len ( ) {
739- return Err ( anyhow ! ( "Provider labels must be unique" ) ) ;
766+ return Err ( anyhow ! ( "chain {}: provider labels must be unique" , name ) ) ;
740767 }
741768
742769 // `Config` validates that `self.shard` references a configured shard
743770 for provider in self . providers . iter_mut ( ) {
744771 provider. validate ( ) ?
745772 }
746773
774+ self . settings
775+ . validate ( )
776+ . map_err ( |e| anyhow ! ( "chain {}: {}" , name, e) ) ?;
777+
747778 Ok ( ( ) )
748779 }
749780}
@@ -1458,7 +1489,15 @@ where
14581489#[ cfg( test) ]
14591490mod tests {
14601491
1461- use crate :: config:: { default_polling_interval, ChainSection , Web3Rule } ;
1492+ use std:: time:: Duration ;
1493+
1494+ use crate :: config:: {
1495+ default_block_batch_size, default_block_ingestor_max_concurrent_json_rpc_calls,
1496+ default_block_ptr_batch_size, default_genesis_block_number, default_get_logs_max_contracts,
1497+ default_json_rpc_timeout, default_max_block_range_size, default_max_event_only_range,
1498+ default_polling_interval, default_request_retries, default_target_triggers_per_block_range,
1499+ ChainSection , Web3Rule ,
1500+ } ;
14621501
14631502 use super :: {
14641503 Chain , ChainSettings , Config , FirehoseProvider , Provider , ProviderDetails , Shard ,
@@ -2341,4 +2380,237 @@ fdw_pool_size = [
23412380 // Node does not match and kill switch is on
23422381 assert ! ( !make_config( "query-0" , "index-0" , true ) . is_block_ingestor( ) ) ;
23432382 }
2383+
2384+ #[ test]
2385+ fn chain_settings_valid_overrides ( ) {
2386+ let mut section = toml:: from_str :: < ChainSection > (
2387+ r#"
2388+ ingestor = "block_ingestor_node"
2389+ [mainnet]
2390+ shard = "primary"
2391+ json_rpc_timeout = 300
2392+ request_retries = 15
2393+ max_block_range_size = 2000
2394+ block_batch_size = 20
2395+ block_ptr_batch_size = 50
2396+ max_event_only_range = 1000
2397+ target_triggers_per_block_range = 200
2398+ get_logs_max_contracts = 5000
2399+ block_ingestor_max_concurrent_json_rpc_calls = 500
2400+ genesis_block_number = 1
2401+ provider = []
2402+ "# ,
2403+ )
2404+ . unwrap ( ) ;
2405+
2406+ assert ! ( section. validate( ) . is_ok( ) ) ;
2407+ let settings = & section. chains . get ( "mainnet" ) . unwrap ( ) . settings ;
2408+ assert_eq ! ( settings. json_rpc_timeout, Duration :: from_secs( 300 ) ) ;
2409+ assert_eq ! ( settings. request_retries, 15 ) ;
2410+ assert_eq ! ( settings. max_block_range_size, 2000 ) ;
2411+ assert_eq ! ( settings. block_batch_size, 20 ) ;
2412+ assert_eq ! ( settings. block_ptr_batch_size, 50 ) ;
2413+ assert_eq ! ( settings. max_event_only_range, 1000 ) ;
2414+ assert_eq ! ( settings. target_triggers_per_block_range, 200 ) ;
2415+ assert_eq ! ( settings. get_logs_max_contracts, 5000 ) ;
2416+ assert_eq ! ( settings. block_ingestor_max_concurrent_json_rpc_calls, 500 ) ;
2417+ assert_eq ! ( settings. genesis_block_number, 1 ) ;
2418+ }
2419+
2420+ #[ test]
2421+ fn chain_settings_defaults_match_env_vars ( ) {
2422+ let settings = ChainSettings :: default ( ) ;
2423+ assert_eq ! ( settings. polling_interval, default_polling_interval( ) ) ;
2424+ assert_eq ! ( settings. json_rpc_timeout, default_json_rpc_timeout( ) ) ;
2425+ assert_eq ! ( settings. request_retries, default_request_retries( ) ) ;
2426+ assert_eq ! (
2427+ settings. max_block_range_size,
2428+ default_max_block_range_size( )
2429+ ) ;
2430+ assert_eq ! ( settings. block_batch_size, default_block_batch_size( ) ) ;
2431+ assert_eq ! (
2432+ settings. block_ptr_batch_size,
2433+ default_block_ptr_batch_size( )
2434+ ) ;
2435+ assert_eq ! (
2436+ settings. max_event_only_range,
2437+ default_max_event_only_range( )
2438+ ) ;
2439+ assert_eq ! (
2440+ settings. target_triggers_per_block_range,
2441+ default_target_triggers_per_block_range( )
2442+ ) ;
2443+ assert_eq ! (
2444+ settings. get_logs_max_contracts,
2445+ default_get_logs_max_contracts( )
2446+ ) ;
2447+ assert_eq ! (
2448+ settings. block_ingestor_max_concurrent_json_rpc_calls,
2449+ default_block_ingestor_max_concurrent_json_rpc_calls( )
2450+ ) ;
2451+ assert_eq ! (
2452+ settings. genesis_block_number,
2453+ default_genesis_block_number( )
2454+ ) ;
2455+ }
2456+
2457+ #[ test]
2458+ fn chain_settings_rejects_zero_max_block_range_size ( ) {
2459+ let mut section = toml:: from_str :: < ChainSection > (
2460+ r#"
2461+ ingestor = "block_ingestor_node"
2462+ [mainnet]
2463+ shard = "primary"
2464+ max_block_range_size = 0
2465+ provider = []
2466+ "# ,
2467+ )
2468+ . unwrap ( ) ;
2469+
2470+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2471+ assert ! (
2472+ err. contains( "max_block_range_size must be > 0" ) ,
2473+ "expected max_block_range_size error, got: {err}"
2474+ ) ;
2475+ }
2476+
2477+ #[ test]
2478+ fn chain_settings_rejects_negative_max_block_range_size ( ) {
2479+ let mut section = toml:: from_str :: < ChainSection > (
2480+ r#"
2481+ ingestor = "block_ingestor_node"
2482+ [mainnet]
2483+ shard = "primary"
2484+ max_block_range_size = -1
2485+ provider = []
2486+ "# ,
2487+ )
2488+ . unwrap ( ) ;
2489+
2490+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2491+ assert ! (
2492+ err. contains( "max_block_range_size must be > 0" ) ,
2493+ "expected max_block_range_size error, got: {err}"
2494+ ) ;
2495+ }
2496+
2497+ #[ test]
2498+ fn chain_settings_rejects_zero_max_event_only_range ( ) {
2499+ let mut section = toml:: from_str :: < ChainSection > (
2500+ r#"
2501+ ingestor = "block_ingestor_node"
2502+ [mainnet]
2503+ shard = "primary"
2504+ max_event_only_range = 0
2505+ provider = []
2506+ "# ,
2507+ )
2508+ . unwrap ( ) ;
2509+
2510+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2511+ assert ! (
2512+ err. contains( "max_event_only_range must be > 0" ) ,
2513+ "expected max_event_only_range error, got: {err}"
2514+ ) ;
2515+ }
2516+
2517+ #[ test]
2518+ fn chain_settings_rejects_zero_block_batch_size ( ) {
2519+ let mut section = toml:: from_str :: < ChainSection > (
2520+ r#"
2521+ ingestor = "block_ingestor_node"
2522+ [mainnet]
2523+ shard = "primary"
2524+ block_batch_size = 0
2525+ provider = []
2526+ "# ,
2527+ )
2528+ . unwrap ( ) ;
2529+
2530+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2531+ assert ! (
2532+ err. contains( "block_batch_size must be > 0" ) ,
2533+ "expected block_batch_size error, got: {err}"
2534+ ) ;
2535+ }
2536+
2537+ #[ test]
2538+ fn chain_settings_rejects_zero_block_ptr_batch_size ( ) {
2539+ let mut section = toml:: from_str :: < ChainSection > (
2540+ r#"
2541+ ingestor = "block_ingestor_node"
2542+ [mainnet]
2543+ shard = "primary"
2544+ block_ptr_batch_size = 0
2545+ provider = []
2546+ "# ,
2547+ )
2548+ . unwrap ( ) ;
2549+
2550+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2551+ assert ! (
2552+ err. contains( "block_ptr_batch_size must be > 0" ) ,
2553+ "expected block_ptr_batch_size error, got: {err}"
2554+ ) ;
2555+ }
2556+
2557+ #[ test]
2558+ fn chain_settings_rejects_zero_concurrent_json_rpc_calls ( ) {
2559+ let mut section = toml:: from_str :: < ChainSection > (
2560+ r#"
2561+ ingestor = "block_ingestor_node"
2562+ [mainnet]
2563+ shard = "primary"
2564+ block_ingestor_max_concurrent_json_rpc_calls = 0
2565+ provider = []
2566+ "# ,
2567+ )
2568+ . unwrap ( ) ;
2569+
2570+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2571+ assert ! (
2572+ err. contains( "block_ingestor_max_concurrent_json_rpc_calls must be > 0" ) ,
2573+ "expected block_ingestor_max_concurrent_json_rpc_calls error, got: {err}"
2574+ ) ;
2575+ }
2576+
2577+ #[ test]
2578+ fn chain_settings_rejects_zero_get_logs_max_contracts ( ) {
2579+ let mut section = toml:: from_str :: < ChainSection > (
2580+ r#"
2581+ ingestor = "block_ingestor_node"
2582+ [mainnet]
2583+ shard = "primary"
2584+ get_logs_max_contracts = 0
2585+ provider = []
2586+ "# ,
2587+ )
2588+ . unwrap ( ) ;
2589+
2590+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2591+ assert ! (
2592+ err. contains( "get_logs_max_contracts must be > 0" ) ,
2593+ "expected get_logs_max_contracts error, got: {err}"
2594+ ) ;
2595+ }
2596+
2597+ #[ test]
2598+ fn chain_settings_validation_error_includes_chain_name ( ) {
2599+ let mut section = toml:: from_str :: < ChainSection > (
2600+ r#"
2601+ ingestor = "block_ingestor_node"
2602+ [my_chain]
2603+ shard = "primary"
2604+ block_batch_size = 0
2605+ provider = []
2606+ "# ,
2607+ )
2608+ . unwrap ( ) ;
2609+
2610+ let err = section. validate ( ) . unwrap_err ( ) . to_string ( ) ;
2611+ assert ! (
2612+ err. contains( "my_chain" ) ,
2613+ "expected chain name in error, got: {err}"
2614+ ) ;
2615+ }
23442616}
0 commit comments