Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66707004e3 | |||
| baa3691851 | |||
| 6e30e6178b | |||
| 7c65d61d91 | |||
| ad2efb7e62 | |||
| a50c9bfa1a | |||
| 30cdbf535a | |||
| f5365cbca9 | |||
| 22e7cb887b | |||
| b571f1a881 | |||
| b1c4e3ded7 | |||
| 4694ded8bd | |||
| 7971573026 | |||
| c4780c8af2 | |||
| 7879d76592 | |||
| 0f62ea25d8 | |||
| cde53c02e7 | |||
| de64da8e20 | |||
| 4aeac1acd2 | |||
| 679be24074 | |||
| 36e07f546d | |||
| d264feaf22 | |||
| 32b7b7afdc | |||
| 091382ef30 | |||
| 1ad97adc7c |
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -6,20 +6,16 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bip39 = { workspace = true }
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
log = "0.4"
|
||||
rand = "0.7.3"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
nym-bin-common = { path = "../../common/bin-common"}
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-pemstore = { path = "../../common/pemstore" }
|
||||
|
||||
@@ -3,24 +3,13 @@
|
||||
|
||||
use clap::{ArgGroup, Args, Subcommand};
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::acquire::state::State;
|
||||
use nym_bin_common::completions::ArgShell;
|
||||
use nym_coconut_interface::{Base58, Parameters};
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credential_storage::PersistentStorage;
|
||||
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
use nym_credentials::coconut::utils::obtain_aggregate_signature;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_network_defaults::VOUCHER_INFO;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||
use nym_validator_client::nyxd::tx::Hash;
|
||||
use nym_validator_client::CoconutApiClient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::error::{CredentialClientError, Result};
|
||||
use crate::error::Result;
|
||||
use crate::recovery_storage::RecoveryStorage;
|
||||
use crate::state::{KeyPair, State};
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Command {
|
||||
@@ -45,10 +34,6 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
pub(crate) client_home_directory: std::path::PathBuf,
|
||||
|
||||
/// The nyxd URL that should be used
|
||||
#[clap(long)]
|
||||
pub(crate) nyxd_url: String,
|
||||
|
||||
/// A mnemonic for the account that buys the credential
|
||||
#[clap(long)]
|
||||
pub(crate) mnemonic: String,
|
||||
@@ -67,81 +52,16 @@ pub(crate) struct Run {
|
||||
pub(crate) recovery_mode: bool,
|
||||
}
|
||||
|
||||
pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
|
||||
let mut rng = OsRng;
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
|
||||
let client = Client::new(nyxd_url, mnemonic);
|
||||
let tx_hash = client
|
||||
.deposit(
|
||||
amount,
|
||||
signing_keypair.public_key.clone(),
|
||||
encryption_keypair.public_key.clone(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let voucher = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_credential<C: DkgQueryClient + Send + Sync>(
|
||||
state: &State,
|
||||
client: &C,
|
||||
shared_storage: PersistentStorage,
|
||||
) -> Result<()> {
|
||||
let epoch_id = client.get_current_epoch().await?.epoch_id;
|
||||
let threshold = client
|
||||
.get_current_epoch_threshold()
|
||||
.await?
|
||||
.ok_or(CredentialClientError::NoThreshold)?;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?;
|
||||
|
||||
let signature = obtain_aggregate_signature(
|
||||
&state.params,
|
||||
&state.voucher,
|
||||
&coconut_api_clients,
|
||||
threshold,
|
||||
)
|
||||
.await?;
|
||||
info!("Signature: {:?}", signature.to_bs58());
|
||||
shared_storage
|
||||
.insert_coconut_credential(
|
||||
state.voucher.get_voucher_value(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
state.voucher.get_private_attributes()[0].to_bs58(),
|
||||
state.voucher.get_private_attributes()[1].to_bs58(),
|
||||
signature.to_bs58(),
|
||||
epoch_id.to_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn recover_credentials<C: DkgQueryClient + Send + Sync>(
|
||||
client: &C,
|
||||
recovery_storage: &RecoveryStorage,
|
||||
shared_storage: PersistentStorage,
|
||||
shared_storage: &PersistentStorage,
|
||||
) -> Result<()> {
|
||||
for voucher in recovery_storage.unconsumed_vouchers()? {
|
||||
let state = State {
|
||||
voucher,
|
||||
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
|
||||
};
|
||||
if let Err(e) = get_credential(&state, client, shared_storage.clone()).await {
|
||||
let state = State::new(voucher);
|
||||
if let Err(e) =
|
||||
nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await
|
||||
{
|
||||
error!(
|
||||
"Could not recover deposit {} due to {:?}, try again later",
|
||||
state.voucher.tx_hash(),
|
||||
|
||||
@@ -6,8 +6,6 @@ use thiserror::Error;
|
||||
|
||||
use nym_credential_storage::error::StorageError;
|
||||
use nym_credentials::error::Error as CredentialError;
|
||||
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
|
||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::ValidatorClientError;
|
||||
|
||||
@@ -18,6 +16,9 @@ pub enum CredentialClientError {
|
||||
#[error("IO error: {0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("Bandwidth controller error: {0}")]
|
||||
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
|
||||
|
||||
#[error("Nyxd error: {0}")]
|
||||
Nyxd(#[from] NyxdError),
|
||||
|
||||
@@ -27,21 +28,9 @@ pub enum CredentialClientError {
|
||||
#[error("Credential error: {0}")]
|
||||
Credential(#[from] CredentialError),
|
||||
|
||||
#[error("The tx hash provided is not valid")]
|
||||
InvalidTxHash,
|
||||
|
||||
#[error("Could not parse Ed25519 data")]
|
||||
Ed25519ParseError(#[from] Ed25519RecoveryError),
|
||||
|
||||
#[error("Could not parse X25519 data")]
|
||||
X25519ParseError(#[from] KeyRecoveryError),
|
||||
|
||||
#[error("Could not use shared storage")]
|
||||
SharedStorageError(#[from] StorageError),
|
||||
|
||||
#[error("Could not get system time")]
|
||||
SysTimeError(#[from] SystemTimeError),
|
||||
|
||||
#[error("Threshold not set yet")]
|
||||
NoThreshold,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod client;
|
||||
mod commands;
|
||||
mod error;
|
||||
mod recovery_storage;
|
||||
mod state;
|
||||
|
||||
use commands::*;
|
||||
use error::Result;
|
||||
@@ -19,7 +17,7 @@ use std::time::{Duration, SystemTime};
|
||||
use clap::{CommandFactory, Parser};
|
||||
use nym_bin_common::logging::setup_logging;
|
||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||
use nym_validator_client::nyxd::CosmWasmClient;
|
||||
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
|
||||
use nym_validator_client::Config;
|
||||
|
||||
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
|
||||
@@ -35,7 +33,7 @@ struct Cli {
|
||||
pub(crate) command: Command,
|
||||
}
|
||||
|
||||
async fn block_until_coconut_is_available<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
async fn block_until_coconut_is_available<C: CosmWasmClient + Send + Sync>(
|
||||
client: &nym_validator_client::Client<C>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
@@ -77,21 +75,34 @@ async fn main() -> Result<()> {
|
||||
.client_home_directory
|
||||
.join(DATA_DIR)
|
||||
.join(CRED_DB_FILE_NAME);
|
||||
let shared_storage = nym_credential_storage::initialise_storage(db_path).await;
|
||||
let shared_storage =
|
||||
nym_credential_storage::initialise_persistent_storage(db_path).await;
|
||||
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
||||
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = Config::try_from_nym_network_details(&network_details)?;
|
||||
let client = nym_validator_client::Client::new_query(config)?;
|
||||
let config = Config::try_from_nym_network_details(&network_details).expect(
|
||||
"failed to construct valid validator client config with the provided network",
|
||||
);
|
||||
let amount = Coin::new(
|
||||
r.amount as u128,
|
||||
network_details.chain_details.mix_denom.base,
|
||||
);
|
||||
let client =
|
||||
nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?;
|
||||
|
||||
block_until_coconut_is_available(&client).await?;
|
||||
info!("Starting depositing funds, don't kill the process");
|
||||
|
||||
if !r.recovery_mode {
|
||||
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
|
||||
if get_credential(&state, &client.nyxd, shared_storage)
|
||||
.await
|
||||
.is_err()
|
||||
let state =
|
||||
nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?;
|
||||
if nym_bandwidth_controller::acquire::get_credential(
|
||||
&state,
|
||||
&client,
|
||||
&shared_storage,
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
warn!("Failed to obtain credential. Dumping recovery data.",);
|
||||
match recovery_storage.insert_voucher(&state.voucher) {
|
||||
@@ -104,11 +115,11 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
recover_credentials(&client.nyxd, &recovery_storage, shared_storage).await?;
|
||||
recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?;
|
||||
}
|
||||
}
|
||||
Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name),
|
||||
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
|
||||
Command::Completions(c) => c.generate(&mut Cli::command(), bin_name),
|
||||
Command::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -34,6 +34,7 @@ tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio-tungstenite = "0.14" # websocket
|
||||
|
||||
## internal
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] }
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
@@ -41,7 +42,6 @@ nym-config = { path = "../../common/config" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::error::ClientError;
|
||||
use crate::websocket;
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
@@ -14,7 +15,6 @@ use nym_client_core::client::received_buffer::{
|
||||
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
|
||||
};
|
||||
use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use nym_gateway_client::bandwidth::BandwidthController;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use nym_task::TaskManager;
|
||||
@@ -23,6 +23,7 @@ use std::error::Error;
|
||||
use tokio::sync::watch::error::SendError;
|
||||
|
||||
pub use nym_client_core::client::key_manager::KeyManager;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
pub use nym_sphinx::addressing::clients::Recipient;
|
||||
pub use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_validator_client::Client;
|
||||
@@ -58,7 +59,7 @@ impl SocketClient {
|
||||
|
||||
async fn create_bandwidth_controller(
|
||||
config: &Config,
|
||||
) -> BandwidthController<Client<QueryNyxdClient>> {
|
||||
) -> BandwidthController<Client<QueryNyxdClient>, PersistentStorage> {
|
||||
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config =
|
||||
nym_validator_client::Config::try_from_nym_network_details(&details)
|
||||
@@ -78,7 +79,10 @@ impl SocketClient {
|
||||
let client = nym_validator_client::Client::new_query(client_config)
|
||||
.expect("Could not construct query client");
|
||||
BandwidthController::new(
|
||||
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await,
|
||||
nym_credential_storage::initialise_persistent_storage(
|
||||
config.get_base().get_database_path(),
|
||||
)
|
||||
.await,
|
||||
client,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::{
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::NymConfig;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::Serialize;
|
||||
@@ -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(),
|
||||
|
||||
@@ -23,10 +23,10 @@ nym-bin-common = { path = "../../common/bin-common", features = ["output_format"
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] }
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-mobile-storage = { path = "../../common/mobile-storage", optional = true }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::{
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::NymConfig;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_socks5_client_core::config::Config;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,12 @@ wasm-bindgen-futures = "0.4"
|
||||
|
||||
# internal
|
||||
nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] }
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||
wasm-utils = { path = "../../common/wasm-utils" }
|
||||
nym-task = { path = "../../common/task" }
|
||||
|
||||
@@ -5,13 +5,14 @@ use self::config::Config;
|
||||
use crate::client::helpers::InputSender;
|
||||
use crate::client::response_pusher::ResponsePusher;
|
||||
use js_sys::Promise;
|
||||
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core::client::base_client::{
|
||||
BaseClientBuilder, ClientInput, ClientOutput, CredentialsToggle,
|
||||
};
|
||||
use nym_client_core::client::replies::reply_storage::browser_backend;
|
||||
use nym_client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager};
|
||||
use nym_gateway_client::bandwidth::BandwidthController;
|
||||
use nym_gateway_client::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
@@ -48,7 +49,8 @@ pub struct NymClientBuilder {
|
||||
on_message: js_sys::Function,
|
||||
|
||||
// unimplemented:
|
||||
bandwidth_controller: Option<BandwidthController<FakeClient<DirectSigningNyxdClient>>>,
|
||||
bandwidth_controller:
|
||||
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
|
||||
disabled_credentials: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "nym-bandwidth-controller"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bip39 = { workspace = true }
|
||||
rand = "0.7.3"
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
|
||||
nym-coconut-interface = { path = "../coconut-interface" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
||||
path = "../client-libs/validator-client"
|
||||
features = ["nyxd-client"]
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use nym_coconut_interface::{Base58, Parameters};
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
use nym_credentials::coconut::utils::obtain_aggregate_signature;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_network_defaults::VOUCHER_INFO;
|
||||
use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient;
|
||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||
use nym_validator_client::nyxd::tx::Hash;
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use nym_validator_client::CoconutApiClient;
|
||||
use rand::rngs::OsRng;
|
||||
use state::{KeyPair, State};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub mod state;
|
||||
|
||||
pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError>
|
||||
where
|
||||
C: CoconutBandwidthSigningClient,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let voucher_value = amount.amount.to_string();
|
||||
|
||||
let tx_hash = client
|
||||
.deposit(
|
||||
amount,
|
||||
String::from(VOUCHER_INFO),
|
||||
signing_keypair.public_key.clone(),
|
||||
encryption_keypair.public_key.clone(),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.transaction_hash
|
||||
.to_string();
|
||||
|
||||
let voucher = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
voucher_value,
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub async fn get_credential<C: DkgQueryClient + Send + Sync, St: Storage>(
|
||||
state: &State,
|
||||
client: &C,
|
||||
storage: &St,
|
||||
) -> Result<(), BandwidthControllerError> {
|
||||
let epoch_id = client.get_current_epoch().await?.epoch_id;
|
||||
let threshold = client
|
||||
.get_current_epoch_threshold()
|
||||
.await?
|
||||
.ok_or(BandwidthControllerError::NoThreshold)?;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?;
|
||||
|
||||
let signature = obtain_aggregate_signature(
|
||||
&state.params,
|
||||
&state.voucher,
|
||||
&coconut_api_clients,
|
||||
threshold,
|
||||
)
|
||||
.await?;
|
||||
storage
|
||||
.insert_coconut_credential(
|
||||
state.voucher.get_voucher_value(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
state.voucher.get_private_attributes()[0].to_bs58(),
|
||||
state.voucher.get_private_attributes()[1].to_bs58(),
|
||||
signature.to_bs58(),
|
||||
epoch_id.to_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_coconut_interface::Parameters;
|
||||
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
|
||||
@@ -29,7 +29,16 @@ impl From<encryption::KeyPair> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct State {
|
||||
pub struct State {
|
||||
pub voucher: BandwidthVoucher,
|
||||
pub params: Parameters,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(voucher: BandwidthVoucher) -> Self {
|
||||
State {
|
||||
voucher,
|
||||
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_coconut_interface::CoconutError;
|
||||
use nym_credential_storage::error::StorageError;
|
||||
use nym_credentials::error::Error as CredentialsError;
|
||||
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
|
||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||
use nym_validator_client::error::ValidatorClientError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BandwidthControllerError {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("Nyxd error: {0}")]
|
||||
Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError),
|
||||
|
||||
#[error("There was a credential storage error - {0}")]
|
||||
CredentialStorageError(#[from] StorageError),
|
||||
|
||||
#[error("Coconut error - {0}")]
|
||||
CoconutError(#[from] CoconutError),
|
||||
|
||||
#[error("Validator client error - {0}")]
|
||||
ValidatorError(#[from] ValidatorClientError),
|
||||
|
||||
#[error("Credential error - {0}")]
|
||||
CredentialError(#[from] CredentialsError),
|
||||
|
||||
#[error("Could not parse Ed25519 data")]
|
||||
Ed25519ParseError(#[from] Ed25519RecoveryError),
|
||||
|
||||
#[error("Could not parse X25519 data")]
|
||||
X25519ParseError(#[from] KeyRecoveryError),
|
||||
|
||||
#[error("The tx hash provided is not valid")]
|
||||
InvalidTxHash,
|
||||
|
||||
#[error("Threshold not set yet")]
|
||||
NoThreshold,
|
||||
}
|
||||
+16
-41
@@ -1,28 +1,10 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use crate::error::BandwidthControllerError;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::Storage;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_credential_storage::storage::Storage;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(target_os = "android")]
|
||||
use mobile_storage::Storage;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(target_os = "android")]
|
||||
use mobile_storage::StorageError;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::StorageError;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_credential_storage::error::StorageError;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
|
||||
use std::str::FromStr;
|
||||
use {
|
||||
@@ -35,38 +17,32 @@ use {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||
|
||||
// TODO: make it nicer for wasm (I don't want to touch it for this experiment)
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::PersistentStorage;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::DkgQueryClient;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_credential_storage::PersistentStorage;
|
||||
pub mod acquire;
|
||||
pub mod error;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm_mockups;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(target_os = "android")]
|
||||
use mobile_storage::PersistentStorage;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct BandwidthController<C, St: Storage = PersistentStorage> {
|
||||
pub struct BandwidthController<C, St: Storage> {
|
||||
storage: St,
|
||||
client: C,
|
||||
}
|
||||
|
||||
impl<C, St> BandwidthController<C, St>
|
||||
where
|
||||
St: Storage + 'static,
|
||||
{
|
||||
impl<C, St: Storage> BandwidthController<C, St> {
|
||||
pub fn new(storage: St, client: C) -> Self {
|
||||
BandwidthController { storage, client }
|
||||
}
|
||||
|
||||
pub fn storage(&self) -> &St {
|
||||
&self.storage
|
||||
}
|
||||
|
||||
pub async fn prepare_coconut_credential(
|
||||
&self,
|
||||
) -> Result<(nym_coconut_interface::Credential, i64), GatewayClientError>
|
||||
) -> Result<(nym_coconut_interface::Credential, i64), BandwidthControllerError>
|
||||
where
|
||||
C: DkgQueryClient + Sync + Send,
|
||||
{
|
||||
@@ -86,8 +62,7 @@ where
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let coconut_api_clients =
|
||||
nym_validator_client::CoconutApiClient::all_coconut_api_clients(&self.client, epoch_id)
|
||||
.await
|
||||
.expect("Could not query api clients");
|
||||
.await?;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let coconut_api_clients = vec![];
|
||||
let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?;
|
||||
@@ -107,7 +82,7 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn consume_credential(&self, id: i64) -> Result<(), GatewayClientError> {
|
||||
pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError> {
|
||||
// JS: shouldn't we send some contract/validator/gateway message here to actually, you know,
|
||||
// consume it?
|
||||
Ok(self.storage.consume_coconut_credential(id).await?)
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct DirectSigningNyxdClient {}
|
||||
|
||||
pub trait DkgQueryClient {}
|
||||
|
||||
// impl CosmWasmClient for DirectSigningNyxdClient {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client<C> {
|
||||
_phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C> DkgQueryClient for Client<C> {}
|
||||
@@ -25,6 +25,7 @@ tokio = { version = "1.24.1", features = ["macros"]}
|
||||
time = "0.3.17"
|
||||
|
||||
# internal
|
||||
nym-bandwidth-controller = { path = "../bandwidth-controller" }
|
||||
nym-config = { path = "../config" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
@@ -36,6 +37,7 @@ nym-pemstore = { path = "../pemstore" }
|
||||
nym-topology = { path = "../topology" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-task = { path = "../task" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
||||
path = "../client-libs/validator-client"
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+68
-16
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,10 @@ use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use nym_gateway_client::wasm_mockups::DirectSigningNyxdClient;
|
||||
use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer::Instant;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -223,12 +224,12 @@ pub(super) async fn query_gateway_details(
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn register_with_gateway(
|
||||
pub(super) async fn register_with_gateway<St: Storage>(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
||||
let timeout = Duration::from_millis(1500);
|
||||
let mut gateway_client: GatewayClient<DirectSigningNyxdClient> = GatewayClient::new_init(
|
||||
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, St> = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
our_identity.clone(),
|
||||
|
||||
@@ -11,6 +11,7 @@ use serde::Serialize;
|
||||
use tap::TapFallible;
|
||||
|
||||
use nym_config::NymConfig;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use url::Url;
|
||||
|
||||
@@ -73,7 +74,7 @@ pub fn new_client_keys() -> KeyManager {
|
||||
/// Either pick one at random by querying the available gateways from the nym-api, or use the
|
||||
/// chosen one if it's among the available ones.
|
||||
/// The shared key is added to the supplied `KeyManager` and the endpoint details are returned.
|
||||
pub async fn register_with_gateway(
|
||||
pub async fn register_with_gateway<St: Storage>(
|
||||
key_manager: &mut KeyManager,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
chosen_gateway_id: Option<identity::PublicKey>,
|
||||
@@ -87,7 +88,7 @@ pub async fn register_with_gateway(
|
||||
let our_identity = key_manager.identity_keypair();
|
||||
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
let shared_keys = helpers::register_with_gateway::<St>(&gateway, our_identity).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
Ok(gateway.into())
|
||||
@@ -100,7 +101,7 @@ pub async fn register_with_gateway(
|
||||
/// b. Create a new gateway configuration but keep existing keys. This assumes that the caller
|
||||
/// knows what they are doing and that the keys match the requested gateway.
|
||||
/// c. Create a new gateway configuration with a newly registered gateway and keys.
|
||||
pub async fn setup_gateway_from_config<C, T>(
|
||||
pub async fn setup_gateway_from_config<C, T, St>(
|
||||
register_gateway: bool,
|
||||
user_chosen_gateway_id: Option<identity::PublicKey>,
|
||||
config: &Config<T>,
|
||||
@@ -109,6 +110,7 @@ pub async fn setup_gateway_from_config<C, T>(
|
||||
where
|
||||
C: NymConfig + ClientCoreConfigTrait,
|
||||
T: NymConfig,
|
||||
St: Storage,
|
||||
{
|
||||
let id = config.get_id();
|
||||
|
||||
@@ -141,7 +143,7 @@ where
|
||||
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
eprintln!("Registering with new gateway");
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
let shared_keys = helpers::register_with_gateway::<St>(&gateway, our_identity).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
// Write all keys to storage and just return the gateway endpoint config. It is assumed that we
|
||||
|
||||
@@ -14,12 +14,12 @@ log = { workspace = true }
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
async-trait = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["macros"] }
|
||||
|
||||
# internal
|
||||
nym-bandwidth-controller = { path = "../../bandwidth-controller" }
|
||||
nym-coconut-interface = { path = "../../coconut-interface" }
|
||||
nym-credentials = { path = "../../credentials" }
|
||||
nym-credential-storage = { path = "../../credential-storage" }
|
||||
nym-crypto = { path = "../../crypto" }
|
||||
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
@@ -28,7 +28,6 @@ nym-pemstore = { path = "../../pemstore" }
|
||||
nym-validator-client = { path = "../validator-client" }
|
||||
nym-task = { path = "../../task" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
nym-mobile-storage = { path = "../../mobile-storage" }
|
||||
|
||||
|
||||
[dependencies.tungstenite]
|
||||
@@ -47,9 +46,6 @@ features = ["net", "sync", "time"]
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-credential-storage]
|
||||
path = "../../credential-storage"
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
version = "0.2"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::bandwidth::BandwidthController;
|
||||
use crate::error::GatewayClientError;
|
||||
use crate::packet_router::PacketRouter;
|
||||
pub use crate::packet_router::{
|
||||
@@ -11,6 +10,7 @@ use crate::socket_state::{PartiallyDelegated, SocketState};
|
||||
use crate::{cleanup_socket_message, try_decrypt_binary_message};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_coconut_interface::Credential;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
|
||||
@@ -26,24 +26,14 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tungstenite::protocol::Message;
|
||||
|
||||
use nym_credential_storage::storage::Storage;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_credential_storage::PersistentStorage;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(target_os = "android")]
|
||||
use mobile_storage::PersistentStorage;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::DkgQueryClient;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::PersistentStorage;
|
||||
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -52,7 +42,7 @@ use wasm_utils::websocket::JSWebsocket;
|
||||
const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
|
||||
const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
|
||||
|
||||
pub struct GatewayClient<C> {
|
||||
pub struct GatewayClient<C, St: Storage> {
|
||||
authenticated: bool,
|
||||
disabled_credentials_mode: bool,
|
||||
bandwidth_remaining: i64,
|
||||
@@ -63,7 +53,7 @@ pub struct GatewayClient<C> {
|
||||
connection: SocketState,
|
||||
packet_router: PacketRouter,
|
||||
response_timeout_duration: Duration,
|
||||
bandwidth_controller: Option<BandwidthController<C, PersistentStorage>>,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
|
||||
// reconnection related variables
|
||||
/// Specifies whether client should try to reconnect to gateway on connection failure.
|
||||
@@ -78,9 +68,10 @@ pub struct GatewayClient<C> {
|
||||
shutdown: TaskClient,
|
||||
}
|
||||
|
||||
impl<C> GatewayClient<C>
|
||||
impl<C, St> GatewayClient<C, St>
|
||||
where
|
||||
C: Sync + Send,
|
||||
St: Storage,
|
||||
{
|
||||
// TODO: put it all in a Config struct
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -92,7 +83,7 @@ where
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
response_timeout_duration: Duration,
|
||||
bandwidth_controller: Option<BandwidthController<C, PersistentStorage>>,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
shutdown: TaskClient,
|
||||
) -> Self {
|
||||
GatewayClient {
|
||||
@@ -146,7 +137,7 @@ where
|
||||
let shutdown = TaskClient::dummy();
|
||||
let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone());
|
||||
|
||||
GatewayClient::<C> {
|
||||
GatewayClient::<C, St> {
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: true,
|
||||
bandwidth_remaining: 0,
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_mockups::StorageError;
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use mobile_storage::StorageError;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_credential_storage::error::StorageError;
|
||||
use nym_gateway_requests::registration::handshake::error::HandshakeError;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
@@ -27,12 +19,6 @@ pub enum GatewayClientError {
|
||||
#[error("There was a network error - {0}")]
|
||||
NetworkError(#[from] WsError),
|
||||
|
||||
#[error("There was a credential storage error - {0}")]
|
||||
CredentialStorageError(#[from] StorageError),
|
||||
|
||||
#[error("Coconut error - {0}")]
|
||||
CoconutError(#[from] nym_coconut_interface::CoconutError),
|
||||
|
||||
// TODO: see if `JsValue` is a reasonable type for this
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[error("There was a network error")]
|
||||
@@ -47,8 +33,8 @@ pub enum GatewayClientError {
|
||||
#[error("No bandwidth controller provided")]
|
||||
NoBandwidthControllerAvailable,
|
||||
|
||||
#[error("Credential error - {0}")]
|
||||
CredentialError(#[from] nym_credentials::error::Error),
|
||||
#[error("Bandwidth controller error - {0}")]
|
||||
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
|
||||
|
||||
#[error("Connection was abruptly closed")]
|
||||
ConnectionAbruptlyClosed,
|
||||
|
||||
@@ -11,13 +11,10 @@ pub use packet_router::{
|
||||
};
|
||||
use tungstenite::{protocol::Message, Error as WsError};
|
||||
|
||||
pub mod bandwidth;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod packet_router;
|
||||
pub mod socket_state;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm_mockups;
|
||||
|
||||
/// Helper method for reading from websocket stream. Helps to flatten the structure.
|
||||
pub(crate) fn cleanup_socket_message(
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use std::marker::PhantomData;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum StorageError {
|
||||
#[error("Wasm client is not yet supported")]
|
||||
WasmNotSupported,
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[error("Code shouldn't reach this point")]
|
||||
InconsistentData,
|
||||
}
|
||||
|
||||
pub struct DirectSigningNyxdClient {}
|
||||
|
||||
pub trait DkgQueryClient {}
|
||||
|
||||
// impl CosmWasmClient for DirectSigningNyxdClient {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client<C> {
|
||||
_phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C> DkgQueryClient for Client<C> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PersistentStorage {}
|
||||
|
||||
pub struct CoconutCredential {
|
||||
pub id: i64,
|
||||
pub voucher_value: String,
|
||||
pub voucher_info: String,
|
||||
pub serial_number: String,
|
||||
pub binding_number: String,
|
||||
pub signature: String,
|
||||
pub epoch_id: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Storage for PersistentStorage {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
_voucher_value: String,
|
||||
_voucher_info: String,
|
||||
_serial_number: String,
|
||||
_binding_number: String,
|
||||
_signature: String,
|
||||
) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
}
|
||||
@@ -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,9 +9,16 @@ edition = "2021"
|
||||
async-trait = { workspace = true }
|
||||
|
||||
log = { workspace = true }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.24.1", features = [ "rt-multi-thread", "net", "signal", "fs" ] }
|
||||
tokio = { version = "1.24.1", features = ["sync"]}
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
version = "0.5"
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
version = "1.24.1"
|
||||
features = [ "rt-multi-thread", "net", "signal", "fs" ]
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::CoconutCredential;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoconutCredentialManager {
|
||||
inner: Arc<RwLock<Vec<CoconutCredential>>>,
|
||||
}
|
||||
|
||||
impl CoconutCredentialManager {
|
||||
/// Creates new empty instance of the `CoconutCredentialManager`.
|
||||
pub fn new() -> Self {
|
||||
CoconutCredentialManager {
|
||||
inner: Arc::new(RwLock::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts provided signature into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `voucher_value`: Plaintext bandwidth value of the credential.
|
||||
/// * `voucher_info`: Plaintext information of the credential.
|
||||
/// * `serial_number`: Base58 representation of the serial number attribute.
|
||||
/// * `binding_number`: Base58 representation of the binding number attribute.
|
||||
/// * `signature`: Coconut credential in the form of a signature.
|
||||
pub async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
) {
|
||||
let mut creds = self.inner.write().await;
|
||||
let id = creds.len() as i64;
|
||||
creds.push(CoconutCredential {
|
||||
id,
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
epoch_id,
|
||||
consumed: false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub async fn get_next_coconut_credential(&self) -> Option<CoconutCredential> {
|
||||
let creds = self.inner.read().await;
|
||||
creds.iter().find(|c| !c.consumed).cloned()
|
||||
}
|
||||
|
||||
/// Consumes in the database the specified credential.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id`: Database id.
|
||||
pub async fn consume_coconut_credential(&self, id: i64) {
|
||||
let mut creds = self.inner.write().await;
|
||||
if let Some(cred) = creds.get_mut(id as usize) {
|
||||
cred.consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod memory;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod sqlite;
|
||||
+5
-5
@@ -4,7 +4,7 @@
|
||||
use crate::models::CoconutCredential;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CoconutCredentialManager {
|
||||
pub struct CoconutCredentialManager {
|
||||
connection_pool: sqlx::SqlitePool,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ impl CoconutCredentialManager {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `connection_pool`: database connection pool to use.
|
||||
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
pub fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
CoconutCredentialManager { connection_pool }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ impl CoconutCredentialManager {
|
||||
/// * `serial_number`: Base58 representation of the serial number attribute.
|
||||
/// * `binding_number`: Base58 representation of the binding number attribute.
|
||||
/// * `signature`: Coconut credential in the form of a signature.
|
||||
pub(crate) async fn insert_coconut_credential(
|
||||
pub async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
@@ -46,7 +46,7 @@ impl CoconutCredentialManager {
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub(crate) async fn get_next_coconut_credential(
|
||||
pub async fn get_next_coconut_credential(
|
||||
&self,
|
||||
) -> Result<Option<CoconutCredential>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
@@ -62,7 +62,7 @@ impl CoconutCredentialManager {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id`: Database id.
|
||||
pub(crate) async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
|
||||
pub async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"UPDATE coconut_credentials SET consumed = TRUE WHERE id = ?",
|
||||
id
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::backends::memory::CoconutCredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::models::CoconutCredential;
|
||||
use crate::storage::Storage;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||
#[derive(Clone)]
|
||||
pub struct EphemeralStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager,
|
||||
}
|
||||
|
||||
impl Default for EphemeralStorage {
|
||||
fn default() -> Self {
|
||||
EphemeralStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Storage for EphemeralStorage {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.insert_coconut_credential(
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
epoch_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_coconut_credential()
|
||||
.await
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::ERC20Credential;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ERC20CredentialManager {
|
||||
connection_pool: sqlx::SqlitePool,
|
||||
}
|
||||
|
||||
impl ERC20CredentialManager {
|
||||
/// Creates new instance of the `ERC20CredentialManager` with the provided sqlite connection pool.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `connection_pool`: database connection pool to use.
|
||||
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
ERC20CredentialManager { connection_pool }
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum StorageError {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("Database experienced an internal error - {0}")]
|
||||
InternalDatabaseError(#[from] sqlx::Error),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("Failed to perform database migration - {0}")]
|
||||
MigrationError(#[from] sqlx::migrate::MigrateError),
|
||||
|
||||
|
||||
@@ -3,111 +3,26 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use crate::coconut::CoconutCredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::storage::Storage;
|
||||
use crate::ephemeral_storage::EphemeralStorage;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::persistent_storage::PersistentStorage;
|
||||
|
||||
use crate::models::CoconutCredential;
|
||||
use async_trait::async_trait;
|
||||
use log::{debug, error};
|
||||
use sqlx::ConnectOptions;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
mod coconut;
|
||||
mod backends;
|
||||
pub mod ephemeral_storage;
|
||||
pub mod error;
|
||||
mod models;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod persistent_storage;
|
||||
pub mod storage;
|
||||
|
||||
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||
#[derive(Clone)]
|
||||
pub struct PersistentStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager,
|
||||
}
|
||||
|
||||
impl PersistentStorage {
|
||||
/// Initialises `PersistentStorage` using the provided path.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `database_path`: path to the database.
|
||||
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
|
||||
debug!(
|
||||
"Attempting to connect to database {:?}",
|
||||
database_path.as_ref().as_os_str()
|
||||
);
|
||||
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(true);
|
||||
|
||||
opts.disable_statement_logging();
|
||||
|
||||
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||
Ok(db) => db,
|
||||
Err(err) => {
|
||||
error!("Failed to connect to SQLx database: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
|
||||
error!("Failed to perform migration on the SQLx database: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
Ok(PersistentStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Storage for PersistentStorage {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.insert_coconut_credential(
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
epoch_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_coconut_credential()
|
||||
.await?
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn initialise_storage(path: PathBuf) -> PersistentStorage {
|
||||
match PersistentStorage::init(path).await {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn initialise_persistent_storage(path: std::path::PathBuf) -> PersistentStorage {
|
||||
match persistent_storage::PersistentStorage::init(path).await {
|
||||
Err(err) => panic!("failed to initialise credential storage - {err}"),
|
||||
Ok(storage) => storage,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialise_ephemeral_storage() -> EphemeralStorage {
|
||||
ephemeral_storage::EphemeralStorage::default()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoconutCredential {
|
||||
#[allow(dead_code)]
|
||||
pub id: i64,
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::backends::sqlite::CoconutCredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::storage::Storage;
|
||||
|
||||
use crate::models::CoconutCredential;
|
||||
use async_trait::async_trait;
|
||||
use log::{debug, error};
|
||||
use sqlx::ConnectOptions;
|
||||
use std::path::Path;
|
||||
|
||||
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||
#[derive(Clone)]
|
||||
pub struct PersistentStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager,
|
||||
}
|
||||
|
||||
impl PersistentStorage {
|
||||
/// Initialises `PersistentStorage` using the provided path.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `database_path`: path to the database.
|
||||
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
|
||||
debug!(
|
||||
"Attempting to connect to database {:?}",
|
||||
database_path.as_ref().as_os_str()
|
||||
);
|
||||
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(true);
|
||||
|
||||
opts.disable_statement_logging();
|
||||
|
||||
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||
Ok(db) => db,
|
||||
Err(err) => {
|
||||
error!("Failed to connect to SQLx database: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
|
||||
error!("Failed to perform migration on the SQLx database: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
Ok(PersistentStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Storage for PersistentStorage {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.insert_coconut_credential(
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
epoch_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_coconut_credential()
|
||||
.await?
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::error::StorageError;
|
||||
use crate::models::CoconutCredential;
|
||||
use crate::StorageError;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum StorageError {
|
||||
#[error("Android is not yet supported")]
|
||||
AndroidNotSupported,
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[error("Code shouldn't reach this point")]
|
||||
InconsistentData,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PersistentStorage {}
|
||||
|
||||
pub struct CoconutCredential {
|
||||
pub id: i64,
|
||||
pub voucher_value: String,
|
||||
pub voucher_info: String,
|
||||
pub serial_number: String,
|
||||
pub binding_number: String,
|
||||
pub signature: String,
|
||||
pub epoch_id: String,
|
||||
pub consumed: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Storage for PersistentStorage {
|
||||
async fn insert_coconut_credential(
|
||||
&self,
|
||||
_voucher_value: String,
|
||||
_voucher_info: String,
|
||||
_serial_number: String,
|
||||
_binding_number: String,
|
||||
_signature: String,
|
||||
) -> Result<(), StorageError> {
|
||||
Err(StorageError::AndroidNotSupported)
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
Err(StorageError::AndroidNotSupported)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
|
||||
Err(StorageError::AndroidNotSupported)
|
||||
}
|
||||
}
|
||||
@@ -19,17 +19,6 @@ pub struct ChainDetails {
|
||||
pub stake_denom: DenomDetailsOwned,
|
||||
}
|
||||
|
||||
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
|
||||
impl Default for ChainDetails {
|
||||
fn default() -> Self {
|
||||
ChainDetails {
|
||||
bech32_account_prefix: mainnet::BECH32_PREFIX.into(),
|
||||
mix_denom: mainnet::MIX_DENOM.into(),
|
||||
stake_denom: mainnet::STAKE_DENOM.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct NymContracts {
|
||||
pub mixnet_contract_address: Option<String>,
|
||||
@@ -43,20 +32,43 @@ pub struct NymContracts {
|
||||
|
||||
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
|
||||
// with `NetworkDetails` defined in all.rs...
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct NymNetworkDetails {
|
||||
pub chain_details: ChainDetails,
|
||||
pub endpoints: Vec<ValidatorDetails>,
|
||||
pub contracts: NymContracts,
|
||||
}
|
||||
|
||||
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
|
||||
impl Default for NymNetworkDetails {
|
||||
fn default() -> Self {
|
||||
NymNetworkDetails::new_mainnet()
|
||||
}
|
||||
}
|
||||
|
||||
impl NymNetworkDetails {
|
||||
pub fn new() -> Self {
|
||||
NymNetworkDetails::default()
|
||||
pub fn new_empty() -> Self {
|
||||
NymNetworkDetails {
|
||||
chain_details: ChainDetails {
|
||||
bech32_account_prefix: Default::default(),
|
||||
mix_denom: DenomDetailsOwned {
|
||||
base: Default::default(),
|
||||
display: Default::default(),
|
||||
display_exponent: Default::default(),
|
||||
},
|
||||
stake_denom: DenomDetailsOwned {
|
||||
base: Default::default(),
|
||||
display: Default::default(),
|
||||
display_exponent: Default::default(),
|
||||
},
|
||||
},
|
||||
endpoints: Default::default(),
|
||||
contracts: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_env() -> Self {
|
||||
NymNetworkDetails::new()
|
||||
NymNetworkDetails::new_empty()
|
||||
.with_bech32_account_prefix(
|
||||
var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"),
|
||||
)
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -17,9 +17,9 @@ use crate::storage::NymApiStorage;
|
||||
use crate::support::config::Config;
|
||||
use crate::support::nyxd;
|
||||
use futures::channel::mpsc;
|
||||
use nym_credential_storage::PersistentStorage;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_gateway_client::bandwidth::BandwidthController;
|
||||
use nym_sphinx::receiver::MessageReceiver;
|
||||
use nym_task::TaskManager;
|
||||
use std::sync::Arc;
|
||||
@@ -98,7 +98,7 @@ impl<'a> NetworkMonitorBuilder<'a> {
|
||||
|
||||
let bandwidth_controller = {
|
||||
BandwidthController::new(
|
||||
nym_credential_storage::initialise_storage(
|
||||
nym_credential_storage::initialise_persistent_storage(
|
||||
self.config.get_credentials_database_path(),
|
||||
)
|
||||
.await,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::support::nyxd;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::identity::PUBLIC_KEY_LENGTH;
|
||||
use nym_gateway_client::GatewayClient;
|
||||
use std::collections::HashMap;
|
||||
@@ -11,16 +12,16 @@ use tokio::sync::{Mutex, MutexGuard, TryLockError};
|
||||
pub(crate) struct GatewayClientHandle(Arc<GatewayClientHandleInner>);
|
||||
|
||||
struct GatewayClientHandleInner {
|
||||
client: Mutex<Option<GatewayClient<nyxd::Client>>>,
|
||||
client: Mutex<Option<GatewayClient<nyxd::Client, PersistentStorage>>>,
|
||||
raw_identity: [u8; PUBLIC_KEY_LENGTH],
|
||||
}
|
||||
|
||||
pub(crate) struct UnlockedGatewayClientHandle<'a>(
|
||||
MutexGuard<'a, Option<GatewayClient<nyxd::Client>>>,
|
||||
MutexGuard<'a, Option<GatewayClient<nyxd::Client, PersistentStorage>>>,
|
||||
);
|
||||
|
||||
impl GatewayClientHandle {
|
||||
pub(crate) fn new(gateway_client: GatewayClient<nyxd::Client>) -> Self {
|
||||
pub(crate) fn new(gateway_client: GatewayClient<nyxd::Client, PersistentStorage>) -> Self {
|
||||
GatewayClientHandle(Arc::new(GatewayClientHandleInner {
|
||||
raw_identity: gateway_client.gateway_identity().to_bytes(),
|
||||
client: Mutex::new(Some(gateway_client)),
|
||||
@@ -59,11 +60,15 @@ impl GatewayClientHandle {
|
||||
}
|
||||
|
||||
impl<'a> UnlockedGatewayClientHandle<'a> {
|
||||
pub(crate) fn get_mut_unchecked(&mut self) -> &mut GatewayClient<nyxd::Client> {
|
||||
pub(crate) fn get_mut_unchecked(
|
||||
&mut self,
|
||||
) -> &mut GatewayClient<nyxd::Client, PersistentStorage> {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn inner_mut(&mut self) -> Option<&mut GatewayClient<nyxd::Client>> {
|
||||
pub(crate) fn inner_mut(
|
||||
&mut self,
|
||||
) -> Option<&mut GatewayClient<nyxd::Client, PersistentStorage>> {
|
||||
self.0.as_mut()
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ use futures::stream::{self, FuturesUnordered, StreamExt};
|
||||
use futures::task::Context;
|
||||
use futures::{Future, Stream};
|
||||
use log::{debug, info, trace, warn};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_config::defaults::REMAINING_BANDWIDTH_THRESHOLD;
|
||||
use nym_credential_storage::PersistentStorage;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH};
|
||||
use nym_gateway_client::bandwidth::BandwidthController;
|
||||
use nym_gateway_client::error::GatewayClientError;
|
||||
use nym_gateway_client::{AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
@@ -206,7 +206,7 @@ impl PacketSender {
|
||||
}
|
||||
|
||||
async fn attempt_to_send_packets(
|
||||
client: &mut GatewayClient<nyxd::Client>,
|
||||
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
|
||||
mut mix_packets: Vec<MixPacket>,
|
||||
max_sending_rate: usize,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
@@ -314,7 +314,7 @@ impl PacketSender {
|
||||
}
|
||||
|
||||
async fn check_remaining_bandwidth(
|
||||
client: &mut GatewayClient<nyxd::Client>,
|
||||
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if client.remaining_bandwidth() < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
Err(GatewayClientError::NotEnoughBandwidth(
|
||||
|
||||
Generated
+50
-16
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Generated
+949
-480
File diff suppressed because it is too large
Load Diff
@@ -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 @@
|
||||
/build
|
||||
+44
@@ -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")
|
||||
}
|
||||
+25
@@ -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>(...);
|
||||
}
|
||||
+28
@@ -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)
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
</manifest>
|
||||
+201
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -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
|
||||
+85
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -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)
|
||||
}
|
||||
}
|
||||
+115
@@ -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?>
|
||||
}
|
||||
}
|
||||
+21
@@ -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('-', '_'))
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -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
|
||||
+19
@@ -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 = ""
|
||||
)
|
||||
+9
@@ -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
|
||||
+8
@@ -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
|
||||
+19
@@ -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> = []
|
||||
)
|
||||
+11
@@ -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) {}
|
||||
}
|
||||
+200
@@ -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)
|
||||
}
|
||||
}
|
||||
+45
@@ -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
Reference in New Issue
Block a user