e2dd8ac743
* squashing localnet-v2 commits (again) cargo fmt fixes to localnet purge provide path in the error message output args log failed exec print based on tty check-prerequisites cmd checked iptables update basic kernel features check enable ipv6 rules add forwarding rules squashing localnet-v2 commits additional changes propagate custom-dns flag to all run containers remove is_mock from EcashManager another localnet squash unused import chore: remove redundant testnet manager missing impl additional linux fixes command to rebuild container image wait for at least 2 blocks additional node startup fixes added --custom-dns flag to nym node setup add gateway probe + wait for DKG magic file fixed localnet down on linux container ls re-enable state resync additional feature locking macos adjustments working nyxd startup on linux wip linux box wip separating network inspect betweewn macos and linux initial linux feature locking moved all container commands into a single location finally working initial node performance squashing orchestrator commits cleanup fixed condition for naive rearrangement added cache of cosmwasm contracts for speed up on subsequent runs 'down' command refreshing described cache after nodes are bonded nym nodes setup + wip on nym api refresh nodes setup WIP first pass cleanup placeholder for nym-node setup bypassing the dkg further progress on nym-api setup wip: api setup up/down/purge placeholders persisting contract setup data fix contract upload by forcing amd64 container platform wip: contracts setup4 wip: contracts setup3 wip: contracts setup2 wip: contracts setup include network setup init and spawn nyxd build nyxd image in dedicated orchestrator build nyxd image squashed cherry-picked lp changes Bits and bobs to make everything work Title MacOS setup instructions Docker/Container localnet * clippy * fixes on non-unix targets --------- Co-authored-by: durch <durch@users.noreply.github.com>
298 lines
9.5 KiB
Rust
298 lines
9.5 KiB
Rust
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//! Collection of initialization steps used by client implementations
|
|
|
|
use crate::client::base_client::storage::helpers::{
|
|
has_gateway_details, load_active_gateway_details, load_client_keys, load_gateway_details,
|
|
store_gateway_details, update_stored_published_data_gateway,
|
|
};
|
|
use crate::client::key_manager::ClientKeys;
|
|
use crate::client::key_manager::persistence::KeyStore;
|
|
use crate::error::ClientCoreError;
|
|
use crate::init::helpers::{
|
|
choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
|
|
};
|
|
use crate::init::types::{
|
|
GatewaySelectionSpecification, GatewaySetup, InitialisationResult, SelectedGateway,
|
|
};
|
|
use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration};
|
|
use nym_client_core_gateways_storage::{GatewayPublishedData, GatewaysDetailsStore};
|
|
use nym_gateway_client::client::InitGatewayClient;
|
|
use nym_topology::node::RoutingNode;
|
|
use rand::rngs::OsRng;
|
|
use rand::{CryptoRng, RngCore};
|
|
use serde::Serialize;
|
|
#[cfg(unix)]
|
|
use std::{os::fd::RawFd, sync::Arc};
|
|
|
|
pub mod helpers;
|
|
pub mod types;
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub(crate) mod websockets;
|
|
|
|
// helpers for error wrapping
|
|
|
|
pub async fn generate_new_client_keys<K, R>(
|
|
rng: &mut R,
|
|
key_store: &K,
|
|
) -> Result<(), ClientCoreError>
|
|
where
|
|
R: RngCore + CryptoRng,
|
|
K: KeyStore,
|
|
K::StorageError: Send + Sync + 'static,
|
|
{
|
|
ClientKeys::generate_new(rng)
|
|
.persist_keys(key_store)
|
|
.await
|
|
.map_err(|source| ClientCoreError::KeyStoreError {
|
|
source: Box::new(source),
|
|
})
|
|
}
|
|
|
|
async fn setup_new_gateway<K, D>(
|
|
key_store: &K,
|
|
details_store: &D,
|
|
selection_specification: GatewaySelectionSpecification,
|
|
available_gateways: Vec<RoutingNode>,
|
|
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
|
) -> Result<InitialisationResult, ClientCoreError>
|
|
where
|
|
K: KeyStore,
|
|
D: GatewaysDetailsStore,
|
|
K::StorageError: Send + Sync + 'static,
|
|
D::StorageError: Send + Sync + 'static,
|
|
{
|
|
tracing::trace!("Setting up new gateway");
|
|
|
|
// if we're setting up new gateway, we must have had generated long-term client keys before
|
|
let client_keys = load_client_keys(key_store).await?;
|
|
|
|
let mut rng = OsRng;
|
|
|
|
let selected_gateway = match selection_specification {
|
|
GatewaySelectionSpecification::UniformRemote {
|
|
must_use_tls,
|
|
no_hostname,
|
|
} => {
|
|
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
|
|
SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?
|
|
}
|
|
GatewaySelectionSpecification::RemoteByLatency {
|
|
must_use_tls,
|
|
no_hostname,
|
|
} => {
|
|
let gateway =
|
|
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
|
|
SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?
|
|
}
|
|
GatewaySelectionSpecification::Specified {
|
|
must_use_tls,
|
|
no_hostname,
|
|
identity,
|
|
} => {
|
|
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
|
|
SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?
|
|
}
|
|
GatewaySelectionSpecification::Custom {
|
|
gateway_identity,
|
|
additional_data,
|
|
} => SelectedGateway::custom(gateway_identity, additional_data)?,
|
|
};
|
|
|
|
// check if we already have details associated with this particular gateway
|
|
// and if so, see if we can overwrite it
|
|
let selected_id = selected_gateway.gateway_id().to_base58_string();
|
|
if has_gateway_details(details_store, &selected_id).await? {
|
|
return Err(ClientCoreError::AlreadyRegistered {
|
|
gateway_id: selected_id,
|
|
});
|
|
}
|
|
|
|
let (gateway_details, authenticated_ephemeral_client) = match selected_gateway {
|
|
SelectedGateway::Remote {
|
|
gateway_id,
|
|
|
|
gateway_listeners,
|
|
} => {
|
|
// if we're using a 'normal' gateway setup, do register
|
|
let our_identity = client_keys.identity_keypair();
|
|
|
|
let registration = helpers::register_with_gateway(
|
|
gateway_id,
|
|
gateway_listeners.clone(),
|
|
our_identity,
|
|
#[cfg(unix)]
|
|
connection_fd_callback,
|
|
)
|
|
.await?;
|
|
(
|
|
GatewayDetails::new_remote(
|
|
gateway_id,
|
|
registration.shared_keys,
|
|
GatewayPublishedData::new(gateway_listeners),
|
|
),
|
|
Some(registration.authenticated_ephemeral_client),
|
|
)
|
|
}
|
|
SelectedGateway::Custom {
|
|
gateway_id,
|
|
additional_data,
|
|
} => (
|
|
GatewayDetails::new_custom(gateway_id, additional_data),
|
|
None,
|
|
),
|
|
};
|
|
|
|
let gateway_registration = gateway_details.into();
|
|
|
|
// persist gateway details
|
|
store_gateway_details(details_store, &gateway_registration).await?;
|
|
|
|
Ok(InitialisationResult {
|
|
gateway_registration,
|
|
client_keys,
|
|
authenticated_ephemeral_client,
|
|
})
|
|
}
|
|
|
|
pub async fn refresh_gateway_published_data<D>(
|
|
details_store: &D,
|
|
registration: GatewayRegistration,
|
|
available_gateways: Vec<RoutingNode>,
|
|
must_use_tls: bool,
|
|
no_hostname: bool,
|
|
) -> Result<(), ClientCoreError>
|
|
where
|
|
D: GatewaysDetailsStore,
|
|
D::StorageError: Send + Sync + 'static,
|
|
{
|
|
let gateway_id = registration.gateway_id().to_base58_string();
|
|
tracing::trace!("Updating gateway details : {gateway_id}");
|
|
|
|
let gateway = get_specified_gateway(&gateway_id, &available_gateways, must_use_tls)?;
|
|
let selected_gateway = SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?;
|
|
|
|
let new_gateway_listeners = match selected_gateway {
|
|
SelectedGateway::Remote {
|
|
gateway_listeners, ..
|
|
} => gateway_listeners,
|
|
SelectedGateway::Custom { .. } => {
|
|
// this should not happen, as `from_topology_node` returns a Remote
|
|
Err(ClientCoreError::UnexpectedCustomGatewaySelection)?
|
|
}
|
|
};
|
|
|
|
let new_published_data = GatewayPublishedData::new(new_gateway_listeners);
|
|
|
|
// update gateway details
|
|
update_stored_published_data_gateway(
|
|
details_store,
|
|
®istration.gateway_id(),
|
|
&new_published_data,
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn use_loaded_gateway_details<K, D>(
|
|
key_store: &K,
|
|
details_store: &D,
|
|
gateway_id: Option<String>,
|
|
) -> Result<InitialisationResult, ClientCoreError>
|
|
where
|
|
K: KeyStore,
|
|
D: GatewaysDetailsStore,
|
|
K::StorageError: Send + Sync + 'static,
|
|
D::StorageError: Send + Sync + 'static,
|
|
{
|
|
let loaded_details = if let Some(gateway_id) = gateway_id {
|
|
load_gateway_details(details_store, &gateway_id).await?
|
|
} else {
|
|
load_active_gateway_details(details_store)
|
|
.await?
|
|
.registration
|
|
.ok_or(ClientCoreError::NoActiveGatewaySet)?
|
|
};
|
|
|
|
let loaded_keys = load_client_keys(key_store).await?;
|
|
|
|
// no need to persist anything as we got everything from the storage
|
|
Ok(InitialisationResult::new_loaded(
|
|
loaded_details,
|
|
loaded_keys,
|
|
))
|
|
}
|
|
|
|
fn reuse_gateway_connection(
|
|
authenticated_ephemeral_client: InitGatewayClient,
|
|
gateway_registration: GatewayRegistration,
|
|
client_keys: ClientKeys,
|
|
) -> InitialisationResult {
|
|
InitialisationResult {
|
|
gateway_registration,
|
|
client_keys,
|
|
authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
|
|
}
|
|
}
|
|
|
|
pub async fn setup_gateway<K, D>(
|
|
setup: GatewaySetup,
|
|
key_store: &K,
|
|
details_store: &D,
|
|
) -> Result<InitialisationResult, ClientCoreError>
|
|
where
|
|
K: KeyStore,
|
|
D: GatewaysDetailsStore,
|
|
K::StorageError: Send + Sync + 'static,
|
|
D::StorageError: Send + Sync + 'static,
|
|
{
|
|
tracing::debug!("Setting up gateway");
|
|
match setup {
|
|
GatewaySetup::MustLoad { gateway_id } => {
|
|
tracing::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}");
|
|
use_loaded_gateway_details(key_store, details_store, gateway_id).await
|
|
}
|
|
GatewaySetup::New {
|
|
specification,
|
|
available_gateways,
|
|
#[cfg(unix)]
|
|
connection_fd_callback,
|
|
} => {
|
|
tracing::debug!("GatewaySetup::New with spec: {specification:?}");
|
|
setup_new_gateway(
|
|
key_store,
|
|
details_store,
|
|
specification,
|
|
available_gateways,
|
|
#[cfg(unix)]
|
|
connection_fd_callback,
|
|
)
|
|
.await
|
|
}
|
|
GatewaySetup::ReuseConnection {
|
|
authenticated_ephemeral_client,
|
|
gateway_details,
|
|
client_keys: managed_keys,
|
|
} => {
|
|
tracing::debug!("GatewaySetup::ReuseConnection");
|
|
Ok(reuse_gateway_connection(
|
|
*authenticated_ephemeral_client,
|
|
*gateway_details,
|
|
managed_keys,
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
|
|
match std::fs::File::create(output_file) {
|
|
Ok(file) => match serde_json::to_writer_pretty(file, init_results) {
|
|
Ok(_) => println!("Saved: {output_file}"),
|
|
Err(err) => eprintln!("Could not save {output_file}: {err}"),
|
|
},
|
|
Err(err) => eprintln!("Could not save {output_file}: {err}"),
|
|
}
|
|
}
|