From 30a18710da918e33344f4289e519437197a3efd1 Mon Sep 17 00:00:00 2001 From: andrussal Date: Fri, 19 Dec 2025 00:50:20 +0100 Subject: [PATCH] configs: split config construction phases Refactors long config builders into small, intent-named helpers. - create_general_configs_with_blend_core_subset - Before: validation + id/port gen + component config build + provider/genesis + kms + per-node assembly. - After: validate_node_counts/generate_ids_and_ports/collect_blend_core_providers/ apply_consensus_genesis_tx/build_kms_configs/build_general_configs. - build_blend_service_config - Before: mixed constants parsing + user/deployment/network settings build. - After: message_frequency_per_round/build_blend_user_config/ build_blend_deployment_settings/build_network_deployment_settings. - create_validator_config/create_executor_config - Before: large struct literals mixing deployment, DA network, storage, dispersal. - After: deployment helpers + backend/settings helpers to keep top-level constructor readable. - create_genesis_tx_with_declarations - Before: inscription + op building + proof signing + tx wrapping in one block. - After: build_genesis_inscription/build_genesis_ops/build_genesis_ops_proofs/build_genesis_tx. --- testing-framework/configs/src/nodes/blend.rs | 41 +++- .../configs/src/nodes/executor.rs | 101 ++++++---- .../configs/src/nodes/validator.rs | 88 ++++---- .../configs/src/topology/configs/consensus.rs | 56 ++++-- .../configs/src/topology/configs/mod.rs | 190 +++++++++++------- 5 files changed, 308 insertions(+), 168 deletions(-) diff --git a/testing-framework/configs/src/nodes/blend.rs b/testing-framework/configs/src/nodes/blend.rs index 9a7ca11..8b7db7c 100644 --- a/testing-framework/configs/src/nodes/blend.rs +++ b/testing-framework/configs/src/nodes/blend.rs @@ -40,19 +40,31 @@ pub(crate) fn build_blend_service_config( BlendDeploymentSettings, NetworkDeploymentSettings, ) { - let message_frequency_per_round = match NonNegativeF64::try_from(MESSAGE_FREQUENCY_PER_ROUND) { + let message_frequency_per_round = message_frequency_per_round(); + let zk_key_id = key_id_for_preload_backend(&Key::from(config.secret_zk_key.clone())); + + let user = build_blend_user_config(config, zk_key_id); + let deployment_settings = build_blend_deployment_settings(config, message_frequency_per_round); + let network_deployment = build_network_deployment_settings(); + + (user, deployment_settings, network_deployment) +} + +fn message_frequency_per_round() -> NonNegativeF64 { + match NonNegativeF64::try_from(MESSAGE_FREQUENCY_PER_ROUND) { Ok(value) => value, Err(_) => unsafe { // Safety: `MESSAGE_FREQUENCY_PER_ROUND` is a finite non-negative constant. std::hint::unreachable_unchecked() }, - }; - let zk_key_id = key_id_for_preload_backend(&Key::from(config.secret_zk_key.clone())); + } +} +fn build_blend_user_config(config: &TopologyBlendConfig, zk_key_id: String) -> BlendUserConfig { let backend_core = &config.backend_core; let backend_edge = &config.backend_edge; - let user = BlendUserConfig { + BlendUserConfig { non_ephemeral_signing_key: config.private_key.clone(), // Persist recovery data under the tempdir so components expecting it // can start cleanly. @@ -76,9 +88,16 @@ pub(crate) fn build_blend_service_config( replication_factor: backend_edge.replication_factor, }, }, - }; + } +} - let deployment_settings = BlendDeploymentSettings { +fn build_blend_deployment_settings( + config: &TopologyBlendConfig, + message_frequency_per_round: NonNegativeF64, +) -> BlendDeploymentSettings { + let backend_core = &config.backend_core; + + BlendDeploymentSettings { common: blend_deployment::CommonSettings { num_blend_layers: unsafe { NonZeroU64::new_unchecked(BLEND_LAYERS_COUNT) }, minimum_network_size: unsafe { NonZeroU64::new_unchecked(MINIMUM_NETWORK_SIZE) }, @@ -113,16 +132,16 @@ pub(crate) fn build_blend_service_config( minimum_messages_coefficient: backend_core.minimum_messages_coefficient, normalization_constant: backend_core.normalization_constant, }, - }; + } +} - let network_deployment = NetworkDeploymentSettings { +fn build_network_deployment_settings() -> NetworkDeploymentSettings { + NetworkDeploymentSettings { identify_protocol_name: nomos_libp2p::protocol_name::StreamProtocol::new( "/integration/nomos/identify/1.0.0", ), kademlia_protocol_name: nomos_libp2p::protocol_name::StreamProtocol::new( "/integration/nomos/kad/1.0.0", ), - }; - - (user, deployment_settings, network_deployment) + } } diff --git a/testing-framework/configs/src/nodes/executor.rs b/testing-framework/configs/src/nodes/executor.rs index c0c3ed8..07e3e65 100644 --- a/testing-framework/configs/src/nodes/executor.rs +++ b/testing-framework/configs/src/nodes/executor.rs @@ -33,13 +33,8 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig { let (blend_user_config, blend_deployment, network_deployment) = build_blend_service_config(&config.blend_config); - let deployment_settings = DeploymentSettings::new_custom( - blend_deployment, - network_deployment, - cryptarchia_deployment(&config), - time_deployment(&config), - mempool_deployment(), - ); + let deployment_settings = + build_executor_deployment_settings(&config, blend_deployment, network_deployment); ExecutorConfig { network: network_config, @@ -47,23 +42,7 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig { deployment: deployment_settings, cryptarchia: cryptarchia_config(&config), da_network: DaNetworkConfig { - backend: DaNetworkExecutorBackendSettings { - validator_settings: DaNetworkBackendSettings { - node_key: config.da_config.node_key.clone(), - listening_address: config.da_config.listening_address.clone(), - policy_settings: config.da_config.policy_settings.clone(), - monitor_settings: config.da_config.monitor_settings.clone(), - balancer_interval: config.da_config.balancer_interval, - redial_cooldown: config.da_config.redial_cooldown, - replication_settings: config.da_config.replication_settings, - subnets_settings: SubnetsConfig { - num_of_subnets: config.da_config.num_samples as usize, - shares_retry_limit: config.da_config.retry_shares_limit, - commitments_retry_limit: config.da_config.retry_commitments_limit, - }, - }, - num_subnets: config.da_config.num_subnets, - }, + backend: build_executor_da_network_backend_settings(&config), membership: config.da_config.membership.clone(), api_adapter_settings: ApiAdapterSettings { api_port: config.api_config.address.port(), @@ -77,22 +56,9 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig { tracing: tracing_settings(&config), http: http_config(&config), da_sampling: da_sampling_config(&config), - storage: RocksBackendSettings { - db_path: "./db".into(), - read_only: false, - column_family: Some("blocks".into()), - }, + storage: rocks_storage_settings(), da_dispersal: DispersalServiceSettings { - backend: DispersalKZGRSBackendSettings { - encoder_settings: EncoderSettings { - num_columns: config.da_config.num_subnets as usize, - with_cache: false, - global_params_path: config.da_config.global_params_path.clone(), - }, - dispersal_timeout: timeouts::dispersal_timeout(), - retry_cooldown: timeouts::retry_cooldown(), - retry_limit: 2, - }, + backend: build_dispersal_backend_settings(&config), }, time: time_config(&config), mempool: mempool_config(), @@ -102,3 +68,60 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig { testing_http: testing_http_config(&config), } } + +fn build_executor_deployment_settings( + config: &GeneralConfig, + blend_deployment: nomos_node::config::blend::deployment::Settings, + network_deployment: nomos_node::config::network::deployment::Settings, +) -> DeploymentSettings { + DeploymentSettings::new_custom( + blend_deployment, + network_deployment, + cryptarchia_deployment(config), + time_deployment(config), + mempool_deployment(), + ) +} + +fn build_executor_da_network_backend_settings( + config: &GeneralConfig, +) -> DaNetworkExecutorBackendSettings { + DaNetworkExecutorBackendSettings { + validator_settings: DaNetworkBackendSettings { + node_key: config.da_config.node_key.clone(), + listening_address: config.da_config.listening_address.clone(), + policy_settings: config.da_config.policy_settings.clone(), + monitor_settings: config.da_config.monitor_settings.clone(), + balancer_interval: config.da_config.balancer_interval, + redial_cooldown: config.da_config.redial_cooldown, + replication_settings: config.da_config.replication_settings, + subnets_settings: SubnetsConfig { + num_of_subnets: config.da_config.num_samples as usize, + shares_retry_limit: config.da_config.retry_shares_limit, + commitments_retry_limit: config.da_config.retry_commitments_limit, + }, + }, + num_subnets: config.da_config.num_subnets, + } +} + +fn rocks_storage_settings() -> RocksBackendSettings { + RocksBackendSettings { + db_path: "./db".into(), + read_only: false, + column_family: Some("blocks".into()), + } +} + +fn build_dispersal_backend_settings(config: &GeneralConfig) -> DispersalKZGRSBackendSettings { + DispersalKZGRSBackendSettings { + encoder_settings: EncoderSettings { + num_columns: config.da_config.num_subnets as usize, + with_cache: false, + global_params_path: config.da_config.global_params_path.clone(), + }, + dispersal_timeout: timeouts::dispersal_timeout(), + retry_cooldown: timeouts::retry_cooldown(), + retry_limit: 2, + } +} diff --git a/testing-framework/configs/src/nodes/validator.rs b/testing-framework/configs/src/nodes/validator.rs index 0b115cd..d6e59c8 100644 --- a/testing-framework/configs/src/nodes/validator.rs +++ b/testing-framework/configs/src/nodes/validator.rs @@ -24,44 +24,20 @@ use crate::{ #[must_use] pub fn create_validator_config(config: GeneralConfig) -> ValidatorConfig { - let da_policy_settings = config.da_config.policy_settings.clone(); let network_config = config.network_config.clone(); let (blend_user_config, blend_deployment, network_deployment) = build_blend_service_config(&config.blend_config); - let deployment_settings = DeploymentSettings::new_custom( - blend_deployment, - network_deployment, - cryptarchia_deployment(&config), - time_deployment(&config), - mempool_deployment(), - ); + + let deployment_settings = + build_validator_deployment_settings(&config, blend_deployment, network_deployment); + ValidatorConfig { network: network_config, blend: blend_user_config, deployment: deployment_settings, cryptarchia: cryptarchia_config(&config), da_network: DaNetworkConfig { - backend: DaNetworkBackendSettings { - node_key: config.da_config.node_key.clone(), - listening_address: config.da_config.listening_address.clone(), - policy_settings: DAConnectionPolicySettings { - min_dispersal_peers: 0, - min_replication_peers: da_policy_settings.min_replication_peers, - max_dispersal_failures: da_policy_settings.max_dispersal_failures, - max_sampling_failures: da_policy_settings.max_sampling_failures, - max_replication_failures: da_policy_settings.max_replication_failures, - malicious_threshold: da_policy_settings.malicious_threshold, - }, - monitor_settings: config.da_config.monitor_settings.clone(), - balancer_interval: config.da_config.balancer_interval, - redial_cooldown: config.da_config.redial_cooldown, - replication_settings: config.da_config.replication_settings, - subnets_settings: SubnetsConfig { - num_of_subnets: config.da_config.num_samples as usize, - shares_retry_limit: config.da_config.retry_shares_limit, - commitments_retry_limit: config.da_config.retry_commitments_limit, - }, - }, + backend: build_validator_da_network_backend_settings(&config), membership: config.da_config.membership.clone(), api_adapter_settings: ApiAdapterSettings { api_port: config.api_config.address.port(), @@ -75,11 +51,7 @@ pub fn create_validator_config(config: GeneralConfig) -> ValidatorConfig { tracing: tracing_settings(&config), http: http_config(&config), da_sampling: da_sampling_config(&config), - storage: RocksBackendSettings { - db_path: "./db".into(), - read_only: false, - column_family: Some("blocks".into()), - }, + storage: rocks_storage_settings(), time: time_config(&config), mempool: mempool_config(), sdp: SdpSettings { declaration: None }, @@ -88,3 +60,51 @@ pub fn create_validator_config(config: GeneralConfig) -> ValidatorConfig { key_management: config.kms_config.clone(), } } + +fn build_validator_deployment_settings( + config: &GeneralConfig, + blend_deployment: nomos_node::config::blend::deployment::Settings, + network_deployment: nomos_node::config::network::deployment::Settings, +) -> DeploymentSettings { + DeploymentSettings::new_custom( + blend_deployment, + network_deployment, + cryptarchia_deployment(config), + time_deployment(config), + mempool_deployment(), + ) +} + +fn build_validator_da_network_backend_settings(config: &GeneralConfig) -> DaNetworkBackendSettings { + let da_policy_settings = config.da_config.policy_settings.clone(); + + DaNetworkBackendSettings { + node_key: config.da_config.node_key.clone(), + listening_address: config.da_config.listening_address.clone(), + policy_settings: DAConnectionPolicySettings { + min_dispersal_peers: 0, + min_replication_peers: da_policy_settings.min_replication_peers, + max_dispersal_failures: da_policy_settings.max_dispersal_failures, + max_sampling_failures: da_policy_settings.max_sampling_failures, + max_replication_failures: da_policy_settings.max_replication_failures, + malicious_threshold: da_policy_settings.malicious_threshold, + }, + monitor_settings: config.da_config.monitor_settings.clone(), + balancer_interval: config.da_config.balancer_interval, + redial_cooldown: config.da_config.redial_cooldown, + replication_settings: config.da_config.replication_settings, + subnets_settings: SubnetsConfig { + num_of_subnets: config.da_config.num_samples as usize, + shares_retry_limit: config.da_config.retry_shares_limit, + commitments_retry_limit: config.da_config.retry_commitments_limit, + }, + } +} + +fn rocks_storage_settings() -> RocksBackendSettings { + RocksBackendSettings { + db_path: "./db".into(), + read_only: false, + column_family: Some("blocks".into()), + } +} diff --git a/testing-framework/configs/src/topology/configs/consensus.rs b/testing-framework/configs/src/topology/configs/consensus.rs index e4c9300..8b2ae6c 100644 --- a/testing-framework/configs/src/topology/configs/consensus.rs +++ b/testing-framework/configs/src/topology/configs/consensus.rs @@ -336,7 +336,23 @@ pub fn create_genesis_tx_with_declarations( ledger_tx: LedgerTx, providers: Vec, ) -> Result { - let inscription = InscriptionOp { + let inscription = build_genesis_inscription()?; + let ledger_tx_hash = ledger_tx.hash(); + + let ops = build_genesis_ops(inscription, ledger_tx_hash, &providers); + let mantle_tx = MantleTx { + ops, + ledger_tx, + execution_gas_price: 0, + storage_gas_price: 0, + }; + + let ops_proofs = build_genesis_ops_proofs(mantle_tx.hash(), providers)?; + build_genesis_tx(mantle_tx, ops_proofs) +} + +fn build_genesis_inscription() -> Result { + Ok(InscriptionOp { channel_id: ChannelId::from([0; 32]), inscription: vec![103, 101, 110, 101, 115, 105, 115], // "genesis" in bytes parent: MsgId::root(), @@ -345,13 +361,18 @@ pub fn create_genesis_tx_with_declarations( message: err.to_string(), } })?, - }; + }) +} - let ledger_tx_hash = ledger_tx.hash(); +fn build_genesis_ops( + inscription: InscriptionOp, + ledger_tx_hash: nomos_core::mantle::TxHash, + providers: &[ProviderInfo], +) -> Vec { + let mut ops = Vec::with_capacity(1 + providers.len()); + ops.push(Op::ChannelInscribe(inscription)); - let mut ops = vec![Op::ChannelInscribe(inscription)]; - - for provider in &providers { + for provider in providers { let utxo = Utxo { tx_hash: ledger_tx_hash, output_index: provider.note.output_index, @@ -367,15 +388,15 @@ pub fn create_genesis_tx_with_declarations( ops.push(Op::SDPDeclare(declaration)); } - let mantle_tx = MantleTx { - ops, - ledger_tx, - execution_gas_price: 0, - storage_gas_price: 0, - }; + ops +} - let mantle_tx_hash = mantle_tx.hash(); - let mut ops_proofs = vec![OpProof::NoProof]; +fn build_genesis_ops_proofs( + mantle_tx_hash: nomos_core::mantle::TxHash, + providers: Vec, +) -> Result, ConsensusConfigError> { + let mut ops_proofs = Vec::with_capacity(1 + providers.len()); + ops_proofs.push(OpProof::NoProof); for provider in providers { let zk_sig = @@ -393,6 +414,13 @@ pub fn create_genesis_tx_with_declarations( }); } + Ok(ops_proofs) +} + +fn build_genesis_tx( + mantle_tx: MantleTx, + ops_proofs: Vec, +) -> Result { let signed_mantle_tx = SignedMantleTx { mantle_tx, ops_proofs, diff --git a/testing-framework/configs/src/topology/configs/mod.rs b/testing-framework/configs/src/topology/configs/mod.rs index c5c8bde..a1d2d3d 100644 --- a/testing-framework/configs/src/topology/configs/mod.rs +++ b/testing-framework/configs/src/topology/configs/mod.rs @@ -96,33 +96,9 @@ pub fn create_general_configs_with_blend_core_subset( n_blend_core_nodes: usize, network_params: &NetworkParams, ) -> Result, GeneralConfigError> { - if n_nodes == 0 { - return Err(GeneralConfigError::EmptyParticipants); - } - if n_blend_core_nodes > n_nodes { - return Err(GeneralConfigError::BlendCoreSubsetTooLarge { - blend_core: n_blend_core_nodes, - participants: n_nodes, - }); - } + validate_node_counts(n_nodes, n_blend_core_nodes)?; - // Blend relies on each node declaring a different ZK public key, so we need - // different IDs to generate different keys. - let mut ids: Vec<_> = (0..n_nodes).map(|i| [i as u8; 32]).collect(); - let mut da_ports = Vec::with_capacity(n_nodes); - let mut blend_ports = Vec::with_capacity(n_nodes); - - for id in &mut ids { - thread_rng().fill(id); - da_ports.push( - get_available_udp_port() - .ok_or(GeneralConfigError::PortAllocationFailed { label: "DA" })?, - ); - blend_ports.push( - get_available_udp_port() - .ok_or(GeneralConfigError::PortAllocationFailed { label: "Blend" })?, - ); - } + let (ids, da_ports, blend_ports) = generate_ids_and_ports(n_nodes)?; validate_generated_vectors(n_nodes, &ids, &da_ports, &blend_ports)?; @@ -142,17 +118,87 @@ pub fn create_general_configs_with_blend_core_subset( return Err(GeneralConfigError::EmptyParticipants); }; - let mut providers = Vec::with_capacity(cmp::min(n_blend_core_nodes, blend_configs.len())); - for (i, blend_conf) in blend_configs - .iter() - .enumerate() - .take(cmp::min(n_blend_core_nodes, blend_configs.len())) - { + let providers = + collect_blend_core_providers(first_consensus, &blend_configs, n_blend_core_nodes)?; + let ledger_tx = first_consensus.genesis_tx.mantle_tx().ledger_tx.clone(); + let genesis_tx = create_genesis_tx_with_declarations(ledger_tx, providers)?; + apply_consensus_genesis_tx(&mut consensus_configs, &genesis_tx); + + // Set Blend and DA keys in KMS of each node config. + let kms_configs = build_kms_configs(&blend_configs); + + build_general_configs( + n_nodes, + &api_configs, + &consensus_configs, + &bootstrap_config, + &da_configs, + &network_configs, + &blend_configs, + &tracing_configs, + &kms_configs, + &time_config, + ) +} + +fn validate_node_counts( + n_nodes: usize, + n_blend_core_nodes: usize, +) -> Result<(), GeneralConfigError> { + if n_nodes == 0 { + return Err(GeneralConfigError::EmptyParticipants); + } + + if n_blend_core_nodes > n_nodes { + return Err(GeneralConfigError::BlendCoreSubsetTooLarge { + blend_core: n_blend_core_nodes, + participants: n_nodes, + }); + } + + Ok(()) +} + +fn generate_ids_and_ports( + n_nodes: usize, +) -> Result<(Vec<[u8; 32]>, Vec, Vec), GeneralConfigError> { + // Blend relies on each node declaring a different ZK public key, so we need + // different IDs to generate different keys. + let mut ids: Vec<_> = (0..n_nodes).map(|i| [i as u8; 32]).collect(); + let mut da_ports = Vec::with_capacity(n_nodes); + let mut blend_ports = Vec::with_capacity(n_nodes); + + for id in &mut ids { + thread_rng().fill(id); + + da_ports.push( + get_available_udp_port() + .ok_or(GeneralConfigError::PortAllocationFailed { label: "DA" })?, + ); + blend_ports.push( + get_available_udp_port() + .ok_or(GeneralConfigError::PortAllocationFailed { label: "Blend" })?, + ); + } + + Ok((ids, da_ports, blend_ports)) +} + +fn collect_blend_core_providers( + first_consensus: &GeneralConsensusConfig, + blend_configs: &[GeneralBlendConfig], + n_blend_core_nodes: usize, +) -> Result, GeneralConfigError> { + let n_core = cmp::min(n_blend_core_nodes, blend_configs.len()); + let mut providers = Vec::with_capacity(n_core); + + for (i, blend_conf) in blend_configs.iter().enumerate().take(n_core) { let note = first_consensus .blend_notes .get(i) .ok_or(GeneralConfigError::EmptyParticipants)? .clone(); + providers.push(ProviderInfo { service_type: ServiceType::BlendNetwork, provider_sk: blend_conf.signer.clone(), @@ -161,14 +207,21 @@ pub fn create_general_configs_with_blend_core_subset( note, }); } - let ledger_tx = first_consensus.genesis_tx.mantle_tx().ledger_tx.clone(); - let genesis_tx = create_genesis_tx_with_declarations(ledger_tx, providers)?; - for c in &mut consensus_configs { + + Ok(providers) +} + +fn apply_consensus_genesis_tx( + consensus_configs: &mut [GeneralConsensusConfig], + genesis_tx: &nomos_core::mantle::genesis_tx::GenesisTx, +) { + for c in consensus_configs { c.genesis_tx = genesis_tx.clone(); } +} - // Set Blend and DA keys in KMS of each node config. - let kms_configs: Vec<_> = blend_configs +fn build_kms_configs(blend_configs: &[GeneralBlendConfig]) -> Vec { + blend_configs .iter() .map(|blend_conf| { let ed_key = blend_conf.signer.clone(); @@ -187,43 +240,33 @@ pub fn create_general_configs_with_blend_core_subset( .into(), } }) - .collect(); + .collect() +} +#[allow(clippy::too_many_arguments)] +fn build_general_configs( + n_nodes: usize, + api_configs: &[GeneralApiConfig], + consensus_configs: &[GeneralConsensusConfig], + bootstrap_config: &[GeneralBootstrapConfig], + da_configs: &[GeneralDaConfig], + network_configs: &[GeneralNetworkConfig], + blend_configs: &[GeneralBlendConfig], + tracing_configs: &[GeneralTracingConfig], + kms_configs: &[PreloadKMSBackendSettings], + time_config: &GeneralTimeConfig, +) -> Result, GeneralConfigError> { let mut general_configs = Vec::with_capacity(n_nodes); for i in 0..n_nodes { - let api_config = api_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let consensus_config = consensus_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let bootstrapping_config = bootstrap_config - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let da_config = da_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let network_config = network_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let blend_config = blend_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let tracing_config = tracing_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); - let kms_config = kms_configs - .get(i) - .ok_or(GeneralConfigError::EmptyParticipants)? - .clone(); + let api_config = get_cloned_or_empty(api_configs, i)?; + let consensus_config = get_cloned_or_empty(consensus_configs, i)?; + let bootstrapping_config = get_cloned_or_empty(bootstrap_config, i)?; + let da_config = get_cloned_or_empty(da_configs, i)?; + let network_config = get_cloned_or_empty(network_configs, i)?; + let blend_config = get_cloned_or_empty(blend_configs, i)?; + let tracing_config = get_cloned_or_empty(tracing_configs, i)?; + let kms_config = get_cloned_or_empty(kms_configs, i)?; general_configs.push(GeneralConfig { api_config, @@ -240,3 +283,10 @@ pub fn create_general_configs_with_blend_core_subset( Ok(general_configs) } + +fn get_cloned_or_empty(items: &[T], index: usize) -> Result { + items + .get(index) + .cloned() + .ok_or(GeneralConfigError::EmptyParticipants) +}