Compare commits

..

25 Commits

Author SHA1 Message Date
pierre 66707004e3 wip 2023-04-21 08:51:37 +02:00
pierre baa3691851 update to latest alpha 2023-04-19 16:25:06 +02:00
Jon Häggblad 6e30e6178b Update Cargo.lock files after bumping internal versions during release 2023-04-19 09:37:38 +02:00
Tommy Verrall 7c65d61d91 Merge pull request #3321 from nymtech/bug/explorer-fix-stake-saturation-sorting
Convert stake saturation string to number type
2023-04-18 13:57:07 +01:00
farbanas ad2efb7e62 Merge branch 'master' into develop 2023-04-18 14:35:56 +02:00
Drazen Urch a50c9bfa1a Outfox compatibility layer (#3317)
* Outfox compatibility layer

* Lint
2023-04-18 14:07:34 +02:00
mx 30cdbf535a added polkachu snapshot links 2023-04-18 13:26:02 +02:00
mx f5365cbca9 reintroduced laddr change to config setup instructions 2023-04-18 13:11:13 +02:00
mx 22e7cb887b final tweaks 2023-04-18 13:08:21 +02:00
fmtabbara b571f1a881 convert stake saturation string to number type 2023-04-18 11:32:35 +01:00
mx b1c4e3ded7 hotfix on sandbox testnet naming 2023-04-18 12:30:44 +02:00
mx 4694ded8bd updated platform release version variable 2023-04-18 12:17:47 +02:00
mx 7971573026 hotfix on sandbox testnet naming 2023-04-18 12:16:49 +02:00
Jon Häggblad c4780c8af2 Rework Poisson process throttling to more aggressively up-regulate (#3298)
* Rework Poisson process throttling

* Tweak parameters
2023-04-18 12:07:54 +02:00
Jon Häggblad 7879d76592 Reduce logging of warnings associated with Poisson process throttling (#3314)
* Reduce logging of warnings associated with Poisson process throttling

* More granularity when logging about slow gateway

* Refine logging of elevated delay multiplier

* clippy

* Tweak output when printing address

* rustfmt

* Fix initial delay
2023-04-18 11:40:50 +02:00
Bogdan-Ștefan Neacşu 0f62ea25d8 Fix nym-cli clap requirement (#3303) 2023-04-18 12:31:42 +03:00
Pierre Dommerc cde53c02e7 feat(wallet): add security settings (#3262) 2023-04-17 16:36:39 +02:00
Pierre Dommerc de64da8e20 feat(wallet): app version check (#3308) 2023-04-17 15:12:26 +02:00
Jędrzej Stuczyński 4aeac1acd2 Revert "another broken test"
This reverts commit 679be24074.
2023-04-14 16:56:26 +01:00
Jędrzej Stuczyński 679be24074 another broken test 2023-04-14 16:52:24 +01:00
Nadim Kobeissi 36e07f546d Merge pull request #3304 from nymtech/nadimkobeissi-fix-readme
Update README.md
2023-04-14 11:19:30 +02:00
Nadim Kobeissi d264feaf22 Update README.md
The link for build instructions was incorrect.
2023-04-14 11:19:11 +02:00
Dave Hrycyszyn 32b7b7afdc Update SECURITY.md 2023-04-13 12:23:01 +01:00
Jon Häggblad 091382ef30 Tidy the service provider directory contract (#3295)
Tidy the service provider directory contract after implementing query
methods on the validator-client.

- split out response types and use consistently
- query msg use "By" prefix
- pass address as String and validator
2023-04-12 09:22:35 +02:00
Bogdan-Ștefan Neacşu 1ad97adc7c 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
2023-04-11 16:17:35 +03:00
131 changed files with 4333 additions and 1514 deletions
+10
View File
@@ -4,6 +4,15 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
- 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.15] (2023-04-18)
- Fix verloc being stuck waiting for shutdown signal ([#3250])
@@ -41,6 +50,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#3187]: https://github.com/nymtech/nym/issues/3187
[#3203]: https://github.com/nymtech/nym/pull/3203
[#3199]: https://github.com/nymtech/nym/pull/3199
>>>>>>> master
## [v1.1.13] (2023-03-15)
Generated
+67 -33
View File
@@ -3031,6 +3031,7 @@ dependencies = [
"lazy_static",
"log",
"nym-api-requests",
"nym-bandwidth-controller",
"nym-bin-common",
"nym-coconut",
"nym-coconut-bandwidth-contract-common",
@@ -3089,6 +3090,22 @@ dependencies = [
"ts-rs",
]
[[package]]
name = "nym-bandwidth-controller"
version = "0.1.0"
dependencies = [
"bip39",
"nym-coconut-interface",
"nym-credential-storage",
"nym-credentials",
"nym-crypto",
"nym-network-defaults",
"nym-validator-client",
"rand 0.7.3",
"thiserror",
"url",
]
[[package]]
name = "nym-bin-common"
version = "0.5.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",
@@ -3804,6 +3810,7 @@ dependencies = [
"fastrand",
"getrandom 0.2.8",
"rayon",
"sphinx-packet 0.1.0 (git+https://github.com/nymtech/sphinx.git)",
"thiserror",
"zeroize",
]
@@ -3819,12 +3826,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 +3888,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",
@@ -3901,19 +3912,8 @@ name = "nym-socks5-client-core"
version = "0.1.0"
dependencies = [
"dirs",
"futures",
"log",
"nym-client-core",
"nym-config",
"nym-credential-storage",
"nym-gateway-client",
"nym-network-defaults",
"nym-service-providers-common",
"nym-socks5-proxy-helpers",
"nym-socks5-requests",
"nym-sphinx",
"nym-task",
"nym-validator-client",
"pin-project",
"rand 0.7.3",
"serde",
@@ -4046,6 +4046,7 @@ dependencies = [
name = "nym-sphinx-forwarding"
version = "0.1.0"
dependencies = [
"nym-outfox",
"nym-sphinx-addressing",
"nym-sphinx-params",
"nym-sphinx-types",
@@ -4076,7 +4077,7 @@ dependencies = [
name = "nym-sphinx-types"
version = "0.2.0"
dependencies = [
"sphinx-packet",
"sphinx-packet 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -4190,7 +4191,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract"
version = "1.3.0"
version = "1.3.1"
dependencies = [
"cosmwasm-derive",
"cosmwasm-std",
@@ -4208,7 +4209,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract-common"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"cosmwasm-std",
"nym-contracts-common",
@@ -4306,6 +4307,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "111.25.2+1.1.1t"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.82"
@@ -4315,6 +4325,7 @@ dependencies = [
"autocfg 1.1.0",
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
@@ -5877,6 +5888,29 @@ dependencies = [
"subtle 2.4.1",
]
[[package]]
name = "sphinx-packet"
version = "0.1.0"
source = "git+https://github.com/nymtech/sphinx.git#ca107d94360cdf8bbfbdb12fe5320ed74f80e40c"
dependencies = [
"aes 0.7.5",
"arrayref",
"blake2",
"bs58",
"byteorder",
"chacha",
"curve25519-dalek",
"digest 0.9.0",
"hkdf 0.11.0",
"hmac 0.11.0",
"lioness",
"log",
"rand 0.7.3",
"rand_distr",
"sha2 0.9.9",
"subtle 2.4.1",
]
[[package]]
name = "spin"
version = "0.5.2"
+1 -1
View File
@@ -22,6 +22,7 @@ members = [
"clients/native/websocket-requests",
"clients/socks5",
"common/async-file-watcher",
"common/bandwidth-controller",
"common/bin-common",
"common/client-core",
"common/client-libs/gateway-client",
@@ -38,7 +39,6 @@ members = [
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/service-provider-directory",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/mobile-storage",
"common/credential-storage",
"common/credentials",
"common/crypto",
+1 -1
View File
@@ -21,7 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym).
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/building-nym.html).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
### Developing
+3 -3
View File
@@ -3,8 +3,8 @@ Critical bug or security issue 💥
If you're here because you're trying to figure out how to notify us of a security issue, go to Discord, and alert the core engineers:
Dave Hrycyszyn futurechimp#5430
Drazen Urch drazen#4873
Jedrzej Stuczynski "Jedrzej | Nym#5666"
Fran Arbanas | franarbanas#0995
Mark Sinclair | marknym#8088
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
+1 -5
View File
@@ -6,20 +6,16 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bip39 = { workspace = true }
clap = { version = "4.0", features = ["cargo", "derive"] }
log = "0.4"
rand = "0.7.3"
serde = { workspace = true, features = ["derive"] }
thiserror = "1.0"
url = "2.2"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
nym-bin-common = { path = "../../common/bin-common"}
nym-network-defaults = { path = "../../common/network-defaults" }
nym-pemstore = { path = "../../common/pemstore" }
+8 -88
View File
@@ -3,24 +3,13 @@
use clap::{ArgGroup, Args, Subcommand};
use log::*;
use nym_bandwidth_controller::acquire::state::State;
use nym_bin_common::completions::ArgShell;
use nym_coconut_interface::{Base58, Parameters};
use nym_credential_storage::storage::Storage;
use nym_credential_storage::PersistentStorage;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_validator_client::nyxd::traits::DkgQueryClient;
use nym_validator_client::nyxd::tx::Hash;
use nym_validator_client::CoconutApiClient;
use rand::rngs::OsRng;
use std::str::FromStr;
use crate::client::Client;
use crate::error::{CredentialClientError, Result};
use crate::error::Result;
use crate::recovery_storage::RecoveryStorage;
use crate::state::{KeyPair, State};
#[derive(Subcommand)]
pub(crate) enum Command {
@@ -45,10 +34,6 @@ pub(crate) struct Run {
#[clap(long)]
pub(crate) client_home_directory: std::path::PathBuf,
/// The nyxd URL that should be used
#[clap(long)]
pub(crate) nyxd_url: String,
/// A mnemonic for the account that buys the credential
#[clap(long)]
pub(crate) mnemonic: String,
@@ -67,81 +52,16 @@ pub(crate) struct Run {
pub(crate) recovery_mode: bool,
}
pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
let mut rng = OsRng;
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let client = Client::new(nyxd_url, mnemonic);
let tx_hash = client
.deposit(
amount,
signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(),
None,
)
.await?;
let voucher = BandwidthVoucher::new(
&params,
amount.to_string(),
VOUCHER_INFO.to_string(),
Hash::from_str(&tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
);
let state = State { voucher, params };
Ok(state)
}
pub(crate) async fn get_credential<C: DkgQueryClient + Send + Sync>(
state: &State,
client: &C,
shared_storage: PersistentStorage,
) -> Result<()> {
let epoch_id = client.get_current_epoch().await?.epoch_id;
let threshold = client
.get_current_epoch_threshold()
.await?
.ok_or(CredentialClientError::NoThreshold)?;
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?;
let signature = obtain_aggregate_signature(
&state.params,
&state.voucher,
&coconut_api_clients,
threshold,
)
.await?;
info!("Signature: {:?}", signature.to_bs58());
shared_storage
.insert_coconut_credential(
state.voucher.get_voucher_value(),
VOUCHER_INFO.to_string(),
state.voucher.get_private_attributes()[0].to_bs58(),
state.voucher.get_private_attributes()[1].to_bs58(),
signature.to_bs58(),
epoch_id.to_string(),
)
.await?;
Ok(())
}
pub(crate) async fn recover_credentials<C: DkgQueryClient + Send + Sync>(
client: &C,
recovery_storage: &RecoveryStorage,
shared_storage: PersistentStorage,
shared_storage: &PersistentStorage,
) -> Result<()> {
for voucher in recovery_storage.unconsumed_vouchers()? {
let state = State {
voucher,
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
};
if let Err(e) = get_credential(&state, client, shared_storage.clone()).await {
let state = State::new(voucher);
if let Err(e) =
nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await
{
error!(
"Could not recover deposit {} due to {:?}, try again later",
state.voucher.tx_hash(),
+3 -14
View File
@@ -6,8 +6,6 @@ use thiserror::Error;
use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::ValidatorClientError;
@@ -18,6 +16,9 @@ pub enum CredentialClientError {
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
#[error("Bandwidth controller error: {0}")]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error("Nyxd error: {0}")]
Nyxd(#[from] NyxdError),
@@ -27,21 +28,9 @@ pub enum CredentialClientError {
#[error("Credential error: {0}")]
Credential(#[from] CredentialError),
#[error("The tx hash provided is not valid")]
InvalidTxHash,
#[error("Could not parse Ed25519 data")]
Ed25519ParseError(#[from] Ed25519RecoveryError),
#[error("Could not parse X25519 data")]
X25519ParseError(#[from] KeyRecoveryError),
#[error("Could not use shared storage")]
SharedStorageError(#[from] StorageError),
#[error("Could not get system time")]
SysTimeError(#[from] SystemTimeError),
#[error("Threshold not set yet")]
NoThreshold,
}
+25 -14
View File
@@ -1,11 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod client;
mod commands;
mod error;
mod recovery_storage;
mod state;
use commands::*;
use error::Result;
@@ -19,7 +17,7 @@ use std::time::{Duration, SystemTime};
use clap::{CommandFactory, Parser};
use nym_bin_common::logging::setup_logging;
use nym_validator_client::nyxd::traits::DkgQueryClient;
use nym_validator_client::nyxd::CosmWasmClient;
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
use nym_validator_client::Config;
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
@@ -35,7 +33,7 @@ struct Cli {
pub(crate) command: Command,
}
async fn block_until_coconut_is_available<C: Clone + CosmWasmClient + Send + Sync>(
async fn block_until_coconut_is_available<C: CosmWasmClient + Send + Sync>(
client: &nym_validator_client::Client<C>,
) -> Result<()> {
loop {
@@ -77,21 +75,34 @@ async fn main() -> Result<()> {
.client_home_directory
.join(DATA_DIR)
.join(CRED_DB_FILE_NAME);
let shared_storage = nym_credential_storage::initialise_storage(db_path).await;
let shared_storage =
nym_credential_storage::initialise_persistent_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
let network_details = NymNetworkDetails::new_from_env();
let config = Config::try_from_nym_network_details(&network_details)?;
let client = nym_validator_client::Client::new_query(config)?;
let config = Config::try_from_nym_network_details(&network_details).expect(
"failed to construct valid validator client config with the provided network",
);
let amount = Coin::new(
r.amount as u128,
network_details.chain_details.mix_denom.base,
);
let client =
nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?;
block_until_coconut_is_available(&client).await?;
info!("Starting depositing funds, don't kill the process");
if !r.recovery_mode {
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
if get_credential(&state, &client.nyxd, shared_storage)
.await
.is_err()
let state =
nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?;
if nym_bandwidth_controller::acquire::get_credential(
&state,
&client,
&shared_storage,
)
.await
.is_err()
{
warn!("Failed to obtain credential. Dumping recovery data.",);
match recovery_storage.insert_voucher(&state.voucher) {
@@ -104,11 +115,11 @@ async fn main() -> Result<()> {
}
}
} else {
recover_credentials(&client.nyxd, &recovery_storage, shared_storage).await?;
recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?;
}
}
Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name),
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
Command::Completions(c) => c.generate(&mut Cli::command(), bin_name),
Command::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
}
Ok(())
+1 -1
View File
@@ -34,6 +34,7 @@ tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
tokio-tungstenite = "0.14" # websocket
## internal
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] }
nym-coconut-interface = { path = "../../common/coconut-interface" }
@@ -41,7 +42,6 @@ nym-config = { path = "../../common/config" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
nym-gateway-client = { path = "../../common/client-libs/gateway-client" }
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../common/network-defaults" }
nym-sphinx = { path = "../../common/nymsphinx" }
+7 -3
View File
@@ -6,6 +6,7 @@ use crate::error::ClientError;
use crate::websocket;
use futures::channel::mpsc;
use log::*;
use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::base_client::{
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
};
@@ -14,7 +15,6 @@ use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
};
use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use nym_gateway_client::bandwidth::BandwidthController;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_task::connections::TransmissionLane;
use nym_task::TaskManager;
@@ -23,6 +23,7 @@ use std::error::Error;
use tokio::sync::watch::error::SendError;
pub use nym_client_core::client::key_manager::KeyManager;
use nym_credential_storage::persistent_storage::PersistentStorage;
pub use nym_sphinx::addressing::clients::Recipient;
pub use nym_sphinx::receiver::ReconstructedMessage;
use nym_validator_client::Client;
@@ -58,7 +59,7 @@ impl SocketClient {
async fn create_bandwidth_controller(
config: &Config,
) -> BandwidthController<Client<QueryNyxdClient>> {
) -> BandwidthController<Client<QueryNyxdClient>, PersistentStorage> {
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
let mut client_config =
nym_validator_client::Config::try_from_nym_network_details(&details)
@@ -78,7 +79,10 @@ impl SocketClient {
let client = nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
BandwidthController::new(
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await,
nym_credential_storage::initialise_persistent_storage(
config.get_base().get_database_path(),
)
.await,
client,
)
}
+3 -2
View File
@@ -10,6 +10,7 @@ use crate::{
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_config::NymConfig;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize;
@@ -114,7 +115,7 @@ impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.client_core)?;
writeln!(f, "Client listening port: {}", self.client_listening_port)?;
write!(f, "address of this client: {}", self.client_address)
write!(f, "Address of this client: {}", self.client_address)
}
}
@@ -151,7 +152,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _>(
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, PersistentStorage>(
register_gateway,
user_chosen_gateway_id,
config.get_base(),
+1 -1
View File
@@ -23,10 +23,10 @@ nym-bin-common = { path = "../../common/bin-common", features = ["output_format"
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
nym-mobile-storage = { path = "../../common/mobile-storage", optional = true }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-network-defaults = { path = "../../common/network-defaults" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
+4 -4
View File
@@ -9,6 +9,7 @@ use crate::{
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_config::NymConfig;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::config::Config;
use nym_sphinx::addressing::clients::Recipient;
@@ -115,8 +116,8 @@ impl InitResults {
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.client_core)?;
write!(f, "SOCKS5 listening port: {}", self.socks5_listening_port)?;
write!(f, "address of this client: {}", self.client_address)
writeln!(f, "SOCKS5 listening port: {}", self.socks5_listening_port)?;
write!(f, "Address of this client: {}", self.client_address)
}
}
@@ -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(),
@@ -180,7 +181,6 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
let init_results = InitResults::new(&config, &address);
println!("{}", args.output.format(&init_results));
eprintln!("\nThe address of this client is: {}\n", address);
Ok(())
}
+2 -1
View File
@@ -31,11 +31,12 @@ wasm-bindgen-futures = "0.4"
# internal
nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] }
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-crypto = { path = "../../common/crypto" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
wasm-utils = { path = "../../common/wasm-utils" }
nym-task = { path = "../../common/task" }
+5 -3
View File
@@ -5,13 +5,14 @@ use self::config::Config;
use crate::client::helpers::InputSender;
use crate::client::response_pusher::ResponsePusher;
use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, CredentialsToggle,
};
use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager};
use nym_gateway_client::bandwidth::BandwidthController;
use nym_gateway_client::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_task::connections::TransmissionLane;
@@ -48,7 +49,8 @@ pub struct NymClientBuilder {
on_message: js_sys::Function,
// unimplemented:
bandwidth_controller: Option<BandwidthController<FakeClient<DirectSigningNyxdClient>>>,
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
}
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "nym-bandwidth-controller"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bip39 = { workspace = true }
rand = "0.7.3"
thiserror = "1.0"
url = "2.2"
nym-coconut-interface = { path = "../coconut-interface" }
nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
nym-network-defaults = { path = "../network-defaults" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
path = "../client-libs/validator-client"
features = ["nyxd-client"]
@@ -0,0 +1,89 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError;
use nym_coconut_interface::{Base58, Parameters};
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO;
use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient;
use nym_validator_client::nyxd::traits::DkgQueryClient;
use nym_validator_client::nyxd::tx::Hash;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::CoconutApiClient;
use rand::rngs::OsRng;
use state::{KeyPair, State};
use std::str::FromStr;
pub mod state;
pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError>
where
C: CoconutBandwidthSigningClient,
{
let mut rng = OsRng;
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let voucher_value = amount.amount.to_string();
let tx_hash = client
.deposit(
amount,
String::from(VOUCHER_INFO),
signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(),
None,
)
.await?
.transaction_hash
.to_string();
let voucher = BandwidthVoucher::new(
&params,
voucher_value,
VOUCHER_INFO.to_string(),
Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?,
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
);
let state = State { voucher, params };
Ok(state)
}
pub async fn get_credential<C: DkgQueryClient + Send + Sync, St: Storage>(
state: &State,
client: &C,
storage: &St,
) -> Result<(), BandwidthControllerError> {
let epoch_id = client.get_current_epoch().await?.epoch_id;
let threshold = client
.get_current_epoch_threshold()
.await?
.ok_or(BandwidthControllerError::NoThreshold)?;
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?;
let signature = obtain_aggregate_signature(
&state.params,
&state.voucher,
&coconut_api_clients,
threshold,
)
.await?;
storage
.insert_coconut_credential(
state.voucher.get_voucher_value(),
VOUCHER_INFO.to_string(),
state.voucher.get_private_attributes()[0].to_bs58(),
state.voucher.get_private_attributes()[1].to_bs58(),
signature.to_bs58(),
epoch_id.to_string(),
)
.await?;
Ok(())
}
@@ -1,8 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_coconut_interface::Parameters;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_crypto::asymmetric::{encryption, identity};
@@ -29,7 +29,16 @@ impl From<encryption::KeyPair> for KeyPair {
}
}
pub(crate) struct State {
pub struct State {
pub voucher: BandwidthVoucher,
pub params: Parameters,
}
impl State {
pub fn new(voucher: BandwidthVoucher) -> Self {
State {
voucher,
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
}
}
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_coconut_interface::CoconutError;
use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialsError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_validator_client::error::ValidatorClientError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BandwidthControllerError {
#[cfg(not(target_arch = "wasm32"))]
#[error("Nyxd error: {0}")]
Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError),
#[error("There was a credential storage error - {0}")]
CredentialStorageError(#[from] StorageError),
#[error("Coconut error - {0}")]
CoconutError(#[from] CoconutError),
#[error("Validator client error - {0}")]
ValidatorError(#[from] ValidatorClientError),
#[error("Credential error - {0}")]
CredentialError(#[from] CredentialsError),
#[error("Could not parse Ed25519 data")]
Ed25519ParseError(#[from] Ed25519RecoveryError),
#[error("Could not parse X25519 data")]
X25519ParseError(#[from] KeyRecoveryError),
#[error("The tx hash provided is not valid")]
InvalidTxHash,
#[error("Threshold not set yet")]
NoThreshold,
}
@@ -1,28 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError;
use crate::error::BandwidthControllerError;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "android"))]
use nym_credential_storage::storage::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(target_os = "android")]
use mobile_storage::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(target_os = "android")]
use mobile_storage::StorageError;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::StorageError;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "android"))]
use nym_credential_storage::error::StorageError;
use nym_credential_storage::storage::Storage;
use std::str::FromStr;
use {
@@ -35,38 +17,32 @@ use {
#[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::traits::DkgQueryClient;
// TODO: make it nicer for wasm (I don't want to touch it for this experiment)
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::PersistentStorage;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::DkgQueryClient;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "android"))]
use nym_credential_storage::PersistentStorage;
pub mod acquire;
pub mod error;
#[cfg(target_arch = "wasm32")]
pub mod wasm_mockups;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(target_os = "android")]
use mobile_storage::PersistentStorage;
#[allow(dead_code)]
pub struct BandwidthController<C, St: Storage = PersistentStorage> {
pub struct BandwidthController<C, St: Storage> {
storage: St,
client: C,
}
impl<C, St> BandwidthController<C, St>
where
St: Storage + 'static,
{
impl<C, St: Storage> BandwidthController<C, St> {
pub fn new(storage: St, client: C) -> Self {
BandwidthController { storage, client }
}
pub fn storage(&self) -> &St {
&self.storage
}
pub async fn prepare_coconut_credential(
&self,
) -> Result<(nym_coconut_interface::Credential, i64), GatewayClientError>
) -> Result<(nym_coconut_interface::Credential, i64), BandwidthControllerError>
where
C: DkgQueryClient + Sync + Send,
{
@@ -86,8 +62,7 @@ where
#[cfg(not(target_arch = "wasm32"))]
let coconut_api_clients =
nym_validator_client::CoconutApiClient::all_coconut_api_clients(&self.client, epoch_id)
.await
.expect("Could not query api clients");
.await?;
#[cfg(target_arch = "wasm32")]
let coconut_api_clients = vec![];
let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?;
@@ -107,7 +82,7 @@ where
))
}
pub async fn consume_credential(&self, id: i64) -> Result<(), GatewayClientError> {
pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError> {
// JS: shouldn't we send some contract/validator/gateway message here to actually, you know,
// consume it?
Ok(self.storage.consume_coconut_credential(id).await?)
@@ -0,0 +1,17 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::marker::PhantomData;
pub struct DirectSigningNyxdClient {}
pub trait DkgQueryClient {}
// impl CosmWasmClient for DirectSigningNyxdClient {}
#[derive(Clone)]
pub struct Client<C> {
_phantom: PhantomData<C>,
}
impl<C> DkgQueryClient for Client<C> {}
+3 -1
View File
@@ -25,6 +25,7 @@ tokio = { version = "1.24.1", features = ["macros"]}
time = "0.3.17"
# internal
nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-config = { path = "../config" }
nym-crypto = { path = "../crypto" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
@@ -36,6 +37,7 @@ nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
path = "../client-libs/validator-client"
@@ -52,7 +54,7 @@ features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
[target."cfg(all(not(target_arch = \"wasm32\"),not(target_os = \"android\")))".dependencies.sqlx]
version = "0.6.2"
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
@@ -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...");
@@ -203,10 +203,6 @@ impl LoopCoverTrafficStream<OsRng> {
// This isn't a problem, if the channel is full means we're already sending the
// max amount of messages downstream can handle.
log::debug!("Failed to send cover message - channel full");
// However it's still useful to alert the user that the gateway or the link to
// the gateway can't keep up. Either due to insufficient bandwidth on the
// client side, or that the gateway is overloaded.
log::warn!("Failed to send sphinx packet - gateway or connection to gateway can't keep up");
}
TrySendError::Closed(_) => {
log::warn!("Failed to send cover message - channel closed");
+8 -6
View File
@@ -6,11 +6,12 @@ use log::*;
use nym_gateway_client::GatewayClient;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_credential_storage::storage::Storage;
#[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::traits::DkgQueryClient;
#[cfg(target_arch = "wasm32")]
use nym_gateway_client::wasm_mockups::DkgQueryClient;
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
@@ -19,10 +20,10 @@ pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
const MAX_FAILURE_COUNT: usize = 100;
pub struct MixTrafficController<C> {
pub struct MixTrafficController<C, St: Storage> {
// TODO: most likely to be replaced by some higher level construct as
// later on gateway_client will need to be accessible by other entities
gateway_client: GatewayClient<C>,
gateway_client: GatewayClient<C, St>,
mix_rx: BatchMixMessageReceiver,
// TODO: this is temporary work-around.
@@ -30,13 +31,14 @@ pub struct MixTrafficController<C> {
consecutive_gateway_failure_count: usize,
}
impl<C> MixTrafficController<C>
impl<C, St> MixTrafficController<C, St>
where
C: DkgQueryClient + Sync + Send + 'static,
St: Storage + 'static,
{
pub fn new(
gateway_client: GatewayClient<C>,
) -> (MixTrafficController<C>, BatchMixMessageSender) {
gateway_client: GatewayClient<C, St>,
) -> (MixTrafficController<C, St>, BatchMixMessageSender) {
let (sphinx_message_sender, sphinx_message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
@@ -302,24 +302,34 @@ where
self.sending_delay_controller.current_multiplier()
);
// Even just a single used slot is enough to signal backpressure
if used_slots > 0 {
if self
.sending_delay_controller
.is_backpressure_currently_detected(used_slots)
{
log::trace!("Backpressure detected");
self.sending_delay_controller.record_backpressure_detected();
}
// If the buffer is running out, slow down the sending rate
// If the buffer is running out, slow down the sending rate by increasing the delay
// multiplier.
if self.mix_tx.capacity() == 0
&& self.sending_delay_controller.not_increased_delay_recently()
{
self.sending_delay_controller.increase_delay_multiplier();
}
// Very carefully step up the sending rate in case it seems like we can solidly handle the
// current rate.
if self.sending_delay_controller.is_sending_reliable() {
// If it looks like we are sending reliably, increase the sending rate by decreasing the
// sending delay multiplier.
if !self
.sending_delay_controller
.was_backpressure_detected_recently()
&& self.sending_delay_controller.not_decreased_delay_recently()
{
self.sending_delay_controller.decrease_delay_multiplier();
}
// Keep track of multiplier changes, and log if necessary.
self.sending_delay_controller.record_delay_multiplier();
}
fn pop_next_message(&mut self) -> Option<RealMessage> {
@@ -498,23 +508,12 @@ where
}
}
#[cfg(not(target_arch = "wasm32"))]
fn log_status_infrequent(&self) {
if self.sending_delay_controller.current_multiplier() > 1 {
log::warn!(
"Unable to send packets at the default rate - rate reduced by setting the delay multiplier set to: {}",
self.sending_delay_controller.current_multiplier()
);
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started OutQueueControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
{
let mut status_timer = tokio::time::interval(Duration::from_secs(5));
let mut infrequent_status_timer = tokio::time::interval(Duration::from_secs(60));
while !shutdown.is_shutdown() {
tokio::select! {
@@ -525,9 +524,6 @@ where
_ = status_timer.tick() => {
self.log_status(&mut shutdown);
}
_ = infrequent_status_timer.tick() => {
self.log_status_infrequent();
}
next_message = self.next() => if let Some(next_message) = next_message {
self.on_message(next_message).await;
} else {
@@ -11,15 +11,20 @@ const INCREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 1;
// The minimum time between decreasing the average delay between packets. We don't want to change
// to quickly to keep things somewhat stable. Also there are buffers downstreams meaning we need to
// wait a little to see the effect before we decrease further.
const DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 30;
const DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 2;
// The queue length that is required for us to register that backpressure occured. If there are
// more than this many packets waiting to be sent, we consider the channel to be under
// backpressure.
const BACKPRESSURE_THRESHOLD: usize = 10;
// If we enough time passes without any sign of backpressure in the channel, we can consider
// lowering the average delay. The goal is to keep somewhat stable, rather than maxing out
// bandwidth at all times.
const ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS: u64 = 30;
// lowering the average delay.
const ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS: u64 = 2;
// The maximum multiplier we apply to the base average Poisson delay.
const MAX_DELAY_MULTIPLIER: u32 = 6;
// The minium multiplier we apply to the base average Poisson delay.
const MIN_DELAY_MULTIPLIER: u32 = 1;
// If the multipler increases we log it, but we don't want to log about it too often.
const INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS: u64 = 60;
pub(crate) struct SendingDelayController {
/// Multiply the average sending delay.
@@ -33,6 +38,14 @@ pub(crate) struct SendingDelayController {
/// Minimum delay multiplier
lower_bound: u32,
/// We counter the number of times the multiplier has been elevated. If it is elevated for long
/// enough we need to log about it.
multiplier_elevated_counter: u32,
/// We can't log about the elevated multiplier too often, so we keep track of the last time we
/// did,
time_when_logged_about_elevated_multiplier: Instant,
/// To make sure we don't change the multiplier to fast, we limit a change to some duration
time_when_changed: Instant,
@@ -55,6 +68,9 @@ impl SendingDelayController {
current_multiplier: MIN_DELAY_MULTIPLIER,
upper_bound,
lower_bound,
multiplier_elevated_counter: 0,
time_when_logged_about_elevated_multiplier: now
- Duration::from_secs(INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS),
time_when_changed: now,
time_when_backpressure_detected: now,
}
@@ -79,7 +95,7 @@ impl SendingDelayController {
self.current_multiplier =
(self.current_multiplier + 1).clamp(self.lower_bound, self.upper_bound);
self.time_when_changed = get_time_now();
log::warn!(
log::debug!(
"Increasing sending delay multiplier to: {}",
self.current_multiplier
);
@@ -100,22 +116,58 @@ impl SendingDelayController {
}
}
pub(crate) fn record_backpressure_detected(&mut self) {
self.time_when_backpressure_detected = get_time_now();
}
pub(crate) fn not_increased_delay_recently(&self) -> bool {
get_time_now()
> self.time_when_changed + Duration::from_secs(INCREASE_DELAY_MIN_CHANGE_INTERVAL_SECS)
}
pub(crate) fn is_sending_reliable(&self) -> bool {
let now = get_time_now();
let delay_change_interval = Duration::from_secs(DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS);
let acceptable_time_without_backpressure =
Duration::from_secs(ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS);
pub(crate) fn not_decreased_delay_recently(&self) -> bool {
get_time_now()
> self.time_when_changed + Duration::from_secs(DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS)
}
now > self.time_when_backpressure_detected + acceptable_time_without_backpressure
&& now > self.time_when_changed + delay_change_interval
pub(crate) fn is_backpressure_currently_detected(&self, queue_length: usize) -> bool {
queue_length > BACKPRESSURE_THRESHOLD
}
pub(crate) fn record_backpressure_detected(&mut self) {
self.time_when_backpressure_detected = get_time_now();
}
pub(crate) fn was_backpressure_detected_recently(&self) -> bool {
get_time_now()
< self.time_when_backpressure_detected
+ Duration::from_secs(ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS)
}
pub(crate) fn record_delay_multiplier(&mut self) {
// Count the number of times the multiplier has been elevated.
let multiplier_elevated = self.current_multiplier - self.lower_bound;
if multiplier_elevated == 0 {
self.multiplier_elevated_counter = 0;
} else {
self.multiplier_elevated_counter += 1;
}
// If needed, log about the elevated multiplier.
let now = get_time_now();
if self.multiplier_elevated_counter > 20
&& now
> self.time_when_logged_about_elevated_multiplier
+ Duration::from_secs(INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS)
{
let status_str = format!(
"Poisson delay currently scaled by: {}",
self.current_multiplier()
);
if self.current_multiplier() > 0 {
log::debug!("{}", status_str);
} else if self.current_multiplier() > 1 {
log::info!("{}", status_str);
} else if self.current_multiplier() > 2 {
log::warn!("{}", status_str);
}
self.time_when_logged_about_elevated_multiplier = now;
}
}
}
+4 -3
View File
@@ -32,9 +32,10 @@ use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
use nym_credential_storage::storage::Storage;
#[cfg(target_arch = "wasm32")]
use nym_gateway_client::wasm_mockups::DirectSigningNyxdClient;
use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient;
#[cfg(target_arch = "wasm32")]
use wasm_timer::Instant;
#[cfg(target_arch = "wasm32")]
@@ -223,12 +224,12 @@ pub(super) async fn query_gateway_details(
}
}
pub(super) async fn register_with_gateway(
pub(super) async fn register_with_gateway<St: Storage>(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Result<Arc<SharedKeys>, ClientCoreError> {
let timeout = Duration::from_millis(1500);
let mut gateway_client: GatewayClient<DirectSigningNyxdClient> = GatewayClient::new_init(
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, St> = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
our_identity.clone(),
+6 -4
View File
@@ -11,6 +11,7 @@ use serde::Serialize;
use tap::TapFallible;
use nym_config::NymConfig;
use nym_credential_storage::storage::Storage;
use nym_crypto::asymmetric::{encryption, identity};
use url::Url;
@@ -73,7 +74,7 @@ pub fn new_client_keys() -> KeyManager {
/// Either pick one at random by querying the available gateways from the nym-api, or use the
/// chosen one if it's among the available ones.
/// The shared key is added to the supplied `KeyManager` and the endpoint details are returned.
pub async fn register_with_gateway(
pub async fn register_with_gateway<St: Storage>(
key_manager: &mut KeyManager,
nym_api_endpoints: Vec<Url>,
chosen_gateway_id: Option<identity::PublicKey>,
@@ -87,7 +88,7 @@ pub async fn register_with_gateway(
let our_identity = key_manager.identity_keypair();
// Establish connection, authenticate and generate keys for talking with the gateway
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
let shared_keys = helpers::register_with_gateway::<St>(&gateway, our_identity).await?;
key_manager.insert_gateway_shared_key(shared_keys);
Ok(gateway.into())
@@ -100,7 +101,7 @@ pub async fn register_with_gateway(
/// b. Create a new gateway configuration but keep existing keys. This assumes that the caller
/// knows what they are doing and that the keys match the requested gateway.
/// c. Create a new gateway configuration with a newly registered gateway and keys.
pub async fn setup_gateway_from_config<C, T>(
pub async fn setup_gateway_from_config<C, T, St>(
register_gateway: bool,
user_chosen_gateway_id: Option<identity::PublicKey>,
config: &Config<T>,
@@ -109,6 +110,7 @@ pub async fn setup_gateway_from_config<C, T>(
where
C: NymConfig + ClientCoreConfigTrait,
T: NymConfig,
St: Storage,
{
let id = config.get_id();
@@ -141,7 +143,7 @@ where
// Establish connection, authenticate and generate keys for talking with the gateway
eprintln!("Registering with new gateway");
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
let shared_keys = helpers::register_with_gateway::<St>(&gateway, our_identity).await?;
key_manager.insert_gateway_shared_key(shared_keys);
// Write all keys to storage and just return the gateway endpoint config. It is assumed that we
+2 -6
View File
@@ -14,12 +14,12 @@ log = { workspace = true }
thiserror = "1.0"
url = "2.2"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
async-trait = { workspace = true }
tokio = { version = "1.24.1", features = ["macros"] }
# internal
nym-bandwidth-controller = { path = "../../bandwidth-controller" }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-credentials = { path = "../../credentials" }
nym-credential-storage = { path = "../../credential-storage" }
nym-crypto = { path = "../../crypto" }
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -28,7 +28,6 @@ nym-pemstore = { path = "../../pemstore" }
nym-validator-client = { path = "../validator-client" }
nym-task = { path = "../../task" }
serde = { workspace = true, features = ["derive"] }
nym-mobile-storage = { path = "../../mobile-storage" }
[dependencies.tungstenite]
@@ -47,9 +46,6 @@ features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-credential-storage]
path = "../../credential-storage"
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2"
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::BandwidthController;
use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter;
pub use crate::packet_router::{
@@ -11,6 +10,7 @@ use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt};
use log::*;
use nym_bandwidth_controller::BandwidthController;
use nym_coconut_interface::Credential;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
@@ -26,24 +26,14 @@ use std::sync::Arc;
use std::time::Duration;
use tungstenite::protocol::Message;
use nym_credential_storage::storage::Storage;
#[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::traits::DkgQueryClient;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "android"))]
use nym_credential_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(target_os = "android")]
use mobile_storage::PersistentStorage;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::DkgQueryClient;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::PersistentStorage;
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
#[cfg(target_arch = "wasm32")]
use wasm_timer;
#[cfg(target_arch = "wasm32")]
@@ -52,7 +42,7 @@ use wasm_utils::websocket::JSWebsocket;
const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayClient<C> {
pub struct GatewayClient<C, St: Storage> {
authenticated: bool,
disabled_credentials_mode: bool,
bandwidth_remaining: i64,
@@ -63,7 +53,7 @@ pub struct GatewayClient<C> {
connection: SocketState,
packet_router: PacketRouter,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, PersistentStorage>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
// reconnection related variables
/// Specifies whether client should try to reconnect to gateway on connection failure.
@@ -78,9 +68,10 @@ pub struct GatewayClient<C> {
shutdown: TaskClient,
}
impl<C> GatewayClient<C>
impl<C, St> GatewayClient<C, St>
where
C: Sync + Send,
St: Storage,
{
// TODO: put it all in a Config struct
#[allow(clippy::too_many_arguments)]
@@ -92,7 +83,7 @@ where
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, PersistentStorage>>,
bandwidth_controller: Option<BandwidthController<C, St>>,
shutdown: TaskClient,
) -> Self {
GatewayClient {
@@ -146,7 +137,7 @@ where
let shutdown = TaskClient::dummy();
let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone());
GatewayClient::<C> {
GatewayClient::<C, St> {
authenticated: false,
disabled_credentials_mode: true,
bandwidth_remaining: 0,
+2 -16
View File
@@ -1,14 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::StorageError;
#[cfg(target_os = "android")]
#[cfg(not(target_arch = "wasm32"))]
use mobile_storage::StorageError;
#[cfg(not(target_os = "android"))]
#[cfg(not(target_arch = "wasm32"))]
use nym_credential_storage::error::StorageError;
use nym_gateway_requests::registration::handshake::error::HandshakeError;
use std::io;
use thiserror::Error;
@@ -27,12 +19,6 @@ pub enum GatewayClientError {
#[error("There was a network error - {0}")]
NetworkError(#[from] WsError),
#[error("There was a credential storage error - {0}")]
CredentialStorageError(#[from] StorageError),
#[error("Coconut error - {0}")]
CoconutError(#[from] nym_coconut_interface::CoconutError),
// TODO: see if `JsValue` is a reasonable type for this
#[cfg(target_arch = "wasm32")]
#[error("There was a network error")]
@@ -47,8 +33,8 @@ pub enum GatewayClientError {
#[error("No bandwidth controller provided")]
NoBandwidthControllerAvailable,
#[error("Credential error - {0}")]
CredentialError(#[from] nym_credentials::error::Error),
#[error("Bandwidth controller error - {0}")]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error("Connection was abruptly closed")]
ConnectionAbruptlyClosed,
@@ -11,13 +11,10 @@ pub use packet_router::{
};
use tungstenite::{protocol::Message, Error as WsError};
pub mod bandwidth;
pub mod client;
pub mod error;
pub mod packet_router;
pub mod socket_state;
#[cfg(target_arch = "wasm32")]
pub mod wasm_mockups;
/// Helper method for reading from websocket stream. Helps to flatten the structure.
pub(crate) fn cleanup_socket_message(
@@ -1,80 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use std::marker::PhantomData;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum StorageError {
#[error("Wasm client is not yet supported")]
WasmNotSupported,
#[allow(dead_code)]
#[error("Code shouldn't reach this point")]
InconsistentData,
}
pub struct DirectSigningNyxdClient {}
pub trait DkgQueryClient {}
// impl CosmWasmClient for DirectSigningNyxdClient {}
#[derive(Clone)]
pub struct Client<C> {
_phantom: PhantomData<C>,
}
impl<C> DkgQueryClient for Client<C> {}
#[derive(Clone)]
pub struct PersistentStorage {}
pub struct CoconutCredential {
pub id: i64,
pub voucher_value: String,
pub voucher_info: String,
pub serial_number: String,
pub binding_number: String,
pub signature: String,
pub epoch_id: String,
}
#[async_trait]
pub trait Storage: Send + Sync {
async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
serial_number: String,
binding_number: String,
signature: String,
) -> Result<(), StorageError>;
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
}
#[async_trait]
impl Storage for PersistentStorage {
async fn insert_coconut_credential(
&self,
_voucher_value: String,
_voucher_info: String,
_serial_number: String,
_binding_number: String,
_signature: String,
) -> Result<(), StorageError> {
Err(StorageError::WasmNotSupported)
}
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
Err(StorageError::WasmNotSupported)
}
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
Err(StorageError::WasmNotSupported)
}
}
@@ -50,6 +50,10 @@ itertools = { version = "0.10", optional = true }
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
cosmwasm-std = { workspace = true, optional = true }
[target.'cfg(android)'.dependencies.reqwest]
version = "0.11"
features = ["json", "native-tls-vendored"]
[dev-dependencies]
bip39 = { workspace = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32"] }
@@ -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;
@@ -23,7 +23,7 @@ pub struct Args {
#[clap(
value_parser,
requires = "fundsDenom",
requires = "funds_denom",
help = "Amount to supply as funds in micro denomination (e.g. unym or unyx)"
)]
pub funds: Option<u128>,
@@ -28,7 +28,7 @@ pub struct Args {
#[clap(
long,
requires = "fundsDenom",
requires = "funds_denom",
help = "Amount to supply as funds in micro denomination (e.g. unym or unyx)"
)]
pub funds: Option<u128>,
@@ -1,5 +1,6 @@
pub mod events;
pub mod msg;
pub mod response;
pub mod types;
// Re-export all types at the top-level
@@ -1,5 +1,5 @@
use crate::{NymAddress, Service, ServiceId, ServiceType};
use cosmwasm_std::{Addr, Coin};
use crate::{NymAddress, ServiceId, ServiceType};
use cosmwasm_std::Coin;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -48,10 +48,10 @@ pub enum QueryMsg {
ServiceId {
service_id: ServiceId,
},
Announcer {
announcer: Addr,
ByAnnouncer {
announcer: String,
},
NymAddress {
ByNymAddress {
nym_address: NymAddress,
},
All {
@@ -72,77 +72,3 @@ impl QueryMsg {
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServiceInfo {
pub service_id: ServiceId,
pub service: Service,
}
impl ServiceInfo {
pub fn new(service_id: ServiceId, service: Service) -> Self {
Self {
service_id,
service,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServicesListResponse {
pub services: Vec<ServiceInfo>,
}
impl ServicesListResponse {
pub fn new(services: Vec<(ServiceId, Service)>) -> ServicesListResponse {
ServicesListResponse {
services: services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect(),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedServicesListResponse {
pub services: Vec<ServiceInfo>,
pub per_page: usize,
pub start_next_after: Option<ServiceId>,
}
impl PagedServicesListResponse {
pub fn new(
services: Vec<(ServiceId, Service)>,
per_page: usize,
start_next_after: Option<ServiceId>,
) -> PagedServicesListResponse {
let services = services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect();
PagedServicesListResponse {
services,
per_page,
start_next_after,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ConfigResponse {
pub deposit_required: Coin,
}
impl From<Service> for ExecuteMsg {
fn from(service: Service) -> Self {
ExecuteMsg::Announce {
nym_address: service.nym_address,
service_type: service.service_type,
}
}
}
@@ -0,0 +1,68 @@
use crate::{msg::ExecuteMsg, Service, ServiceId, ServiceInfo};
use cosmwasm_std::Coin;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServiceInfoResponse {
pub service_id: ServiceId,
pub service: Option<Service>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServicesListResponse {
pub services: Vec<ServiceInfo>,
}
impl ServicesListResponse {
pub fn new(services: Vec<(ServiceId, Service)>) -> ServicesListResponse {
ServicesListResponse {
services: services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect(),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedServicesListResponse {
pub services: Vec<ServiceInfo>,
pub per_page: usize,
pub start_next_after: Option<ServiceId>,
}
impl PagedServicesListResponse {
pub fn new(
services: Vec<(ServiceId, Service)>,
per_page: usize,
start_next_after: Option<ServiceId>,
) -> PagedServicesListResponse {
let services = services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect();
PagedServicesListResponse {
services,
per_page,
start_next_after,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ConfigResponse {
pub deposit_required: Coin,
}
impl From<Service> for ExecuteMsg {
fn from(service: Service) -> Self {
ExecuteMsg::Announce {
nym_address: service.nym_address,
service_type: service.service_type,
}
}
}
@@ -65,3 +65,19 @@ impl std::fmt::Display for ServiceType {
write!(f, "{service_type}")
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServiceInfo {
pub service_id: ServiceId,
pub service: Service,
}
impl ServiceInfo {
pub fn new(service_id: ServiceId, service: Service) -> Self {
Self {
service_id,
service,
}
}
}
+9 -2
View File
@@ -9,9 +9,16 @@ edition = "2021"
async-trait = { workspace = true }
log = { workspace = true }
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
thiserror = "1.0"
tokio = { version = "1.24.1", features = [ "rt-multi-thread", "net", "signal", "fs" ] }
tokio = { version = "1.24.1", features = ["sync"]}
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
version = "0.5"
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
version = "1.24.1"
features = [ "rt-multi-thread", "net", "signal", "fs" ]
[build-dependencies]
@@ -0,0 +1,70 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::CoconutCredential;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct CoconutCredentialManager {
inner: Arc<RwLock<Vec<CoconutCredential>>>,
}
impl CoconutCredentialManager {
/// Creates new empty instance of the `CoconutCredentialManager`.
pub fn new() -> Self {
CoconutCredentialManager {
inner: Arc::new(RwLock::new(Vec::new())),
}
}
/// Inserts provided signature into the database.
///
/// # Arguments
///
/// * `voucher_value`: Plaintext bandwidth value of the credential.
/// * `voucher_info`: Plaintext information of the credential.
/// * `serial_number`: Base58 representation of the serial number attribute.
/// * `binding_number`: Base58 representation of the binding number attribute.
/// * `signature`: Coconut credential in the form of a signature.
pub async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
serial_number: String,
binding_number: String,
signature: String,
epoch_id: String,
) {
let mut creds = self.inner.write().await;
let id = creds.len() as i64;
creds.push(CoconutCredential {
id,
voucher_value,
voucher_info,
serial_number,
binding_number,
signature,
epoch_id,
consumed: false,
});
}
/// Tries to retrieve one of the stored, unused credentials.
pub async fn get_next_coconut_credential(&self) -> Option<CoconutCredential> {
let creds = self.inner.read().await;
creds.iter().find(|c| !c.consumed).cloned()
}
/// Consumes in the database the specified credential.
///
/// # Arguments
///
/// * `id`: Database id.
pub async fn consume_coconut_credential(&self, id: i64) {
let mut creds = self.inner.write().await;
if let Some(cred) = creds.get_mut(id as usize) {
cred.consumed = true;
}
}
}
@@ -0,0 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod memory;
#[cfg(not(target_arch = "wasm32"))]
pub mod sqlite;
@@ -4,7 +4,7 @@
use crate::models::CoconutCredential;
#[derive(Clone)]
pub(crate) struct CoconutCredentialManager {
pub struct CoconutCredentialManager {
connection_pool: sqlx::SqlitePool,
}
@@ -14,7 +14,7 @@ impl CoconutCredentialManager {
/// # Arguments
///
/// * `connection_pool`: database connection pool to use.
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
pub fn new(connection_pool: sqlx::SqlitePool) -> Self {
CoconutCredentialManager { connection_pool }
}
@@ -27,7 +27,7 @@ impl CoconutCredentialManager {
/// * `serial_number`: Base58 representation of the serial number attribute.
/// * `binding_number`: Base58 representation of the binding number attribute.
/// * `signature`: Coconut credential in the form of a signature.
pub(crate) async fn insert_coconut_credential(
pub async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
@@ -46,7 +46,7 @@ impl CoconutCredentialManager {
}
/// Tries to retrieve one of the stored, unused credentials.
pub(crate) async fn get_next_coconut_credential(
pub async fn get_next_coconut_credential(
&self,
) -> Result<Option<CoconutCredential>, sqlx::Error> {
sqlx::query_as!(
@@ -62,7 +62,7 @@ impl CoconutCredentialManager {
/// # Arguments
///
/// * `id`: Database id.
pub(crate) async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
pub async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
sqlx::query!(
"UPDATE coconut_credentials SET consumed = TRUE WHERE id = ?",
id
@@ -0,0 +1,67 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::backends::memory::CoconutCredentialManager;
use crate::error::StorageError;
use crate::models::CoconutCredential;
use crate::storage::Storage;
use async_trait::async_trait;
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub struct EphemeralStorage {
coconut_credential_manager: CoconutCredentialManager,
}
impl Default for EphemeralStorage {
fn default() -> Self {
EphemeralStorage {
coconut_credential_manager: CoconutCredentialManager::new(),
}
}
}
#[async_trait]
impl Storage for EphemeralStorage {
async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
serial_number: String,
binding_number: String,
signature: String,
epoch_id: String,
) -> Result<(), StorageError> {
self.coconut_credential_manager
.insert_coconut_credential(
voucher_value,
voucher_info,
serial_number,
binding_number,
signature,
epoch_id,
)
.await;
Ok(())
}
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
let credential = self
.coconut_credential_manager
.get_next_coconut_credential()
.await
.ok_or(StorageError::NoCredential)?;
Ok(credential)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
self.coconut_credential_manager
.consume_coconut_credential(id)
.await;
Ok(())
}
}
-20
View File
@@ -1,20 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::ERC20Credential;
#[derive(Clone)]
pub(crate) struct ERC20CredentialManager {
connection_pool: sqlx::SqlitePool,
}
impl ERC20CredentialManager {
/// Creates new instance of the `ERC20CredentialManager` with the provided sqlite connection pool.
///
/// # Arguments
///
/// * `connection_pool`: database connection pool to use.
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
ERC20CredentialManager { connection_pool }
}
}
+2
View File
@@ -5,9 +5,11 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum StorageError {
#[cfg(not(target_arch = "wasm32"))]
#[error("Database experienced an internal error - {0}")]
InternalDatabaseError(#[from] sqlx::Error),
#[cfg(not(target_arch = "wasm32"))]
#[error("Failed to perform database migration - {0}")]
MigrationError(#[from] sqlx::migrate::MigrateError),
+14 -99
View File
@@ -3,111 +3,26 @@
* SPDX-License-Identifier: Apache-2.0
*/
use crate::coconut::CoconutCredentialManager;
use crate::error::StorageError;
use crate::storage::Storage;
use crate::ephemeral_storage::EphemeralStorage;
#[cfg(not(target_arch = "wasm32"))]
use crate::persistent_storage::PersistentStorage;
use crate::models::CoconutCredential;
use async_trait::async_trait;
use log::{debug, error};
use sqlx::ConnectOptions;
use std::path::{Path, PathBuf};
mod coconut;
mod backends;
pub mod ephemeral_storage;
pub mod error;
mod models;
#[cfg(not(target_arch = "wasm32"))]
pub mod persistent_storage;
pub mod storage;
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub struct PersistentStorage {
coconut_credential_manager: CoconutCredentialManager,
}
impl PersistentStorage {
/// Initialises `PersistentStorage` using the provided path.
///
/// # Arguments
///
/// * `database_path`: path to the database.
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
debug!(
"Attempting to connect to database {:?}",
database_path.as_ref().as_os_str()
);
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(true);
opts.disable_statement_logging();
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(db) => db,
Err(err) => {
error!("Failed to connect to SQLx database: {err}");
return Err(err.into());
}
};
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
error!("Failed to perform migration on the SQLx database: {err}");
return Err(err.into());
}
Ok(PersistentStorage {
coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()),
})
}
}
#[async_trait]
impl Storage for PersistentStorage {
async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
serial_number: String,
binding_number: String,
signature: String,
epoch_id: String,
) -> Result<(), StorageError> {
self.coconut_credential_manager
.insert_coconut_credential(
voucher_value,
voucher_info,
serial_number,
binding_number,
signature,
epoch_id,
)
.await?;
Ok(())
}
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
let credential = self
.coconut_credential_manager
.get_next_coconut_credential()
.await?
.ok_or(StorageError::NoCredential)?;
Ok(credential)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
self.coconut_credential_manager
.consume_coconut_credential(id)
.await?;
Ok(())
}
}
pub async fn initialise_storage(path: PathBuf) -> PersistentStorage {
match PersistentStorage::init(path).await {
#[cfg(not(target_arch = "wasm32"))]
pub async fn initialise_persistent_storage(path: std::path::PathBuf) -> PersistentStorage {
match persistent_storage::PersistentStorage::init(path).await {
Err(err) => panic!("failed to initialise credential storage - {err}"),
Ok(storage) => storage,
}
}
pub fn initialise_ephemeral_storage() -> EphemeralStorage {
ephemeral_storage::EphemeralStorage::default()
}
+1
View File
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[derive(Clone)]
pub struct CoconutCredential {
#[allow(dead_code)]
pub id: i64,
@@ -0,0 +1,99 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::backends::sqlite::CoconutCredentialManager;
use crate::error::StorageError;
use crate::storage::Storage;
use crate::models::CoconutCredential;
use async_trait::async_trait;
use log::{debug, error};
use sqlx::ConnectOptions;
use std::path::Path;
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub struct PersistentStorage {
coconut_credential_manager: CoconutCredentialManager,
}
impl PersistentStorage {
/// Initialises `PersistentStorage` using the provided path.
///
/// # Arguments
///
/// * `database_path`: path to the database.
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
debug!(
"Attempting to connect to database {:?}",
database_path.as_ref().as_os_str()
);
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(true);
opts.disable_statement_logging();
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(db) => db,
Err(err) => {
error!("Failed to connect to SQLx database: {err}");
return Err(err.into());
}
};
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
error!("Failed to perform migration on the SQLx database: {err}");
return Err(err.into());
}
Ok(PersistentStorage {
coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()),
})
}
}
#[async_trait]
impl Storage for PersistentStorage {
async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
serial_number: String,
binding_number: String,
signature: String,
epoch_id: String,
) -> Result<(), StorageError> {
self.coconut_credential_manager
.insert_coconut_credential(
voucher_value,
voucher_info,
serial_number,
binding_number,
signature,
epoch_id,
)
.await?;
Ok(())
}
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
let credential = self
.coconut_credential_manager
.get_next_coconut_credential()
.await?
.ok_or(StorageError::NoCredential)?;
Ok(credential)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
self.coconut_credential_manager
.consume_coconut_credential(id)
.await?;
Ok(())
}
}
+1 -1
View File
@@ -3,8 +3,8 @@
use async_trait::async_trait;
use crate::error::StorageError;
use crate::models::CoconutCredential;
use crate::StorageError;
#[async_trait]
pub trait Storage: Send + Sync {
-67
View File
@@ -1,67 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum StorageError {
#[error("Android is not yet supported")]
AndroidNotSupported,
#[allow(dead_code)]
#[error("Code shouldn't reach this point")]
InconsistentData,
}
#[derive(Clone)]
pub struct PersistentStorage {}
pub struct CoconutCredential {
pub id: i64,
pub voucher_value: String,
pub voucher_info: String,
pub serial_number: String,
pub binding_number: String,
pub signature: String,
pub epoch_id: String,
pub consumed: bool,
}
#[async_trait]
pub trait Storage: Send + Sync {
async fn insert_coconut_credential(
&self,
voucher_value: String,
voucher_info: String,
serial_number: String,
binding_number: String,
signature: String,
) -> Result<(), StorageError>;
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
}
#[async_trait]
impl Storage for PersistentStorage {
async fn insert_coconut_credential(
&self,
_voucher_value: String,
_voucher_info: String,
_serial_number: String,
_binding_number: String,
_signature: String,
) -> Result<(), StorageError> {
Err(StorageError::AndroidNotSupported)
}
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
Err(StorageError::AndroidNotSupported)
}
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
Err(StorageError::AndroidNotSupported)
}
}
+27 -15
View File
@@ -19,17 +19,6 @@ pub struct ChainDetails {
pub stake_denom: DenomDetailsOwned,
}
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
impl Default for ChainDetails {
fn default() -> Self {
ChainDetails {
bech32_account_prefix: mainnet::BECH32_PREFIX.into(),
mix_denom: mainnet::MIX_DENOM.into(),
stake_denom: mainnet::STAKE_DENOM.into(),
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct NymContracts {
pub mixnet_contract_address: Option<String>,
@@ -43,20 +32,43 @@ pub struct NymContracts {
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
// with `NetworkDetails` defined in all.rs...
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct NymNetworkDetails {
pub chain_details: ChainDetails,
pub endpoints: Vec<ValidatorDetails>,
pub contracts: NymContracts,
}
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
impl Default for NymNetworkDetails {
fn default() -> Self {
NymNetworkDetails::new_mainnet()
}
}
impl NymNetworkDetails {
pub fn new() -> Self {
NymNetworkDetails::default()
pub fn new_empty() -> Self {
NymNetworkDetails {
chain_details: ChainDetails {
bech32_account_prefix: Default::default(),
mix_denom: DenomDetailsOwned {
base: Default::default(),
display: Default::default(),
display_exponent: Default::default(),
},
stake_denom: DenomDetailsOwned {
base: Default::default(),
display: Default::default(),
display_exponent: Default::default(),
},
},
endpoints: Default::default(),
contracts: Default::default(),
}
}
pub fn new_from_env() -> Self {
NymNetworkDetails::new()
NymNetworkDetails::new_empty()
.with_bech32_account_prefix(
var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"),
)
+1
View File
@@ -11,3 +11,4 @@ repository = { workspace = true }
nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" }
nym-sphinx-types = { path = "../types" }
nym-outfox = { path = "../../../nym-outfox" }
+8 -3
View File
@@ -16,10 +16,15 @@ tap = "1.0.1"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] }
[target.'cfg(target_os = "android")'.dependencies.nym-client-core]
path = "../client-core"
features = []
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 +34,4 @@ nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
[features]
default = ["nym-credential-storage"]
default = []
+23 -15
View File
@@ -11,6 +11,7 @@ use crate::socks::{
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_bandwidth_controller::BandwidthController;
#[cfg(target_os = "android")]
use nym_client_core::client::base_client::helpers::setup_empty_reply_surb_backend;
#[cfg(not(target_os = "android"))]
@@ -20,8 +21,7 @@ use nym_client_core::client::base_client::{
};
use nym_client_core::client::key_manager::KeyManager;
use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(not(target_os = "android"))]
use nym_gateway_client::bandwidth::BandwidthController;
use nym_credential_storage::storage::Storage;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::{TaskClient, TaskManager};
use nym_validator_client::nyxd::QueryNyxdClient;
@@ -74,10 +74,10 @@ impl NymClient {
}
}
#[cfg(not(target_os = "android"))]
async fn create_bandwidth_controller(
async fn create_bandwidth_controller<St: Storage>(
config: &Config,
) -> BandwidthController<Client<QueryNyxdClient>> {
storage: St,
) -> BandwidthController<Client<QueryNyxdClient>, St> {
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
let mut client_config =
nym_validator_client::Config::try_from_nym_network_details(&details)
@@ -97,13 +97,6 @@ impl NymClient {
let client = nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
#[cfg(not(target_os = "android"))]
let storage =
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await;
#[cfg(target_os = "android")]
let storage = mobile_storage::PersistentStorage {};
BandwidthController::new(storage, client)
}
@@ -223,7 +216,16 @@ impl NymClient {
let base_builder = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
self.key_manager,
Some(Self::create_bandwidth_controller(&self.config).await),
Some(
Self::create_bandwidth_controller(
&self.config,
nym_credential_storage::initialise_persistent_storage(
self.config.get_base().get_database_path(),
)
.await,
)
.await,
),
non_wasm_helpers::setup_fs_reply_surb_backend(
Some(self.config.get_base().get_reply_surb_database_path()),
self.config.get_debug_settings(),
@@ -232,10 +234,16 @@ impl NymClient {
);
#[cfg(target_os = "android")]
let base_builder = BaseClientBuilder::<_, Client<QueryNyxdClient>>::new_from_base_config(
let base_builder = BaseClientBuilder::<_, Client<QueryNyxdClient>, _>::new_from_base_config(
self.config.get_base(),
self.key_manager,
None,
Some(
Self::create_bandwidth_controller(
&self.config,
nym_credential_storage::initialise_ephemeral_storage(),
)
.await,
),
setup_empty_reply_surb_backend(self.config.get_debug_settings()),
);
@@ -88,8 +88,8 @@ pub fn execute(
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<Binary> {
let response = match msg {
QueryMsg::ServiceId { service_id } => to_binary(&query::query_id(deps, service_id)?),
QueryMsg::Announcer { announcer } => to_binary(&query::query_announcer(deps, announcer)?),
QueryMsg::NymAddress { nym_address } => {
QueryMsg::ByAnnouncer { announcer } => to_binary(&query::query_announcer(deps, announcer)?),
QueryMsg::ByNymAddress { nym_address } => {
to_binary(&query::query_nym_address(deps, nym_address)?)
}
QueryMsg::All { limit, start_after } => {
@@ -116,10 +116,7 @@ mod tests {
testing::{mock_dependencies, mock_env, mock_info},
Addr, Coin,
};
use nym_service_provider_directory_common::{
msg::{ExecuteMsg, ServiceInfo},
ServiceId,
};
use nym_service_provider_directory_common::{msg::ExecuteMsg, ServiceId, ServiceInfo};
const DENOM: &str = "unym";
@@ -26,7 +26,7 @@ fn ensure_correct_deposit(will_deposit: Uint128, deposit_required: Uint128) -> R
}
fn ensure_max_services_per_announcer(deps: Deps, announcer: Addr) -> Result<()> {
let current_entries = query::query_announcer(deps, announcer.clone())?;
let current_entries = query::query_announcer(deps, announcer.to_string())?;
if current_entries.services.len() < MAX_NUMBER_OF_PROVIDERS_PER_ANNOUNCER as usize {
Ok(())
} else {
@@ -1,8 +1,8 @@
use cosmwasm_std::{Addr, Deps};
use cosmwasm_std::Deps;
use nym_contracts_common::ContractBuildInformation;
use nym_service_provider_directory_common::{
msg::{ConfigResponse, PagedServicesListResponse, ServiceInfo, ServicesListResponse},
NymAddress, ServiceId,
response::{ConfigResponse, PagedServicesListResponse, ServicesListResponse},
NymAddress, ServiceId, ServiceInfo,
};
use crate::{
@@ -18,7 +18,8 @@ pub fn query_id(deps: Deps, service_id: ServiceId) -> Result<ServiceInfo> {
})
}
pub fn query_announcer(deps: Deps, announcer: Addr) -> Result<ServicesListResponse> {
pub fn query_announcer(deps: Deps, announcer: String) -> Result<ServicesListResponse> {
let announcer = deps.api.addr_validate(&announcer)?;
let services = state::services::load_announcer(deps.storage, announcer)?;
Ok(ServicesListResponse::new(services))
}
@@ -2,8 +2,8 @@
use cosmwasm_std::Addr;
use nym_service_provider_directory_common::{
msg::{ConfigResponse, PagedServicesListResponse, ServiceInfo},
NymAddress, Service, ServiceType,
response::{ConfigResponse, PagedServicesListResponse},
NymAddress, Service, ServiceInfo, ServiceType,
};
use crate::{
@@ -1,6 +1,6 @@
use cosmwasm_std::{Coin, Storage};
use cw_storage_plus::Item;
use nym_service_provider_directory_common::msg::ConfigResponse;
use nym_service_provider_directory_common::response::ConfigResponse;
use serde::{Deserialize, Serialize};
use crate::{constants::CONFIG_KEY, error::Result};
@@ -17,7 +17,7 @@ pub(crate) fn next_service_id_counter(store: &mut dyn Storage) -> Result<Service
#[cfg(test)]
mod tests {
use nym_service_provider_directory_common::msg::ServiceInfo;
use nym_service_provider_directory_common::ServiceInfo;
use crate::test_helpers::{
assert::assert_services,
@@ -1,7 +1,8 @@
use cosmwasm_std::{from_binary, testing::mock_env, Addr, Coin, Deps};
use nym_service_provider_directory_common::{
msg::{ConfigResponse, PagedServicesListResponse, QueryMsg, ServiceInfo},
ServiceId,
msg::QueryMsg,
response::{ConfigResponse, PagedServicesListResponse},
ServiceId, ServiceInfo,
};
use crate::{constants::SERVICE_DEFAULT_RETRIEVAL_LIMIT, error::ContractError};
@@ -1,6 +1,6 @@
use cosmwasm_std::Addr;
use nym_service_provider_directory_common::{
msg::ServiceInfo, NymAddress, Service, ServiceId, ServiceType,
NymAddress, Service, ServiceId, ServiceInfo, ServiceType,
};
use super::helpers::nyms;
@@ -2,11 +2,9 @@ use anyhow::Result;
use cosmwasm_std::{coins, Addr, Coin, Uint128};
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
use nym_service_provider_directory_common::{
msg::{
ConfigResponse, ExecuteMsg, InstantiateMsg, PagedServicesListResponse, QueryMsg,
ServiceInfo,
},
NymAddress, ServiceId, ServiceType,
msg::{ExecuteMsg, InstantiateMsg, QueryMsg},
response::{ConfigResponse, PagedServicesListResponse},
NymAddress, ServiceId, ServiceInfo, ServiceType,
};
use serde::de::DeserializeOwned;
+1 -1
View File
@@ -51,7 +51,7 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
minimum_rust_version = "1.66"
# minimum_node_version = ""
# nym platform code most recent release
platform_release_version = "v1.1.14"
platform_release_version = "v1.1.15"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+28 -31
View File
@@ -4,19 +4,19 @@ The validator is built using [Cosmos SDK](https://cosmos.network) and [Tendermin
## Building your validator
### Prerequisites
#### `git`, `gcc`
#### `git`, `gcc`, `jq`
* Debian-based systems:
```
apt install git, build-essential
apt install git build-essential jq
# optional additional manual pages can be installed with:
apt-get install manpages-dev
```
* Arch-based systems:
Install `git` and `gcc` with the following:
Install `git`, `gcc` and `jq` with the following:
```
pacman -S git, gcc
pacman -S git gcc jq
```
#### `Go`
@@ -59,7 +59,7 @@ cd nyxd
make build
# Sandbox testnet
BECH32_PREFIX=nyxt make build
BECH32_PREFIX=nymt make build
```
At this point, you will have a copy of the `nyxd` binary in your `build/` directory. Test that it's compiled properly by running:
@@ -89,15 +89,6 @@ This will output something like:
'/home/username/go/pkg/mod/github.com/!cosm!wasm/wasmvm@v0.13.0/api/libwasmvm.so'
```
~~~admonish note
If you are on Mac OSX use this command instead:
```
WASMVM_SO=$(otool -L build/nymd | grep libwasmvm.so | awk '{ print $3 }')
ls ${WASMVM_SO}
```
To get the location of the `libwasmvm.so` file.
~~~
When you upload your `nyxd` binary, you'll need to tell it where `libwasmvm.so` is when you start your validator, or it will not run. If you have compiled them on your server then this is not necessary, as the compiled `nyxd` already has access to `libwasmvm.so`.
Upload both `nyxd` and `libwasmvm.so` to your validator machine. If `nyxd` can't find `libwasmvm.so` you will see an error like the following:
@@ -106,7 +97,7 @@ Upload both `nyxd` and `libwasmvm.so` to your validator machine. If `nyxd` can't
./nyxd: error while loading shared libraries: libwasmvm.so: cannot open shared object file: No such file or directory
```
You'll need to set `LD_LIBRARY_PATH` in your user's `~/.bashrc` file, and add that to our path. Replace `/home/youruser/path/to/nym/binaries` in the command below to the locations of `nymd` and `libwasmvm.so` and run it. If you have compiled these on the server, they will be in the `build/` folder:
You'll need to set `LD_LIBRARY_PATH` in your user's `~/.bashrc` file, and add that to our path. Replace `/home/youruser/path/to/nym/binaries` in the command below to the locations of `nyxd` and `libwasmvm.so` and run it. If you have compiled these on the server, they will be in the `build/` folder:
```
NYX_BINARIES=/home/youruser/path/to/validator/binary
@@ -134,7 +125,11 @@ This should return the regular help text.
Choose a name for your validator and use it in place of `<ID>` in the following command:
```
# Mainnet
nyxd init <ID> --chain-id=nyx
# Sandbox testnet
nyxd init <ID> --chain-id=sandbox
```
```admonish caution
@@ -155,8 +150,8 @@ You can use the following command to download them for the correct network:
# Mainnet
wget -O $HOME/.nyxd/config/genesis.json https://nymtech.net/genesis/genesis.json
# Sandbox Testnet
wget -O $HOME/.nymd/config/genesis.json https://sandbox-validator1.nymtech.net/genesis
# Sandbox testnet
curl "https://sandbox-validator1.nymtech.net/genesis" | jq .result.genesis > ~/.nyxd/config/genesis.json
```
### `config.toml` configuration
@@ -171,7 +166,7 @@ laddr = "tcp://0.0.0.0:26656"
```
```
# Sandbox Testnet
# Sandbox testnet
cors_allowed_origins = ["*"]
persistent_peers = "8421c0a3d90d490e27e8061f2abcb1276c8358b6@sandbox-validator1.nymtech.net:26656"
create_empty_blocks = false
@@ -197,16 +192,16 @@ And if you wish to add a human-readable moniker to your node:
Finally, if you plan on using [Cockpit](https://cockpit-project.org/documentation.html) on your server, change the `grpc` port from `9090` as this is the port used by Cockpit.
### `app.toml` configuration
In the file `$HOME/.${NYM_APP_NAME}/config/app.toml`, set the following values:
In the file `$HOME/nyxd/config/app.toml`, set the following values:
```
# Mainnet
minimum-gas-prices = "0.025unym"
minimum-gas-prices = "0.025unym,0.025unyx"
enable = true in the `[api]` section to get the API server running
```
```
# Sandbox Testnet
minimum-gas-prices = "0.025unymt"
minimum-gas-prices = "0.025unymt,0.025unyxt"
enable = true` in the `[api]` section to get the API server running
```
@@ -267,6 +262,8 @@ nyxd start
Once your validator starts, it will start requesting blocks from other validators. This may take several hours. Once it's up to date, you can issue a request to join the validator set with the command below.
If you wish to sync from a snapshot or via state-sync please check out the Polkachu [mainnet](https://polkachu.com/networks/nym) or [testnet](https://polkachu.com/testnets/nym/) resources.
> If you are having trouble upgrading your validator binary, try replacing (or re-compile) the `libwasmvm.so` file and replace it on your validator server.
```admonish caution
@@ -294,12 +291,12 @@ nyxd tx staking create-validator
```
```
# Sandbox Testnet
nymd tx staking create-validator
nyxd tx staking create-validator
--amount=10000000unyxt
--fees=5000unyxt
--pubkey=$(/home/youruser/path/to/nym/binaries/nymd tendermint show-validator)
--pubkey=$(/home/youruser/path/to/nym/binaries/nyxd tendermint show-validator)
--moniker="whatever you called your validator"
--chain-id=nym-sandbox
--chain-id=sandbox
--commission-rate="0.10"
--commission-max-rate="0.20"
--commission-max-change-rate="0.01"
@@ -307,7 +304,7 @@ nymd tx staking create-validator
--gas="auto"
--gas-adjustment=1.15
--from="KEYRING_NAME"
--node https://sandbox-validator.nymtech.net:443
--node https://sandbox-validator1.nymtech.net:443
```
You'll need either `unyxt` tokens on Sandbox, or `unyx` tokens on mainnet to perform this command.
@@ -330,11 +327,11 @@ nyxd tx staking edit-validator
--fees 2000unyx
```
```
# Sandbox Testnet
# Sandbox testnet
nyxd tx staking edit-validator
--chain-id=nyx-sandbox
--chain-id=sandbox
--moniker="whatever you called your validator"
--details="Nym validator"
--details="Sandbox testnet validator"
--security-contact="your email"
--identity="your identity"
--gas="auto"
@@ -414,7 +411,7 @@ Proxying your validator's port `26657` to nginx port `80` can then be done by cr
server {
listen 80;
listen [::]:80;
server_name "{{ domain }}";
server_name "domain_name";
location / {
proxy_pass http://127.0.0.1:26657;
@@ -570,7 +567,7 @@ nyxd tx slashing unjail
nyxd tx slashing unjail
--broadcast-mode=block
--from="KEYRING_NAME"
--chain-id=nym-sandbox
--chain-id=sandbox
--gas=auto
--gas-adjustment=1.4
--fees=7000unyxt
@@ -622,7 +619,7 @@ nyxd tx staking delegate VALOPERADDRESS AMOUNTunym
nyxd tx staking delegate VALOPERADDRESS AMOUNTunymt
--from="KEYRING_NAME"
--keyring-backend=os
--chain-id=nym-sandbox
--chain-id=sandbox
--gas="auto"
--gas-adjustment=1.15
--fees 5000unyxt
+1 -1
View File
@@ -49,7 +49,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
layer: item?.layer || '',
profit_percentage: `${profitPercentage}%`,
avg_uptime: `${toPercentIntegerString(item.node_performance.last_24h)}%`,
stake_saturation: uncappedSaturation.toFixed(2),
stake_saturation: Number(uncappedSaturation.toFixed(2)),
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
node_performance: `${toPercentIntegerString(item.node_performance.most_recent)}%`,
blacklisted: item.blacklisted,
+6
View File
@@ -64,6 +64,7 @@ rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
schemars = { version = "0.8", features = ["preserve_order"] }
## internal
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" }
nym-coconut-interface = { path = "../common/coconut-interface" }
@@ -92,6 +93,11 @@ nym-validator-client = { path = "../common/client-libs/validator-client", featur
] }
nym-bin-common = { path = "../common/bin-common" }
[target.'cfg(target_os = "android")'.dependencies.reqwest]
version = "0.11.11"
features = ["json", "native-tls-vendored"]
[features]
no-reward = []
generate-ts = ["ts-rs"]
+3 -3
View File
@@ -17,9 +17,9 @@ use crate::storage::NymApiStorage;
use crate::support::config::Config;
use crate::support::nyxd;
use futures::channel::mpsc;
use nym_credential_storage::PersistentStorage;
use nym_bandwidth_controller::BandwidthController;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::bandwidth::BandwidthController;
use nym_sphinx::receiver::MessageReceiver;
use nym_task::TaskManager;
use std::sync::Arc;
@@ -98,7 +98,7 @@ impl<'a> NetworkMonitorBuilder<'a> {
let bandwidth_controller = {
BandwidthController::new(
nym_credential_storage::initialise_storage(
nym_credential_storage::initialise_persistent_storage(
self.config.get_credentials_database_path(),
)
.await,
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::support::nyxd;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::identity::PUBLIC_KEY_LENGTH;
use nym_gateway_client::GatewayClient;
use std::collections::HashMap;
@@ -11,16 +12,16 @@ use tokio::sync::{Mutex, MutexGuard, TryLockError};
pub(crate) struct GatewayClientHandle(Arc<GatewayClientHandleInner>);
struct GatewayClientHandleInner {
client: Mutex<Option<GatewayClient<nyxd::Client>>>,
client: Mutex<Option<GatewayClient<nyxd::Client, PersistentStorage>>>,
raw_identity: [u8; PUBLIC_KEY_LENGTH],
}
pub(crate) struct UnlockedGatewayClientHandle<'a>(
MutexGuard<'a, Option<GatewayClient<nyxd::Client>>>,
MutexGuard<'a, Option<GatewayClient<nyxd::Client, PersistentStorage>>>,
);
impl GatewayClientHandle {
pub(crate) fn new(gateway_client: GatewayClient<nyxd::Client>) -> Self {
pub(crate) fn new(gateway_client: GatewayClient<nyxd::Client, PersistentStorage>) -> Self {
GatewayClientHandle(Arc::new(GatewayClientHandleInner {
raw_identity: gateway_client.gateway_identity().to_bytes(),
client: Mutex::new(Some(gateway_client)),
@@ -59,11 +60,15 @@ impl GatewayClientHandle {
}
impl<'a> UnlockedGatewayClientHandle<'a> {
pub(crate) fn get_mut_unchecked(&mut self) -> &mut GatewayClient<nyxd::Client> {
pub(crate) fn get_mut_unchecked(
&mut self,
) -> &mut GatewayClient<nyxd::Client, PersistentStorage> {
self.0.as_mut().unwrap()
}
pub(crate) fn inner_mut(&mut self) -> Option<&mut GatewayClient<nyxd::Client>> {
pub(crate) fn inner_mut(
&mut self,
) -> Option<&mut GatewayClient<nyxd::Client, PersistentStorage>> {
self.0.as_mut()
}
@@ -12,10 +12,10 @@ use futures::stream::{self, FuturesUnordered, StreamExt};
use futures::task::Context;
use futures::{Future, Stream};
use log::{debug, info, trace, warn};
use nym_bandwidth_controller::BandwidthController;
use nym_config::defaults::REMAINING_BANDWIDTH_THRESHOLD;
use nym_credential_storage::PersistentStorage;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH};
use nym_gateway_client::bandwidth::BandwidthController;
use nym_gateway_client::error::GatewayClientError;
use nym_gateway_client::{AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver};
use nym_sphinx::forwarding::packet::MixPacket;
@@ -206,7 +206,7 @@ impl PacketSender {
}
async fn attempt_to_send_packets(
client: &mut GatewayClient<nyxd::Client>,
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
mut mix_packets: Vec<MixPacket>,
max_sending_rate: usize,
) -> Result<(), GatewayClientError> {
@@ -314,7 +314,7 @@ impl PacketSender {
}
async fn check_remaining_bandwidth(
client: &mut GatewayClient<nyxd::Client>,
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
) -> Result<(), GatewayClientError> {
if client.remaining_bandwidth() < REMAINING_BANDWIDTH_THRESHOLD {
Err(GatewayClientError::NotEnoughBandwidth(
+50 -16
View File
@@ -3173,9 +3173,25 @@ 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.4.0"
version = "0.5.0"
dependencies = [
"atty",
"clap",
@@ -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"
@@ -3557,6 +3566,7 @@ dependencies = [
"curve25519-dalek",
"getrandom 0.2.8",
"rayon",
"sphinx-packet 0.1.0 (git+https://github.com/nymtech/sphinx.git)",
"thiserror",
"zeroize",
]
@@ -3588,10 +3598,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",
@@ -3727,6 +3737,7 @@ dependencies = [
name = "nym-sphinx-forwarding"
version = "0.1.0"
dependencies = [
"nym-outfox",
"nym-sphinx-addressing",
"nym-sphinx-params",
"nym-sphinx-types",
@@ -3757,7 +3768,7 @@ dependencies = [
name = "nym-sphinx-types"
version = "0.2.0"
dependencies = [
"sphinx-packet",
"sphinx-packet 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -3830,7 +3841,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract"
version = "1.3.0"
version = "1.3.1"
dependencies = [
"cosmwasm-derive",
"cosmwasm-std",
@@ -3848,7 +3859,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract-common"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"cosmwasm-std",
"nym-contracts-common",
@@ -5366,6 +5377,29 @@ dependencies = [
"subtle 2.4.1",
]
[[package]]
name = "sphinx-packet"
version = "0.1.0"
source = "git+https://github.com/nymtech/sphinx.git#ca107d94360cdf8bbfbdb12fe5320ed74f80e40c"
dependencies = [
"aes 0.7.5",
"arrayref",
"blake2",
"bs58",
"byteorder",
"chacha",
"curve25519-dalek",
"digest 0.9.0",
"hkdf 0.11.0",
"hmac 0.11.0",
"lioness",
"log",
"rand 0.7.3",
"rand_distr",
"sha2 0.9.9",
"subtle 2.4.1",
]
[[package]]
name = "spin"
version = "0.5.2"
+1
View File
@@ -50,6 +50,7 @@ nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
nym-contracts-common = { path = "../../../common/cosmwasm-smart-contracts/contracts-common"}
nym-config-common = { path = "../../../common/config", package = "nym-config" }
nym-crypto = { path = "../../../common/crypto" }
nym-credential-storage = { path = "../../../common/credential-storage" }
nym-bin-common = { path = "../../../common/bin-common"}
nym-socks5-client-core = { path = "../../../common/socks5-client-core" }
nym-task = { path = "../../../common/task" }
@@ -4,6 +4,7 @@ use crate::{
};
use nym_client_core::config::Config as BaseConfig;
use nym_config_common::NymConfig;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::config::{Config as Socks5Config, Socks5};
use std::path::PathBuf;
@@ -138,14 +139,15 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
.map_err(|_| BackendError::UnableToParseGateway)?;
// Setup gateway by either registering a new one, or reusing exiting keys
let gateway = nym_client_core::init::setup_gateway_from_config::<Socks5Config, _>(
register_gateway,
Some(chosen_gateway_id),
config.get_base(),
// TODO: another instance where this setting should probably get used
false,
)
.await?;
let gateway =
nym_client_core::init::setup_gateway_from_config::<Socks5Config, _, PersistentStorage>(
register_gateway,
Some(chosen_gateway_id),
config.get_base(),
// TODO: another instance where this setting should probably get used
false,
)
.await?;
config.get_base_mut().set_gateway_endpoint(gateway);
+4 -4
View File
@@ -9,7 +9,7 @@
"webpack:dev:onlyThis": "yarn webpack serve --config webpack.dev.js",
"webpack:prod": "yarn webpack --progress --config webpack.prod.js",
"tauri": "tauri",
"tauri:dev:android": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android dev",
"tauri:dev:android": "cargo tauri android dev",
"tauri:build:android": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android build",
"dev:android": "run-p webpack:dev:onlyThis tauri:dev:android",
"prebuild": "yarn --cwd ../.. build",
@@ -27,7 +27,7 @@
"clean:node": "rm -rf node_modules",
"clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml",
"clean:android": "cd src-tauri/gen/android/nym_connect/ && gradlew clean",
"clean:": "run-p clean:node clean:rust clean:android"
"clean": "run-p clean:node clean:rust clean:android"
},
"dependencies": {
"@emotion/react": "^11.7.0",
@@ -39,7 +39,7 @@
"@mui/system": ">= 5",
"@mui/lab": "^5.0.0-alpha.72",
"@nymproject/react": "^1.0.0",
"@tauri-apps/api": "^2.0.0-alpha.0",
"@tauri-apps/api": "^2.0.0-alpha.3",
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
"clsx": "^1.1.1",
"luxon": "^2.3.0",
@@ -64,7 +64,7 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/react": "^6.5.15",
"@svgr/webpack": "^6.1.1",
"@tauri-apps/cli": "^2.0.0-alpha.2",
"@tauri-apps/cli": "^2.0.0-alpha.8",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@types/jest": "^27.0.1",
+949 -480
View File
File diff suppressed because it is too large Load Diff
+6 -9
View File
@@ -16,13 +16,9 @@ rust-version = "1.58"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.0.0-alpha.1", features = [] }
tauri-build = { version = "2.0.0-alpha.4", features = [] }
# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] }
# TODO untill new tauri version includes https://github.com/tauri-apps/tauri-mobile/pull/111
[patch.crates-io]
tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev" }
[dependencies]
anyhow = "1.0"
bip39 = { version = "2.0.0", features = ["zeroize"] }
@@ -35,14 +31,14 @@ itertools = "0.10.5"
log = { version = "0.4", features = ["serde"] }
pretty_env_logger = "0.4.0"
rand = "0.8"
reqwest = { version = "0.11", features = ["json", "socks"] }
reqwest = { version = "0.11", features = ["json", "socks", "native-tls-vendored"] }
rust-embed = { version = "6.4.2", features = ["include-exclude"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_repr = "0.1"
tap = "1.0.1"
# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
tauri = { version = "2.0.0-alpha.3", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
tauri = { version = "2.0.0-alpha.8", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
tendermint-rpc = "0.23.0"
thiserror = "1.0"
time = { version = "0.3.17", features = ["local-offset"] }
@@ -50,13 +46,14 @@ 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 = [] }
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 }
nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = true }
nym-task = { path = "../../../common/task" }
[dev-dependencies]
@@ -0,0 +1,44 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "app.tauri"
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 33
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("proguard-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
@@ -0,0 +1,25 @@
-keep class app.tauri.** {
@app.tauri.JniMethod public <methods>;
native <methods>;
}
-keep class app.tauri.plugin.JSArray {
public <init>(...);
}
-keepclassmembers class org.json.JSONArray {
public put(...);
}
-keep class app.tauri.plugin.JSObject {
public <init>(...);
public put(...);
}
-keep @app.tauri.annotation.TauriPlugin public class * {
@app.tauri.annotation.Command public <methods>;
@app.tauri.annotation.PermissionCallback <methods>;
@app.tauri.annotation.ActivityCallback <methods>;
@app.tauri.annotation.Permission <methods>;
public <init>(...);
}
@@ -0,0 +1,28 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("app.tauri.test", appContext.packageName)
}
}
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,201 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.OpenableColumns
import java.io.File
import java.io.FileOutputStream
import kotlin.math.min
internal class FsUtils {
companion object {
fun getFileUrlForUri(context: Context, uri: Uri): String? {
// DocumentProvider
if (DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
val docId: String = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return legacyPrimaryPath(split[1])
} else {
val splitIndex = docId.indexOf(':', 1)
val tag = docId.substring(0, splitIndex)
val path = docId.substring(splitIndex + 1)
val nonPrimaryVolume = getPathToNonPrimaryVolume(context, tag)
if (nonPrimaryVolume != null) {
val result = "$nonPrimaryVolume/$path"
val file = File(result)
return if (file.exists() && file.canRead()) {
result
} else null
}
}
} else if (isDownloadsDocument(uri)) {
val id: String = DocumentsContract.getDocumentId(uri)
val contentUri: Uri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
java.lang.Long.valueOf(id)
)
return getDataColumn(context, contentUri, null, null)
} else if (isMediaDocument(uri)) {
val docId: String = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
val type = split[0]
var contentUri: Uri? = null
when (type) {
"image" -> {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
"video" -> {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
"audio" -> {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
if (contentUri != null) {
return getDataColumn(context, contentUri, selection, selectionArgs)
}
}
} else if ("content".equals(uri.scheme, ignoreCase = true)) {
// Return the remote address
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
context,
uri,
null,
null
)
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
return uri.path
}
return null
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
private fun getDataColumn(
context: Context,
uri: Uri,
selection: String?,
selectionArgs: Array<String>?
): String? {
var path: String? = null
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try {
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
path = cursor.getString(index)
}
} catch (ex: IllegalArgumentException) {
return getCopyFilePath(uri, context)
} finally {
cursor?.close()
}
return path ?: getCopyFilePath(uri, context)
}
private fun getCopyFilePath(uri: Uri, context: Context): String? {
val cursor = context.contentResolver.query(uri, null, null, null, null)!!
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
val name = cursor.getString(nameIndex)
val file = File(context.filesDir, name)
try {
val inputStream = context.contentResolver.openInputStream(uri)
val outputStream = FileOutputStream(file)
var read: Int
val maxBufferSize = 1024 * 1024
val bufferSize = min(inputStream!!.available(), maxBufferSize)
val buffers = ByteArray(bufferSize)
while (inputStream.read(buffers).also { read = it } != -1) {
outputStream.write(buffers, 0, read)
}
inputStream.close()
outputStream.close()
} catch (e: Exception) {
return null
} finally {
cursor.close()
}
return file.path
}
private fun legacyPrimaryPath(pathPart: String): String {
return Environment.getExternalStorageDirectory().toString() + "/" + pathPart
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
private fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
private fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
private fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
private fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority
}
private fun getPathToNonPrimaryVolume(context: Context, tag: String): String? {
val volumes = context.externalCacheDirs
if (volumes != null) {
for (volume in volumes) {
if (volume != null) {
val path = volume.absolutePath
val index = path.indexOf(tag)
if (index != -1) {
return path.substring(0, index) + tag
}
}
}
}
return null
}
}
}
@@ -0,0 +1,8 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
@Retention(AnnotationRetention.RUNTIME)
internal annotation class JniMethod
@@ -0,0 +1,85 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
// taken from https://github.com/ionic-team/capacitor/blob/6658bca41e78239347e458175b14ca8bd5c1d6e8/android/capacitor/src/main/java/com/getcapacitor/Logger.java
import android.text.TextUtils;
import android.util.Log;
class Logger {
companion object {
private const val LOG_TAG_CORE = "Tauri"
fun tags(vararg subtags: String): String {
return if (subtags.isNotEmpty()) {
LOG_TAG_CORE + "/" + TextUtils.join("/", subtags)
} else LOG_TAG_CORE
}
fun verbose(message: String) {
verbose(LOG_TAG_CORE, message)
}
fun verbose(tag: String, message: String) {
if (!shouldLog()) {
return
}
Log.v(tag, message)
}
fun debug(message: String) {
debug(LOG_TAG_CORE, message)
}
fun debug(tag: String, message: String) {
if (!shouldLog()) {
return
}
Log.d(tag, message)
}
fun info(message: String) {
info(LOG_TAG_CORE, message)
}
fun info(tag: String, message: String) {
if (!shouldLog()) {
return
}
Log.i(tag, message)
}
fun warn(message: String) {
warn(LOG_TAG_CORE, message)
}
fun warn(tag: String, message: String) {
if (!shouldLog()) {
return
}
Log.w(tag, message)
}
fun error(message: String) {
error(LOG_TAG_CORE, message, null)
}
fun error(message: String, e: Throwable?) {
error(LOG_TAG_CORE, message, e)
}
fun error(tag: String, message: String, e: Throwable?) {
if (!shouldLog()) {
return
}
Log.e(tag, message, e)
}
private fun shouldLog(): Boolean {
return BuildConfig.DEBUG
}
}
}
@@ -0,0 +1,78 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
import android.app.Activity
import android.os.Environment
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.JSObject
@TauriPlugin
class PathPlugin(private val activity: Activity): Plugin(activity) {
private fun resolvePath(invoke: Invoke, path: String?) {
val obj = JSObject()
obj.put("path", path)
invoke.resolve(obj)
}
@Command
fun getAudioDir(invoke: Invoke) {
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_MUSIC)?.absolutePath)
}
@Command
fun getExternalCacheDir(invoke: Invoke) {
resolvePath(invoke, activity.externalCacheDir?.absolutePath)
}
@Command
fun getConfigDir(invoke: Invoke) {
resolvePath(invoke, activity.dataDir.absolutePath)
}
@Command
fun getDataDir(invoke: Invoke) {
resolvePath(invoke, activity.dataDir.absolutePath)
}
@Command
fun getDocumentDir(invoke: Invoke) {
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.absolutePath)
}
@Command
fun getDownloadDir(invoke: Invoke) {
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath)
}
@Command
fun getPictureDir(invoke: Invoke) {
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath)
}
@Command
fun getPublicDir(invoke: Invoke) {
resolvePath(invoke, activity.getExternalFilesDir(Environment.DIRECTORY_DCIM)?.absolutePath)
}
@Command
fun getVideoDir(invoke: Invoke) {
resolvePath(invoke, activity.externalCacheDir?.absolutePath)
}
@Command
fun getResourcesDir(invoke: Invoke) {
// TODO
resolvePath(invoke, activity.cacheDir.absolutePath)
}
@Command
fun getCacheDir(invoke: Invoke) {
resolvePath(invoke, activity.cacheDir.absolutePath)
}
}
@@ -0,0 +1,115 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
// taken from https://github.com/ionic-team/capacitor/blob/6658bca41e78239347e458175b14ca8bd5c1d6e8/android/capacitor/src/main/java/com/getcapacitor/PermissionHelper.java
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
object PermissionHelper {
/**
* Checks if a list of given permissions are all granted by the user
*
* @param permissions Permissions to check.
* @return True if all permissions are granted, false if at least one is not.
*/
fun hasPermissions(context: Context?, permissions: Array<String>): Boolean {
for (perm in permissions) {
if (ActivityCompat.checkSelfPermission(
context!!,
perm
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
return true
}
/**
* Check whether the given permission has been defined in the AndroidManifest.xml
*
* @param permission A permission to check.
* @return True if the permission has been defined in the Manifest, false if not.
*/
fun hasDefinedPermission(context: Context, permission: String): Boolean {
var hasPermission = false
val requestedPermissions = getManifestPermissions(context)
if (requestedPermissions != null && requestedPermissions.isNotEmpty()) {
val requestedPermissionsList = listOf(*requestedPermissions)
val requestedPermissionsArrayList = ArrayList(requestedPermissionsList)
if (requestedPermissionsArrayList.contains(permission)) {
hasPermission = true
}
}
return hasPermission
}
/**
* Check whether all of the given permissions have been defined in the AndroidManifest.xml
* @param context the app context
* @param permissions a list of permissions
* @return true only if all permissions are defined in the AndroidManifest.xml
*/
fun hasDefinedPermissions(context: Context, permissions: Array<String>): Boolean {
for (permission in permissions) {
if (!hasDefinedPermission(context, permission)) {
return false
}
}
return true
}
/**
* Get the permissions defined in AndroidManifest.xml
*
* @return The permissions defined in AndroidManifest.xml
*/
private fun getManifestPermissions(context: Context): Array<String>? {
var requestedPermissions: Array<String>? = null
try {
val pm = context.packageManager
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pm.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()))
} else {
@Suppress("DEPRECATION")
pm.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
}
if (packageInfo != null) {
requestedPermissions = packageInfo.requestedPermissions
}
} catch (_: Exception) {
}
return requestedPermissions
}
/**
* Given a list of permissions, return a new list with the ones not present in AndroidManifest.xml
*
* @param neededPermissions The permissions needed.
* @return The permissions not present in AndroidManifest.xml
*/
fun getUndefinedPermissions(context: Context, neededPermissions: Array<String>): Array<String?> {
val undefinedPermissions = ArrayList<String?>()
val requestedPermissions = getManifestPermissions(context)
if (requestedPermissions != null && requestedPermissions.isNotEmpty()) {
val requestedPermissionsList = listOf(*requestedPermissions)
val requestedPermissionsArrayList = ArrayList(requestedPermissionsList)
for (permission in neededPermissions) {
if (!requestedPermissionsArrayList.contains(permission)) {
undefinedPermissions.add(permission)
}
}
var undefinedPermissionArray = arrayOfNulls<String>(undefinedPermissions.size)
undefinedPermissionArray = undefinedPermissions.toArray(undefinedPermissionArray)
return undefinedPermissionArray
}
return neededPermissions as Array<String?>
}
}
@@ -0,0 +1,21 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri
import java.util.*
enum class PermissionState(private val state: String) {
GRANTED("granted"), DENIED("denied"), PROMPT("prompt"), PROMPT_WITH_RATIONALE("prompt-with-rationale");
override fun toString(): String {
return state
}
companion object {
fun byState(state: String): PermissionState {
return valueOf(state.uppercase(Locale.ROOT).replace('-', '_'))
}
}
}
@@ -0,0 +1,9 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.annotation
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class ActivityCallback
@@ -0,0 +1,19 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.annotation
@Retention(AnnotationRetention.RUNTIME)
annotation class Permission(
/**
* An array of Android permission strings.
* Eg: {Manifest.permission.ACCESS_COARSE_LOCATION}
* or {"android.permission.ACCESS_COARSE_LOCATION"}
*/
val strings: Array<String> = [],
/**
* An optional name to use instead of the Android permission string.
*/
val alias: String = ""
)
@@ -0,0 +1,9 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.annotation
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class PermissionCallback
@@ -0,0 +1,8 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.annotation
@Retention(AnnotationRetention.RUNTIME)
annotation class Command
@@ -0,0 +1,19 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.annotation
import app.tauri.annotation.Permission
/**
* Base annotation for all Plugins
*/
@Retention(AnnotationRetention.RUNTIME)
annotation class TauriPlugin(
/**
* Permissions this plugin needs, in order to make permission requests
* easy if the plugin only needs basic permission prompting
*/
val permissions: Array<Permission> = []
)
@@ -0,0 +1,11 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.plugin
internal class InvalidCommandException : Exception {
constructor(s: String?) : super(s) {}
constructor(t: Throwable?) : super(t) {}
constructor(s: String?, t: Throwable?) : super(s, t) {}
}
@@ -0,0 +1,200 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.plugin
import app.tauri.Logger
class Invoke(
val id: Long,
val command: String,
private val sendResponse: (success: PluginResult?, error: PluginResult?) -> Unit,
val data: JSObject) {
fun resolve(data: JSObject?) {
val result = PluginResult(data)
sendResponse(result, null)
}
fun resolve() {
sendResponse(null, null)
}
fun reject(msg: String?, code: String?, ex: Exception?, data: JSObject?) {
val errorResult = PluginResult()
if (ex != null) {
Logger.error(Logger.tags("Plugin"), msg!!, ex)
}
try {
errorResult.put("message", msg)
errorResult.put("code", code)
if (null != data) {
errorResult.put("data", data)
}
} catch (jsonEx: Exception) {
Logger.error(Logger.tags("Plugin"), jsonEx.message!!, jsonEx)
}
sendResponse(null, errorResult)
}
fun reject(msg: String?, ex: Exception?, data: JSObject?) {
reject(msg, null, ex, data)
}
fun reject(msg: String?, code: String?, data: JSObject?) {
reject(msg, code, null, data)
}
fun reject(msg: String?, code: String?, ex: Exception?) {
reject(msg, code, ex, null)
}
fun reject(msg: String?, data: JSObject?) {
reject(msg, null, null, data)
}
fun reject(msg: String?, ex: Exception?) {
reject(msg, null, ex, null)
}
fun reject(msg: String?, code: String?) {
reject(msg, code, null, null)
}
fun reject(msg: String?) {
reject(msg, null, null, null)
}
fun getString(name: String): String? {
return getStringInternal(name, null)
}
fun getString(name: String, defaultValue: String): String {
return getStringInternal(name, defaultValue)!!
}
private fun getStringInternal(name: String, defaultValue: String?): String? {
val value = data.opt(name) ?: return defaultValue
return if (value is String) {
value
} else defaultValue
}
fun getInt(name: String): Int? {
return getIntInternal(name, null)
}
fun getInt(name: String, defaultValue: Int): Int {
return getIntInternal(name, defaultValue)!!
}
private fun getIntInternal(name: String, defaultValue: Int?): Int? {
val value = data.opt(name) ?: return defaultValue
return if (value is Int) {
value
} else defaultValue
}
fun getLong(name: String): Long? {
return getLongInternal(name, null)
}
fun getLong(name: String, defaultValue: Long): Long {
return getLongInternal(name, defaultValue)!!
}
private fun getLongInternal(name: String, defaultValue: Long?): Long? {
val value = data.opt(name) ?: return defaultValue
return if (value is Long) {
value
} else defaultValue
}
fun getFloat(name: String): Float? {
return getFloatInternal(name, null)
}
fun getFloat(name: String, defaultValue: Float): Float {
return getFloatInternal(name, defaultValue)!!
}
private fun getFloatInternal(name: String, defaultValue: Float?): Float? {
val value = data.opt(name) ?: return defaultValue
if (value is Float) {
return value
}
if (value is Double) {
return value.toFloat()
}
return if (value is Int) {
value.toFloat()
} else defaultValue
}
fun getDouble(name: String): Double? {
return getDoubleInternal(name, null)
}
fun getDouble(name: String, defaultValue: Double): Double {
return getDoubleInternal(name, defaultValue)!!
}
private fun getDoubleInternal(name: String, defaultValue: Double?): Double? {
val value = data.opt(name) ?: return defaultValue
if (value is Double) {
return value
}
if (value is Float) {
return value.toDouble()
}
return if (value is Int) {
value.toDouble()
} else defaultValue
}
fun getBoolean(name: String): Boolean? {
return getBooleanInternal(name, null)
}
fun getBoolean(name: String, defaultValue: Boolean): Boolean {
return getBooleanInternal(name, defaultValue)!!
}
private fun getBooleanInternal(name: String, defaultValue: Boolean?): Boolean? {
val value = data.opt(name) ?: return defaultValue
return if (value is Boolean) {
value
} else defaultValue
}
fun getObject(name: String): JSObject? {
return getObjectInternal(name, null)
}
fun getObject(name: String, defaultValue: JSObject): JSObject {
return getObjectInternal(name, defaultValue)!!
}
private fun getObjectInternal(name: String, defaultValue: JSObject?): JSObject? {
val value = data.opt(name) ?: return defaultValue
return if (value is JSObject) value else defaultValue
}
fun getArray(name: String): JSArray? {
return getArrayInternal(name, null)
}
fun getArray(name: String, defaultValue: JSArray): JSArray {
return getArrayInternal(name, defaultValue)!!
}
private fun getArrayInternal(name: String, defaultValue: JSArray?): JSArray? {
val value = data.opt(name) ?: return defaultValue
return if (value is JSArray) value else defaultValue
}
fun hasOption(name: String): Boolean {
return data.has(name)
}
}
@@ -0,0 +1,45 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.plugin
import org.json.JSONArray
import org.json.JSONException
class JSArray : JSONArray {
constructor() : super() {}
constructor(json: String?) : super(json) {}
constructor(copyFrom: Collection<*>?) : super(copyFrom) {}
constructor(array: Any?) : super(array) {}
@Suppress("UNCHECKED_CAST", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
@Throws(JSONException::class)
fun <E> toList(): List<E> {
val items: MutableList<E> = ArrayList()
var o: Any? = null
for (i in 0 until this.length()) {
this.get(i).also { o = it }
try {
items.add(this.get(i) as E)
} catch (ex: Exception) {
throw JSONException("Not all items are instances of the given type")
}
}
return items
}
companion object {
/**
* Create a new JSArray without throwing a error
*/
fun from(array: Any?): JSArray? {
try {
return JSArray(array)
} catch (ex: JSONException) {
//
}
return null
}
}
}

Some files were not shown because too many files have changed in this diff Show More