2025-12-18 22:59:16 +01:00
|
|
|
use std::{collections::HashMap, sync::Arc, time::Duration};
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2026-02-09 10:28:15 +02:00
|
|
|
use lb_tracing_service::TracingSettings;
|
2025-12-01 12:48:39 +01:00
|
|
|
use testing_framework_config::topology::configs::{
|
2026-01-25 10:11:16 +02:00
|
|
|
GeneralConfig, consensus::ConsensusParams, wallet::WalletConfig,
|
2025-12-01 12:48:39 +01:00
|
|
|
};
|
2025-12-18 22:59:16 +01:00
|
|
|
use tokio::{
|
|
|
|
|
sync::{Mutex, oneshot::Sender},
|
|
|
|
|
time::timeout,
|
|
|
|
|
};
|
2025-12-18 14:46:27 +01:00
|
|
|
use tracing::{error, info, warn};
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2025-12-18 14:46:27 +01:00
|
|
|
use crate::{config::builder::try_create_node_configs, host::Host, server::CfgSyncConfig};
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2025-12-15 22:29:36 +01:00
|
|
|
const HOST_POLLING_INTERVAL: Duration = Duration::from_secs(1);
|
|
|
|
|
|
2025-12-01 12:48:39 +01:00
|
|
|
pub enum RepoResponse {
|
|
|
|
|
Config(Box<GeneralConfig>),
|
|
|
|
|
Timeout,
|
2025-12-18 14:46:27 +01:00
|
|
|
Error(String),
|
2025-12-01 12:48:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct ConfigRepo {
|
|
|
|
|
waiting_hosts: Mutex<HashMap<Host, Sender<RepoResponse>>>,
|
|
|
|
|
n_hosts: usize,
|
|
|
|
|
consensus_params: ConsensusParams,
|
|
|
|
|
tracing_settings: TracingSettings,
|
|
|
|
|
wallet_config: WalletConfig,
|
|
|
|
|
timeout_duration: Duration,
|
|
|
|
|
ids: Option<Vec<[u8; 32]>>,
|
|
|
|
|
blend_ports: Option<Vec<u16>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<CfgSyncConfig> for Arc<ConfigRepo> {
|
|
|
|
|
fn from(config: CfgSyncConfig) -> Self {
|
|
|
|
|
let consensus_params = config.to_consensus_params();
|
|
|
|
|
let tracing_settings = config.to_tracing_settings();
|
|
|
|
|
let wallet_config = config.wallet_config();
|
|
|
|
|
let ids = config.ids;
|
|
|
|
|
let blend_ports = config.blend_ports;
|
|
|
|
|
|
|
|
|
|
ConfigRepo::new(
|
|
|
|
|
config.n_hosts,
|
|
|
|
|
consensus_params,
|
|
|
|
|
tracing_settings,
|
|
|
|
|
wallet_config,
|
|
|
|
|
ids,
|
|
|
|
|
blend_ports,
|
|
|
|
|
Duration::from_secs(config.timeout),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ConfigRepo {
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn new(
|
|
|
|
|
n_hosts: usize,
|
|
|
|
|
consensus_params: ConsensusParams,
|
|
|
|
|
tracing_settings: TracingSettings,
|
|
|
|
|
wallet_config: WalletConfig,
|
|
|
|
|
ids: Option<Vec<[u8; 32]>>,
|
|
|
|
|
blend_ports: Option<Vec<u16>>,
|
|
|
|
|
timeout_duration: Duration,
|
|
|
|
|
) -> Arc<Self> {
|
|
|
|
|
let repo = Arc::new(Self {
|
|
|
|
|
waiting_hosts: Mutex::new(HashMap::new()),
|
|
|
|
|
n_hosts,
|
|
|
|
|
consensus_params,
|
|
|
|
|
tracing_settings,
|
|
|
|
|
wallet_config,
|
|
|
|
|
ids,
|
|
|
|
|
blend_ports,
|
|
|
|
|
timeout_duration,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let repo_clone = Arc::clone(&repo);
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
repo_clone.run().await;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
repo
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 22:59:16 +01:00
|
|
|
pub async fn register(&self, host: Host, reply_tx: Sender<RepoResponse>) {
|
|
|
|
|
let mut waiting_hosts = self.waiting_hosts.lock().await;
|
2025-12-01 12:48:39 +01:00
|
|
|
waiting_hosts.insert(host, reply_tx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn run(&self) {
|
|
|
|
|
let timeout_duration = self.timeout_duration;
|
|
|
|
|
|
2025-12-19 01:48:50 +01:00
|
|
|
if wait_for_hosts_with_timeout(self, timeout_duration).await {
|
2025-12-18 14:46:27 +01:00
|
|
|
info!("all hosts have announced their IPs");
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2025-12-19 01:48:50 +01:00
|
|
|
let mut waiting_hosts = take_waiting_hosts(self).await;
|
2025-12-01 12:48:39 +01:00
|
|
|
let hosts = waiting_hosts.keys().cloned().collect();
|
|
|
|
|
|
2025-12-19 01:48:50 +01:00
|
|
|
let configs = match generate_node_configs(self, hosts) {
|
2025-12-18 14:46:27 +01:00
|
|
|
Ok(configs) => configs,
|
2025-12-19 01:48:50 +01:00
|
|
|
Err(message) => {
|
|
|
|
|
send_error_to_all(&mut waiting_hosts, &message);
|
2025-12-18 14:46:27 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2025-12-19 01:48:50 +01:00
|
|
|
send_configs_to_all_hosts(&mut waiting_hosts, &configs);
|
|
|
|
|
return;
|
2025-12-01 12:48:39 +01:00
|
|
|
}
|
2025-12-19 01:48:50 +01:00
|
|
|
|
|
|
|
|
warn!("timeout: not all hosts announced within the time limit");
|
|
|
|
|
let mut waiting_hosts = take_waiting_hosts(self).await;
|
|
|
|
|
send_timeout_to_all(&mut waiting_hosts);
|
2025-12-01 12:48:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn wait_for_hosts(&self) {
|
|
|
|
|
loop {
|
2025-12-18 22:59:16 +01:00
|
|
|
let len = { self.waiting_hosts.lock().await.len() };
|
|
|
|
|
if len >= self.n_hosts {
|
2025-12-01 12:48:39 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2025-12-15 22:29:36 +01:00
|
|
|
tokio::time::sleep(HOST_POLLING_INTERVAL).await;
|
2025-12-01 12:48:39 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-19 01:48:50 +01:00
|
|
|
|
|
|
|
|
async fn wait_for_hosts_with_timeout(repo: &ConfigRepo, timeout_duration: Duration) -> bool {
|
|
|
|
|
timeout(timeout_duration, repo.wait_for_hosts())
|
|
|
|
|
.await
|
|
|
|
|
.is_ok()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn take_waiting_hosts(repo: &ConfigRepo) -> HashMap<Host, Sender<RepoResponse>> {
|
|
|
|
|
let mut guard = repo.waiting_hosts.lock().await;
|
|
|
|
|
std::mem::take(&mut *guard)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_node_configs(
|
|
|
|
|
repo: &ConfigRepo,
|
|
|
|
|
hosts: Vec<Host>,
|
|
|
|
|
) -> Result<HashMap<Host, GeneralConfig>, String> {
|
|
|
|
|
try_create_node_configs(
|
|
|
|
|
&repo.consensus_params,
|
|
|
|
|
&repo.tracing_settings,
|
|
|
|
|
&repo.wallet_config,
|
|
|
|
|
repo.ids.clone(),
|
|
|
|
|
repo.blend_ports.clone(),
|
|
|
|
|
hosts,
|
|
|
|
|
)
|
|
|
|
|
.map_err(|err| {
|
|
|
|
|
error!(error = %err, "failed to generate node configs");
|
|
|
|
|
err.to_string()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send_error_to_all(waiting_hosts: &mut HashMap<Host, Sender<RepoResponse>>, message: &str) {
|
|
|
|
|
for (_, sender) in waiting_hosts.drain() {
|
|
|
|
|
let _ = sender.send(RepoResponse::Error(message.to_string()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send_timeout_to_all(waiting_hosts: &mut HashMap<Host, Sender<RepoResponse>>) {
|
|
|
|
|
for (_, sender) in waiting_hosts.drain() {
|
|
|
|
|
let _ = sender.send(RepoResponse::Timeout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send_configs_to_all_hosts(
|
|
|
|
|
waiting_hosts: &mut HashMap<Host, Sender<RepoResponse>>,
|
|
|
|
|
configs: &HashMap<Host, GeneralConfig>,
|
|
|
|
|
) {
|
|
|
|
|
for (host, sender) in waiting_hosts.drain() {
|
|
|
|
|
match configs.get(&host) {
|
|
|
|
|
Some(config) => {
|
|
|
|
|
let _ = sender.send(RepoResponse::Config(Box::new(config.to_owned())));
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
warn!(identifier = %host.identifier, "missing config for host");
|
|
|
|
|
let _ = sender.send(RepoResponse::Error("missing config for host".to_string()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|