mirror of
https://github.com/logos-blockchain/logos-blockchain-testing.git
synced 2026-01-04 22:33:09 +00:00
configs: make consensus/genesis generation fallible
This commit is contained in:
parent
c46f815293
commit
17180d8c37
@ -8,6 +8,8 @@ use super::{
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BaseConfigError {
|
||||
#[error(transparent)]
|
||||
Consensus(#[from] consensus::ConsensusConfigError),
|
||||
#[error(transparent)]
|
||||
Da(#[from] da::DaConfigError),
|
||||
#[error(transparent)]
|
||||
@ -36,7 +38,7 @@ pub fn build_base_configs(
|
||||
ids,
|
||||
consensus_params,
|
||||
wallet_config,
|
||||
),
|
||||
)?,
|
||||
bootstrap_configs: bootstrap::create_bootstrap_configs(
|
||||
ids,
|
||||
SHORT_PROLONGED_BOOTSTRAP_PERIOD,
|
||||
|
||||
@ -29,6 +29,18 @@ use num_bigint::BigUint;
|
||||
|
||||
use super::wallet::{WalletAccount, WalletConfig};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConsensusConfigError {
|
||||
#[error("failed to build genesis inscription signer: {message}")]
|
||||
InscriptionSigner { message: String },
|
||||
#[error("failed to build genesis transaction: {message}")]
|
||||
GenesisTx { message: String },
|
||||
#[error("failed to build ledger config: {message}")]
|
||||
LedgerConfig { message: String },
|
||||
#[error("failed to sign genesis declarations: {message}")]
|
||||
DeclarationSignature { message: String },
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConsensusParams {
|
||||
pub n_participants: usize,
|
||||
@ -43,31 +55,18 @@ impl ConsensusParams {
|
||||
#[must_use]
|
||||
pub fn default_for_participants(n_participants: usize) -> Self {
|
||||
let active_slot_coeff = env::var(Self::CONSENSUS_ACTIVE_SLOT_COEFF_VAR)
|
||||
.map(|s| {
|
||||
f64::from_str(&s).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"invalid {}='{}' (expected a float in (0.0, 1.0]): {err}",
|
||||
Self::CONSENSUS_ACTIVE_SLOT_COEFF_VAR,
|
||||
s
|
||||
)
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.and_then(|raw| f64::from_str(&raw).ok())
|
||||
.filter(|value| (0.0..=1.0).contains(value) && *value > 0.0)
|
||||
.unwrap_or(Self::DEFAULT_ACTIVE_SLOT_COEFF);
|
||||
|
||||
assert!(
|
||||
(0.0..=1.0).contains(&active_slot_coeff) && active_slot_coeff > 0.0,
|
||||
"{} must be in (0.0, 1.0], got {}",
|
||||
Self::CONSENSUS_ACTIVE_SLOT_COEFF_VAR,
|
||||
active_slot_coeff
|
||||
);
|
||||
|
||||
Self {
|
||||
n_participants,
|
||||
// by setting the slot coeff to 1, we also increase the probability of multiple blocks
|
||||
// (forks) being produced in the same slot (epoch). Setting the security
|
||||
// parameter to some value > 1 ensures nodes have some time to sync before
|
||||
// deciding on the longest chain.
|
||||
security_param: NonZero::new(10).unwrap(),
|
||||
security_param: unsafe { NonZero::new_unchecked(10) },
|
||||
// a block should be produced (on average) every slot
|
||||
active_slot_coeff,
|
||||
}
|
||||
@ -116,13 +115,17 @@ pub struct ServiceNote {
|
||||
pub output_index: usize,
|
||||
}
|
||||
|
||||
fn create_genesis_tx(utxos: &[Utxo]) -> GenesisTx {
|
||||
fn create_genesis_tx(utxos: &[Utxo]) -> Result<GenesisTx, ConsensusConfigError> {
|
||||
// Create a genesis inscription op (similar to config.yaml)
|
||||
let inscription = InscriptionOp {
|
||||
channel_id: ChannelId::from([0; 32]),
|
||||
inscription: vec![103, 101, 110, 101, 115, 105, 115], // "genesis" in bytes
|
||||
parent: MsgId::root(),
|
||||
signer: Ed25519PublicKey::from_bytes(&[0; 32]).unwrap(),
|
||||
signer: Ed25519PublicKey::from_bytes(&[0; 32]).map_err(|err| {
|
||||
ConsensusConfigError::InscriptionSigner {
|
||||
message: err.to_string(),
|
||||
}
|
||||
})?,
|
||||
};
|
||||
|
||||
// Create ledger transaction with the utxos as outputs
|
||||
@ -143,15 +146,19 @@ fn create_genesis_tx(utxos: &[Utxo]) -> GenesisTx {
|
||||
};
|
||||
|
||||
// Wrap in GenesisTx
|
||||
GenesisTx::from_tx(signed_mantle_tx).expect("Invalid genesis transaction")
|
||||
GenesisTx::from_tx(signed_mantle_tx).map_err(|err| ConsensusConfigError::GenesisTx {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn build_ledger_config(consensus_params: &ConsensusParams) -> nomos_ledger::Config {
|
||||
nomos_ledger::Config {
|
||||
fn build_ledger_config(
|
||||
consensus_params: &ConsensusParams,
|
||||
) -> Result<nomos_ledger::Config, ConsensusConfigError> {
|
||||
Ok(nomos_ledger::Config {
|
||||
epoch_config: EpochConfig {
|
||||
epoch_stake_distribution_stabilization: NonZero::new(3).unwrap(),
|
||||
epoch_period_nonce_buffer: NonZero::new(3).unwrap(),
|
||||
epoch_period_nonce_stabilization: NonZero::new(4).unwrap(),
|
||||
epoch_stake_distribution_stabilization: unsafe { NonZero::new_unchecked(3) },
|
||||
epoch_period_nonce_buffer: unsafe { NonZero::new_unchecked(3) },
|
||||
epoch_period_nonce_stabilization: unsafe { NonZero::new_unchecked(4) },
|
||||
},
|
||||
consensus_config: cryptarchia_engine::Config {
|
||||
security_param: consensus_params.security_param,
|
||||
@ -189,14 +196,18 @@ fn build_ledger_config(consensus_params: &ConsensusParams) -> nomos_ledger::Conf
|
||||
},
|
||||
service_rewards_params: nomos_ledger::mantle::sdp::ServiceRewardsParameters {
|
||||
blend: nomos_ledger::mantle::sdp::rewards::blend::RewardsParameters {
|
||||
rounds_per_session: NonZeroU64::new(10).unwrap(),
|
||||
message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(),
|
||||
num_blend_layers: NonZeroU64::new(3).unwrap(),
|
||||
minimum_network_size: NonZeroU64::new(1).unwrap(),
|
||||
rounds_per_session: unsafe { NonZeroU64::new_unchecked(10) },
|
||||
message_frequency_per_round: NonNegativeF64::try_from(1.0).map_err(|_| {
|
||||
ConsensusConfigError::LedgerConfig {
|
||||
message: "message_frequency_per_round must be non-negative".to_owned(),
|
||||
}
|
||||
})?,
|
||||
num_blend_layers: unsafe { NonZeroU64::new_unchecked(3) },
|
||||
minimum_network_size: unsafe { NonZeroU64::new_unchecked(1) },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -204,7 +215,7 @@ pub fn create_consensus_configs(
|
||||
ids: &[[u8; 32]],
|
||||
consensus_params: &ConsensusParams,
|
||||
wallet: &WalletConfig,
|
||||
) -> Vec<GeneralConsensusConfig> {
|
||||
) -> Result<Vec<GeneralConsensusConfig>, ConsensusConfigError> {
|
||||
let mut leader_keys = Vec::new();
|
||||
let mut blend_notes = Vec::new();
|
||||
let mut da_notes = Vec::new();
|
||||
@ -216,10 +227,10 @@ pub fn create_consensus_configs(
|
||||
&mut da_notes,
|
||||
);
|
||||
let utxos = append_wallet_utxos(utxos, wallet);
|
||||
let genesis_tx = create_genesis_tx(&utxos);
|
||||
let ledger_config = build_ledger_config(consensus_params);
|
||||
let genesis_tx = create_genesis_tx(&utxos)?;
|
||||
let ledger_config = build_ledger_config(consensus_params)?;
|
||||
|
||||
leader_keys
|
||||
Ok(leader_keys
|
||||
.into_iter()
|
||||
.map(|(pk, sk)| GeneralConsensusConfig {
|
||||
leader_config: LeaderConfig { pk, sk },
|
||||
@ -230,7 +241,7 @@ pub fn create_consensus_configs(
|
||||
blend_notes: blend_notes.clone(),
|
||||
wallet_accounts: wallet.accounts.clone(),
|
||||
})
|
||||
.collect()
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn create_utxos_for_leader_and_services(
|
||||
@ -324,12 +335,16 @@ fn append_wallet_utxos(mut utxos: Vec<Utxo>, wallet: &WalletConfig) -> Vec<Utxo>
|
||||
pub fn create_genesis_tx_with_declarations(
|
||||
ledger_tx: LedgerTx,
|
||||
providers: Vec<ProviderInfo>,
|
||||
) -> GenesisTx {
|
||||
) -> Result<GenesisTx, ConsensusConfigError> {
|
||||
let inscription = InscriptionOp {
|
||||
channel_id: ChannelId::from([0; 32]),
|
||||
inscription: vec![103, 101, 110, 101, 115, 105, 115], // "genesis" in bytes
|
||||
parent: MsgId::root(),
|
||||
signer: Ed25519PublicKey::from_bytes(&[0; 32]).unwrap(),
|
||||
signer: Ed25519PublicKey::from_bytes(&[0; 32]).map_err(|err| {
|
||||
ConsensusConfigError::InscriptionSigner {
|
||||
message: err.to_string(),
|
||||
}
|
||||
})?,
|
||||
};
|
||||
|
||||
let ledger_tx_hash = ledger_tx.hash();
|
||||
@ -365,7 +380,9 @@ pub fn create_genesis_tx_with_declarations(
|
||||
for provider in providers {
|
||||
let zk_sig =
|
||||
ZkKey::multi_sign(&[provider.note.sk, provider.zk_sk], mantle_tx_hash.as_ref())
|
||||
.unwrap();
|
||||
.map_err(|err| ConsensusConfigError::DeclarationSignature {
|
||||
message: format!("{err:?}"),
|
||||
})?;
|
||||
let ed25519_sig = provider
|
||||
.provider_sk
|
||||
.sign_payload(mantle_tx_hash.as_signing_bytes().as_ref());
|
||||
@ -382,5 +399,7 @@ pub fn create_genesis_tx_with_declarations(
|
||||
ledger_tx_proof: ZkSignature::new(CompressedGroth16Proof::from_bytes(&[0u8; 128])),
|
||||
};
|
||||
|
||||
GenesisTx::from_tx(signed_mantle_tx).expect("Invalid genesis transaction")
|
||||
GenesisTx::from_tx(signed_mantle_tx).map_err(|err| ConsensusConfigError::GenesisTx {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,8 +9,12 @@ pub mod time;
|
||||
pub mod tracing;
|
||||
pub mod wallet;
|
||||
|
||||
use std::cmp;
|
||||
|
||||
use blend::GeneralBlendConfig;
|
||||
use consensus::{GeneralConsensusConfig, ProviderInfo, create_genesis_tx_with_declarations};
|
||||
use consensus::{
|
||||
ConsensusConfigError, GeneralConsensusConfig, ProviderInfo, create_genesis_tx_with_declarations,
|
||||
};
|
||||
use da::GeneralDaConfig;
|
||||
use key_management_system_service::{backend::preload::PreloadKMSBackendSettings, keys::Key};
|
||||
use network::GeneralNetworkConfig;
|
||||
@ -38,6 +42,29 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GeneralConfigError {
|
||||
#[error("participant count must be > 0")]
|
||||
EmptyParticipants,
|
||||
#[error("blend core subset {blend_core} exceeds participants {participants}")]
|
||||
BlendCoreSubsetTooLarge {
|
||||
blend_core: usize,
|
||||
participants: usize,
|
||||
},
|
||||
#[error("failed to allocate a free UDP port for {label}")]
|
||||
PortAllocationFailed { label: &'static str },
|
||||
#[error(transparent)]
|
||||
Invariants(#[from] crate::topology::invariants::TopologyInvariantError),
|
||||
#[error(transparent)]
|
||||
Consensus(#[from] ConsensusConfigError),
|
||||
#[error(transparent)]
|
||||
Network(#[from] network::NetworkConfigError),
|
||||
#[error(transparent)]
|
||||
Da(#[from] da::DaConfigError),
|
||||
#[error(transparent)]
|
||||
Api(#[from] api::ApiConfigError),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GeneralConfig {
|
||||
pub api_config: GeneralApiConfig,
|
||||
@ -51,79 +78,91 @@ pub struct GeneralConfig {
|
||||
pub kms_config: PreloadKMSBackendSettings,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_general_configs(n_nodes: usize) -> Vec<GeneralConfig> {
|
||||
pub fn create_general_configs(n_nodes: usize) -> Result<Vec<GeneralConfig>, GeneralConfigError> {
|
||||
create_general_configs_with_network(n_nodes, &NetworkParams::default())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_general_configs_with_network(
|
||||
n_nodes: usize,
|
||||
network_params: &NetworkParams,
|
||||
) -> Vec<GeneralConfig> {
|
||||
) -> Result<Vec<GeneralConfig>, GeneralConfigError> {
|
||||
create_general_configs_with_blend_core_subset(n_nodes, n_nodes, network_params)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_general_configs_with_blend_core_subset(
|
||||
n_nodes: usize,
|
||||
// TODO: Instead of this, define a config struct for each node.
|
||||
// That would be also useful for non-even token distributions: https://github.com/logos-co/nomos/issues/1888
|
||||
n_blend_core_nodes: usize,
|
||||
network_params: &NetworkParams,
|
||||
) -> Vec<GeneralConfig> {
|
||||
assert!(
|
||||
n_blend_core_nodes <= n_nodes,
|
||||
"n_blend_core_nodes({n_blend_core_nodes}) must be less than or equal to n_nodes({n_nodes})",
|
||||
);
|
||||
) -> 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,
|
||||
});
|
||||
}
|
||||
|
||||
// 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![];
|
||||
let mut blend_ports = vec![];
|
||||
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().unwrap());
|
||||
blend_ports.push(get_available_udp_port().unwrap());
|
||||
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" })?,
|
||||
);
|
||||
}
|
||||
|
||||
validate_generated_vectors(n_nodes, &ids, &da_ports, &blend_ports)
|
||||
.expect("invalid generated test topology inputs");
|
||||
validate_generated_vectors(n_nodes, &ids, &da_ports, &blend_ports)?;
|
||||
|
||||
let consensus_params = ConsensusParams::default_for_participants(n_nodes);
|
||||
let mut consensus_configs =
|
||||
consensus::create_consensus_configs(&ids, &consensus_params, &WalletConfig::default());
|
||||
consensus::create_consensus_configs(&ids, &consensus_params, &WalletConfig::default())?;
|
||||
let bootstrap_config =
|
||||
bootstrap::create_bootstrap_configs(&ids, SHORT_PROLONGED_BOOTSTRAP_PERIOD);
|
||||
let network_configs =
|
||||
network::create_network_configs(&ids, network_params).expect("network config generation");
|
||||
let da_configs =
|
||||
da::try_create_da_configs(&ids, &DaParams::default(), &da_ports).expect("DA configs");
|
||||
let api_configs = api::create_api_configs(&ids).expect("api config generation");
|
||||
let network_configs = network::create_network_configs(&ids, network_params)?;
|
||||
let da_configs = da::try_create_da_configs(&ids, &DaParams::default(), &da_ports)?;
|
||||
let api_configs = api::create_api_configs(&ids)?;
|
||||
let blend_configs = blend::create_blend_configs(&ids, &blend_ports);
|
||||
let tracing_configs = tracing::create_tracing_configs(&ids);
|
||||
let time_config = time::default_time_config();
|
||||
|
||||
let providers: Vec<_> = blend_configs
|
||||
let Some(first_consensus) = consensus_configs.first() else {
|
||||
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(n_blend_core_nodes)
|
||||
.map(|(i, blend_conf)| ProviderInfo {
|
||||
.take(cmp::min(n_blend_core_nodes, blend_configs.len()))
|
||||
{
|
||||
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(),
|
||||
zk_sk: blend_conf.secret_zk_key.clone(),
|
||||
locator: Locator(blend_conf.backend_core.listening_address.clone()),
|
||||
note: consensus_configs[0].blend_notes[i].clone(),
|
||||
})
|
||||
.collect();
|
||||
let ledger_tx = consensus_configs[0]
|
||||
.genesis_tx
|
||||
.mantle_tx()
|
||||
.ledger_tx
|
||||
.clone();
|
||||
let genesis_tx = create_genesis_tx_with_declarations(ledger_tx, providers);
|
||||
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();
|
||||
}
|
||||
@ -150,21 +189,54 @@ pub fn create_general_configs_with_blend_core_subset(
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut general_configs = vec![];
|
||||
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();
|
||||
|
||||
general_configs.push(GeneralConfig {
|
||||
api_config: api_configs[i].clone(),
|
||||
consensus_config: consensus_configs[i].clone(),
|
||||
bootstrapping_config: bootstrap_config[i].clone(),
|
||||
da_config: da_configs[i].clone(),
|
||||
network_config: network_configs[i].clone(),
|
||||
blend_config: blend_configs[i].clone(),
|
||||
tracing_config: tracing_configs[i].clone(),
|
||||
api_config,
|
||||
consensus_config,
|
||||
bootstrapping_config,
|
||||
da_config,
|
||||
network_config,
|
||||
blend_config,
|
||||
tracing_config,
|
||||
time_config: time_config.clone(),
|
||||
kms_config: kms_configs[i].clone(),
|
||||
kms_config,
|
||||
});
|
||||
}
|
||||
|
||||
general_configs
|
||||
Ok(general_configs)
|
||||
}
|
||||
|
||||
@ -309,9 +309,15 @@ impl TopologyBuilder {
|
||||
.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();
|
||||
match create_genesis_tx_with_declarations(ledger_tx, providers) {
|
||||
Ok(genesis_tx) => {
|
||||
for c in &mut consensus_configs {
|
||||
c.genesis_tx = genesis_tx.clone();
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(error = ?err, "failed to build genesis declarations; using base genesis transaction");
|
||||
}
|
||||
}
|
||||
|
||||
let kms_configs =
|
||||
|
||||
@ -9,7 +9,7 @@ use testing_framework_config::topology::configs::{
|
||||
GeneralConfig,
|
||||
api::GeneralApiConfig,
|
||||
base::{BaseConfigError, BaseConfigs, build_base_configs},
|
||||
consensus::{ConsensusParams, create_genesis_tx_with_declarations},
|
||||
consensus::{ConsensusConfigError, ConsensusParams, create_genesis_tx_with_declarations},
|
||||
da::DaParams,
|
||||
network::NetworkParams,
|
||||
time::default_time_config,
|
||||
@ -60,6 +60,8 @@ pub enum NodeConfigBuildError {
|
||||
Providers(#[from] ProviderBuildError),
|
||||
#[error(transparent)]
|
||||
Base(#[from] BaseConfigError),
|
||||
#[error(transparent)]
|
||||
Genesis(#[from] ConsensusConfigError),
|
||||
#[error("failed to allocate an available UDP port")]
|
||||
PortAllocFailed,
|
||||
#[error("failed to parse multiaddr '{value}': {message}")]
|
||||
@ -143,7 +145,7 @@ pub fn try_create_node_configs(
|
||||
.get(0)
|
||||
.ok_or(NodeConfigBuildError::MissingConsensusConfig)?;
|
||||
let ledger_tx = first_consensus.genesis_tx.mantle_tx().ledger_tx.clone();
|
||||
let genesis_tx = create_genesis_tx_with_declarations(ledger_tx, providers);
|
||||
let genesis_tx = create_genesis_tx_with_declarations(ledger_tx, providers)?;
|
||||
|
||||
for c in &mut consensus_configs {
|
||||
c.genesis_tx = genesis_tx.clone();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user