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
This commit is contained in:
Bogdan-Ștefan Neacşu
2023-04-11 16:17:35 +03:00
committed by GitHub
parent df0b0367a4
commit 1ad97adc7c
69 changed files with 1065 additions and 675 deletions
+2
View File
@@ -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)
Generated
+30 -20
View File
@@ -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",
+1 -1
View File
@@ -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",
+1 -5
View File
@@ -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" }
+8 -88
View File
@@ -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<State> {
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(
&params,
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<C: DkgQueryClient + Send + Sync>(
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<C: DkgQueryClient + Send + Sync>(
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(),
+3 -14
View File
@@ -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,
}
+25 -14
View File
@@ -1,11 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<C: Clone + CosmWasmClient + Send + Sync>(
async fn block_until_coconut_is_available<C: CosmWasmClient + Send + Sync>(
client: &nym_validator_client::Client<C>,
) -> 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(())
+1 -1
View File
@@ -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" }
+7 -3
View File
@@ -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<Client<QueryNyxdClient>> {
) -> BandwidthController<Client<QueryNyxdClient>, 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,
)
}
+2 -1
View File
@@ -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::<Config, _>(
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, PersistentStorage>(
register_gateway,
user_chosen_gateway_id,
config.get_base(),
+1 -1
View File
@@ -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" }
+2 -1
View File
@@ -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::<Config, _>(
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, PersistentStorage>(
register_gateway,
user_chosen_gateway_id,
config.get_base(),
+2 -1
View File
@@ -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" }
+5 -3
View File
@@ -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<BandwidthController<FakeClient<DirectSigningNyxdClient>>>,
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
}
+24
View File
@@ -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"]
@@ -0,0 +1,89 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError>
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(
&params,
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<C: DkgQueryClient + Send + Sync, St: Storage>(
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(())
}
@@ -1,8 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<encryption::KeyPair> 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(),
}
}
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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,
}
@@ -1,28 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<C, St: Storage = PersistentStorage> {
pub struct BandwidthController<C, St: Storage> {
storage: St,
client: C,
}
impl<C, St> BandwidthController<C, St>
where
St: Storage + 'static,
{
impl<C, St: Storage> BandwidthController<C, St> {
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?)
@@ -0,0 +1,17 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<C> {
_phantom: PhantomData<C>,
}
impl<C> DkgQueryClient for Client<C> {}
+2
View File
@@ -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"
@@ -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<bool> 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<Box<dyn TopologyProvider>>,
bandwidth_controller: Option<BandwidthController<C>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
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<T>(
base_config: &'a Config<T>,
key_manager: KeyManager,
bandwidth_controller: Option<BandwidthController<C>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
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<BandwidthController<C>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
reply_storage_backend: B,
credentials_toggle: CredentialsToggle,
nym_api_endpoints: Vec<Url>,
) -> 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<GatewayClient<C>, ClientCoreError> {
) -> Result<GatewayClient<C, St>, 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<C>,
gateway_client: GatewayClient<C, St>,
shutdown: TaskClient,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller...");
+8 -6
View File
@@ -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<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
@@ -19,10 +20,10 @@ pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
const MAX_FAILURE_COUNT: usize = 100;
pub struct MixTrafficController<C> {
pub struct MixTrafficController<C, St: Storage> {
// 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<C>,
gateway_client: GatewayClient<C, St>,
mix_rx: BatchMixMessageReceiver,
// TODO: this is temporary work-around.
@@ -30,13 +31,14 @@ pub struct MixTrafficController<C> {
consecutive_gateway_failure_count: usize,
}
impl<C> MixTrafficController<C>
impl<C, St> MixTrafficController<C, St>
where
C: DkgQueryClient + Sync + Send + 'static,
St: Storage + 'static,
{
pub fn new(
gateway_client: GatewayClient<C>,
) -> (MixTrafficController<C>, BatchMixMessageSender) {
gateway_client: GatewayClient<C, St>,
) -> (MixTrafficController<C, St>, BatchMixMessageSender) {
let (sphinx_message_sender, sphinx_message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
+4 -3
View File
@@ -32,9 +32,10 @@ use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
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<St: Storage>(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Result<Arc<SharedKeys>, ClientCoreError> {
let timeout = Duration::from_millis(1500);
let mut gateway_client: GatewayClient<DirectSigningNyxdClient> = GatewayClient::new_init(
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, St> = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
our_identity.clone(),
+6 -4
View File
@@ -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<St: Storage>(
key_manager: &mut KeyManager,
nym_api_endpoints: Vec<Url>,
chosen_gateway_id: Option<identity::PublicKey>,
@@ -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::<St>(&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<C, T>(
pub async fn setup_gateway_from_config<C, T, St>(
register_gateway: bool,
user_chosen_gateway_id: Option<identity::PublicKey>,
config: &Config<T>,
@@ -109,6 +110,7 @@ pub async fn setup_gateway_from_config<C, T>(
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::<St>(&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
+2 -6
View File
@@ -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"
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// 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<C> {
pub struct GatewayClient<C, St: Storage> {
authenticated: bool,
disabled_credentials_mode: bool,
bandwidth_remaining: i64,
@@ -63,7 +53,7 @@ pub struct GatewayClient<C> {
connection: SocketState,
packet_router: PacketRouter,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, PersistentStorage>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
// reconnection related variables
/// Specifies whether client should try to reconnect to gateway on connection failure.
@@ -78,9 +68,10 @@ pub struct GatewayClient<C> {
shutdown: TaskClient,
}
impl<C> GatewayClient<C>
impl<C, St> GatewayClient<C, St>
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<BandwidthController<C, PersistentStorage>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
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::<C> {
GatewayClient::<C, St> {
authenticated: false,
disabled_credentials_mode: true,
bandwidth_remaining: 0,
+2 -16
View File
@@ -1,14 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// 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,
@@ -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(
@@ -1,80 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<C> {
_phantom: PhantomData<C>,
}
impl<C> DkgQueryClient for Client<C> {}
#[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<CoconutCredential, StorageError>;
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<CoconutCredential, StorageError> {
Err(StorageError::WasmNotSupported)
}
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
Err(StorageError::WasmNotSupported)
}
}
@@ -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;
+9 -2
View File
@@ -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]
@@ -0,0 +1,70 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<RwLock<Vec<CoconutCredential>>>,
}
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<CoconutCredential> {
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;
}
}
}
@@ -0,0 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod memory;
#[cfg(not(target_arch = "wasm32"))]
pub mod sqlite;
@@ -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<Option<CoconutCredential>, 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
@@ -0,0 +1,67 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<CoconutCredential, StorageError> {
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(())
}
}
-20
View File
@@ -1,20 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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 }
}
}
+2
View File
@@ -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),
+14 -99
View File
@@ -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<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
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<CoconutCredential, StorageError> {
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()
}
+1
View File
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[derive(Clone)]
pub struct CoconutCredential {
#[allow(dead_code)]
pub id: i64,
@@ -0,0 +1,99 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
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<CoconutCredential, StorageError> {
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(())
}
}
+1 -1
View File
@@ -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 {
-67
View File
@@ -1,67 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<CoconutCredential, StorageError>;
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<CoconutCredential, StorageError> {
Err(StorageError::AndroidNotSupported)
}
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
Err(StorageError::AndroidNotSupported)
}
}
+27 -15
View File
@@ -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<String>,
@@ -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<ValidatorDetails>,
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"),
)
+3 -3
View File
@@ -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 = []
+23 -15
View File
@@ -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<St: Storage>(
config: &Config,
) -> BandwidthController<Client<QueryNyxdClient>> {
storage: St,
) -> BandwidthController<Client<QueryNyxdClient>, 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<QueryNyxdClient>>::new_from_base_config(
let base_builder = BaseClientBuilder::<_, Client<QueryNyxdClient>, _>::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()),
);
+1
View File
@@ -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" }
+3 -3
View File
@@ -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,
@@ -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<GatewayClientHandleInner>);
struct GatewayClientHandleInner {
client: Mutex<Option<GatewayClient<nyxd::Client>>>,
client: Mutex<Option<GatewayClient<nyxd::Client, PersistentStorage>>>,
raw_identity: [u8; PUBLIC_KEY_LENGTH],
}
pub(crate) struct UnlockedGatewayClientHandle<'a>(
MutexGuard<'a, Option<GatewayClient<nyxd::Client>>>,
MutexGuard<'a, Option<GatewayClient<nyxd::Client, PersistentStorage>>>,
);
impl GatewayClientHandle {
pub(crate) fn new(gateway_client: GatewayClient<nyxd::Client>) -> Self {
pub(crate) fn new(gateway_client: GatewayClient<nyxd::Client, PersistentStorage>) -> 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<nyxd::Client> {
pub(crate) fn get_mut_unchecked(
&mut self,
) -> &mut GatewayClient<nyxd::Client, PersistentStorage> {
self.0.as_mut().unwrap()
}
pub(crate) fn inner_mut(&mut self) -> Option<&mut GatewayClient<nyxd::Client>> {
pub(crate) fn inner_mut(
&mut self,
) -> Option<&mut GatewayClient<nyxd::Client, PersistentStorage>> {
self.0.as_mut()
}
@@ -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<nyxd::Client>,
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
mut mix_packets: Vec<MixPacket>,
max_sending_rate: usize,
) -> Result<(), GatewayClientError> {
@@ -314,7 +314,7 @@ impl PacketSender {
}
async fn check_remaining_bandwidth(
client: &mut GatewayClient<nyxd::Client>,
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
) -> Result<(), GatewayClientError> {
if client.remaining_bandwidth() < REMAINING_BANDWIDTH_THRESHOLD {
Err(GatewayClientError::NotEnoughBandwidth(
+21 -12
View File
@@ -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",
+1
View File
@@ -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" }
@@ -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::<Socks5Config, _>(
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::<Socks5Config, _, PersistentStorage>(
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);
+60 -2
View File
@@ -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",
+2 -1
View File
@@ -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 }
@@ -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::<EphemeralStorage>(
&mut key_manager,
nym_api_endpoints,
Some(chosen_gateway_id),
+5 -1
View File
@@ -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"] }
+42
View File
@@ -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::<mixnet::EmptyReplyStorage>()
.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;
}
@@ -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::<mixnet::EmptyReplyStorage>()
.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::<mixnet::EmptyReplyStorage>()
.await
.unwrap()
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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::<mixnet::EmptyReplyStorage>()
//! .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};
+67
View File
@@ -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<u8>;
/// 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<SigningNyxdClient<DirectSecp256k1HdWallet>>,
storage: EphemeralStorage,
}
impl BandwidthAcquireClient {
pub(crate) fn new(
network_details: NymNetworkDetails,
mnemonic: String,
storage: EphemeralStorage,
) -> Result<Self> {
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(())
}
}
+24
View File
@@ -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<dyn std::error::Error + Send + Sync>,
+1
View File
@@ -4,6 +4,7 @@
mod error;
pub mod bandwidth;
pub mod mixnet;
pub use error::{Error, Result};
+1
View File
@@ -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},
+88 -23
View File
@@ -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: Config,
storage_paths: Option<StoragePaths>,
keys: Option<Keys>,
gateway_config: Option<GatewayEndpointConfig>,
@@ -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,
<B as ReplyStorageBackend>::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<Client<QueryNyxdClient>, EphemeralStorage>,
/// The storage backend for reply-SURBs
reply_storage_backend: B,
@@ -151,14 +187,15 @@ impl<B> DisconnectedMixnetClient<B>
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: Config,
socks5_config: Option<Socks5>,
paths: Option<StoragePaths>,
custom_topology_provider: Option<Box<dyn TopologyProvider>>,
@@ -166,9 +203,13 @@ where
where
<B as ReplyStorageBackend>::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<Url> {
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::<EphemeralStorage>(
&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<BandwidthAcquireClient> {
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
<B as ReplyStorageBackend>::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<DirectSigningNyxdClient>> =
let mut base_builder: BaseClientBuilder<'_, _, Client<QueryNyxdClient>, 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 {
+7 -26
View File
@@ -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<String>,
/// List of nym-api endpoints
pub nym_api_endpoints: Vec<Url>,
/// 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<String>, nym_api_endpoints: Vec<Url>) -> Self {
Self {
user_chosen_gateway,
nym_api_endpoints,
debug_config: DebugConfig::default(),
}
}
}
@@ -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" }
@@ -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::<Config, _>(
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, PersistentStorage>(
register_gateway,
user_chosen_gateway_id,
config.get_base(),
@@ -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<T>(
config: &nym_client_core::config::Config<T>,
) -> Result<nym_sdk::mixnet::MixnetClient, NetworkRequesterError> {
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::<nym_sdk::mixnet::ReplyStorage>()
.await
.map_err(|err| NetworkRequesterError::FailedToSetupMixnetClient { source: err })?;