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:
committed by
GitHub
parent
df0b0367a4
commit
1ad97adc7c
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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(
|
||||
¶ms,
|
||||
amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_credential<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(),
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
¶ms,
|
||||
voucher_value,
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub async fn get_credential<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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
+16
-41
@@ -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> {}
|
||||
@@ -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...");
|
||||
|
||||
@@ -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);
|
||||
(
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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;
|
||||
+5
-5
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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,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(())
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
)
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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()),
|
||||
);
|
||||
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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(
|
||||
|
||||
Generated
+21
-12
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Generated
+60
-2
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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};
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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>,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
mod error;
|
||||
|
||||
pub mod bandwidth;
|
||||
pub mod mixnet;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 })?;
|
||||
|
||||
Reference in New Issue
Block a user