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.
This commit is contained in:
andrussal 2025-12-19 00:50:20 +01:00
parent 4d2c4c77e4
commit 30a18710da
5 changed files with 308 additions and 168 deletions

View File

@ -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)
}
}

View File

@ -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,7 +42,51 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig {
deployment: deployment_settings,
cryptarchia: cryptarchia_config(&config),
da_network: DaNetworkConfig {
backend: DaNetworkExecutorBackendSettings {
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(),
is_secure: false,
},
subnet_refresh_interval: config.da_config.subnets_refresh_interval,
subnet_threshold: config.da_config.num_samples as usize,
min_session_members: config.da_config.num_samples as usize,
},
da_verifier: da_verifier_config(&config),
tracing: tracing_settings(&config),
http: http_config(&config),
da_sampling: da_sampling_config(&config),
storage: rocks_storage_settings(),
da_dispersal: DispersalServiceSettings {
backend: build_dispersal_backend_settings(&config),
},
time: time_config(&config),
mempool: mempool_config(),
sdp: SdpSettings { declaration: None },
wallet: wallet_settings(&config),
key_management: config.kms_config.clone(),
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(),
@ -63,27 +102,19 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig {
},
},
num_subnets: config.da_config.num_subnets,
},
membership: config.da_config.membership.clone(),
api_adapter_settings: ApiAdapterSettings {
api_port: config.api_config.address.port(),
is_secure: false,
},
subnet_refresh_interval: config.da_config.subnets_refresh_interval,
subnet_threshold: config.da_config.num_samples as usize,
min_session_members: config.da_config.num_samples as usize,
},
da_verifier: da_verifier_config(&config),
tracing: tracing_settings(&config),
http: http_config(&config),
da_sampling: da_sampling_config(&config),
storage: RocksBackendSettings {
}
}
fn rocks_storage_settings() -> RocksBackendSettings {
RocksBackendSettings {
db_path: "./db".into(),
read_only: false,
column_family: Some("blocks".into()),
},
da_dispersal: DispersalServiceSettings {
backend: DispersalKZGRSBackendSettings {
}
}
fn build_dispersal_backend_settings(config: &GeneralConfig) -> DispersalKZGRSBackendSettings {
DispersalKZGRSBackendSettings {
encoder_settings: EncoderSettings {
num_columns: config.da_config.num_subnets as usize,
with_cache: false,
@ -92,13 +123,5 @@ pub fn create_executor_config(config: GeneralConfig) -> ExecutorConfig {
dispersal_timeout: timeouts::dispersal_timeout(),
retry_cooldown: timeouts::retry_cooldown(),
retry_limit: 2,
},
},
time: time_config(&config),
mempool: mempool_config(),
sdp: SdpSettings { declaration: None },
wallet: wallet_settings(&config),
key_management: config.kms_config.clone(),
testing_http: testing_http_config(&config),
}
}

View File

@ -24,24 +24,61 @@ 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 {
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(),
is_secure: false,
},
subnet_refresh_interval: config.da_config.subnets_refresh_interval,
subnet_threshold: config.da_config.num_samples as usize,
min_session_members: config.da_config.num_samples as usize,
},
da_verifier: da_verifier_config(&config),
tracing: tracing_settings(&config),
http: http_config(&config),
da_sampling: da_sampling_config(&config),
storage: rocks_storage_settings(),
time: time_config(&config),
mempool: mempool_config(),
sdp: SdpSettings { declaration: None },
testing_http: testing_http_config(&config),
wallet: wallet_settings(&config),
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 {
@ -61,30 +98,13 @@ pub fn create_validator_config(config: GeneralConfig) -> ValidatorConfig {
shares_retry_limit: config.da_config.retry_shares_limit,
commitments_retry_limit: config.da_config.retry_commitments_limit,
},
},
membership: config.da_config.membership.clone(),
api_adapter_settings: ApiAdapterSettings {
api_port: config.api_config.address.port(),
is_secure: false,
},
subnet_refresh_interval: config.da_config.subnets_refresh_interval,
subnet_threshold: config.da_config.num_samples as usize,
min_session_members: config.da_config.num_samples as usize,
},
da_verifier: da_verifier_config(&config),
tracing: tracing_settings(&config),
http: http_config(&config),
da_sampling: da_sampling_config(&config),
storage: RocksBackendSettings {
}
}
fn rocks_storage_settings() -> RocksBackendSettings {
RocksBackendSettings {
db_path: "./db".into(),
read_only: false,
column_family: Some("blocks".into()),
},
time: time_config(&config),
mempool: mempool_config(),
sdp: SdpSettings { declaration: None },
testing_http: testing_http_config(&config),
wallet: wallet_settings(&config),
key_management: config.kms_config.clone(),
}
}

View File

@ -336,7 +336,23 @@ pub fn create_genesis_tx_with_declarations(
ledger_tx: LedgerTx,
providers: Vec<ProviderInfo>,
) -> Result<GenesisTx, ConsensusConfigError> {
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<InscriptionOp, ConsensusConfigError> {
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<Op> {
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<ProviderInfo>,
) -> Result<Vec<OpProof>, 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<OpProof>,
) -> Result<GenesisTx, ConsensusConfigError> {
let signed_mantle_tx = SignedMantleTx {
mantle_tx,
ops_proofs,

View File

@ -96,33 +96,9 @@ pub fn create_general_configs_with_blend_core_subset(
n_blend_core_nodes: usize,
network_params: &NetworkParams,
) -> Result<Vec<GeneralConfig>, 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<u16>, Vec<u16>), 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<Vec<ProviderInfo>, 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 {
c.genesis_tx = genesis_tx.clone();
Ok(providers)
}
// Set Blend and DA keys in KMS of each node config.
let kms_configs: Vec<_> = blend_configs
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();
}
}
fn build_kms_configs(blend_configs: &[GeneralBlendConfig]) -> Vec<PreloadKMSBackendSettings> {
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<Vec<GeneralConfig>, 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<T: Clone>(items: &[T], index: usize) -> Result<T, GeneralConfigError> {
items
.get(index)
.cloned()
.ok_or(GeneralConfigError::EmptyParticipants)
}