cf3fd00350
* - standardise versions for all nym-sdk workspace dependencies - prepend sqlx-pool-guard with 'nym-' * Test remove nym-api from deps * Add oneliner to client_pool doc comments * Add note to commented out docs.rs link in sdk * remove nym-api from script * add publishing file * bring non-binary / contract / tools into workspace version * added more info to publishing.md * make deps workspace version * remove uploaded sphinx-types crate from script * remove erroueously included ignore-defaults * add zeroise to feature * chore: Release * add topology to batch * more cargo versioning * more cargo versioning - wasm utils * more cargo versioning - wasm utils * Add publish=false to manifest for cargo workspaces / crates.io publishing exclusion * remove script now switched to manifest based exclusion * rename import based on rename of contracts-common dep * Making workspace versions for publication + removing unnecessary crates from publication * Remove OOD info from publishing sdk guide * rename contract imports + remove package * temp commit: continuing with removal of path from cargo manifest and replacing with workspace version import for publication * continuing with cargo.toml updates * dryrun only erroring on known version problem crates * remove old published-crates file * Minor comment change * remove default features warning * Additional info on workspace dep comment re publish list * Add missing description to cargo.toml * Fix missing feature flags * Add missing descriptions * Fix remaining path import * Add workspace repo / homepage / documentation links to cargo.toml files * remove workspace version from excluded crate * Remove todo descriptions * Minor comment change * add homepage etc * move from bls git import to nym_bls_fork crate * Modify rest of imports from path to workspace import, excluding binaries * add directory/homepage info * fix cargo fmt * add notes to gitignore * better solution to contracts/ experiment * wasm -> nym_wasm crate renaming * fix fatfinger * add metadata to ecash cargo.toml * stub publishing guide * fix misrevolved netlink- version * Fixes and block publication of rebase re: LP * first pass @ workflows
280 lines
9.1 KiB
Rust
280 lines
9.1 KiB
Rust
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::error::WasmCoreError;
|
|
use crate::storage::wasm_client_traits::WasmClientStorage;
|
|
use crate::storage::ClientStorage;
|
|
use js_sys::Promise;
|
|
use nym_client_core::client::base_client::storage::helpers::set_active_gateway;
|
|
use nym_client_core::client::base_client::storage::GatewaysDetailsStore;
|
|
use nym_client_core::client::replies::reply_storage::browser_backend;
|
|
use nym_client_core::config;
|
|
use nym_client_core::error::ClientCoreError;
|
|
use nym_client_core::init::helpers::gateways_for_init;
|
|
use nym_client_core::init::types::GatewaySelectionSpecification;
|
|
use nym_client_core::init::{
|
|
self, setup_gateway,
|
|
types::{GatewaySetup, InitialisationResult},
|
|
};
|
|
use nym_sphinx::addressing::clients::Recipient;
|
|
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
|
use nym_topology::wasm_helpers::WasmFriendlyNymTopology;
|
|
use nym_topology::{EpochRewardedSet, NymTopology, RoutingNode};
|
|
use nym_validator_client::client::IdentityKey;
|
|
use nym_validator_client::{nym_api::NymApiClientExt, UserAgent};
|
|
use nym_wasm_utils::error::PromisableResult;
|
|
use rand::thread_rng;
|
|
use url::Url;
|
|
use wasm_bindgen::prelude::wasm_bindgen;
|
|
use wasm_bindgen_futures::future_to_promise;
|
|
|
|
pub use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
|
use nym_topology::provider_trait::ToTopologyMetadata;
|
|
use nym_wasm_utils::{console_log, console_warn};
|
|
|
|
// don't get too excited about the name, under the hood it's just a big fat placeholder
|
|
// with no disk_persistence
|
|
pub fn setup_reply_surb_storage_backend(config: config::ReplySurbs) -> browser_backend::Backend {
|
|
browser_backend::Backend::new(
|
|
config.minimum_reply_surb_storage_threshold,
|
|
config.maximum_reply_surb_storage_threshold,
|
|
)
|
|
}
|
|
|
|
pub fn parse_recipient(recipient: &str) -> Result<Recipient, WasmCoreError> {
|
|
Recipient::try_from_base58_string(recipient).map_err(|source| {
|
|
WasmCoreError::MalformedRecipient {
|
|
raw: recipient.to_string(),
|
|
source,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn parse_sender_tag(tag: &str) -> Result<AnonymousSenderTag, WasmCoreError> {
|
|
AnonymousSenderTag::try_from_base58_string(tag).map_err(|source| {
|
|
WasmCoreError::MalformedSenderTag {
|
|
raw: tag.to_string(),
|
|
source,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub async fn current_network_topology_async(
|
|
nym_api_url: String,
|
|
) -> Result<WasmFriendlyNymTopology, WasmCoreError> {
|
|
let url: Url = match nym_api_url.parse() {
|
|
Ok(url) => url,
|
|
Err(source) => {
|
|
return Err(WasmCoreError::MalformedUrl {
|
|
raw: nym_api_url,
|
|
source,
|
|
})
|
|
}
|
|
};
|
|
|
|
let api_client = nym_http_api_client::Client::builder(url.clone())
|
|
.map_err(|_err| WasmCoreError::MalformedUrl {
|
|
raw: nym_api_url.to_string(),
|
|
source: url::ParseError::EmptyHost,
|
|
})?
|
|
.build()
|
|
.map_err(|_err| WasmCoreError::MalformedUrl {
|
|
raw: nym_api_url.to_string(),
|
|
source: url::ParseError::EmptyHost,
|
|
})?;
|
|
let rewarded_set = api_client.get_current_rewarded_set().await?;
|
|
let mixnodes_res = api_client
|
|
.get_all_basic_active_mixing_assigned_nodes_with_metadata()
|
|
.await?;
|
|
let metadata = mixnodes_res.metadata;
|
|
let mixnodes = mixnodes_res.nodes;
|
|
|
|
let gateways_res = api_client
|
|
.get_all_basic_entry_assigned_nodes_with_metadata()
|
|
.await?;
|
|
if !gateways_res.metadata.consistency_check(&metadata) {
|
|
console_warn!("inconsistent nodes metadata between mixnodes and gateways calls! {metadata:?} and {:?}", gateways_res.metadata);
|
|
return Err(WasmCoreError::UnavailableNetworkTopology);
|
|
}
|
|
|
|
let gateways = gateways_res.nodes;
|
|
|
|
let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into();
|
|
let topology = NymTopology::new(
|
|
metadata.to_topology_metadata(),
|
|
epoch_rewarded_set,
|
|
Vec::new(),
|
|
)
|
|
.with_skimmed_nodes(&mixnodes)
|
|
.with_skimmed_nodes(&gateways);
|
|
|
|
Ok(topology.into())
|
|
}
|
|
|
|
#[wasm_bindgen(js_name = "currentNetworkTopology")]
|
|
pub fn current_network_topology(nym_api_url: String) -> Promise {
|
|
// blame js for that serde conversion
|
|
future_to_promise(async move {
|
|
current_network_topology_async(nym_api_url)
|
|
.await
|
|
.map(|topology| serde_wasm_bindgen::to_value(&topology).unwrap())
|
|
.into_promise_result()
|
|
})
|
|
}
|
|
|
|
pub async fn setup_gateway_wasm(
|
|
client_store: &ClientStorage,
|
|
force_tls: bool,
|
|
chosen_gateway: Option<IdentityKey>,
|
|
gateways: Vec<RoutingNode>,
|
|
) -> Result<InitialisationResult, WasmCoreError> {
|
|
// TODO: so much optimization and extra features could be added here, but that's for the future
|
|
|
|
let setup = if client_store
|
|
.get_active_gateway_id()
|
|
.await?
|
|
.active_gateway_id_bs58
|
|
.is_some()
|
|
{
|
|
GatewaySetup::MustLoad { gateway_id: None }
|
|
} else {
|
|
let selection_spec =
|
|
GatewaySelectionSpecification::new(chosen_gateway.clone(), None, force_tls, false);
|
|
|
|
GatewaySetup::New {
|
|
specification: selection_spec,
|
|
available_gateways: gateways,
|
|
}
|
|
};
|
|
|
|
init::setup_gateway(setup, client_store, client_store)
|
|
.await
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub async fn setup_gateway_from_api(
|
|
client_store: &ClientStorage,
|
|
force_tls: bool,
|
|
chosen_gateway: Option<IdentityKey>,
|
|
nym_apis: &[Url],
|
|
minimum_performance: u8,
|
|
ignore_epoch_roles: bool,
|
|
) -> Result<InitialisationResult, WasmCoreError> {
|
|
let gateways = gateways_for_init(
|
|
nym_apis,
|
|
None,
|
|
minimum_performance,
|
|
ignore_epoch_roles,
|
|
None,
|
|
)
|
|
.await?;
|
|
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).await
|
|
}
|
|
|
|
pub async fn current_gateways_wasm(
|
|
nym_apis: &[Url],
|
|
user_agent: Option<UserAgent>,
|
|
minimum_performance: u8,
|
|
ignore_epoch_roles: bool,
|
|
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
|
gateways_for_init(
|
|
nym_apis,
|
|
user_agent,
|
|
minimum_performance,
|
|
ignore_epoch_roles,
|
|
None,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn setup_from_topology(
|
|
explicit_gateway: Option<IdentityKey>,
|
|
force_tls: bool,
|
|
topology: &NymTopology,
|
|
client_store: &ClientStorage,
|
|
) -> Result<InitialisationResult, WasmCoreError> {
|
|
let gateways = topology.entry_capable_nodes().cloned().collect::<Vec<_>>();
|
|
setup_gateway_wasm(client_store, force_tls, explicit_gateway, gateways).await
|
|
}
|
|
|
|
pub async fn generate_new_client_keys(store: &ClientStorage) -> Result<(), WasmCoreError> {
|
|
let mut rng = thread_rng();
|
|
init::generate_new_client_keys(&mut rng, store).await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn add_gateway(
|
|
preferred_gateway: Option<IdentityKey>,
|
|
latency_based_selection: Option<bool>,
|
|
force_tls: bool,
|
|
nym_apis: &[Url],
|
|
user_agent: UserAgent,
|
|
min_performance: u8,
|
|
ignore_epoch_roles: bool,
|
|
storage: &ClientStorage,
|
|
) -> Result<(), WasmCoreError> {
|
|
let selection_spec = GatewaySelectionSpecification::new(
|
|
preferred_gateway.clone(),
|
|
latency_based_selection,
|
|
force_tls,
|
|
false,
|
|
);
|
|
|
|
let preferred_gateway = preferred_gateway
|
|
.as_ref()
|
|
.map(|g| g.parse())
|
|
.transpose()
|
|
.map_err(|source| WasmCoreError::InvalidGatewayIdentity { source })?;
|
|
|
|
let registered_gateways = storage.all_gateways_identities().await.map_err(|source| {
|
|
ClientCoreError::GatewaysDetailsStoreError {
|
|
source: Box::new(source),
|
|
}
|
|
})?;
|
|
|
|
// if user provided gateway id (and we can't overwrite data), make sure we're not trying to register
|
|
// with a known gateway
|
|
if let Some(user_chosen) = preferred_gateway {
|
|
if registered_gateways.contains(&user_chosen) {
|
|
return Err(ClientCoreError::AlreadyRegistered {
|
|
gateway_id: user_chosen.to_base58_string(),
|
|
}
|
|
.into());
|
|
}
|
|
}
|
|
|
|
// Setup gateway by either registering a new one, or creating a new config from the selected
|
|
// one but with keys kept, or reusing the gateway configuration.
|
|
let available_gateways = current_gateways_wasm(
|
|
nym_apis,
|
|
Some(user_agent),
|
|
min_performance,
|
|
ignore_epoch_roles,
|
|
)
|
|
.await?;
|
|
|
|
// since we're registering with a brand new gateway,
|
|
// make sure the list of available gateways doesn't overlap the list of known gateways
|
|
let available_gateways = available_gateways
|
|
.into_iter()
|
|
.filter(|g| !registered_gateways.contains(&g.identity()))
|
|
.collect::<Vec<_>>();
|
|
|
|
if available_gateways.is_empty() {
|
|
return Err(ClientCoreError::NoNewGatewaysAvailable.into());
|
|
}
|
|
|
|
let gateway_setup = GatewaySetup::New {
|
|
specification: selection_spec,
|
|
available_gateways,
|
|
};
|
|
|
|
let init_details = setup_gateway(gateway_setup, storage, storage).await?;
|
|
let gateway = init_details.gateway_id().to_base58_string();
|
|
set_active_gateway(storage, &gateway).await?;
|
|
|
|
console_log!("finished registration with gateway {gateway}");
|
|
Ok(())
|
|
}
|