config: add shared topology invariants validator

This commit is contained in:
andrussal 2025-12-18 18:02:50 +01:00
parent 2c0f1b1acc
commit 128e073ed8
5 changed files with 129 additions and 51 deletions

View File

@ -24,13 +24,16 @@ use wallet::WalletConfig;
use crate::{
nodes::kms::key_id_for_preload_backend,
topology::configs::{
api::GeneralApiConfig,
bootstrap::{GeneralBootstrapConfig, SHORT_PROLONGED_BOOTSTRAP_PERIOD},
consensus::ConsensusParams,
da::DaParams,
network::NetworkParams,
time::GeneralTimeConfig,
topology::{
configs::{
api::GeneralApiConfig,
bootstrap::{GeneralBootstrapConfig, SHORT_PROLONGED_BOOTSTRAP_PERIOD},
consensus::ConsensusParams,
da::DaParams,
network::NetworkParams,
time::GeneralTimeConfig,
},
invariants::validate_generated_vectors,
},
};
@ -85,6 +88,9 @@ pub fn create_general_configs_with_blend_core_subset(
blend_ports.push(get_available_udp_port().unwrap());
}
validate_generated_vectors(n_nodes, &ids, &da_ports, &blend_ports)
.expect("invalid generated test topology inputs");
let consensus_params = ConsensusParams::default_for_participants(n_nodes);
let mut consensus_configs =
consensus::create_consensus_configs(&ids, &consensus_params, &WalletConfig::default());

View File

@ -0,0 +1,91 @@
use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum TopologyInvariantError {
#[error("participant count must be > 0")]
EmptyParticipants,
#[error("id count {actual} does not match participants {expected}")]
IdCountMismatch { actual: usize, expected: usize },
#[error("da port count {actual} does not match participants {expected}")]
DaPortCountMismatch { actual: usize, expected: usize },
#[error("blend port count {actual} does not match participants {expected}")]
BlendPortCountMismatch { actual: usize, expected: usize },
}
/// Validate basic invariants shared across all config generation pipelines.
///
/// This intentionally focuses on "shape" invariants (counts, non-empty) and
/// avoids opinionated checks so behavior stays unchanged.
pub fn validate_node_vectors(
participants: usize,
ids: Option<&Vec<[u8; 32]>>,
da_ports: Option<&Vec<u16>>,
blend_ports: Option<&Vec<u16>>,
) -> Result<(), TopologyInvariantError> {
if participants == 0 {
return Err(TopologyInvariantError::EmptyParticipants);
}
if let Some(ids) = ids {
if ids.len() != participants {
return Err(TopologyInvariantError::IdCountMismatch {
actual: ids.len(),
expected: participants,
});
}
}
if let Some(ports) = da_ports {
if ports.len() != participants {
return Err(TopologyInvariantError::DaPortCountMismatch {
actual: ports.len(),
expected: participants,
});
}
}
if let Some(ports) = blend_ports {
if ports.len() != participants {
return Err(TopologyInvariantError::BlendPortCountMismatch {
actual: ports.len(),
expected: participants,
});
}
}
Ok(())
}
pub fn validate_generated_vectors(
participants: usize,
ids: &[[u8; 32]],
da_ports: &[u16],
blend_ports: &[u16],
) -> Result<(), TopologyInvariantError> {
if participants == 0 {
return Err(TopologyInvariantError::EmptyParticipants);
}
if ids.len() != participants {
return Err(TopologyInvariantError::IdCountMismatch {
actual: ids.len(),
expected: participants,
});
}
if da_ports.len() != participants {
return Err(TopologyInvariantError::DaPortCountMismatch {
actual: da_ports.len(),
expected: participants,
});
}
if blend_ports.len() != participants {
return Err(TopologyInvariantError::BlendPortCountMismatch {
actual: blend_ports.len(),
expected: participants,
});
}
Ok(())
}

View File

@ -1 +1,2 @@
pub mod configs;
pub mod invariants;

View File

@ -5,18 +5,21 @@ use nomos_core::{
sdp::{Locator, ServiceType},
};
use nomos_da_network_core::swarm::DAConnectionPolicySettings;
use testing_framework_config::topology::configs::{
api::create_api_configs,
blend::create_blend_configs,
bootstrap::{SHORT_PROLONGED_BOOTSTRAP_PERIOD, create_bootstrap_configs},
consensus::{
ConsensusParams, ProviderInfo, create_consensus_configs,
create_genesis_tx_with_declarations,
use testing_framework_config::topology::{
configs::{
api::create_api_configs,
blend::create_blend_configs,
bootstrap::{SHORT_PROLONGED_BOOTSTRAP_PERIOD, create_bootstrap_configs},
consensus::{
ConsensusParams, ProviderInfo, create_consensus_configs,
create_genesis_tx_with_declarations,
},
da::{DaParams, create_da_configs},
network::{Libp2pNetworkLayout, NetworkParams, create_network_configs},
tracing::create_tracing_configs,
wallet::WalletConfig,
},
da::{DaParams, create_da_configs},
network::{Libp2pNetworkLayout, NetworkParams, create_network_configs},
tracing::create_tracing_configs,
wallet::WalletConfig,
invariants::validate_generated_vectors,
};
use crate::topology::{
@ -258,6 +261,9 @@ impl TopologyBuilder {
let da_ports = resolve_ports(da_ports, n_participants, "DA");
let blend_ports = resolve_ports(blend_ports, n_participants, "Blend");
validate_generated_vectors(n_participants, &ids, &da_ports, &blend_ports)
.expect("invalid generated topology inputs");
let mut consensus_configs =
create_consensus_configs(&ids, &config.consensus_params, &config.wallet_config);
let bootstrapping_config = create_bootstrap_configs(&ids, SHORT_PROLONGED_BOOTSTRAP_PERIOD);

View File

@ -1,4 +1,7 @@
use testing_framework_config::topology::configs::consensus::ConsensusParams;
use testing_framework_config::topology::{
configs::consensus::ConsensusParams,
invariants::{TopologyInvariantError, validate_node_vectors},
};
use thiserror::Error;
use crate::host::Host;
@ -7,12 +10,8 @@ use crate::host::Host;
pub enum ValidationError {
#[error("host count {actual} does not match participants {expected}")]
HostCountMismatch { actual: usize, expected: usize },
#[error("id count {actual} does not match participants {expected}")]
IdCountMismatch { actual: usize, expected: usize },
#[error("da port count {actual} does not match participants {expected}")]
DaPortCountMismatch { actual: usize, expected: usize },
#[error("blend port count {actual} does not match participants {expected}")]
BlendPortCountMismatch { actual: usize, expected: usize },
#[error(transparent)]
TopologyInvariant(#[from] TopologyInvariantError),
}
pub fn validate_inputs(
@ -31,32 +30,7 @@ pub fn validate_inputs(
});
}
if let Some(ids) = ids {
if ids.len() != expected {
return Err(ValidationError::IdCountMismatch {
actual: ids.len(),
expected,
});
}
}
if let Some(ports) = da_ports {
if ports.len() != expected {
return Err(ValidationError::DaPortCountMismatch {
actual: ports.len(),
expected,
});
}
}
if let Some(ports) = blend_ports {
if ports.len() != expected {
return Err(ValidationError::BlendPortCountMismatch {
actual: ports.len(),
expected,
});
}
}
validate_node_vectors(expected, ids, da_ports, blend_ports)?;
Ok(())
}