From 1ad97adc7c3f0e623a3e21eff110823ddd2cbdba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Tue, 11 Apr 2023 16:17:35 +0300 Subject: [PATCH] Feature/sdk coconut (#3273) * Replace expect with error * Move PersistentStorage in separate file * Add in-memory cred manager * Make wasm and mobile build * Unify wasm and mobile cred storage * Network defaults has mainnet default * Add network_details to SDK * Move BandwidthController in its own crate * Move out credential into lib crate * Remove nyxd arg in credential binary * Use acquire cred in sdk * Add example file, in sandbox * Mobile lock file * Update changelog * Clearer builder methods and more documentation for them * Sign only amount, without denom * Toggle credentials mode on when enabled --- CHANGELOG.md | 2 + Cargo.lock | 50 ++++---- Cargo.toml | 2 +- clients/credential/Cargo.toml | 6 +- clients/credential/src/commands.rs | 96 ++------------- clients/credential/src/error.rs | 17 +-- clients/credential/src/main.rs | 39 +++--- clients/native/Cargo.toml | 2 +- clients/native/src/client/mod.rs | 10 +- clients/native/src/commands/init.rs | 3 +- clients/socks5/Cargo.toml | 2 +- clients/socks5/src/commands/init.rs | 3 +- clients/webassembly/Cargo.toml | 3 +- clients/webassembly/src/client/mod.rs | 8 +- common/bandwidth-controller/Cargo.toml | 24 ++++ .../bandwidth-controller/src/acquire/mod.rs | 89 ++++++++++++++ .../src/acquire}/state.rs | 15 ++- common/bandwidth-controller/src/error.rs | 41 +++++++ .../src/lib.rs} | 57 +++------ .../bandwidth-controller/src/wasm_mockups.rs | 17 +++ common/client-core/Cargo.toml | 2 + .../client-core/src/client/base_client/mod.rs | 24 ++-- common/client-core/src/client/mix_traffic.rs | 14 ++- common/client-core/src/init/helpers.rs | 7 +- common/client-core/src/init/mod.rs | 10 +- common/client-libs/gateway-client/Cargo.toml | 8 +- .../client-libs/gateway-client/src/client.rs | 27 ++--- .../client-libs/gateway-client/src/error.rs | 18 +-- common/client-libs/gateway-client/src/lib.rs | 3 - .../gateway-client/src/wasm_mockups.rs | 80 ------------- .../client-libs/validator-client/src/lib.rs | 2 +- common/credential-storage/Cargo.toml | 11 +- .../credential-storage/src/backends/memory.rs | 70 +++++++++++ common/credential-storage/src/backends/mod.rs | 6 + .../src/{coconut.rs => backends/sqlite.rs} | 10 +- .../src/ephemeral_storage.rs | 67 +++++++++++ common/credential-storage/src/erc20.rs | 20 ---- common/credential-storage/src/error.rs | 2 + common/credential-storage/src/lib.rs | 113 +++--------------- common/credential-storage/src/models.rs | 1 + .../src/persistent_storage.rs | 99 +++++++++++++++ common/credential-storage/src/storage.rs | 2 +- common/mobile-storage/src/lib.rs | 67 ----------- common/network-defaults/src/lib.rs | 42 ++++--- common/socks5-client-core/Cargo.toml | 6 +- common/socks5-client-core/src/lib.rs | 38 +++--- nym-api/Cargo.toml | 1 + nym-api/src/network_monitor/mod.rs | 6 +- .../monitor/gateway_clients_cache.rs | 15 ++- nym-api/src/network_monitor/monitor/sender.rs | 8 +- nym-connect/desktop/Cargo.lock | 33 +++-- nym-connect/desktop/src-tauri/Cargo.toml | 1 + .../desktop/src-tauri/src/config/mod.rs | 18 +-- nym-connect/mobile/src-tauri/Cargo.lock | 62 +++++++++- nym-connect/mobile/src-tauri/Cargo.toml | 3 +- .../mobile/src-tauri/src/config/mod.rs | 3 +- sdk/rust/nym-sdk/Cargo.toml | 6 +- sdk/rust/nym-sdk/examples/bandwidth.rs | 42 +++++++ .../manually_handle_keys_and_config.rs | 8 +- sdk/rust/nym-sdk/src/bandwidth.rs | 45 +++++++ sdk/rust/nym-sdk/src/bandwidth/client.rs | 67 +++++++++++ sdk/rust/nym-sdk/src/error.rs | 24 ++++ sdk/rust/nym-sdk/src/lib.rs | 1 + sdk/rust/nym-sdk/src/mixnet.rs | 1 + sdk/rust/nym-sdk/src/mixnet/client.rs | 111 +++++++++++++---- sdk/rust/nym-sdk/src/mixnet/config.rs | 33 ++--- .../network-requester/Cargo.toml | 1 + .../network-requester/src/cli/init.rs | 3 +- .../network-requester/src/core.rs | 13 +- 69 files changed, 1065 insertions(+), 675 deletions(-) create mode 100644 common/bandwidth-controller/Cargo.toml create mode 100644 common/bandwidth-controller/src/acquire/mod.rs rename {clients/credential/src => common/bandwidth-controller/src/acquire}/state.rs (68%) create mode 100644 common/bandwidth-controller/src/error.rs rename common/{client-libs/gateway-client/src/bandwidth.rs => bandwidth-controller/src/lib.rs} (70%) create mode 100644 common/bandwidth-controller/src/wasm_mockups.rs delete mode 100644 common/client-libs/gateway-client/src/wasm_mockups.rs create mode 100644 common/credential-storage/src/backends/memory.rs create mode 100644 common/credential-storage/src/backends/mod.rs rename common/credential-storage/src/{coconut.rs => backends/sqlite.rs} (87%) create mode 100644 common/credential-storage/src/ephemeral_storage.rs delete mode 100644 common/credential-storage/src/erc20.rs create mode 100644 common/credential-storage/src/persistent_storage.rs delete mode 100644 common/mobile-storage/src/lib.rs create mode 100644 sdk/rust/nym-sdk/examples/bandwidth.rs create mode 100644 sdk/rust/nym-sdk/src/bandwidth.rs create mode 100644 sdk/rust/nym-sdk/src/bandwidth/client.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4884200983..b0e4ffb59c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - nym-network-statistics properly handles signals ([#3209]) - add socks5 support for Rust SDK ([#3226], [#3255]) +- add coconut bandwidth credential support for Rust SDK ([#3273]) [#3209]: https://github.com/nymtech/nym/issues/3209 [#3226]: https://github.com/nymtech/nym/pull/3226 [#3255]: https://github.com/nymtech/nym/pull/3255 +[#3273]: https://github.com/nymtech/nym/pull/3273 ## [v1.1.13] (2023-03-15) diff --git a/Cargo.lock b/Cargo.lock index 872506e068..ee7de90b0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3031,6 +3031,7 @@ dependencies = [ "lazy_static", "log", "nym-api-requests", + "nym-bandwidth-controller", "nym-bin-common", "nym-coconut", "nym-coconut-bandwidth-contract-common", @@ -3089,6 +3090,22 @@ dependencies = [ "ts-rs", ] +[[package]] +name = "nym-bandwidth-controller" +version = "0.1.0" +dependencies = [ + "bip39", + "nym-coconut-interface", + "nym-credential-storage", + "nym-credentials", + "nym-crypto", + "nym-network-defaults", + "nym-validator-client", + "rand 0.7.3", + "thiserror", + "url", +] + [[package]] name = "nym-bin-common" version = "0.3.0" @@ -3193,6 +3210,7 @@ dependencies = [ "futures", "lazy_static", "log", + "nym-bandwidth-controller", "nym-bin-common", "nym-client-core", "nym-client-websocket-requests", @@ -3201,7 +3219,6 @@ dependencies = [ "nym-credential-storage", "nym-credentials", "nym-crypto", - "nym-gateway-client", "nym-gateway-requests", "nym-network-defaults", "nym-pemstore", @@ -3231,7 +3248,9 @@ dependencies = [ "gloo-timers", "humantime-serde", "log", + "nym-bandwidth-controller", "nym-config", + "nym-credential-storage", "nym-crypto", "nym-gateway-client", "nym-gateway-requests", @@ -3354,23 +3373,19 @@ dependencies = [ name = "nym-credential-client" version = "0.1.0" dependencies = [ - "bip39", "clap 4.1.11", "log", + "nym-bandwidth-controller", "nym-bin-common", - "nym-coconut-interface", "nym-config", "nym-credential-storage", "nym-credentials", - "nym-crypto", "nym-network-defaults", "nym-pemstore", "nym-validator-client", - "rand 0.7.3", "serde", "thiserror", "tokio", - "url", ] [[package]] @@ -3507,16 +3522,14 @@ dependencies = [ name = "nym-gateway-client" version = "0.1.0" dependencies = [ - "async-trait", "futures", "getrandom 0.2.8", "log", + "nym-bandwidth-controller", "nym-coconut-interface", "nym-credential-storage", - "nym-credentials", "nym-crypto", "nym-gateway-requests", - "nym-mobile-storage", "nym-network-defaults", "nym-pemstore", "nym-sphinx", @@ -3679,14 +3692,6 @@ dependencies = [ "url", ] -[[package]] -name = "nym-mobile-storage" -version = "0.1.0" -dependencies = [ - "async-trait", - "thiserror", -] - [[package]] name = "nym-multisig-contract-common" version = "0.1.0" @@ -3731,6 +3736,7 @@ dependencies = [ "nym-client-core", "nym-client-websocket-requests", "nym-config", + "nym-credential-storage", "nym-crypto", "nym-network-defaults", "nym-ordered-buffer", @@ -3819,12 +3825,16 @@ dependencies = [ name = "nym-sdk" version = "0.1.0" dependencies = [ + "bip39", + "dotenvy", "futures", "log", + "nym-bandwidth-controller", "nym-bin-common", "nym-client-core", + "nym-credential-storage", + "nym-credentials", "nym-crypto", - "nym-gateway-client", "nym-gateway-requests", "nym-network-defaults", "nym-socks5-client-core", @@ -3877,10 +3887,10 @@ dependencies = [ "nym-client-core", "nym-coconut-interface", "nym-config", + "nym-credential-storage", "nym-credentials", "nym-crypto", "nym-gateway-requests", - "nym-mobile-storage", "nym-network-defaults", "nym-ordered-buffer", "nym-pemstore", @@ -3903,10 +3913,10 @@ dependencies = [ "dirs", "futures", "log", + "nym-bandwidth-controller", "nym-client-core", "nym-config", "nym-credential-storage", - "nym-gateway-client", "nym-network-defaults", "nym-service-providers-common", "nym-socks5-proxy-helpers", diff --git a/Cargo.toml b/Cargo.toml index 5a3b065ee8..2c3d11eb09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "clients/native/websocket-requests", "clients/socks5", "common/async-file-watcher", + "common/bandwidth-controller", "common/bin-common", "common/client-core", "common/client-libs/gateway-client", @@ -38,7 +39,6 @@ members = [ "common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/service-provider-directory", "common/cosmwasm-smart-contracts/vesting-contract", - "common/mobile-storage", "common/credential-storage", "common/credentials", "common/crypto", diff --git a/clients/credential/Cargo.toml b/clients/credential/Cargo.toml index 1ac02361af..3a47f059b0 100644 --- a/clients/credential/Cargo.toml +++ b/clients/credential/Cargo.toml @@ -6,20 +6,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bip39 = { workspace = true } clap = { version = "4.0", features = ["cargo", "derive"] } log = "0.4" -rand = "0.7.3" serde = { workspace = true, features = ["derive"] } thiserror = "1.0" -url = "2.2" tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime -nym-coconut-interface = { path = "../../common/coconut-interface" } +nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-config = { path = "../../common/config" } nym-credentials = { path = "../../common/credentials" } nym-credential-storage = { path = "../../common/credential-storage" } -nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] } nym-bin-common = { path = "../../common/bin-common"} nym-network-defaults = { path = "../../common/network-defaults" } nym-pemstore = { path = "../../common/pemstore" } diff --git a/clients/credential/src/commands.rs b/clients/credential/src/commands.rs index c47c6232fa..64993d2d7d 100644 --- a/clients/credential/src/commands.rs +++ b/clients/credential/src/commands.rs @@ -3,24 +3,13 @@ use clap::{ArgGroup, Args, Subcommand}; use log::*; +use nym_bandwidth_controller::acquire::state::State; use nym_bin_common::completions::ArgShell; -use nym_coconut_interface::{Base58, Parameters}; -use nym_credential_storage::storage::Storage; -use nym_credential_storage::PersistentStorage; -use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; -use nym_credentials::coconut::utils::obtain_aggregate_signature; -use nym_crypto::asymmetric::{encryption, identity}; -use nym_network_defaults::VOUCHER_INFO; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_validator_client::nyxd::traits::DkgQueryClient; -use nym_validator_client::nyxd::tx::Hash; -use nym_validator_client::CoconutApiClient; -use rand::rngs::OsRng; -use std::str::FromStr; -use crate::client::Client; -use crate::error::{CredentialClientError, Result}; +use crate::error::Result; use crate::recovery_storage::RecoveryStorage; -use crate::state::{KeyPair, State}; #[derive(Subcommand)] pub(crate) enum Command { @@ -45,10 +34,6 @@ pub(crate) struct Run { #[clap(long)] pub(crate) client_home_directory: std::path::PathBuf, - /// The nyxd URL that should be used - #[clap(long)] - pub(crate) nyxd_url: String, - /// A mnemonic for the account that buys the credential #[clap(long)] pub(crate) mnemonic: String, @@ -67,81 +52,16 @@ pub(crate) struct Run { pub(crate) recovery_mode: bool, } -pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result { - let mut rng = OsRng; - let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); - let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); - let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); - - let client = Client::new(nyxd_url, mnemonic); - let tx_hash = client - .deposit( - amount, - signing_keypair.public_key.clone(), - encryption_keypair.public_key.clone(), - None, - ) - .await?; - - let voucher = BandwidthVoucher::new( - ¶ms, - amount.to_string(), - VOUCHER_INFO.to_string(), - Hash::from_str(&tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?, - identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?, - encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?, - ); - - let state = State { voucher, params }; - - Ok(state) -} - -pub(crate) async fn get_credential( - state: &State, - client: &C, - shared_storage: PersistentStorage, -) -> Result<()> { - let epoch_id = client.get_current_epoch().await?.epoch_id; - let threshold = client - .get_current_epoch_threshold() - .await? - .ok_or(CredentialClientError::NoThreshold)?; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?; - - let signature = obtain_aggregate_signature( - &state.params, - &state.voucher, - &coconut_api_clients, - threshold, - ) - .await?; - info!("Signature: {:?}", signature.to_bs58()); - shared_storage - .insert_coconut_credential( - state.voucher.get_voucher_value(), - VOUCHER_INFO.to_string(), - state.voucher.get_private_attributes()[0].to_bs58(), - state.voucher.get_private_attributes()[1].to_bs58(), - signature.to_bs58(), - epoch_id.to_string(), - ) - .await?; - - Ok(()) -} - pub(crate) async fn recover_credentials( client: &C, recovery_storage: &RecoveryStorage, - shared_storage: PersistentStorage, + shared_storage: &PersistentStorage, ) -> Result<()> { for voucher in recovery_storage.unconsumed_vouchers()? { - let state = State { - voucher, - params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(), - }; - if let Err(e) = get_credential(&state, client, shared_storage.clone()).await { + let state = State::new(voucher); + if let Err(e) = + nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await + { error!( "Could not recover deposit {} due to {:?}, try again later", state.voucher.tx_hash(), diff --git a/clients/credential/src/error.rs b/clients/credential/src/error.rs index 9f5518cffe..1c6ac1521b 100644 --- a/clients/credential/src/error.rs +++ b/clients/credential/src/error.rs @@ -6,8 +6,6 @@ use thiserror::Error; use nym_credential_storage::error::StorageError; use nym_credentials::error::Error as CredentialError; -use nym_crypto::asymmetric::encryption::KeyRecoveryError; -use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::ValidatorClientError; @@ -18,6 +16,9 @@ pub enum CredentialClientError { #[error("IO error: {0}")] IOError(#[from] std::io::Error), + #[error("Bandwidth controller error: {0}")] + BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError), + #[error("Nyxd error: {0}")] Nyxd(#[from] NyxdError), @@ -27,21 +28,9 @@ pub enum CredentialClientError { #[error("Credential error: {0}")] Credential(#[from] CredentialError), - #[error("The tx hash provided is not valid")] - InvalidTxHash, - - #[error("Could not parse Ed25519 data")] - Ed25519ParseError(#[from] Ed25519RecoveryError), - - #[error("Could not parse X25519 data")] - X25519ParseError(#[from] KeyRecoveryError), - #[error("Could not use shared storage")] SharedStorageError(#[from] StorageError), #[error("Could not get system time")] SysTimeError(#[from] SystemTimeError), - - #[error("Threshold not set yet")] - NoThreshold, } diff --git a/clients/credential/src/main.rs b/clients/credential/src/main.rs index 1a613138f6..3fb0adcad8 100644 --- a/clients/credential/src/main.rs +++ b/clients/credential/src/main.rs @@ -1,11 +1,9 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -mod client; mod commands; mod error; mod recovery_storage; -mod state; use commands::*; use error::Result; @@ -19,7 +17,7 @@ use std::time::{Duration, SystemTime}; use clap::{CommandFactory, Parser}; use nym_bin_common::logging::setup_logging; use nym_validator_client::nyxd::traits::DkgQueryClient; -use nym_validator_client::nyxd::CosmWasmClient; +use nym_validator_client::nyxd::{Coin, CosmWasmClient}; use nym_validator_client::Config; const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute @@ -35,7 +33,7 @@ struct Cli { pub(crate) command: Command, } -async fn block_until_coconut_is_available( +async fn block_until_coconut_is_available( client: &nym_validator_client::Client, ) -> Result<()> { loop { @@ -77,21 +75,34 @@ async fn main() -> Result<()> { .client_home_directory .join(DATA_DIR) .join(CRED_DB_FILE_NAME); - let shared_storage = nym_credential_storage::initialise_storage(db_path).await; + let shared_storage = + nym_credential_storage::initialise_persistent_storage(db_path).await; let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?; let network_details = NymNetworkDetails::new_from_env(); - let config = Config::try_from_nym_network_details(&network_details)?; - let client = nym_validator_client::Client::new_query(config)?; + let config = Config::try_from_nym_network_details(&network_details).expect( + "failed to construct valid validator client config with the provided network", + ); + let amount = Coin::new( + r.amount as u128, + network_details.chain_details.mix_denom.base, + ); + let client = + nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?; block_until_coconut_is_available(&client).await?; info!("Starting depositing funds, don't kill the process"); if !r.recovery_mode { - let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?; - if get_credential(&state, &client.nyxd, shared_storage) - .await - .is_err() + let state = + nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?; + if nym_bandwidth_controller::acquire::get_credential( + &state, + &client, + &shared_storage, + ) + .await + .is_err() { warn!("Failed to obtain credential. Dumping recovery data.",); match recovery_storage.insert_voucher(&state.voucher) { @@ -104,11 +115,11 @@ async fn main() -> Result<()> { } } } else { - recover_credentials(&client.nyxd, &recovery_storage, shared_storage).await?; + recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?; } } - Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name), - Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name), + Command::Completions(c) => c.generate(&mut Cli::command(), bin_name), + Command::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), } Ok(()) diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 05e3fecefd..013b8d9daf 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -34,6 +34,7 @@ tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } tokio-tungstenite = "0.14" # websocket ## internal +nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] } nym-coconut-interface = { path = "../../common/coconut-interface" } @@ -41,7 +42,6 @@ nym-config = { path = "../../common/config" } nym-credential-storage = { path = "../../common/credential-storage" } nym-credentials = { path = "../../common/credentials" } nym-crypto = { path = "../../common/crypto" } -nym-gateway-client = { path = "../../common/client-libs/gateway-client" } nym-gateway-requests = { path = "../../gateway/gateway-requests" } nym-network-defaults = { path = "../../common/network-defaults" } nym-sphinx = { path = "../../common/nymsphinx" } diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index 71cf20a324..8672745329 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -6,6 +6,7 @@ use crate::error::ClientError; use crate::websocket; use futures::channel::mpsc; use log::*; +use nym_bandwidth_controller::BandwidthController; use nym_client_core::client::base_client::{ non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState, }; @@ -14,7 +15,6 @@ use nym_client_core::client::received_buffer::{ ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver, }; use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; -use nym_gateway_client::bandwidth::BandwidthController; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_task::connections::TransmissionLane; use nym_task::TaskManager; @@ -23,6 +23,7 @@ use std::error::Error; use tokio::sync::watch::error::SendError; pub use nym_client_core::client::key_manager::KeyManager; +use nym_credential_storage::persistent_storage::PersistentStorage; pub use nym_sphinx::addressing::clients::Recipient; pub use nym_sphinx::receiver::ReconstructedMessage; use nym_validator_client::Client; @@ -58,7 +59,7 @@ impl SocketClient { async fn create_bandwidth_controller( config: &Config, - ) -> BandwidthController> { + ) -> BandwidthController, PersistentStorage> { let details = nym_network_defaults::NymNetworkDetails::new_from_env(); let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details) @@ -78,7 +79,10 @@ impl SocketClient { let client = nym_validator_client::Client::new_query(client_config) .expect("Could not construct query client"); BandwidthController::new( - nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await, + nym_credential_storage::initialise_persistent_storage( + config.get_base().get_database_path(), + ) + .await, client, ) } diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index f4e1091e9e..5622f26c3c 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -10,6 +10,7 @@ use crate::{ use clap::Args; use nym_bin_common::output_format::OutputFormat; use nym_config::NymConfig; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::identity; use nym_sphinx::addressing::clients::Recipient; use serde::Serialize; @@ -151,7 +152,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> { // 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 gateway = nym_client_core::init::setup_gateway_from_config::( + let gateway = nym_client_core::init::setup_gateway_from_config::( register_gateway, user_chosen_gateway_id, config.get_base(), diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 72b6389812..d5bc74d027 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -23,10 +23,10 @@ nym-bin-common = { path = "../../common/bin-common", features = ["output_format" nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] } nym-coconut-interface = { path = "../../common/coconut-interface" } nym-config = { path = "../../common/config" } -nym-mobile-storage = { path = "../../common/mobile-storage", optional = true } nym-credentials = { path = "../../common/credentials" } nym-crypto = { path = "../../common/crypto" } nym-gateway-requests = { path = "../../gateway/gateway-requests" } +nym-credential-storage = { path = "../../common/credential-storage" } nym-network-defaults = { path = "../../common/network-defaults" } nym-sphinx = { path = "../../common/nymsphinx" } nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" } diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index f83b8be352..5a5ec6b05f 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -9,6 +9,7 @@ use crate::{ use clap::Args; use nym_bin_common::output_format::OutputFormat; use nym_config::NymConfig; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::identity; use nym_socks5_client_core::config::Config; use nym_sphinx::addressing::clients::Recipient; @@ -157,7 +158,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> { // 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 gateway = nym_client_core::init::setup_gateway_from_config::( + let gateway = nym_client_core::init::setup_gateway_from_config::( register_gateway, user_chosen_gateway_id, config.get_base(), diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index abcc7e0757..69e9294068 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -31,11 +31,12 @@ wasm-bindgen-futures = "0.4" # internal nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] } +nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-coconut-interface = { path = "../../common/coconut-interface" } nym-credentials = { path = "../../common/credentials" } +nym-credential-storage = { path = "../../common/credential-storage" } nym-crypto = { path = "../../common/crypto" } nym-sphinx = { path = "../../common/nymsphinx" } -nym-gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] } nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false } wasm-utils = { path = "../../common/wasm-utils" } nym-task = { path = "../../common/task" } diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 41279ea947..26eae97c2a 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -5,13 +5,14 @@ use self::config::Config; use crate::client::helpers::InputSender; use crate::client::response_pusher::ResponsePusher; use js_sys::Promise; +use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; +use nym_bandwidth_controller::BandwidthController; use nym_client_core::client::base_client::{ BaseClientBuilder, ClientInput, ClientOutput, CredentialsToggle, }; use nym_client_core::client::replies::reply_storage::browser_backend; use nym_client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager}; -use nym_gateway_client::bandwidth::BandwidthController; -use nym_gateway_client::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; +use nym_credential_storage::ephemeral_storage::EphemeralStorage; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_task::connections::TransmissionLane; @@ -48,7 +49,8 @@ pub struct NymClientBuilder { on_message: js_sys::Function, // unimplemented: - bandwidth_controller: Option>>, + bandwidth_controller: + Option, EphemeralStorage>>, disabled_credentials: bool, } diff --git a/common/bandwidth-controller/Cargo.toml b/common/bandwidth-controller/Cargo.toml new file mode 100644 index 0000000000..40fe146d81 --- /dev/null +++ b/common/bandwidth-controller/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "nym-bandwidth-controller" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bip39 = { workspace = true } +rand = "0.7.3" +thiserror = "1.0" +url = "2.2" + +nym-coconut-interface = { path = "../coconut-interface" } +nym-credential-storage = { path = "../credential-storage" } +nym-credentials = { path = "../credentials" } +nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] } +nym-network-defaults = { path = "../network-defaults" } +nym-validator-client = { path = "../client-libs/validator-client", default-features = false } + + +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] +path = "../client-libs/validator-client" +features = ["nyxd-client"] diff --git a/common/bandwidth-controller/src/acquire/mod.rs b/common/bandwidth-controller/src/acquire/mod.rs new file mode 100644 index 0000000000..b73e46876e --- /dev/null +++ b/common/bandwidth-controller/src/acquire/mod.rs @@ -0,0 +1,89 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::BandwidthControllerError; +use nym_coconut_interface::{Base58, Parameters}; +use nym_credential_storage::storage::Storage; +use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; +use nym_credentials::coconut::utils::obtain_aggregate_signature; +use nym_crypto::asymmetric::{encryption, identity}; +use nym_network_defaults::VOUCHER_INFO; +use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient; +use nym_validator_client::nyxd::traits::DkgQueryClient; +use nym_validator_client::nyxd::tx::Hash; +use nym_validator_client::nyxd::Coin; +use nym_validator_client::CoconutApiClient; +use rand::rngs::OsRng; +use state::{KeyPair, State}; +use std::str::FromStr; + +pub mod state; + +pub async fn deposit(client: &C, amount: Coin) -> Result +where + C: CoconutBandwidthSigningClient, +{ + let mut rng = OsRng; + let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); + let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); + let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); + let voucher_value = amount.amount.to_string(); + + let tx_hash = client + .deposit( + amount, + String::from(VOUCHER_INFO), + signing_keypair.public_key.clone(), + encryption_keypair.public_key.clone(), + None, + ) + .await? + .transaction_hash + .to_string(); + + let voucher = BandwidthVoucher::new( + ¶ms, + voucher_value, + VOUCHER_INFO.to_string(), + Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?, + identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?, + encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?, + ); + + let state = State { voucher, params }; + + Ok(state) +} + +pub async fn get_credential( + state: &State, + client: &C, + storage: &St, +) -> Result<(), BandwidthControllerError> { + let epoch_id = client.get_current_epoch().await?.epoch_id; + let threshold = client + .get_current_epoch_threshold() + .await? + .ok_or(BandwidthControllerError::NoThreshold)?; + let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?; + + let signature = obtain_aggregate_signature( + &state.params, + &state.voucher, + &coconut_api_clients, + threshold, + ) + .await?; + storage + .insert_coconut_credential( + state.voucher.get_voucher_value(), + VOUCHER_INFO.to_string(), + state.voucher.get_private_attributes()[0].to_bs58(), + state.voucher.get_private_attributes()[1].to_bs58(), + signature.to_bs58(), + epoch_id.to_string(), + ) + .await?; + + Ok(()) +} diff --git a/clients/credential/src/state.rs b/common/bandwidth-controller/src/acquire/state.rs similarity index 68% rename from clients/credential/src/state.rs rename to common/bandwidth-controller/src/acquire/state.rs index a4894a8ced..de77992962 100644 --- a/clients/credential/src/state.rs +++ b/common/bandwidth-controller/src/acquire/state.rs @@ -1,8 +1,8 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use nym_coconut_interface::Parameters; -use nym_credentials::coconut::bandwidth::BandwidthVoucher; +use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; use nym_crypto::asymmetric::{encryption, identity}; @@ -29,7 +29,16 @@ impl From for KeyPair { } } -pub(crate) struct State { +pub struct State { pub voucher: BandwidthVoucher, pub params: Parameters, } + +impl State { + pub fn new(voucher: BandwidthVoucher) -> Self { + State { + voucher, + params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(), + } + } +} diff --git a/common/bandwidth-controller/src/error.rs b/common/bandwidth-controller/src/error.rs new file mode 100644 index 0000000000..ecc9468930 --- /dev/null +++ b/common/bandwidth-controller/src/error.rs @@ -0,0 +1,41 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_coconut_interface::CoconutError; +use nym_credential_storage::error::StorageError; +use nym_credentials::error::Error as CredentialsError; +use nym_crypto::asymmetric::encryption::KeyRecoveryError; +use nym_crypto::asymmetric::identity::Ed25519RecoveryError; +use nym_validator_client::error::ValidatorClientError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum BandwidthControllerError { + #[cfg(not(target_arch = "wasm32"))] + #[error("Nyxd error: {0}")] + Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError), + + #[error("There was a credential storage error - {0}")] + CredentialStorageError(#[from] StorageError), + + #[error("Coconut error - {0}")] + CoconutError(#[from] CoconutError), + + #[error("Validator client error - {0}")] + ValidatorError(#[from] ValidatorClientError), + + #[error("Credential error - {0}")] + CredentialError(#[from] CredentialsError), + + #[error("Could not parse Ed25519 data")] + Ed25519ParseError(#[from] Ed25519RecoveryError), + + #[error("Could not parse X25519 data")] + X25519ParseError(#[from] KeyRecoveryError), + + #[error("The tx hash provided is not valid")] + InvalidTxHash, + + #[error("Threshold not set yet")] + NoThreshold, +} diff --git a/common/client-libs/gateway-client/src/bandwidth.rs b/common/bandwidth-controller/src/lib.rs similarity index 70% rename from common/client-libs/gateway-client/src/bandwidth.rs rename to common/bandwidth-controller/src/lib.rs index 500c3063b8..f004dbf7aa 100644 --- a/common/client-libs/gateway-client/src/bandwidth.rs +++ b/common/bandwidth-controller/src/lib.rs @@ -1,28 +1,10 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::error::GatewayClientError; +use crate::error::BandwidthControllerError; -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::Storage; -#[cfg(not(target_arch = "wasm32"))] -#[cfg(not(target_os = "android"))] -use nym_credential_storage::storage::Storage; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(target_os = "android")] -use mobile_storage::Storage; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(target_os = "android")] -use mobile_storage::StorageError; - -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::StorageError; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(not(target_os = "android"))] use nym_credential_storage::error::StorageError; +use nym_credential_storage::storage::Storage; use std::str::FromStr; use { @@ -35,38 +17,32 @@ use { #[cfg(not(target_arch = "wasm32"))] use nym_validator_client::nyxd::traits::DkgQueryClient; -// TODO: make it nicer for wasm (I don't want to touch it for this experiment) -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::PersistentStorage; - #[cfg(target_arch = "wasm32")] use crate::wasm_mockups::DkgQueryClient; #[cfg(not(target_arch = "wasm32"))] -#[cfg(not(target_os = "android"))] -use nym_credential_storage::PersistentStorage; +pub mod acquire; +pub mod error; +#[cfg(target_arch = "wasm32")] +pub mod wasm_mockups; -#[cfg(not(target_arch = "wasm32"))] -#[cfg(target_os = "android")] -use mobile_storage::PersistentStorage; - -#[allow(dead_code)] -pub struct BandwidthController { +pub struct BandwidthController { storage: St, client: C, } -impl BandwidthController -where - St: Storage + 'static, -{ +impl BandwidthController { pub fn new(storage: St, client: C) -> Self { BandwidthController { storage, client } } + pub fn storage(&self) -> &St { + &self.storage + } + pub async fn prepare_coconut_credential( &self, - ) -> Result<(nym_coconut_interface::Credential, i64), GatewayClientError> + ) -> Result<(nym_coconut_interface::Credential, i64), BandwidthControllerError> where C: DkgQueryClient + Sync + Send, { @@ -86,8 +62,7 @@ where #[cfg(not(target_arch = "wasm32"))] let coconut_api_clients = nym_validator_client::CoconutApiClient::all_coconut_api_clients(&self.client, epoch_id) - .await - .expect("Could not query api clients"); + .await?; #[cfg(target_arch = "wasm32")] let coconut_api_clients = vec![]; let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?; @@ -107,7 +82,7 @@ where )) } - pub async fn consume_credential(&self, id: i64) -> Result<(), GatewayClientError> { + pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError> { // JS: shouldn't we send some contract/validator/gateway message here to actually, you know, // consume it? Ok(self.storage.consume_coconut_credential(id).await?) diff --git a/common/bandwidth-controller/src/wasm_mockups.rs b/common/bandwidth-controller/src/wasm_mockups.rs new file mode 100644 index 0000000000..be7ee06a79 --- /dev/null +++ b/common/bandwidth-controller/src/wasm_mockups.rs @@ -0,0 +1,17 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::marker::PhantomData; + +pub struct DirectSigningNyxdClient {} + +pub trait DkgQueryClient {} + +// impl CosmWasmClient for DirectSigningNyxdClient {} + +#[derive(Clone)] +pub struct Client { + _phantom: PhantomData, +} + +impl DkgQueryClient for Client {} diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index a37a24a8be..fbb5c38cde 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -25,6 +25,7 @@ tokio = { version = "1.24.1", features = ["macros"]} time = "0.3.17" # internal +nym-bandwidth-controller = { path = "../bandwidth-controller" } nym-config = { path = "../config" } nym-crypto = { path = "../crypto" } nym-gateway-client = { path = "../client-libs/gateway-client" } @@ -36,6 +37,7 @@ nym-pemstore = { path = "../pemstore" } nym-topology = { path = "../topology" } nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-task = { path = "../task" } +nym-credential-storage = { path = "../credential-storage" } [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] path = "../client-libs/validator-client" diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 5480e3bf11..21826dce48 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -25,8 +25,8 @@ use crate::error::ClientCoreError; use crate::spawn_future; use futures::channel::mpsc; use log::{debug, info}; +use nym_bandwidth_controller::BandwidthController; use nym_crypto::asymmetric::{encryption, identity}; -use nym_gateway_client::bandwidth::BandwidthController; use nym_gateway_client::{ AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver, MixnetMessageSender, @@ -43,11 +43,12 @@ use std::time::Duration; use tap::TapFallible; use url::Url; +use nym_credential_storage::storage::Storage; #[cfg(not(target_arch = "wasm32"))] use nym_validator_client::nyxd::traits::DkgQueryClient; #[cfg(target_arch = "wasm32")] -use nym_gateway_client::wasm_mockups::DkgQueryClient; +use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] pub mod non_wasm_helpers; @@ -151,7 +152,7 @@ impl From for CredentialsToggle { } } -pub struct BaseClientBuilder<'a, B, C> { +pub struct BaseClientBuilder<'a, B, C, St: Storage> { // due to wasm limitations I had to split it like this : ( gateway_config: &'a GatewayEndpointConfig, debug_config: &'a DebugConfig, @@ -160,21 +161,22 @@ pub struct BaseClientBuilder<'a, B, C> { reply_storage_backend: B, custom_topology_provider: Option>, - bandwidth_controller: Option>, + bandwidth_controller: Option>, key_manager: KeyManager, } -impl<'a, B, C> BaseClientBuilder<'a, B, C> +impl<'a, B, C, St> BaseClientBuilder<'a, B, C, St> where B: ReplyStorageBackend + Send + Sync + 'static, C: DkgQueryClient + Sync + Send + 'static, + St: Storage + 'static, { pub fn new_from_base_config( base_config: &'a Config, key_manager: KeyManager, - bandwidth_controller: Option>, + bandwidth_controller: Option>, reply_storage_backend: B, - ) -> BaseClientBuilder<'a, B, C> { + ) -> BaseClientBuilder<'a, B, C, St> { BaseClientBuilder { gateway_config: base_config.get_gateway_endpoint_config(), debug_config: base_config.get_debug_config(), @@ -191,11 +193,11 @@ where gateway_config: &'a GatewayEndpointConfig, debug_config: &'a DebugConfig, key_manager: KeyManager, - bandwidth_controller: Option>, + bandwidth_controller: Option>, reply_storage_backend: B, credentials_toggle: CredentialsToggle, nym_api_endpoints: Vec, - ) -> BaseClientBuilder<'a, B, C> { + ) -> BaseClientBuilder<'a, B, C, St> { BaseClientBuilder { gateway_config, debug_config, @@ -306,7 +308,7 @@ where mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, shutdown: TaskClient, - ) -> Result, ClientCoreError> { + ) -> Result, ClientCoreError> { let gateway_id = self.gateway_config.gateway_id.clone(); if gateway_id.is_empty() { return Err(ClientCoreError::GatewayIdUnknown); @@ -403,7 +405,7 @@ where // over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for // requests? fn start_mix_traffic_controller( - gateway_client: GatewayClient, + gateway_client: GatewayClient, shutdown: TaskClient, ) -> BatchMixMessageSender { info!("Starting mix traffic controller..."); diff --git a/common/client-core/src/client/mix_traffic.rs b/common/client-core/src/client/mix_traffic.rs index d2c305bbaa..bb67314e6a 100644 --- a/common/client-core/src/client/mix_traffic.rs +++ b/common/client-core/src/client/mix_traffic.rs @@ -6,11 +6,12 @@ use log::*; use nym_gateway_client::GatewayClient; use nym_sphinx::forwarding::packet::MixPacket; +use nym_credential_storage::storage::Storage; #[cfg(not(target_arch = "wasm32"))] use nym_validator_client::nyxd::traits::DkgQueryClient; #[cfg(target_arch = "wasm32")] -use nym_gateway_client::wasm_mockups::DkgQueryClient; +use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender>; pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver>; @@ -19,10 +20,10 @@ pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver>; pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32; const MAX_FAILURE_COUNT: usize = 100; -pub struct MixTrafficController { +pub struct MixTrafficController { // TODO: most likely to be replaced by some higher level construct as // later on gateway_client will need to be accessible by other entities - gateway_client: GatewayClient, + gateway_client: GatewayClient, mix_rx: BatchMixMessageReceiver, // TODO: this is temporary work-around. @@ -30,13 +31,14 @@ pub struct MixTrafficController { consecutive_gateway_failure_count: usize, } -impl MixTrafficController +impl MixTrafficController where C: DkgQueryClient + Sync + Send + 'static, + St: Storage + 'static, { pub fn new( - gateway_client: GatewayClient, - ) -> (MixTrafficController, BatchMixMessageSender) { + gateway_client: GatewayClient, + ) -> (MixTrafficController, BatchMixMessageSender) { let (sphinx_message_sender, sphinx_message_receiver) = tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE); ( diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 66bcc94fb6..5471aed64d 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -32,9 +32,10 @@ use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; #[cfg(not(target_arch = "wasm32"))] type WsConn = WebSocketStream>; +use nym_credential_storage::storage::Storage; #[cfg(target_arch = "wasm32")] -use nym_gateway_client::wasm_mockups::DirectSigningNyxdClient; +use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient; #[cfg(target_arch = "wasm32")] use wasm_timer::Instant; #[cfg(target_arch = "wasm32")] @@ -223,12 +224,12 @@ pub(super) async fn query_gateway_details( } } -pub(super) async fn register_with_gateway( +pub(super) async fn register_with_gateway( gateway: &gateway::Node, our_identity: Arc, ) -> Result, ClientCoreError> { let timeout = Duration::from_millis(1500); - let mut gateway_client: GatewayClient = GatewayClient::new_init( + let mut gateway_client: GatewayClient = GatewayClient::new_init( gateway.clients_address(), gateway.identity_key, our_identity.clone(), diff --git a/common/client-core/src/init/mod.rs b/common/client-core/src/init/mod.rs index d418ba13b7..8f3f50d956 100644 --- a/common/client-core/src/init/mod.rs +++ b/common/client-core/src/init/mod.rs @@ -11,6 +11,7 @@ use serde::Serialize; use tap::TapFallible; use nym_config::NymConfig; +use nym_credential_storage::storage::Storage; use nym_crypto::asymmetric::{encryption, identity}; use url::Url; @@ -73,7 +74,7 @@ pub fn new_client_keys() -> KeyManager { /// Either pick one at random by querying the available gateways from the nym-api, or use the /// chosen one if it's among the available ones. /// The shared key is added to the supplied `KeyManager` and the endpoint details are returned. -pub async fn register_with_gateway( +pub async fn register_with_gateway( key_manager: &mut KeyManager, nym_api_endpoints: Vec, chosen_gateway_id: Option, @@ -87,7 +88,7 @@ pub async fn register_with_gateway( let our_identity = key_manager.identity_keypair(); // Establish connection, authenticate and generate keys for talking with the gateway - let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?; + let shared_keys = helpers::register_with_gateway::(&gateway, our_identity).await?; key_manager.insert_gateway_shared_key(shared_keys); Ok(gateway.into()) @@ -100,7 +101,7 @@ pub async fn register_with_gateway( /// b. Create a new gateway configuration but keep existing keys. This assumes that the caller /// knows what they are doing and that the keys match the requested gateway. /// c. Create a new gateway configuration with a newly registered gateway and keys. -pub async fn setup_gateway_from_config( +pub async fn setup_gateway_from_config( register_gateway: bool, user_chosen_gateway_id: Option, config: &Config, @@ -109,6 +110,7 @@ pub async fn setup_gateway_from_config( where C: NymConfig + ClientCoreConfigTrait, T: NymConfig, + St: Storage, { let id = config.get_id(); @@ -141,7 +143,7 @@ where // Establish connection, authenticate and generate keys for talking with the gateway eprintln!("Registering with new gateway"); - let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?; + let shared_keys = helpers::register_with_gateway::(&gateway, our_identity).await?; key_manager.insert_gateway_shared_key(shared_keys); // Write all keys to storage and just return the gateway endpoint config. It is assumed that we diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index 0eed0e25f3..20d5c958b0 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -14,12 +14,12 @@ log = { workspace = true } thiserror = "1.0" url = "2.2" rand = { version = "0.7.3", features = ["wasm-bindgen"] } -async-trait = { workspace = true } tokio = { version = "1.24.1", features = ["macros"] } # internal +nym-bandwidth-controller = { path = "../../bandwidth-controller" } nym-coconut-interface = { path = "../../coconut-interface" } -nym-credentials = { path = "../../credentials" } +nym-credential-storage = { path = "../../credential-storage" } nym-crypto = { path = "../../crypto" } nym-gateway-requests = { path = "../../../gateway/gateway-requests" } nym-network-defaults = { path = "../../network-defaults" } @@ -28,7 +28,6 @@ nym-pemstore = { path = "../../pemstore" } nym-validator-client = { path = "../validator-client" } nym-task = { path = "../../task" } serde = { workspace = true, features = ["derive"] } -nym-mobile-storage = { path = "../../mobile-storage" } [dependencies.tungstenite] @@ -47,9 +46,6 @@ features = ["net", "sync", "time"] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] version = "0.14" -[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-credential-storage] -path = "../../credential-storage" - # wasm-only dependencies [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] version = "0.2" diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index 2652303b03..5efb5a8119 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -1,7 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::bandwidth::BandwidthController; use crate::error::GatewayClientError; use crate::packet_router::PacketRouter; pub use crate::packet_router::{ @@ -11,6 +10,7 @@ use crate::socket_state::{PartiallyDelegated, SocketState}; use crate::{cleanup_socket_message, try_decrypt_binary_message}; use futures::{SinkExt, StreamExt}; use log::*; +use nym_bandwidth_controller::BandwidthController; use nym_coconut_interface::Credential; use nym_crypto::asymmetric::identity; use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; @@ -26,24 +26,14 @@ use std::sync::Arc; use std::time::Duration; use tungstenite::protocol::Message; +use nym_credential_storage::storage::Storage; #[cfg(not(target_arch = "wasm32"))] use nym_validator_client::nyxd::traits::DkgQueryClient; - #[cfg(not(target_arch = "wasm32"))] use tokio_tungstenite::connect_async; -#[cfg(not(target_arch = "wasm32"))] -#[cfg(not(target_os = "android"))] -use nym_credential_storage::PersistentStorage; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(target_os = "android")] -use mobile_storage::PersistentStorage; - #[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::DkgQueryClient; -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::PersistentStorage; +use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; #[cfg(target_arch = "wasm32")] use wasm_timer; #[cfg(target_arch = "wasm32")] @@ -52,7 +42,7 @@ use wasm_utils::websocket::JSWebsocket; const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10; const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5); -pub struct GatewayClient { +pub struct GatewayClient { authenticated: bool, disabled_credentials_mode: bool, bandwidth_remaining: i64, @@ -63,7 +53,7 @@ pub struct GatewayClient { connection: SocketState, packet_router: PacketRouter, response_timeout_duration: Duration, - bandwidth_controller: Option>, + bandwidth_controller: Option>, // reconnection related variables /// Specifies whether client should try to reconnect to gateway on connection failure. @@ -78,9 +68,10 @@ pub struct GatewayClient { shutdown: TaskClient, } -impl GatewayClient +impl GatewayClient where C: Sync + Send, + St: Storage, { // TODO: put it all in a Config struct #[allow(clippy::too_many_arguments)] @@ -92,7 +83,7 @@ where mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, response_timeout_duration: Duration, - bandwidth_controller: Option>, + bandwidth_controller: Option>, shutdown: TaskClient, ) -> Self { GatewayClient { @@ -146,7 +137,7 @@ where let shutdown = TaskClient::dummy(); let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone()); - GatewayClient:: { + GatewayClient:: { authenticated: false, disabled_credentials_mode: true, bandwidth_remaining: 0, diff --git a/common/client-libs/gateway-client/src/error.rs b/common/client-libs/gateway-client/src/error.rs index 07010c793f..64414bcf99 100644 --- a/common/client-libs/gateway-client/src/error.rs +++ b/common/client-libs/gateway-client/src/error.rs @@ -1,14 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::StorageError; -#[cfg(target_os = "android")] -#[cfg(not(target_arch = "wasm32"))] -use mobile_storage::StorageError; -#[cfg(not(target_os = "android"))] -#[cfg(not(target_arch = "wasm32"))] -use nym_credential_storage::error::StorageError; use nym_gateway_requests::registration::handshake::error::HandshakeError; use std::io; use thiserror::Error; @@ -27,12 +19,6 @@ pub enum GatewayClientError { #[error("There was a network error - {0}")] NetworkError(#[from] WsError), - #[error("There was a credential storage error - {0}")] - CredentialStorageError(#[from] StorageError), - - #[error("Coconut error - {0}")] - CoconutError(#[from] nym_coconut_interface::CoconutError), - // TODO: see if `JsValue` is a reasonable type for this #[cfg(target_arch = "wasm32")] #[error("There was a network error")] @@ -47,8 +33,8 @@ pub enum GatewayClientError { #[error("No bandwidth controller provided")] NoBandwidthControllerAvailable, - #[error("Credential error - {0}")] - CredentialError(#[from] nym_credentials::error::Error), + #[error("Bandwidth controller error - {0}")] + BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError), #[error("Connection was abruptly closed")] ConnectionAbruptlyClosed, diff --git a/common/client-libs/gateway-client/src/lib.rs b/common/client-libs/gateway-client/src/lib.rs index ecaa49a9a6..b14e7b0acf 100644 --- a/common/client-libs/gateway-client/src/lib.rs +++ b/common/client-libs/gateway-client/src/lib.rs @@ -11,13 +11,10 @@ pub use packet_router::{ }; use tungstenite::{protocol::Message, Error as WsError}; -pub mod bandwidth; pub mod client; pub mod error; pub mod packet_router; pub mod socket_state; -#[cfg(target_arch = "wasm32")] -pub mod wasm_mockups; /// Helper method for reading from websocket stream. Helps to flatten the structure. pub(crate) fn cleanup_socket_message( diff --git a/common/client-libs/gateway-client/src/wasm_mockups.rs b/common/client-libs/gateway-client/src/wasm_mockups.rs deleted file mode 100644 index 8d432e3f57..0000000000 --- a/common/client-libs/gateway-client/src/wasm_mockups.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use std::marker::PhantomData; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum StorageError { - #[error("Wasm client is not yet supported")] - WasmNotSupported, - - #[allow(dead_code)] - #[error("Code shouldn't reach this point")] - InconsistentData, -} - -pub struct DirectSigningNyxdClient {} - -pub trait DkgQueryClient {} - -// impl CosmWasmClient for DirectSigningNyxdClient {} - -#[derive(Clone)] -pub struct Client { - _phantom: PhantomData, -} - -impl DkgQueryClient for Client {} - -#[derive(Clone)] -pub struct PersistentStorage {} - -pub struct CoconutCredential { - pub id: i64, - pub voucher_value: String, - pub voucher_info: String, - pub serial_number: String, - pub binding_number: String, - pub signature: String, - pub epoch_id: String, -} - -#[async_trait] -pub trait Storage: Send + Sync { - async fn insert_coconut_credential( - &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - ) -> Result<(), StorageError>; - - async fn get_next_coconut_credential(&self) -> Result; - - async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>; -} - -#[async_trait] -impl Storage for PersistentStorage { - async fn insert_coconut_credential( - &self, - _voucher_value: String, - _voucher_info: String, - _serial_number: String, - _binding_number: String, - _signature: String, - ) -> Result<(), StorageError> { - Err(StorageError::WasmNotSupported) - } - - async fn get_next_coconut_credential(&self) -> Result { - Err(StorageError::WasmNotSupported) - } - - async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> { - Err(StorageError::WasmNotSupported) - } -} diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index 261ea6ec55..a4f5518234 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -4,7 +4,7 @@ pub mod client; #[cfg(feature = "nyxd-client")] pub mod connection_tester; -mod error; +pub mod error; pub mod nym_api; #[cfg(feature = "nyxd-client")] pub mod nyxd; diff --git a/common/credential-storage/Cargo.toml b/common/credential-storage/Cargo.toml index c488eab99b..f86d919197 100644 --- a/common/credential-storage/Cargo.toml +++ b/common/credential-storage/Cargo.toml @@ -9,9 +9,16 @@ edition = "2021" async-trait = { workspace = true } log = { workspace = true } -sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]} thiserror = "1.0" -tokio = { version = "1.24.1", features = [ "rt-multi-thread", "net", "signal", "fs" ] } +tokio = { version = "1.24.1", features = ["sync"]} + +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx] +version = "0.5" +features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] + +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] +version = "1.24.1" +features = [ "rt-multi-thread", "net", "signal", "fs" ] [build-dependencies] diff --git a/common/credential-storage/src/backends/memory.rs b/common/credential-storage/src/backends/memory.rs new file mode 100644 index 0000000000..2dbdff0925 --- /dev/null +++ b/common/credential-storage/src/backends/memory.rs @@ -0,0 +1,70 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::models::CoconutCredential; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[derive(Clone)] +pub struct CoconutCredentialManager { + inner: Arc>>, +} + +impl CoconutCredentialManager { + /// Creates new empty instance of the `CoconutCredentialManager`. + pub fn new() -> Self { + CoconutCredentialManager { + inner: Arc::new(RwLock::new(Vec::new())), + } + } + + /// Inserts provided signature into the database. + /// + /// # Arguments + /// + /// * `voucher_value`: Plaintext bandwidth value of the credential. + /// * `voucher_info`: Plaintext information of the credential. + /// * `serial_number`: Base58 representation of the serial number attribute. + /// * `binding_number`: Base58 representation of the binding number attribute. + /// * `signature`: Coconut credential in the form of a signature. + pub async fn insert_coconut_credential( + &self, + voucher_value: String, + voucher_info: String, + serial_number: String, + binding_number: String, + signature: String, + epoch_id: String, + ) { + let mut creds = self.inner.write().await; + let id = creds.len() as i64; + creds.push(CoconutCredential { + id, + voucher_value, + voucher_info, + serial_number, + binding_number, + signature, + epoch_id, + consumed: false, + }); + } + + /// Tries to retrieve one of the stored, unused credentials. + pub async fn get_next_coconut_credential(&self) -> Option { + let creds = self.inner.read().await; + creds.iter().find(|c| !c.consumed).cloned() + } + + /// Consumes in the database the specified credential. + /// + /// # Arguments + /// + /// * `id`: Database id. + pub async fn consume_coconut_credential(&self, id: i64) { + let mut creds = self.inner.write().await; + if let Some(cred) = creds.get_mut(id as usize) { + cred.consumed = true; + } + } +} diff --git a/common/credential-storage/src/backends/mod.rs b/common/credential-storage/src/backends/mod.rs new file mode 100644 index 0000000000..2cd1334af3 --- /dev/null +++ b/common/credential-storage/src/backends/mod.rs @@ -0,0 +1,6 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod memory; +#[cfg(not(target_arch = "wasm32"))] +pub mod sqlite; diff --git a/common/credential-storage/src/coconut.rs b/common/credential-storage/src/backends/sqlite.rs similarity index 87% rename from common/credential-storage/src/coconut.rs rename to common/credential-storage/src/backends/sqlite.rs index f4b78d8846..f64ef811b0 100644 --- a/common/credential-storage/src/coconut.rs +++ b/common/credential-storage/src/backends/sqlite.rs @@ -4,7 +4,7 @@ use crate::models::CoconutCredential; #[derive(Clone)] -pub(crate) struct CoconutCredentialManager { +pub struct CoconutCredentialManager { connection_pool: sqlx::SqlitePool, } @@ -14,7 +14,7 @@ impl CoconutCredentialManager { /// # Arguments /// /// * `connection_pool`: database connection pool to use. - pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self { + pub fn new(connection_pool: sqlx::SqlitePool) -> Self { CoconutCredentialManager { connection_pool } } @@ -27,7 +27,7 @@ impl CoconutCredentialManager { /// * `serial_number`: Base58 representation of the serial number attribute. /// * `binding_number`: Base58 representation of the binding number attribute. /// * `signature`: Coconut credential in the form of a signature. - pub(crate) async fn insert_coconut_credential( + pub async fn insert_coconut_credential( &self, voucher_value: String, voucher_info: String, @@ -46,7 +46,7 @@ impl CoconutCredentialManager { } /// Tries to retrieve one of the stored, unused credentials. - pub(crate) async fn get_next_coconut_credential( + pub async fn get_next_coconut_credential( &self, ) -> Result, sqlx::Error> { sqlx::query_as!( @@ -62,7 +62,7 @@ impl CoconutCredentialManager { /// # Arguments /// /// * `id`: Database id. - pub(crate) async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> { + pub async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> { sqlx::query!( "UPDATE coconut_credentials SET consumed = TRUE WHERE id = ?", id diff --git a/common/credential-storage/src/ephemeral_storage.rs b/common/credential-storage/src/ephemeral_storage.rs new file mode 100644 index 0000000000..3c6e751089 --- /dev/null +++ b/common/credential-storage/src/ephemeral_storage.rs @@ -0,0 +1,67 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::backends::memory::CoconutCredentialManager; +use crate::error::StorageError; +use crate::models::CoconutCredential; +use crate::storage::Storage; + +use async_trait::async_trait; + +// note that clone here is fine as upon cloning the same underlying pool will be used +#[derive(Clone)] +pub struct EphemeralStorage { + coconut_credential_manager: CoconutCredentialManager, +} + +impl Default for EphemeralStorage { + fn default() -> Self { + EphemeralStorage { + coconut_credential_manager: CoconutCredentialManager::new(), + } + } +} + +#[async_trait] +impl Storage for EphemeralStorage { + async fn insert_coconut_credential( + &self, + voucher_value: String, + voucher_info: String, + serial_number: String, + binding_number: String, + signature: String, + epoch_id: String, + ) -> Result<(), StorageError> { + self.coconut_credential_manager + .insert_coconut_credential( + voucher_value, + voucher_info, + serial_number, + binding_number, + signature, + epoch_id, + ) + .await; + + Ok(()) + } + + async fn get_next_coconut_credential(&self) -> Result { + let credential = self + .coconut_credential_manager + .get_next_coconut_credential() + .await + .ok_or(StorageError::NoCredential)?; + + Ok(credential) + } + + async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> { + self.coconut_credential_manager + .consume_coconut_credential(id) + .await; + + Ok(()) + } +} diff --git a/common/credential-storage/src/erc20.rs b/common/credential-storage/src/erc20.rs deleted file mode 100644 index 74559cfd30..0000000000 --- a/common/credential-storage/src/erc20.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::models::ERC20Credential; - -#[derive(Clone)] -pub(crate) struct ERC20CredentialManager { - connection_pool: sqlx::SqlitePool, -} - -impl ERC20CredentialManager { - /// Creates new instance of the `ERC20CredentialManager` with the provided sqlite connection pool. - /// - /// # Arguments - /// - /// * `connection_pool`: database connection pool to use. - pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self { - ERC20CredentialManager { connection_pool } - } -} diff --git a/common/credential-storage/src/error.rs b/common/credential-storage/src/error.rs index 83082e0d6a..39ea67ba3b 100644 --- a/common/credential-storage/src/error.rs +++ b/common/credential-storage/src/error.rs @@ -5,9 +5,11 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum StorageError { + #[cfg(not(target_arch = "wasm32"))] #[error("Database experienced an internal error - {0}")] InternalDatabaseError(#[from] sqlx::Error), + #[cfg(not(target_arch = "wasm32"))] #[error("Failed to perform database migration - {0}")] MigrationError(#[from] sqlx::migrate::MigrateError), diff --git a/common/credential-storage/src/lib.rs b/common/credential-storage/src/lib.rs index 202cd952c8..87f2dd2239 100644 --- a/common/credential-storage/src/lib.rs +++ b/common/credential-storage/src/lib.rs @@ -3,111 +3,26 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::coconut::CoconutCredentialManager; -use crate::error::StorageError; -use crate::storage::Storage; +use crate::ephemeral_storage::EphemeralStorage; +#[cfg(not(target_arch = "wasm32"))] +use crate::persistent_storage::PersistentStorage; -use crate::models::CoconutCredential; -use async_trait::async_trait; -use log::{debug, error}; -use sqlx::ConnectOptions; -use std::path::{Path, PathBuf}; - -mod coconut; +mod backends; +pub mod ephemeral_storage; pub mod error; mod models; +#[cfg(not(target_arch = "wasm32"))] +pub mod persistent_storage; pub mod storage; -// note that clone here is fine as upon cloning the same underlying pool will be used -#[derive(Clone)] -pub struct PersistentStorage { - coconut_credential_manager: CoconutCredentialManager, -} - -impl PersistentStorage { - /// Initialises `PersistentStorage` using the provided path. - /// - /// # Arguments - /// - /// * `database_path`: path to the database. - pub async fn init + Send>(database_path: P) -> Result { - debug!( - "Attempting to connect to database {:?}", - database_path.as_ref().as_os_str() - ); - - let mut opts = sqlx::sqlite::SqliteConnectOptions::new() - .filename(database_path) - .create_if_missing(true); - - opts.disable_statement_logging(); - - let connection_pool = match sqlx::SqlitePool::connect_with(opts).await { - Ok(db) => db, - Err(err) => { - error!("Failed to connect to SQLx database: {err}"); - return Err(err.into()); - } - }; - - if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await { - error!("Failed to perform migration on the SQLx database: {err}"); - return Err(err.into()); - } - - Ok(PersistentStorage { - coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()), - }) - } -} - -#[async_trait] -impl Storage for PersistentStorage { - async fn insert_coconut_credential( - &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - epoch_id: String, - ) -> Result<(), StorageError> { - self.coconut_credential_manager - .insert_coconut_credential( - voucher_value, - voucher_info, - serial_number, - binding_number, - signature, - epoch_id, - ) - .await?; - - Ok(()) - } - - async fn get_next_coconut_credential(&self) -> Result { - let credential = self - .coconut_credential_manager - .get_next_coconut_credential() - .await? - .ok_or(StorageError::NoCredential)?; - - Ok(credential) - } - - async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> { - self.coconut_credential_manager - .consume_coconut_credential(id) - .await?; - - Ok(()) - } -} - -pub async fn initialise_storage(path: PathBuf) -> PersistentStorage { - match PersistentStorage::init(path).await { +#[cfg(not(target_arch = "wasm32"))] +pub async fn initialise_persistent_storage(path: std::path::PathBuf) -> PersistentStorage { + match persistent_storage::PersistentStorage::init(path).await { Err(err) => panic!("failed to initialise credential storage - {err}"), Ok(storage) => storage, } } + +pub fn initialise_ephemeral_storage() -> EphemeralStorage { + ephemeral_storage::EphemeralStorage::default() +} diff --git a/common/credential-storage/src/models.rs b/common/credential-storage/src/models.rs index 67f6e4790c..014054f3fa 100644 --- a/common/credential-storage/src/models.rs +++ b/common/credential-storage/src/models.rs @@ -1,6 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[derive(Clone)] pub struct CoconutCredential { #[allow(dead_code)] pub id: i64, diff --git a/common/credential-storage/src/persistent_storage.rs b/common/credential-storage/src/persistent_storage.rs new file mode 100644 index 0000000000..7f729c3a63 --- /dev/null +++ b/common/credential-storage/src/persistent_storage.rs @@ -0,0 +1,99 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::backends::sqlite::CoconutCredentialManager; +use crate::error::StorageError; +use crate::storage::Storage; + +use crate::models::CoconutCredential; +use async_trait::async_trait; +use log::{debug, error}; +use sqlx::ConnectOptions; +use std::path::Path; + +// note that clone here is fine as upon cloning the same underlying pool will be used +#[derive(Clone)] +pub struct PersistentStorage { + coconut_credential_manager: CoconutCredentialManager, +} + +impl PersistentStorage { + /// Initialises `PersistentStorage` using the provided path. + /// + /// # Arguments + /// + /// * `database_path`: path to the database. + pub async fn init + Send>(database_path: P) -> Result { + debug!( + "Attempting to connect to database {:?}", + database_path.as_ref().as_os_str() + ); + + let mut opts = sqlx::sqlite::SqliteConnectOptions::new() + .filename(database_path) + .create_if_missing(true); + + opts.disable_statement_logging(); + + let connection_pool = match sqlx::SqlitePool::connect_with(opts).await { + Ok(db) => db, + Err(err) => { + error!("Failed to connect to SQLx database: {err}"); + return Err(err.into()); + } + }; + + if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await { + error!("Failed to perform migration on the SQLx database: {err}"); + return Err(err.into()); + } + + Ok(PersistentStorage { + coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()), + }) + } +} + +#[async_trait] +impl Storage for PersistentStorage { + async fn insert_coconut_credential( + &self, + voucher_value: String, + voucher_info: String, + serial_number: String, + binding_number: String, + signature: String, + epoch_id: String, + ) -> Result<(), StorageError> { + self.coconut_credential_manager + .insert_coconut_credential( + voucher_value, + voucher_info, + serial_number, + binding_number, + signature, + epoch_id, + ) + .await?; + + Ok(()) + } + + async fn get_next_coconut_credential(&self) -> Result { + let credential = self + .coconut_credential_manager + .get_next_coconut_credential() + .await? + .ok_or(StorageError::NoCredential)?; + + Ok(credential) + } + + async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> { + self.coconut_credential_manager + .consume_coconut_credential(id) + .await?; + + Ok(()) + } +} diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index bf3285cae6..bdfd6ea3ae 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -3,8 +3,8 @@ use async_trait::async_trait; +use crate::error::StorageError; use crate::models::CoconutCredential; -use crate::StorageError; #[async_trait] pub trait Storage: Send + Sync { diff --git a/common/mobile-storage/src/lib.rs b/common/mobile-storage/src/lib.rs deleted file mode 100644 index 0de91b0ec4..0000000000 --- a/common/mobile-storage/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum StorageError { - #[error("Android is not yet supported")] - AndroidNotSupported, - - #[allow(dead_code)] - #[error("Code shouldn't reach this point")] - InconsistentData, -} - -#[derive(Clone)] -pub struct PersistentStorage {} - -pub struct CoconutCredential { - pub id: i64, - pub voucher_value: String, - pub voucher_info: String, - pub serial_number: String, - pub binding_number: String, - pub signature: String, - pub epoch_id: String, - pub consumed: bool, -} - -#[async_trait] -pub trait Storage: Send + Sync { - async fn insert_coconut_credential( - &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - ) -> Result<(), StorageError>; - - async fn get_next_coconut_credential(&self) -> Result; - - async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>; -} - -#[async_trait] -impl Storage for PersistentStorage { - async fn insert_coconut_credential( - &self, - _voucher_value: String, - _voucher_info: String, - _serial_number: String, - _binding_number: String, - _signature: String, - ) -> Result<(), StorageError> { - Err(StorageError::AndroidNotSupported) - } - - async fn get_next_coconut_credential(&self) -> Result { - Err(StorageError::AndroidNotSupported) - } - - async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> { - Err(StorageError::AndroidNotSupported) - } -} diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 37e3b8d956..58c4b8d250 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -19,17 +19,6 @@ pub struct ChainDetails { pub stake_denom: DenomDetailsOwned, } -// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms -impl Default for ChainDetails { - fn default() -> Self { - ChainDetails { - bech32_account_prefix: mainnet::BECH32_PREFIX.into(), - mix_denom: mainnet::MIX_DENOM.into(), - stake_denom: mainnet::STAKE_DENOM.into(), - } - } -} - #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct NymContracts { pub mixnet_contract_address: Option, @@ -43,20 +32,43 @@ pub struct NymContracts { // I wanted to use the simpler `NetworkDetails` name, but there's a clash // with `NetworkDetails` defined in all.rs... -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct NymNetworkDetails { pub chain_details: ChainDetails, pub endpoints: Vec, pub contracts: NymContracts, } +// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms +impl Default for NymNetworkDetails { + fn default() -> Self { + NymNetworkDetails::new_mainnet() + } +} + impl NymNetworkDetails { - pub fn new() -> Self { - NymNetworkDetails::default() + pub fn new_empty() -> Self { + NymNetworkDetails { + chain_details: ChainDetails { + bech32_account_prefix: Default::default(), + mix_denom: DenomDetailsOwned { + base: Default::default(), + display: Default::default(), + display_exponent: Default::default(), + }, + stake_denom: DenomDetailsOwned { + base: Default::default(), + display: Default::default(), + display_exponent: Default::default(), + }, + }, + endpoints: Default::default(), + contracts: Default::default(), + } } pub fn new_from_env() -> Self { - NymNetworkDetails::new() + NymNetworkDetails::new_empty() .with_bech32_account_prefix( var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"), ) diff --git a/common/socks5-client-core/Cargo.toml b/common/socks5-client-core/Cargo.toml index 021a3fe501..99b876ba2c 100644 --- a/common/socks5-client-core/Cargo.toml +++ b/common/socks5-client-core/Cargo.toml @@ -17,9 +17,9 @@ tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] } futures = "0.3" -nym-gateway-client = { path = "../client-libs/gateway-client" } +nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-config = { path = "../config" } -nym-credential-storage = { path = "../credential-storage", optional = true } +nym-credential-storage = { path = "../credential-storage" } nym-network-defaults = { path = "../network-defaults" } nym-socks5-proxy-helpers = { path = "../socks5/proxy-helpers" } nym-service-providers-common = { path = "../../service-providers/common" } @@ -29,4 +29,4 @@ nym-task = { path = "../task" } nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] } [features] -default = ["nym-credential-storage"] +default = [] diff --git a/common/socks5-client-core/src/lib.rs b/common/socks5-client-core/src/lib.rs index f394e62498..daea560837 100644 --- a/common/socks5-client-core/src/lib.rs +++ b/common/socks5-client-core/src/lib.rs @@ -11,6 +11,7 @@ use crate::socks::{ use futures::channel::mpsc; use futures::StreamExt; use log::*; +use nym_bandwidth_controller::BandwidthController; #[cfg(target_os = "android")] use nym_client_core::client::base_client::helpers::setup_empty_reply_surb_backend; #[cfg(not(target_os = "android"))] @@ -20,8 +21,7 @@ use nym_client_core::client::base_client::{ }; use nym_client_core::client::key_manager::KeyManager; use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; -#[cfg(not(target_os = "android"))] -use nym_gateway_client::bandwidth::BandwidthController; +use nym_credential_storage::storage::Storage; use nym_sphinx::addressing::clients::Recipient; use nym_task::{TaskClient, TaskManager}; use nym_validator_client::nyxd::QueryNyxdClient; @@ -74,10 +74,10 @@ impl NymClient { } } - #[cfg(not(target_os = "android"))] - async fn create_bandwidth_controller( + async fn create_bandwidth_controller( config: &Config, - ) -> BandwidthController> { + storage: St, + ) -> BandwidthController, St> { let details = nym_network_defaults::NymNetworkDetails::new_from_env(); let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details) @@ -97,13 +97,6 @@ impl NymClient { let client = nym_validator_client::Client::new_query(client_config) .expect("Could not construct query client"); - #[cfg(not(target_os = "android"))] - let storage = - nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await; - - #[cfg(target_os = "android")] - let storage = mobile_storage::PersistentStorage {}; - BandwidthController::new(storage, client) } @@ -223,7 +216,16 @@ impl NymClient { let base_builder = BaseClientBuilder::new_from_base_config( self.config.get_base(), self.key_manager, - Some(Self::create_bandwidth_controller(&self.config).await), + Some( + Self::create_bandwidth_controller( + &self.config, + nym_credential_storage::initialise_persistent_storage( + self.config.get_base().get_database_path(), + ) + .await, + ) + .await, + ), non_wasm_helpers::setup_fs_reply_surb_backend( Some(self.config.get_base().get_reply_surb_database_path()), self.config.get_debug_settings(), @@ -232,10 +234,16 @@ impl NymClient { ); #[cfg(target_os = "android")] - let base_builder = BaseClientBuilder::<_, Client>::new_from_base_config( + let base_builder = BaseClientBuilder::<_, Client, _>::new_from_base_config( self.config.get_base(), self.key_manager, - None, + Some( + Self::create_bandwidth_controller( + &self.config, + nym_credential_storage::initialise_ephemeral_storage(), + ) + .await, + ), setup_empty_reply_surb_backend(self.config.get_debug_settings()), ); diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 53ce10d83c..56546bc3eb 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -64,6 +64,7 @@ rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] } schemars = { version = "0.8", features = ["preserve_order"] } ## internal +nym-bandwidth-controller = { path = "../common/bandwidth-controller" } nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" } nym-coconut-interface = { path = "../common/coconut-interface" } diff --git a/nym-api/src/network_monitor/mod.rs b/nym-api/src/network_monitor/mod.rs index 9dbbdb0282..f19cccc35e 100644 --- a/nym-api/src/network_monitor/mod.rs +++ b/nym-api/src/network_monitor/mod.rs @@ -17,9 +17,9 @@ use crate::storage::NymApiStorage; use crate::support::config::Config; use crate::support::nyxd; use futures::channel::mpsc; -use nym_credential_storage::PersistentStorage; +use nym_bandwidth_controller::BandwidthController; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::{encryption, identity}; -use nym_gateway_client::bandwidth::BandwidthController; use nym_sphinx::receiver::MessageReceiver; use nym_task::TaskManager; use std::sync::Arc; @@ -98,7 +98,7 @@ impl<'a> NetworkMonitorBuilder<'a> { let bandwidth_controller = { BandwidthController::new( - nym_credential_storage::initialise_storage( + nym_credential_storage::initialise_persistent_storage( self.config.get_credentials_database_path(), ) .await, diff --git a/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs b/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs index 1c54295a9e..9895f18a5a 100644 --- a/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs +++ b/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::support::nyxd; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::identity::PUBLIC_KEY_LENGTH; use nym_gateway_client::GatewayClient; use std::collections::HashMap; @@ -11,16 +12,16 @@ use tokio::sync::{Mutex, MutexGuard, TryLockError}; pub(crate) struct GatewayClientHandle(Arc); struct GatewayClientHandleInner { - client: Mutex>>, + client: Mutex>>, raw_identity: [u8; PUBLIC_KEY_LENGTH], } pub(crate) struct UnlockedGatewayClientHandle<'a>( - MutexGuard<'a, Option>>, + MutexGuard<'a, Option>>, ); impl GatewayClientHandle { - pub(crate) fn new(gateway_client: GatewayClient) -> Self { + pub(crate) fn new(gateway_client: GatewayClient) -> Self { GatewayClientHandle(Arc::new(GatewayClientHandleInner { raw_identity: gateway_client.gateway_identity().to_bytes(), client: Mutex::new(Some(gateway_client)), @@ -59,11 +60,15 @@ impl GatewayClientHandle { } impl<'a> UnlockedGatewayClientHandle<'a> { - pub(crate) fn get_mut_unchecked(&mut self) -> &mut GatewayClient { + pub(crate) fn get_mut_unchecked( + &mut self, + ) -> &mut GatewayClient { self.0.as_mut().unwrap() } - pub(crate) fn inner_mut(&mut self) -> Option<&mut GatewayClient> { + pub(crate) fn inner_mut( + &mut self, + ) -> Option<&mut GatewayClient> { self.0.as_mut() } diff --git a/nym-api/src/network_monitor/monitor/sender.rs b/nym-api/src/network_monitor/monitor/sender.rs index 67a6579e9f..bbf40e6f72 100644 --- a/nym-api/src/network_monitor/monitor/sender.rs +++ b/nym-api/src/network_monitor/monitor/sender.rs @@ -12,10 +12,10 @@ use futures::stream::{self, FuturesUnordered, StreamExt}; use futures::task::Context; use futures::{Future, Stream}; use log::{debug, info, trace, warn}; +use nym_bandwidth_controller::BandwidthController; use nym_config::defaults::REMAINING_BANDWIDTH_THRESHOLD; -use nym_credential_storage::PersistentStorage; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH}; -use nym_gateway_client::bandwidth::BandwidthController; use nym_gateway_client::error::GatewayClientError; use nym_gateway_client::{AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver}; use nym_sphinx::forwarding::packet::MixPacket; @@ -206,7 +206,7 @@ impl PacketSender { } async fn attempt_to_send_packets( - client: &mut GatewayClient, + client: &mut GatewayClient, mut mix_packets: Vec, max_sending_rate: usize, ) -> Result<(), GatewayClientError> { @@ -314,7 +314,7 @@ impl PacketSender { } async fn check_remaining_bandwidth( - client: &mut GatewayClient, + client: &mut GatewayClient, ) -> Result<(), GatewayClientError> { if client.remaining_bandwidth() < REMAINING_BANDWIDTH_THRESHOLD { Err(GatewayClientError::NotEnoughBandwidth( diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index 27d298ce23..0004985769 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -3173,6 +3173,22 @@ dependencies = [ "serde", ] +[[package]] +name = "nym-bandwidth-controller" +version = "0.1.0" +dependencies = [ + "bip39", + "nym-coconut-interface", + "nym-credential-storage", + "nym-credentials", + "nym-crypto", + "nym-network-defaults", + "nym-validator-client", + "rand 0.7.3", + "thiserror", + "url", +] + [[package]] name = "nym-bin-common" version = "0.3.0" @@ -3199,7 +3215,9 @@ dependencies = [ "gloo-timers", "humantime-serde", "log", + "nym-bandwidth-controller", "nym-config", + "nym-credential-storage", "nym-crypto", "nym-gateway-client", "nym-gateway-requests", @@ -3311,6 +3329,7 @@ dependencies = [ "nym-client-core", "nym-config", "nym-contracts-common", + "nym-credential-storage", "nym-crypto", "nym-socks5-client-core", "nym-task", @@ -3418,16 +3437,14 @@ dependencies = [ name = "nym-gateway-client" version = "0.1.0" dependencies = [ - "async-trait", "futures", "getrandom 0.2.8", "log", + "nym-bandwidth-controller", "nym-coconut-interface", "nym-credential-storage", - "nym-credentials", "nym-crypto", "nym-gateway-requests", - "nym-mobile-storage", "nym-network-defaults", "nym-pemstore", "nym-sphinx", @@ -3492,14 +3509,6 @@ dependencies = [ "time", ] -[[package]] -name = "nym-mobile-storage" -version = "0.1.0" -dependencies = [ - "async-trait", - "thiserror", -] - [[package]] name = "nym-multisig-contract-common" version = "0.1.0" @@ -3588,10 +3597,10 @@ dependencies = [ "dirs", "futures", "log", + "nym-bandwidth-controller", "nym-client-core", "nym-config", "nym-credential-storage", - "nym-gateway-client", "nym-network-defaults", "nym-service-providers-common", "nym-socks5-proxy-helpers", diff --git a/nym-connect/desktop/src-tauri/Cargo.toml b/nym-connect/desktop/src-tauri/Cargo.toml index da244d36c3..0b72eef236 100644 --- a/nym-connect/desktop/src-tauri/Cargo.toml +++ b/nym-connect/desktop/src-tauri/Cargo.toml @@ -50,6 +50,7 @@ nym-api-requests = { path = "../../../nym-api/nym-api-requests" } nym-contracts-common = { path = "../../../common/cosmwasm-smart-contracts/contracts-common"} nym-config-common = { path = "../../../common/config", package = "nym-config" } nym-crypto = { path = "../../../common/crypto" } +nym-credential-storage = { path = "../../../common/credential-storage" } nym-bin-common = { path = "../../../common/bin-common"} nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-task = { path = "../../../common/task" } diff --git a/nym-connect/desktop/src-tauri/src/config/mod.rs b/nym-connect/desktop/src-tauri/src/config/mod.rs index dac2e16367..1288e4887c 100644 --- a/nym-connect/desktop/src-tauri/src/config/mod.rs +++ b/nym-connect/desktop/src-tauri/src/config/mod.rs @@ -4,6 +4,7 @@ use crate::{ }; use nym_client_core::config::Config as BaseConfig; use nym_config_common::NymConfig; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::identity; use nym_socks5_client_core::config::{Config as Socks5Config, Socks5}; use std::path::PathBuf; @@ -138,14 +139,15 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str .map_err(|_| BackendError::UnableToParseGateway)?; // Setup gateway by either registering a new one, or reusing exiting keys - let gateway = nym_client_core::init::setup_gateway_from_config::( - register_gateway, - Some(chosen_gateway_id), - config.get_base(), - // TODO: another instance where this setting should probably get used - false, - ) - .await?; + let gateway = + nym_client_core::init::setup_gateway_from_config::( + register_gateway, + Some(chosen_gateway_id), + config.get_base(), + // TODO: another instance where this setting should probably get used + false, + ) + .await?; config.get_base_mut().set_gateway_endpoint(gateway); diff --git a/nym-connect/mobile/src-tauri/Cargo.lock b/nym-connect/mobile/src-tauri/Cargo.lock index 1f3209a7fc..82622e8759 100644 --- a/nym-connect/mobile/src-tauri/Cargo.lock +++ b/nym-connect/mobile/src-tauri/Cargo.lock @@ -620,6 +620,47 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "client-core" +version = "1.1.13" +dependencies = [ + "async-trait", + "dashmap", + "dirs", + "futures", + "gateway-client", + "gateway-requests", + "gloo-timers", + "humantime-serde", + "log", + "nym-bandwidth-controller", + "nym-config", + "nym-credential-storage", + "nym-crypto", + "nym-nonexhaustive-delayqueue", + "nym-pemstore", + "nym-sphinx", + "nym-task", + "nym-topology", + "rand 0.7.3", + "serde", + "serde_json", + "sqlx 0.6.3", + "tap", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", + "validator-client", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-timer", + "wasm-utils", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -3143,6 +3184,22 @@ dependencies = [ "serde", ] +[[package]] +name = "nym-bandwidth-controller" +version = "0.1.0" +dependencies = [ + "bip39", + "nym-coconut-interface", + "nym-credential-storage", + "nym-credentials", + "nym-crypto", + "nym-network-defaults", + "rand 0.7.3", + "thiserror", + "url", + "validator-client", +] + [[package]] name = "nym-bin-common" version = "0.3.0" @@ -3281,6 +3338,7 @@ dependencies = [ "nym-client-core", "nym-config", "nym-contracts-common", + "nym-credential-storage", "nym-crypto", "nym-socks5-client-core", "nym-task", @@ -3556,9 +3614,9 @@ dependencies = [ "dirs", "futures", "log", - "nym-client-core", + "nym-bandwidth-controller", "nym-config", - "nym-gateway-client", + "nym-credential-storage", "nym-network-defaults", "nym-service-providers-common", "nym-socks5-proxy-helpers", diff --git a/nym-connect/mobile/src-tauri/Cargo.toml b/nym-connect/mobile/src-tauri/Cargo.toml index b0543cce30..0817f11d62 100644 --- a/nym-connect/mobile/src-tauri/Cargo.toml +++ b/nym-connect/mobile/src-tauri/Cargo.toml @@ -50,10 +50,11 @@ tokio = { version = "1.24.1", features = ["sync", "time"] } url = "2.2" yaml-rust = "0.4" -nym-client-core = { path = "../../../common/client-core" } +nym-client-core = { path = "../../../common/client-core", features = ["mobile"], default-features = false } nym-api-requests = { path = "../../../nym-api/nym-api-requests" } nym-contracts-common = { path = "../../../common/cosmwasm-smart-contracts/contracts-common"} nym-config-common = { path = "../../../common/config", package = "nym-config" } +nym-credential-storage = { path = "../../../common/credential-storage" } nym-crypto = { path = "../../../common/crypto" } nym-bin-common = { path = "../../../common/bin-common"} nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = false } diff --git a/nym-connect/mobile/src-tauri/src/config/mod.rs b/nym-connect/mobile/src-tauri/src/config/mod.rs index df26452ab0..57c76d6492 100644 --- a/nym-connect/mobile/src-tauri/src/config/mod.rs +++ b/nym-connect/mobile/src-tauri/src/config/mod.rs @@ -4,6 +4,7 @@ use crate::{ }; use nym_client_core::{client::key_manager::KeyManager, config::Config as BaseConfig}; use nym_config_common::NymConfig; +use nym_credential_storage::ephemeral_storage::EphemeralStorage; use nym_crypto::asymmetric::identity; use nym_socks5_client_core::config::{Config as Socks5Config, Socks5}; use std::path::PathBuf; @@ -118,7 +119,7 @@ pub async fn init_socks5_config( let mut key_manager = nym_client_core::init::new_client_keys(); // Setup gateway and register a new key each time - let gateway = nym_client_core::init::register_with_gateway( + let gateway = nym_client_core::init::register_with_gateway::( &mut key_manager, nym_api_endpoints, Some(chosen_gateway_id), diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index b948a0db56..8bf83bcadb 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -6,10 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bip39 = { workspace = true } nym-client-core = { path = "../../../common/client-core", features = ["fs-surb-storage"]} nym-crypto = { path = "../../../common/crypto" } -nym-gateway-client = { path = "../../../common/client-libs/gateway-client" } nym-gateway-requests = { path = "../../../gateway/gateway-requests" } +nym-bandwidth-controller = { path = "../../../common/bandwidth-controller" } +nym-credentials = { path = "../../../common/credentials" } +nym-credential-storage = { path = "../../../common/credential-storage" } nym-network-defaults = { path = "../../../common/network-defaults" } nym-sphinx = { path = "../../../common/nymsphinx" } nym-task = { path = "../../../common/task" } @@ -26,6 +29,7 @@ url = "2.2" toml = "0.5.10" [dev-dependencies] +dotenvy = { workspace = true } pretty_env_logger = "0.4.0" reqwest = { version = "0.11", features = ["json", "socks"] } tokio = { version = "1", features = ["full"] } diff --git a/sdk/rust/nym-sdk/examples/bandwidth.rs b/sdk/rust/nym-sdk/examples/bandwidth.rs new file mode 100644 index 0000000000..73f732aaed --- /dev/null +++ b/sdk/rust/nym-sdk/examples/bandwidth.rs @@ -0,0 +1,42 @@ +use nym_sdk::mixnet; + +#[tokio::main] +async fn main() { + nym_bin_common::logging::setup_logging(); + // right now, only sandbox has coconut setup + // this should be run from the `sdk/rust/nym-sdk` directory + dotenvy::from_path("../../../envs/sandbox.env").unwrap(); + + let sandbox_network = mixnet::NymNetworkDetails::new_from_env(); + + let mixnet_client = mixnet::MixnetClientBuilder::new() + .network_details(sandbox_network) + .enable_credentials_mode() + .build::() + .await + .unwrap(); + + let bandwidth_client = mixnet_client + .create_bandwidth_client(String::from("very secret mnemonic")) + .unwrap(); + + // Get a bandwidth credential worth 1000000 unym for the mixnet_client + bandwidth_client.acquire(1000000).await.unwrap(); + + // Connect using paid bandwidth credential + let mut client = mixnet_client.connect_to_mixnet().await.unwrap(); + + let our_address = client.nym_address(); + + // Send a message throughout the mixnet to ourselves + client.send_str(*our_address, "hello there").await; + + println!("Waiting for message"); + if let Some(received) = client.wait_for_messages().await { + for r in received { + println!("Received: {}", String::from_utf8_lossy(&r.message)); + } + } + + client.disconnect().await; +} diff --git a/sdk/rust/nym-sdk/examples/manually_handle_keys_and_config.rs b/sdk/rust/nym-sdk/examples/manually_handle_keys_and_config.rs index 8c40e86870..029992dc65 100644 --- a/sdk/rust/nym-sdk/examples/manually_handle_keys_and_config.rs +++ b/sdk/rust/nym-sdk/examples/manually_handle_keys_and_config.rs @@ -4,10 +4,6 @@ use nym_sdk::mixnet; async fn main() { nym_bin_common::logging::setup_logging(); - let user_chosen_gateway_id = None; - let nym_api_endpoints = vec!["https://validator.nymtech.net/api/".parse().unwrap()]; - let config = mixnet::Config::new(user_chosen_gateway_id, nym_api_endpoints); - // Just some plain data to pretend we have some external storage that the application // implementer is using. let mut mock_storage = MockStorage::empty(); @@ -17,7 +13,6 @@ async fn main() { let client = if first_run { // Create a client without a storage backend let mut client = mixnet::MixnetClientBuilder::new() - .config(config) .build::() .await .unwrap(); @@ -33,9 +28,8 @@ async fn main() { // Create a client without a storage backend, but with explicitly set keys and gateway // configuration. This creates the client in a registered state. mixnet::MixnetClientBuilder::new() - .config(config) .keys(keys) - .gateway_config(gateway_config) + .registered_gateway(gateway_config) .build::() .await .unwrap() diff --git a/sdk/rust/nym-sdk/src/bandwidth.rs b/sdk/rust/nym-sdk/src/bandwidth.rs new file mode 100644 index 0000000000..7581a0e599 --- /dev/null +++ b/sdk/rust/nym-sdk/src/bandwidth.rs @@ -0,0 +1,45 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 +//! The coconut bandwidth component of the Rust SDK for the Nym platform +//! +//! +//! # Basic example +//! +//! ```no_run +//! use nym_sdk::mixnet; +//! +//! #[tokio::main] +//! async fn main() { +//! let mixnet_client = mixnet::MixnetClientBuilder::new() +//! .enable_credentials_mode() +//! .build::() +//! .await +//! .unwrap(); +//! +//! let bandwidth_client = mixnet_client.create_bandwidth_client(String::from("my super secret mnemonic")).unwrap(); +//! +//! // Get a bandwidth credential worth 1000000 unym for the mixnet_client +//! bandwidth_client.acquire(1000000).await.unwrap(); +//! +//! // Connect using paid bandwidth credential +//! let mut client = mixnet_client.connect_to_mixnet().await.unwrap(); +//! +//! let our_address = client.nym_address(); +//! +//! // Send a message throughout the mixnet to ourselves +//! client.send_str(*our_address, "hello there").await; +//! +//! println!("Waiting for message"); +//! if let Some(received) = client.wait_for_messages().await { +//! for r in received { +//! println!("Received: {}", String::from_utf8_lossy(&r.message)); +//! } +//! } +//! +//! client.disconnect().await; +//! } +//! ``` + +mod client; + +pub use client::{BandwidthAcquireClient, VoucherBlob}; diff --git a/sdk/rust/nym-sdk/src/bandwidth/client.rs b/sdk/rust/nym-sdk/src/bandwidth/client.rs new file mode 100644 index 0000000000..7388b19fa7 --- /dev/null +++ b/sdk/rust/nym-sdk/src/bandwidth/client.rs @@ -0,0 +1,67 @@ +use crate::error::{Error, Result}; +use nym_bandwidth_controller::acquire::state::State; +use nym_credential_storage::ephemeral_storage::EphemeralStorage; +use nym_credentials::coconut::bandwidth::BandwidthVoucher; +use nym_network_defaults::NymNetworkDetails; +use nym_validator_client::nyxd::{Coin, SigningNyxdClient}; +use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; +use nym_validator_client::{Client, Config}; + +/// The serialized version of the yet untransformed bandwidth voucher. It can be used to complete +/// the acquirement process of a bandwidth credential. +/// Its serialized nature makes it easy to store and load it to e.g. disk. +pub type VoucherBlob = Vec; + +/// Represents a client that can be used to acquire bandwidth. You typically create one when you +/// want to connect to the mixnet using paid coconut bandwidth credentials. +/// The way to create this client is by calling +/// [`crate::mixnet::DisconnectedMixnetClient::create_bandwidth_client`] on the associated mixnet +/// client. +pub struct BandwidthAcquireClient { + network_details: NymNetworkDetails, + client: Client>, + storage: EphemeralStorage, +} + +impl BandwidthAcquireClient { + pub(crate) fn new( + network_details: NymNetworkDetails, + mnemonic: String, + storage: EphemeralStorage, + ) -> Result { + let config = Config::try_from_nym_network_details(&network_details)?; + let client = nym_validator_client::Client::new_signing(config, mnemonic.parse()?)?; + Ok(Self { + network_details, + client, + storage, + }) + } + + /// Buy a credential worth amount utokens. If [`Error::UnconvertedDeposit`] is returned, it + /// means the tokens have been deposited, but the proper bandwidth credential hasn't yet been + /// created. A [`VoucherBlob`] is returned that can be used for a later recovery of the + /// associated bandwidth credential, using [`Self::recover`]. + pub async fn acquire(&self, amount: u128) -> Result<()> { + let amount = Coin::new(amount, &self.network_details.chain_details.mix_denom.base); + let state = nym_bandwidth_controller::acquire::deposit(&self.client.nyxd, amount).await?; + nym_bandwidth_controller::acquire::get_credential(&state, &self.client, &self.storage) + .await + .map_err(|reason| Error::UnconvertedDeposit { + reason, + voucher_blob: state.voucher.to_bytes(), + }) + } + + /// In case of an error in the mid of the acquire process, this function should be used for + /// later retries to recover the bandwidth credential, either immediately or after some time. + pub async fn recover(&self, voucher_blob: &VoucherBlob) -> Result<()> { + let voucher = BandwidthVoucher::try_from_bytes(voucher_blob) + .map_err(|_| Error::InvalidVoucherBlob)?; + let state = State::new(voucher); + nym_bandwidth_controller::acquire::get_credential(&state, &self.client, &self.storage) + .await?; + + Ok(()) + } +} diff --git a/sdk/rust/nym-sdk/src/error.rs b/sdk/rust/nym-sdk/src/error.rs index 2b62495905..8e57e82227 100644 --- a/sdk/rust/nym-sdk/src/error.rs +++ b/sdk/rust/nym-sdk/src/error.rs @@ -42,12 +42,36 @@ pub enum Error { #[error("no gateway key set")] NoGatewayKeySet, + #[error("credentials mode not enabled")] + DisabledCredentialsMode, + + #[error("bad validator details: {0}")] + BadValidatorDetails(#[from] nym_validator_client::ValidatorClientError), + #[error("socks5 configuration set: {}, but expected to be {}", set, !set)] Socks5Config { set: bool }, #[error("socks5 channel could not be started")] Socks5NotStarted, + #[error( + "deposited funds were not converted to a deposit - {reason}; the voucher blob can be used for \ + later retry" + )] + UnconvertedDeposit { + reason: nym_bandwidth_controller::error::BandwidthControllerError, + voucher_blob: crate::bandwidth::VoucherBlob, + }, + + #[error("bandwidth controller error: {0}")] + BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError), + + #[error("invalid voucher blob")] + InvalidVoucherBlob, + + #[error("invalid mnemonic: {0}")] + InvalidMnemonic(#[from] bip39::Error), + #[error("failed to create reply storage backend: {source}")] StorageError { source: Box, diff --git a/sdk/rust/nym-sdk/src/lib.rs b/sdk/rust/nym-sdk/src/lib.rs index d4306ab336..fee4a2f323 100644 --- a/sdk/rust/nym-sdk/src/lib.rs +++ b/sdk/rust/nym-sdk/src/lib.rs @@ -4,6 +4,7 @@ mod error; +pub mod bandwidth; pub mod mixnet; pub use error::{Error, Result}; diff --git a/sdk/rust/nym-sdk/src/mixnet.rs b/sdk/rust/nym-sdk/src/mixnet.rs index f83e4f7c9d..e84f37ed9d 100644 --- a/sdk/rust/nym-sdk/src/mixnet.rs +++ b/sdk/rust/nym-sdk/src/mixnet.rs @@ -50,6 +50,7 @@ pub use nym_client_core::{ }, config::GatewayEndpointConfig, }; +pub use nym_network_defaults::NymNetworkDetails; pub use nym_socks5_client_core::config::Socks5; pub use nym_sphinx::{ addressing::clients::{ClientIdentity, Recipient}, diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index de9835678c..d37dd42a56 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -1,8 +1,11 @@ use futures::channel::mpsc; use futures::StreamExt; use std::{path::Path, sync::Arc}; +use url::Url; +use nym_bandwidth_controller::BandwidthController; use nym_client_core::client::base_client::BaseClient; +use nym_client_core::config::DebugConfig; use nym_client_core::{ client::{ base_client::{BaseClientBuilder, CredentialsToggle}, @@ -11,14 +14,18 @@ use nym_client_core::{ }, config::{persistence::key_pathfinder::ClientKeyPathfinder, GatewayEndpointConfig}, }; +use nym_credential_storage::ephemeral_storage::EphemeralStorage; +use nym_credential_storage::initialise_ephemeral_storage; use nym_crypto::asymmetric::identity; +use nym_network_defaults::NymNetworkDetails; use nym_socks5_client_core::config::Socks5; use nym_task::manager::TaskStatus; use nym_topology::provider_trait::TopologyProvider; -use nym_validator_client::nyxd::DirectSigningNyxdClient; +use nym_validator_client::nyxd::QueryNyxdClient; use nym_validator_client::Client; +use crate::bandwidth::BandwidthAcquireClient; use crate::mixnet::native_client::MixnetClient; use crate::mixnet::socks5_client::Socks5MixnetClient; use crate::mixnet::Recipient; @@ -31,7 +38,7 @@ const DEFAULT_NUMBER_OF_SURBS: u32 = 5; #[derive(Default)] pub struct MixnetClientBuilder { - config: Option, + config: Config, storage_paths: Option, keys: Option, gateway_config: Option, @@ -40,41 +47,68 @@ pub struct MixnetClientBuilder { } impl MixnetClientBuilder { + /// Create a client builder with default values. pub fn new() -> Self { Self::default() } + /// Request a specific gateway instead of a random one. #[must_use] - pub fn config(mut self, config: Config) -> Self { - self.config = Some(config); + pub fn request_gateway(mut self, user_chosen_gateway: String) -> Self { + self.config.user_chosen_gateway = Some(user_chosen_gateway); self } - /// Enabled storage + /// Use a specific network instead of the default (mainnet) one. + #[must_use] + pub fn network_details(mut self, network_details: NymNetworkDetails) -> Self { + self.config.network_details = network_details; + self + } + + /// Enable paid coconut bandwidth credentials mode. + #[must_use] + pub fn enable_credentials_mode(mut self) -> Self { + self.config.enabled_credentials_mode = true; + self + } + + /// Use a custom debugging configuration. + #[must_use] + pub fn debug_config(mut self, debug_config: DebugConfig) -> Self { + self.config.debug_config = debug_config; + self + } + + /// Enabled storage. #[must_use] pub fn enable_storage(mut self, paths: StoragePaths) -> Self { self.storage_paths = Some(paths); self } + /// Use a previously generated set of client keys. #[must_use] pub fn keys(mut self, keys: Keys) -> Self { self.keys = Some(keys); self } + /// Use a gateway that you previously registered with. #[must_use] - pub fn gateway_config(mut self, gateway_config: GatewayEndpointConfig) -> Self { + pub fn registered_gateway(mut self, gateway_config: GatewayEndpointConfig) -> Self { self.gateway_config = Some(gateway_config); self } + /// Configure the SOCKS5 mode. #[must_use] pub fn socks5_config(mut self, socks5_config: Socks5) -> Self { self.socks5_config = Some(socks5_config); self } + /// Use a custom topology provider. #[must_use] pub fn custom_topology_provider( mut self, @@ -90,11 +124,10 @@ impl MixnetClientBuilder { B: ReplyStorageBackend + Send + Sync + 'static, ::StorageError: Send + Sync, { - let config = self.config.unwrap_or_default(); let storage_paths = self.storage_paths; let mut client = DisconnectedMixnetClient::new( - Some(config), + self.config, self.socks5_config, storage_paths, self.custom_topology_provider, @@ -140,6 +173,9 @@ where /// connected to the mixnet. state: BuilderState, + /// Controller of bandwidth credentials that the mixnet client can use to connect + bandwidth_controller: BandwidthController, EphemeralStorage>, + /// The storage backend for reply-SURBs reply_storage_backend: B, @@ -151,14 +187,15 @@ impl DisconnectedMixnetClient where B: ReplyStorageBackend + Sync + Send + 'static, { - /// Create a new mixnet client in a disconnected state. If no config options are supplied, - /// creates a new client with ephemeral keys stored in RAM, which will be discarded at + /// Create a new mixnet client in a disconnected state. The default configuration, + /// creates a new mainnet client with ephemeral keys stored in RAM, which will be discarded at /// application close. /// - /// Callers have the option of supplying futher parameters to store persistent identities at a - /// location on-disk, if desired. + /// Callers have the option of supplying further parameters to: + /// - store persistent identities at a location on-disk, if desired; + /// - use SOCKS5 mode async fn new( - config: Option, + config: Config, socks5_config: Option, paths: Option, custom_topology_provider: Option>, @@ -166,9 +203,13 @@ where where ::StorageError: Send + Sync, { - let config = config.unwrap_or_default(); let reply_surb_database_path = paths.as_ref().map(|p| p.reply_surb_database_path.clone()); + let client_config = + nym_validator_client::Config::try_from_nym_network_details(&config.network_details)?; + let client = nym_validator_client::Client::new_query(client_config)?; + let bandwidth_controller = BandwidthController::new(initialise_ephemeral_storage(), client); + // The reply storage backend is generic, and can be set by the caller/instantiator let reply_storage_backend = B::new(&config.debug_config, reply_surb_database_path) .await @@ -219,10 +260,21 @@ where storage_paths: paths, state: BuilderState::New, reply_storage_backend, + bandwidth_controller, custom_topology_provider, }) } + fn get_api_endpoints(&self) -> Vec { + self.config + .network_details + .endpoints + .iter() + .filter_map(|details| details.api_url.as_ref()) + .filter_map(|s| Url::parse(s).ok()) + .collect() + } + /// Client keys are generated at client creation if none were found. The gateway shared /// key, however, is created during the gateway registration handshake so it might not /// necessarily be available. @@ -286,9 +338,10 @@ where .map(identity::PublicKey::from_base58_string) .transpose()?; - let gateway_config = nym_client_core::init::register_with_gateway( + let api_endpoints = self.get_api_endpoints(); + let gateway_config = nym_client_core::init::register_with_gateway::( &mut self.key_manager, - self.config.nym_api_endpoints.clone(), + api_endpoints, user_chosen_gateway, // TODO: this should probably be configurable with the config false, @@ -342,6 +395,19 @@ where Ok(()) } + /// Creates an associated [`BandwidthAcquireClient`] that can be used to acquire bandwidth + /// credentials for this client to consume. + pub fn create_bandwidth_client(&self, mnemonic: String) -> Result { + if !self.config.enabled_credentials_mode { + return Err(Error::DisabledCredentialsMode); + } + BandwidthAcquireClient::new( + self.config.network_details.clone(), + mnemonic, + self.bandwidth_controller.storage().clone(), + ) + } + async fn connect_to_mixnet_common(mut self) -> Result<(BaseClient, Recipient)> where ::StorageError: Sync + Send, @@ -375,6 +441,8 @@ where return Err(Error::NoGatewayKeySet); } + let api_endpoints = self.get_api_endpoints(); + // At this point we should be in a registered state, either at function entry or by the // above convenience logic. let BuilderState::Registered { gateway_endpoint_config } = self.state else { @@ -384,18 +452,15 @@ where let nym_address = nym_client_core::init::get_client_address(&self.key_manager, &gateway_endpoint_config); - // TODO: we currently don't support having a bandwidth controller - let bandwidth_controller = None; - - let mut base_builder: BaseClientBuilder<'_, _, Client> = + let mut base_builder: BaseClientBuilder<'_, _, Client, EphemeralStorage> = BaseClientBuilder::new( &gateway_endpoint_config, &self.config.debug_config, self.key_manager.clone(), - bandwidth_controller, + Some(self.bandwidth_controller), self.reply_storage_backend, - CredentialsToggle::Disabled, - self.config.nym_api_endpoints.clone(), + CredentialsToggle::from(self.config.enabled_credentials_mode), + api_endpoints, ); if let Some(topology_provider) = self.custom_topology_provider { diff --git a/sdk/rust/nym-sdk/src/mixnet/config.rs b/sdk/rust/nym-sdk/src/mixnet/config.rs index 4ed18335bb..5c2ff15914 100644 --- a/sdk/rust/nym-sdk/src/mixnet/config.rs +++ b/sdk/rust/nym-sdk/src/mixnet/config.rs @@ -1,38 +1,19 @@ use nym_client_core::config::DebugConfig; -use nym_network_defaults::mainnet; -use url::Url; +use nym_network_defaults::NymNetworkDetails; /// Config struct for [`crate::mixnet::MixnetClient`] +#[derive(Default)] pub struct Config { /// If the user has explicitly specified a gateway. pub user_chosen_gateway: Option, - /// List of nym-api endpoints - pub nym_api_endpoints: Vec, + /// The details of the network we're using. It defaults to the mainnet network. + pub network_details: NymNetworkDetails, + + /// Whether to attempt to use gateway with bandwidth credential requirement. + pub enabled_credentials_mode: bool, /// Flags controlling all sorts of internal client behaviour. /// Changing these risk compromising network anonymity! pub debug_config: DebugConfig, } - -impl Default for Config { - fn default() -> Self { - let nym_api_endpoints = vec![mainnet::NYM_API.to_string().parse().unwrap()]; - Self { - user_chosen_gateway: Default::default(), - nym_api_endpoints, - debug_config: Default::default(), - } - } -} - -impl Config { - /// Creates a new [`Config`]. - pub fn new(user_chosen_gateway: Option, nym_api_endpoints: Vec) -> Self { - Self { - user_chosen_gateway, - nym_api_endpoints, - debug_config: DebugConfig::default(), - } - } -} diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index b388d49e36..7d63740681 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -35,6 +35,7 @@ url = { workspace = true } async-file-watcher = { path = "../../common/async-file-watcher" } nym-client-core = { path = "../../common/client-core" } nym-config = { path = "../../common/config" } +nym-credential-storage = { path = "../../common/credential-storage" } nym-crypto = { path = "../../common/crypto" } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-network-defaults = { path = "../../common/network-defaults" } diff --git a/service-providers/network-requester/src/cli/init.rs b/service-providers/network-requester/src/cli/init.rs index 7369e3c38a..17b06e60d2 100644 --- a/service-providers/network-requester/src/cli/init.rs +++ b/service-providers/network-requester/src/cli/init.rs @@ -10,6 +10,7 @@ use crate::{ use clap::Args; use nym_bin_common::output_format::OutputFormat; use nym_config::NymConfig; +use nym_credential_storage::persistent_storage::PersistentStorage; use nym_crypto::asymmetric::identity; use nym_sphinx::addressing::clients::Recipient; use serde::Serialize; @@ -122,7 +123,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { // 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 gateway = nym_client_core::init::setup_gateway_from_config::( + let gateway = nym_client_core::init::setup_gateway_from_config::( register_gateway, user_chosen_gateway_id, config.get_base(), diff --git a/service-providers/network-requester/src/core.rs b/service-providers/network-requester/src/core.rs index 060941f422..d8665a1306 100644 --- a/service-providers/network-requester/src/core.rs +++ b/service-providers/network-requester/src/core.rs @@ -14,6 +14,7 @@ use async_trait::async_trait; use futures::channel::mpsc; use log::warn; use nym_bin_common::build_information::BinaryBuildInformation; +use nym_network_defaults::NymNetworkDetails; use nym_service_providers_common::interface::{ BinaryInformation, ProviderInterfaceVersion, Request, RequestVersion, }; @@ -468,21 +469,15 @@ impl NRServiceProvider { async fn create_mixnet_client( config: &nym_client_core::config::Config, ) -> Result { - let nym_api_endpoints = config.get_nym_api_endpoints(); let debug_config = *config.get_debug_config(); - let mixnet_config = nym_sdk::mixnet::Config { - user_chosen_gateway: None, - nym_api_endpoints, - debug_config, - }; - let storage_paths = nym_sdk::mixnet::StoragePaths::from(config); let mixnet_client = nym_sdk::mixnet::MixnetClientBuilder::new() - .config(mixnet_config) + .network_details(NymNetworkDetails::new_from_env()) + .debug_config(debug_config) .enable_storage(storage_paths) - .gateway_config(config.get_gateway_endpoint_config().clone()) + .registered_gateway(config.get_gateway_endpoint_config().clone()) .build::() .await .map_err(|err| NetworkRequesterError::FailedToSetupMixnetClient { source: err })?;