Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c98d4305fa | |||
| 2eecbca6eb | |||
| a58c80ef08 | |||
| ac9d0db8be | |||
| 7521d98963 | |||
| 1e98131090 | |||
| 46bf65462c | |||
| e3df4c2d68 | |||
| 45c013350f | |||
| 6fecc53975 | |||
| e4dbfb1904 | |||
| f822d3db7b | |||
| 9d23766288 | |||
| fd4930b198 | |||
| 5d7be89edb | |||
| 47f5b4ceac | |||
| 790220039b | |||
| 3f0d4846df | |||
| de020f46a6 | |||
| 9bfcdbe8e2 | |||
| 8c4885ce2c | |||
| 926389df89 | |||
| b55db00408 | |||
| cfcb64f7e5 | |||
| 0e8f60d501 | |||
| 9c6c5f5170 | |||
| f28888e3e7 | |||
| 9549bed8bb | |||
| 7c65d61d91 | |||
| ad2efb7e62 | |||
| a50c9bfa1a | |||
| 30cdbf535a | |||
| f5365cbca9 | |||
| 22e7cb887b | |||
| b571f1a881 | |||
| b1c4e3ded7 | |||
| 4694ded8bd | |||
| 7971573026 | |||
| c4780c8af2 | |||
| 246f0b3f52 | |||
| 7879d76592 | |||
| 0f62ea25d8 | |||
| cde53c02e7 | |||
| de64da8e20 | |||
| 4aeac1acd2 | |||
| 679be24074 | |||
| 36e07f546d | |||
| d264feaf22 | |||
| 32b7b7afdc | |||
| 7a50f0c3b2 | |||
| 091382ef30 | |||
| 2da6a2fbfa | |||
| d910a4e0ee | |||
| 672ab79421 | |||
| 1ad97adc7c |
@@ -30,6 +30,7 @@ jobs:
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -63,4 +64,4 @@ jobs:
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path contracts/Cargo.toml --workspace -- -D warnings
|
||||
args: --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings
|
||||
|
||||
@@ -64,4 +64,4 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features -- -D warnings
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features --all-targets -- -D warnings
|
||||
|
||||
@@ -4,6 +4,30 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.1.16] (2023-04-25)
|
||||
|
||||
- Explorer - Fix sorting function on Stake Saturation. It is currently working per page and not globally ([#3320])
|
||||
- Poisson process gets stuck at too slow rate. Rework to more aggressively up-regulate ([#3309])
|
||||
- decrease the logging level of warnings associated with clients dropping packets due to gateway being overloaded (I'd say reduce it to debug/trace) - there are few sources of those, e.g. in real and cover traffic streams ([#3299])
|
||||
- Make the buffer size in `AvailableReader` depend on packet sizes the client is using + introduce read timeouts ([#3213])
|
||||
- Rust SDK - Support coconut, credential storage etc ([#2755])
|
||||
- version bump for next release ([#3349])
|
||||
- added coconut credential generation example ([#3339])
|
||||
- update mix-node setup docs with node description ([#3325])
|
||||
- exposed missing gateway commands in nym-cli ([#3324])
|
||||
- make sure to clear inner 'ack_map' in 'GatewaysReader' ([#3300])
|
||||
|
||||
[#3320]: https://github.com/nymtech/nym/issues/3320
|
||||
[#3309]: https://github.com/nymtech/nym/issues/3309
|
||||
[#3299]: https://github.com/nymtech/nym/issues/3299
|
||||
[#3213]: https://github.com/nymtech/nym/issues/3213
|
||||
[#2755]: https://github.com/nymtech/nym/issues/2755
|
||||
[#3349]: https://github.com/nymtech/nym/pull/3349
|
||||
[#3339]: https://github.com/nymtech/nym/pull/3339
|
||||
[#3325]: https://github.com/nymtech/nym/pull/3325
|
||||
[#3324]: https://github.com/nymtech/nym/pull/3324
|
||||
[#3300]: https://github.com/nymtech/nym/pull/3300
|
||||
|
||||
## [v1.1.15] (2023-04-18)
|
||||
|
||||
- Fix verloc being stuck waiting for shutdown signal ([#3250])
|
||||
@@ -41,6 +65,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
+68
-33
@@ -1576,7 +1576,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.1.11",
|
||||
@@ -3011,7 +3011,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.16"
|
||||
version = "1.1.17"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -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"
|
||||
@@ -3125,7 +3142,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -3186,13 +3203,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"clap 4.1.11",
|
||||
"dirs",
|
||||
"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]]
|
||||
@@ -3456,7 +3471,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -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",
|
||||
@@ -3587,7 +3600,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3606,7 +3619,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.16"
|
||||
version = "1.1.17"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -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"
|
||||
@@ -3716,7 +3721,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"async-file-watcher",
|
||||
"async-trait",
|
||||
@@ -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",
|
||||
@@ -3757,7 +3763,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -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",
|
||||
@@ -3868,7 +3879,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
dependencies = [
|
||||
"clap 4.1.11",
|
||||
"lazy_static",
|
||||
@@ -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",
|
||||
@@ -3903,10 +3914,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",
|
||||
@@ -4046,6 +4057,7 @@ dependencies = [
|
||||
name = "nym-sphinx-forwarding"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-outfox",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-types",
|
||||
@@ -4076,7 +4088,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 +4202,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"cosmwasm-derive",
|
||||
"cosmwasm-std",
|
||||
@@ -4208,7 +4220,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.4.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
@@ -5877,6 +5889,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(())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
@@ -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(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.15"
|
||||
version = "1.1.16"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -24,8 +24,9 @@ use nym_mixnet_contract_common::{
|
||||
MixOwnershipResponse, MixnodeDetailsResponse, NumberOfPendingEventsResponse,
|
||||
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedFamiliesResponse,
|
||||
PagedGatewayResponse, PagedMembersResponse, PagedMixNodeDelegationsResponse,
|
||||
PagedMixnodeBondsResponse, PagedRewardedSetResponse, PendingEpochEventsResponse,
|
||||
PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg,
|
||||
PagedMixnodeBondsResponse, PagedRewardedSetResponse, PendingEpochEventResponse,
|
||||
PendingEpochEventsResponse, PendingIntervalEventResponse, PendingIntervalEventsResponse,
|
||||
QueryMsg as MixnetQueryMsg,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -174,6 +175,16 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_details_by_identity(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
) -> Result<Option<MixNodeDetails>, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity {
|
||||
mix_identity,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_rewarding_details(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
@@ -374,14 +385,20 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_details_by_identity(
|
||||
async fn get_pending_epoch_event(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
) -> Result<Option<MixNodeDetails>, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity {
|
||||
mix_identity,
|
||||
})
|
||||
.await
|
||||
event_id: EpochEventId,
|
||||
) -> Result<PendingEpochEventResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_interval_event(
|
||||
&self,
|
||||
event_id: IntervalEventId,
|
||||
) -> Result<PendingIntervalEventResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_number_of_pending_events(
|
||||
|
||||
@@ -331,6 +331,38 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn decrease_pledge(
|
||||
&self,
|
||||
decrease_by: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::DecreasePledge {
|
||||
decrease_by: decrease_by.into(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn decrease_pledge_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
decrease_by: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::DecreasePledgeOnBehalf {
|
||||
owner: owner.to_string(),
|
||||
decrease_by: decrease_by.into(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondMixnode {}, vec![])
|
||||
.await
|
||||
|
||||
@@ -91,6 +91,21 @@ pub trait VestingSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_decrease_pledge(
|
||||
&self,
|
||||
decrease_by: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::DecreasePledge {
|
||||
amount: decrease_by.into(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn vesting_track_unbond_mixnode(
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -6,11 +6,9 @@ use clap::{Args, Subcommand};
|
||||
pub mod rewards;
|
||||
|
||||
pub mod delegate_to_mixnode;
|
||||
pub mod pledge_more;
|
||||
pub mod query_for_delegations;
|
||||
pub mod undelegate_from_mixnode;
|
||||
pub mod vesting_delegate_to_mixnode;
|
||||
pub mod vesting_pledge_more;
|
||||
pub mod vesting_undelegate_from_mixnode;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@@ -34,8 +32,4 @@ pub enum MixnetDelegatorsCommands {
|
||||
DelegateVesting(vesting_delegate_to_mixnode::Args),
|
||||
/// Undelegate from a mixnode (when originally using locked tokens)
|
||||
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
|
||||
/// Pledge more
|
||||
PledgeMore(pledge_more::Args),
|
||||
/// Pledge more with locked tokens
|
||||
PledgeMoreVesting(vesting_pledge_more::Args),
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ pub struct Args {
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn vesting_update_config(client: SigningClient, args: Args) {
|
||||
pub async fn vesting_update_config(args: Args, client: SigningClient) {
|
||||
info!("Update vesting gateway config!");
|
||||
|
||||
let current_details = match client
|
||||
|
||||
@@ -45,7 +45,9 @@ pub struct Args {
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str) {
|
||||
pub async fn vesting_bond_gateway(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
info!("Starting vesting gateway bonding!");
|
||||
|
||||
// if we're trying to bond less than 1 token
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::Coin;
|
||||
use nym_validator_client::nyxd::traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub decrease_by: u128,
|
||||
}
|
||||
|
||||
pub async fn decrease_pledge(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
info!("Starting to decrease pledge");
|
||||
|
||||
let coin = Coin::new(args.decrease_by, denom);
|
||||
|
||||
let res = client
|
||||
.pledge_more(coin.into(), None)
|
||||
.await
|
||||
.expect("failed to decrease pledge!");
|
||||
|
||||
info!("decreasing pledge: {:?}", res);
|
||||
}
|
||||
@@ -4,13 +4,17 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_mixnode;
|
||||
pub mod decrease_pledge;
|
||||
pub mod families;
|
||||
pub mod keys;
|
||||
pub mod mixnode_bonding_sign_payload;
|
||||
pub mod pledge_more;
|
||||
pub mod rewards;
|
||||
pub mod settings;
|
||||
pub mod unbond_mixnode;
|
||||
pub mod vesting_bond_mixnode;
|
||||
pub mod vesting_decrease_pledge;
|
||||
pub mod vesting_pledge_more;
|
||||
pub mod vesting_unbond_mixnode;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@@ -40,4 +44,12 @@ pub enum MixnetOperatorsMixnodeCommands {
|
||||
UnbondVesting(vesting_unbond_mixnode::Args),
|
||||
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||
CreateMixnodeBondingSignPayload(mixnode_bonding_sign_payload::Args),
|
||||
/// Pledge more
|
||||
PledgeMore(pledge_more::Args),
|
||||
/// Pledge more with locked tokens
|
||||
PledgeMoreVesting(vesting_pledge_more::Args),
|
||||
/// Decrease pledge
|
||||
DecreasePledge(decrease_pledge::Args),
|
||||
/// Decrease pledge with locked tokens
|
||||
DecreasePledgeVesting(vesting_decrease_pledge::Args),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::Coin;
|
||||
use nym_validator_client::nyxd::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub decrease_by: u128,
|
||||
}
|
||||
|
||||
pub async fn vesting_decrease_pledge(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
info!("Starting vesting to decrease pledge");
|
||||
|
||||
let coin = Coin::new(args.decrease_by, denom);
|
||||
|
||||
let res = client
|
||||
.vesting_decrease_pledge(coin.into(), None)
|
||||
.await
|
||||
.expect("failed to vesting decrease pledge!");
|
||||
|
||||
info!("vesting decreasing pledge: {:?}", res);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
description = "Common library for the Nym mixnet contract"
|
||||
rust-version = "1.62"
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{EpochState, IdentityKey, MixId};
|
||||
use crate::{EpochEventId, EpochState, IdentityKey, MixId};
|
||||
use contracts_common::signing::verifier::ApiVerifierError;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum MixnetContractError {
|
||||
#[error("could not perform contract migration: {comment}")]
|
||||
FailedMigration { comment: String },
|
||||
|
||||
#[error("{source}")]
|
||||
StdErr {
|
||||
#[from]
|
||||
@@ -26,6 +29,17 @@ pub enum MixnetContractError {
|
||||
#[error("Not enough funds sent for node pledge. (received {received}, minimum {minimum})")]
|
||||
InsufficientPledge { received: Coin, minimum: Coin },
|
||||
|
||||
#[error("Attempted to reduce node pledge ({current}{denom} - {decrease_by}{denom}) below the minimum amount: {minimum}{denom}")]
|
||||
InvalidPledgeReduction {
|
||||
current: Uint128,
|
||||
decrease_by: Uint128,
|
||||
minimum: Uint128,
|
||||
denom: String,
|
||||
},
|
||||
|
||||
#[error("A pledge change is already pending in this epoch. The event id: {pending_event_id}")]
|
||||
PendingPledgeChange { pending_event_id: EpochEventId },
|
||||
|
||||
#[error("Not enough funds sent for node delegation. (received {received}, minimum {minimum})")]
|
||||
InsufficientDelegation { received: Coin, minimum: Coin },
|
||||
|
||||
@@ -190,6 +204,9 @@ pub enum MixnetContractError {
|
||||
#[error("epoch duration must be > 0")]
|
||||
EpochDurationZero,
|
||||
|
||||
#[error("attempted to perform the operation with 0 coins. This is not allowed")]
|
||||
ZeroCoinAmount,
|
||||
|
||||
#[error("this validator ({current_validator}) is not the one responsible for advancing this epoch. It's responsibility of {chosen_validator}.")]
|
||||
RewardingValidatorMismatch {
|
||||
current_validator: Addr,
|
||||
@@ -226,3 +243,11 @@ pub enum MixnetContractError {
|
||||
source: ApiVerifierError,
|
||||
},
|
||||
}
|
||||
|
||||
impl MixnetContractError {
|
||||
pub fn inconsistent_state<S: Into<String>>(comment: S) -> Self {
|
||||
MixnetContractError::InconsistentState {
|
||||
comment: comment.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ pub enum MixnetEventType {
|
||||
MixnodeBonding,
|
||||
PendingPledgeIncrease,
|
||||
PledgeIncrease,
|
||||
PendingPledgeDecrease,
|
||||
PledgeDecrease,
|
||||
GatewayBonding,
|
||||
GatewayUnbonding,
|
||||
PendingMixnodeUnbonding,
|
||||
@@ -58,6 +60,8 @@ impl ToString for MixnetEventType {
|
||||
MixnetEventType::MixnodeBonding => "mixnode_bonding",
|
||||
MixnetEventType::PendingPledgeIncrease => "pending_pledge_increase",
|
||||
MixnetEventType::PledgeIncrease => "pledge_increase",
|
||||
MixnetEventType::PendingPledgeDecrease => "pending_pledge_decrease",
|
||||
MixnetEventType::PledgeDecrease => "pledge_decrease",
|
||||
MixnetEventType::GatewayBonding => "gateway_bonding",
|
||||
MixnetEventType::GatewayUnbonding => "gateway_unbonding",
|
||||
MixnetEventType::PendingMixnodeUnbonding => "pending_mixnode_unbonding",
|
||||
@@ -354,6 +358,19 @@ pub fn new_pledge_increase_event(created_at: BlockHeight, mix_id: MixId, amount:
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_pledge_decrease_event(mix_id: MixId, amount: &Coin) -> Event {
|
||||
Event::new(MixnetEventType::PendingPledgeDecrease)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pledge_decrease_event(created_at: BlockHeight, mix_id: MixId, amount: &Coin) -> Event {
|
||||
Event::new(MixnetEventType::PledgeDecrease)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeUnbonding)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::pending_events::{PendingEpochEvent, PendingIntervalEvent};
|
||||
use crate::{EpochId, IntervalId, MixId};
|
||||
use crate::{
|
||||
EpochEventId, EpochId, IntervalEventId, IntervalId, MixId, PendingEpochEventData,
|
||||
PendingIntervalEventData,
|
||||
};
|
||||
use cosmwasm_std::{Addr, Env};
|
||||
use schemars::gen::SchemaGenerator;
|
||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
@@ -528,6 +531,30 @@ impl PendingIntervalEventsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct PendingEpochEventResponse {
|
||||
pub event_id: EpochEventId,
|
||||
pub event: Option<PendingEpochEventData>,
|
||||
}
|
||||
|
||||
impl PendingEpochEventResponse {
|
||||
pub fn new(event_id: EpochEventId, event: Option<PendingEpochEventData>) -> Self {
|
||||
PendingEpochEventResponse { event_id, event }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct PendingIntervalEventResponse {
|
||||
pub event_id: IntervalEventId,
|
||||
pub event: Option<PendingIntervalEventData>,
|
||||
}
|
||||
|
||||
impl PendingIntervalEventResponse {
|
||||
pub fn new(event_id: IntervalEventId, event: Option<PendingIntervalEventData>) -> Self {
|
||||
PendingIntervalEventResponse { event_id, event }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct NumberOfPendingEventsResponse {
|
||||
pub epoch_events: u32,
|
||||
|
||||
@@ -32,7 +32,8 @@ pub use gateway::{
|
||||
};
|
||||
pub use interval::{
|
||||
CurrentIntervalResponse, EpochState, EpochStatus, Interval, NumberOfPendingEventsResponse,
|
||||
PendingEpochEventsResponse, PendingIntervalEventsResponse,
|
||||
PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEventResponse,
|
||||
PendingIntervalEventsResponse,
|
||||
};
|
||||
pub use mixnode::{
|
||||
Layer, MixNode, MixNodeBond, MixNodeConfigUpdate, MixNodeCostParams, MixNodeDetails,
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::helpers::IntoBaseDecimal;
|
||||
use crate::reward_params::{NodeRewardParams, RewardingParams};
|
||||
use crate::rewarding::helpers::truncate_reward;
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{Delegation, EpochId, IdentityKey, MixId, Percent, SphinxKey};
|
||||
use crate::{Delegation, EpochEventId, EpochId, IdentityKey, MixId, Percent, SphinxKey};
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -37,13 +37,20 @@ impl RewardedSetNodeStatus {
|
||||
pub struct MixNodeDetails {
|
||||
pub bond_information: MixNodeBond,
|
||||
pub rewarding_details: MixNodeRewarding,
|
||||
#[serde(default)]
|
||||
pub pending_changes: PendingMixNodeChanges,
|
||||
}
|
||||
|
||||
impl MixNodeDetails {
|
||||
pub fn new(bond_information: MixNodeBond, rewarding_details: MixNodeRewarding) -> Self {
|
||||
pub fn new(
|
||||
bond_information: MixNodeBond,
|
||||
rewarding_details: MixNodeRewarding,
|
||||
pending_changes: PendingMixNodeChanges,
|
||||
) -> Self {
|
||||
MixNodeDetails {
|
||||
bond_information,
|
||||
rewarding_details,
|
||||
pending_changes,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +80,10 @@ impl MixNodeDetails {
|
||||
pub fn total_stake(&self) -> Decimal {
|
||||
self.rewarding_details.node_bond()
|
||||
}
|
||||
|
||||
pub fn pending_pledge_change(&self) -> Option<EpochEventId> {
|
||||
self.pending_changes.pledge_change
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
@@ -332,6 +343,22 @@ impl MixNodeRewarding {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decreases total pledge of operator by the specified amount.
|
||||
pub fn decrease_operator_uint128(
|
||||
&mut self,
|
||||
amount: Uint128,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
let amount_decimal = amount.into_base_decimal()?;
|
||||
if self.operator < amount_decimal {
|
||||
return Err(MixnetContractError::OverflowDecimalSubtraction {
|
||||
minuend: self.operator,
|
||||
subtrahend: amount_decimal,
|
||||
});
|
||||
}
|
||||
self.operator -= amount_decimal;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn increase_delegates_uint128(
|
||||
&mut self,
|
||||
amount: Uint128,
|
||||
@@ -601,6 +628,25 @@ impl From<Layer> for u8 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/PendingMixnodeChanges.ts")
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PendingMixNodeChanges {
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
// pub cost_params_change: Option<IntervalEventId>,
|
||||
}
|
||||
|
||||
impl PendingMixNodeChanges {
|
||||
pub fn new_empty() -> PendingMixNodeChanges {
|
||||
PendingMixNodeChanges {
|
||||
pledge_change: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
|
||||
@@ -10,10 +10,13 @@ use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::reward_params::{
|
||||
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
||||
};
|
||||
use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent};
|
||||
use crate::{
|
||||
delegation, ContractStateParams, EpochEventId, IntervalEventId, Layer, LayerAssignment, MixId,
|
||||
Percent,
|
||||
};
|
||||
use crate::{Gateway, IdentityKey, MixNode};
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::Decimal;
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@@ -161,6 +164,13 @@ pub enum ExecuteMsg {
|
||||
PledgeMoreOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
DecreasePledge {
|
||||
decrease_by: Coin,
|
||||
},
|
||||
DecreasePledgeOnBehalf {
|
||||
owner: String,
|
||||
decrease_by: Coin,
|
||||
},
|
||||
UnbondMixnode {},
|
||||
UnbondMixnodeOnBehalf {
|
||||
owner: String,
|
||||
@@ -297,6 +307,10 @@ impl ExecuteMsg {
|
||||
}
|
||||
ExecuteMsg::PledgeMore {} => "pledging additional tokens".into(),
|
||||
ExecuteMsg::PledgeMoreOnBehalf { .. } => "pledging additional tokens on behalf".into(),
|
||||
ExecuteMsg::DecreasePledge { .. } => "decreasing mixnode pledge".into(),
|
||||
ExecuteMsg::DecreasePledgeOnBehalf { .. } => {
|
||||
"decreasing mixnode pledge on behalf".into()
|
||||
}
|
||||
ExecuteMsg::UnbondMixnode { .. } => "unbonding mixnode".into(),
|
||||
ExecuteMsg::UnbondMixnodeOnBehalf { .. } => "unbonding mixnode on behalf".into(),
|
||||
ExecuteMsg::UpdateMixnodeCostParams { .. } => "updating mixnode cost parameters".into(),
|
||||
@@ -506,6 +520,12 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
GetPendingEpochEvent {
|
||||
event_id: EpochEventId,
|
||||
},
|
||||
GetPendingIntervalEvent {
|
||||
event_id: IntervalEventId,
|
||||
},
|
||||
GetNumberOfPendingEvents {},
|
||||
|
||||
// signing-related
|
||||
|
||||
@@ -38,6 +38,10 @@ pub enum PendingEpochEventKind {
|
||||
mix_id: MixId,
|
||||
amount: Coin,
|
||||
},
|
||||
DecreasePledge {
|
||||
mix_id: MixId,
|
||||
decrease_by: Coin,
|
||||
},
|
||||
UnbondMixnode {
|
||||
mix_id: MixId,
|
||||
},
|
||||
@@ -66,7 +70,7 @@ impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingIntervalEvent {
|
||||
pub id: EpochEventId,
|
||||
pub id: IntervalEventId,
|
||||
pub event: PendingIntervalEventData,
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
description = "Common library for the Nym vesting contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -9,7 +9,7 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.4.0" }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.5.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
|
||||
@@ -15,6 +15,7 @@ pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding";
|
||||
pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding";
|
||||
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
|
||||
pub const VESTING_PLEDGE_MORE_EVENT_TYPE: &str = "vesting_pledge_more";
|
||||
pub const VESTING_DECREASE_PLEDGE_EVENT_TYPE: &str = "vesting_pledge_decrease";
|
||||
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
|
||||
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
|
||||
pub const VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE: &str = "vesting_update_gateway_config";
|
||||
@@ -22,6 +23,7 @@ pub const VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE: &str =
|
||||
"vesting_update_mixnode_cost_params";
|
||||
|
||||
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
|
||||
pub const TRACK_MIXNODE_PLEDGE_DECREASE_EVENT_TYPE: &str = "track_mixnode_pledge_decrease";
|
||||
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
|
||||
pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation";
|
||||
pub const TRACK_REWARD_EVENT_TYPE: &str = "track_reaward";
|
||||
@@ -118,6 +120,10 @@ pub fn new_vesting_pledge_more_event() -> Event {
|
||||
Event::new(VESTING_PLEDGE_MORE_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_decrease_pledge_event() -> Event {
|
||||
Event::new(VESTING_DECREASE_PLEDGE_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_update_mixnode_config_event() -> Event {
|
||||
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
|
||||
}
|
||||
@@ -146,6 +152,10 @@ pub fn new_track_mixnode_unbond_event() -> Event {
|
||||
Event::new(TRACK_MIXNODE_UNBOND_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_mixnode_pledge_decrease_event() -> Event {
|
||||
Event::new(TRACK_MIXNODE_PLEDGE_DECREASE_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_gateway_unbond_event() -> Event {
|
||||
Event::new(TRACK_GATEWAY_UNBOND_EVENT_TYPE)
|
||||
}
|
||||
|
||||
@@ -123,11 +123,18 @@ pub enum ExecuteMsg {
|
||||
PledgeMore {
|
||||
amount: Coin,
|
||||
},
|
||||
DecreasePledge {
|
||||
amount: Coin,
|
||||
},
|
||||
UnbondMixnode {},
|
||||
TrackUnbondMixnode {
|
||||
owner: String,
|
||||
amount: Coin,
|
||||
},
|
||||
TrackDecreasePledge {
|
||||
owner: String,
|
||||
amount: Coin,
|
||||
},
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: MessageSignature,
|
||||
@@ -175,8 +182,10 @@ impl ExecuteMsg {
|
||||
ExecuteMsg::TrackUndelegation { .. } => "VestingExecuteMsg::TrackUndelegation",
|
||||
ExecuteMsg::BondMixnode { .. } => "VestingExecuteMsg::BondMixnode",
|
||||
ExecuteMsg::PledgeMore { .. } => "VestingExecuteMsg::PledgeMore",
|
||||
ExecuteMsg::DecreasePledge { .. } => "VestingExecuteMsg::DecreasePledge",
|
||||
ExecuteMsg::UnbondMixnode { .. } => "VestingExecuteMsg::UnbondMixnode",
|
||||
ExecuteMsg::TrackUnbondMixnode { .. } => "VestingExecuteMsg::TrackUnbondMixnode",
|
||||
ExecuteMsg::TrackDecreasePledge { .. } => "VestingExecuteMsg::TrackDecreasePledge",
|
||||
ExecuteMsg::BondGateway { .. } => "VestingExecuteMsg::BondGateway",
|
||||
ExecuteMsg::UnbondGateway { .. } => "VestingExecuteMsg::UnbondGateway",
|
||||
ExecuteMsg::TrackUnbondGateway { .. } => "VestingExecuteMsg::TrackUnbondGateway",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -17,9 +17,9 @@ tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
|
||||
nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
||||
futures = "0.3"
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-config = { path = "../config" }
|
||||
nym-credential-storage = { path = "../credential-storage", optional = true }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-socks5-proxy-helpers = { path = "../socks5/proxy-helpers" }
|
||||
nym-service-providers-common = { path = "../../service-providers/common" }
|
||||
@@ -29,4 +29,4 @@ nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
||||
|
||||
[features]
|
||||
default = ["nym-credential-storage"]
|
||||
default = []
|
||||
|
||||
@@ -7,27 +7,28 @@ use crate::socks::{
|
||||
authentication::{AuthenticationMethods, Authenticator, User},
|
||||
server::SphinxSocksServer,
|
||||
};
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
#[cfg(target_os = "android")]
|
||||
use nym_client_core::client::base_client::helpers::setup_empty_reply_surb_backend;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_client_core::client::base_client::non_wasm_helpers;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core::client::base_client::{
|
||||
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
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;
|
||||
use nym_validator_client::Client;
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
use nym_client_core::client::base_client::helpers::setup_empty_reply_surb_backend;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_client_core::client::base_client::non_wasm_helpers;
|
||||
use nym_client_core::config::DebugConfig;
|
||||
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod socks;
|
||||
@@ -74,10 +75,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,18 +98,12 @@ 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)
|
||||
}
|
||||
|
||||
pub fn start_socks5_listener(
|
||||
socks5_config: &Socks5,
|
||||
debug_config: DebugConfig,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_status: ClientState,
|
||||
@@ -133,6 +128,11 @@ impl NymClient {
|
||||
..
|
||||
} = client_status;
|
||||
|
||||
let packet_size = debug_config
|
||||
.traffic
|
||||
.secondary_packet_size
|
||||
.unwrap_or(debug_config.traffic.primary_packet_size);
|
||||
|
||||
let authenticator = Authenticator::new(auth_methods, allowed_users);
|
||||
let mut sphinx_socks = SphinxSocksServer::new(
|
||||
socks5_config.get_listening_port(),
|
||||
@@ -141,6 +141,7 @@ impl NymClient {
|
||||
self_address,
|
||||
shared_lane_queue_lengths,
|
||||
socks::client::Config::new(
|
||||
packet_size,
|
||||
socks5_config.get_provider_interface_version(),
|
||||
socks5_config.get_socks5_protocol_version(),
|
||||
socks5_config.get_send_anonymously(),
|
||||
@@ -223,7 +224,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 +242,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()),
|
||||
);
|
||||
|
||||
@@ -247,6 +263,7 @@ impl NymClient {
|
||||
|
||||
Self::start_socks5_listener(
|
||||
self.config.get_socks5(),
|
||||
*self.config.get_debug_settings(),
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
|
||||
@@ -17,6 +17,7 @@ use nym_socks5_requests::{
|
||||
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
|
||||
};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::params::PacketSize;
|
||||
use nym_task::connections::{LaneQueueLengths, TransmissionLane};
|
||||
use nym_task::TaskClient;
|
||||
use pin_project::pin_project;
|
||||
@@ -131,6 +132,7 @@ impl AsyncWrite for StreamState {
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct Config {
|
||||
biggest_packet_size: PacketSize,
|
||||
provider_interface_version: ProviderInterfaceVersion,
|
||||
socks5_protocol_version: Socks5ProtocolVersion,
|
||||
use_surbs_for_responses: bool,
|
||||
@@ -140,6 +142,7 @@ pub(crate) struct Config {
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn new(
|
||||
biggest_packet_size: PacketSize,
|
||||
provider_interface_version: ProviderInterfaceVersion,
|
||||
socks5_protocol_version: Socks5ProtocolVersion,
|
||||
use_surbs_for_responses: bool,
|
||||
@@ -147,6 +150,7 @@ impl Config {
|
||||
per_request_surbs: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
biggest_packet_size,
|
||||
provider_interface_version,
|
||||
socks5_protocol_version,
|
||||
use_surbs_for_responses,
|
||||
@@ -410,6 +414,9 @@ impl SocksClient {
|
||||
remote_proxy_target,
|
||||
conn_receiver,
|
||||
input_sender,
|
||||
// FIXME: this does NOT include overhead due to acks or chunking
|
||||
// (so actual true plaintext is smaller)
|
||||
self.config.biggest_packet_size.plaintext_size(),
|
||||
connection_id,
|
||||
Some(self.lane_queue_lengths.clone()),
|
||||
self.shutdown_listener.clone(),
|
||||
|
||||
@@ -1,208 +1,36 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use bytes::Bytes;
|
||||
use futures::Stream;
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::ops::DerefMut;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio::time::{sleep, Duration, Instant, Sleep};
|
||||
use tokio_util::io::poll_read_buf;
|
||||
|
||||
const MAX_READ_AMOUNT: usize = 500 * 1000; // 0.5MB
|
||||
const GRACE_DURATION: Duration = Duration::from_millis(1);
|
||||
// note, min_capacity doesn't mean we're going to always read at least this amount of data,
|
||||
// it defines the smallest allowed (by yours truly) upper bound
|
||||
const MIN_CAPACITY: usize = 16 * 1024;
|
||||
const DEFAULT_CAPACITY: usize = 64 * 1024;
|
||||
|
||||
pub struct AvailableReader<'a, R: AsyncRead + Unpin> {
|
||||
// TODO: come up with a way to avoid using RefCell (not sure if possible though due to having to
|
||||
// mutably borrow both inner reader and buffer at the same time)
|
||||
buf: RefCell<BytesMut>,
|
||||
inner: RefCell<&'a mut R>,
|
||||
grace_period: Option<Pin<Box<Sleep>>>,
|
||||
pub struct AvailableReader<R> {
|
||||
inner: tokio_util::io::ReaderStream<R>,
|
||||
}
|
||||
|
||||
impl<'a, R> AvailableReader<'a, R>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
{
|
||||
const BUF_INCREMENT: usize = 4096;
|
||||
impl<R: AsyncRead> AvailableReader<R> {
|
||||
pub fn new(reader: R, capacity: Option<usize>) -> Self {
|
||||
let capacity = capacity.unwrap_or(DEFAULT_CAPACITY).max(MIN_CAPACITY);
|
||||
|
||||
pub fn new(reader: &'a mut R) -> Self {
|
||||
AvailableReader {
|
||||
buf: RefCell::new(BytesMut::with_capacity(Self::BUF_INCREMENT)),
|
||||
inner: RefCell::new(reader),
|
||||
grace_period: Some(Box::pin(sleep(GRACE_DURATION))),
|
||||
inner: tokio_util::io::ReaderStream::with_capacity(reader, capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncRead + Unpin> Stream for AvailableReader<'a, R> {
|
||||
impl<R: AsyncRead + Unpin> Stream for AvailableReader<R> {
|
||||
type Item = io::Result<Bytes>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
// if we have no space in buffer left - expand it
|
||||
if !self.buf.borrow().has_remaining_mut() {
|
||||
self.buf.borrow_mut().reserve(Self::BUF_INCREMENT);
|
||||
}
|
||||
|
||||
// note: poll_read_buf calls `buf.advance_mut(n)`
|
||||
let poll_res = poll_read_buf(
|
||||
Pin::new(self.inner.borrow_mut().deref_mut()),
|
||||
cx,
|
||||
self.buf.borrow_mut().deref_mut(),
|
||||
);
|
||||
|
||||
match poll_res {
|
||||
Poll::Pending => {
|
||||
// there's nothing for us here, just return whatever we have (assuming we read anything!)
|
||||
if self.buf.borrow().is_empty() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
// if exists - check grace period
|
||||
if let Some(grace_period) = self.grace_period.as_mut() {
|
||||
if Pin::new(grace_period).poll(cx).is_pending() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let buf = self.buf.replace(BytesMut::new());
|
||||
Poll::Ready(Some(Ok(buf.freeze())))
|
||||
}
|
||||
}
|
||||
Poll::Ready(Err(err)) => Poll::Ready(Some(Err(err))),
|
||||
Poll::Ready(Ok(n)) => {
|
||||
// if exists - reset grace period
|
||||
if let Some(grace_period) = self.grace_period.as_mut() {
|
||||
let now = Instant::now();
|
||||
grace_period.as_mut().reset(now + GRACE_DURATION);
|
||||
}
|
||||
|
||||
// if we read a non-0 amount, we're not done yet!
|
||||
if n == 0 {
|
||||
let buf = self.buf.replace(BytesMut::new());
|
||||
if !buf.is_empty() {
|
||||
Poll::Ready(Some(Ok(buf.freeze())))
|
||||
} else {
|
||||
Poll::Ready(None)
|
||||
}
|
||||
} else {
|
||||
// tell the waker we should be polled again!
|
||||
cx.waker().wake_by_ref();
|
||||
|
||||
// if we reached our maximum amount - return it
|
||||
let read_bytes_len = self.buf.borrow().len();
|
||||
if read_bytes_len >= MAX_READ_AMOUNT {
|
||||
let buf = self.buf.replace(BytesMut::new());
|
||||
return Poll::Ready(Some(Ok(buf.freeze())));
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
Pin::new(&mut self.inner).poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures::{poll, StreamExt};
|
||||
use std::io::Cursor;
|
||||
use std::time::Duration;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio_test::assert_pending;
|
||||
|
||||
#[tokio::test]
|
||||
async fn available_reader_reads_all_available_data_smaller_than_its_buf() {
|
||||
let data = vec![42u8; 100];
|
||||
let mut reader = Cursor::new(data.clone());
|
||||
|
||||
let mut available_reader = AvailableReader::new(&mut reader);
|
||||
let read_data = available_reader.next().await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(read_data, data);
|
||||
assert!(available_reader.next().await.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn available_reader_reads_all_available_data_bigger_than_its_buf() {
|
||||
let data = vec![42u8; AvailableReader::<Cursor<Vec<u8>>>::BUF_INCREMENT + 100];
|
||||
let mut reader = Cursor::new(data.clone());
|
||||
|
||||
let mut available_reader = AvailableReader::new(&mut reader);
|
||||
let read_data = available_reader.next().await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(read_data, data);
|
||||
assert!(available_reader.next().await.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn available_reader_will_not_wait_for_more_data_if_it_already_has_some() {
|
||||
let first_data_chunk = vec![42u8; 100];
|
||||
let second_data_chunk = vec![123u8; 100];
|
||||
|
||||
let mut reader_mock = tokio_test::io::Builder::new()
|
||||
.read(&first_data_chunk)
|
||||
.wait(Duration::from_millis(100)) // delay is irrelevant, what matters is that we don't get everything immediately
|
||||
.read(&second_data_chunk)
|
||||
.build();
|
||||
|
||||
let mut available_reader = AvailableReader::new(&mut reader_mock);
|
||||
let read_data = available_reader.next().await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(read_data, first_data_chunk);
|
||||
assert_pending!(poll!(available_reader.next()));
|
||||
|
||||
// before dropping the mock, we need to empty it
|
||||
let mut buf = vec![0u8; second_data_chunk.len()];
|
||||
assert_eq!(reader_mock.read(&mut buf).await.unwrap(), 100);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn available_reader_will_wait_for_more_data_if_it_doesnt_have_anything() {
|
||||
let data = vec![42u8; 100];
|
||||
|
||||
let mut reader_mock = tokio_test::io::Builder::new()
|
||||
.wait(Duration::from_millis(100))
|
||||
.read(&data)
|
||||
.build();
|
||||
|
||||
let mut available_reader = AvailableReader::new(&mut reader_mock);
|
||||
let read_data = available_reader.next().await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(read_data, data);
|
||||
assert!(available_reader.next().await.is_none());
|
||||
}
|
||||
|
||||
// perhaps the issue of tokio io builder will be resolved in tokio 0.3?
|
||||
// #[tokio::test]
|
||||
// async fn available_reader_will_wait_for_more_data_if_its_within_grace_period() {
|
||||
// let first_data_chunk = vec![42u8; 100];
|
||||
// let second_data_chunk = vec![123u8; 100];
|
||||
//
|
||||
// let combined_chunks: Vec<_> = first_data_chunk
|
||||
// .iter()
|
||||
// .cloned()
|
||||
// .chain(second_data_chunk.iter().cloned())
|
||||
// .collect();
|
||||
//
|
||||
// let mut reader_mock = tokio_test::io::Builder::new()
|
||||
// .read(&first_data_chunk)
|
||||
// .wait(Duration::from_millis(2))
|
||||
// .read(&second_data_chunk)
|
||||
// .build();
|
||||
//
|
||||
// let mut available_reader = AvailableReader {
|
||||
// buf: RefCell::new(BytesMut::with_capacity(4096)),
|
||||
// inner: RefCell::new(&mut reader_mock),
|
||||
// grace_period: Some(delay_for(Duration::from_millis(5))),
|
||||
// };
|
||||
//
|
||||
// let read_data = available_reader.next().await.unwrap().unwrap();
|
||||
//
|
||||
// assert_eq!(read_data, combined_chunks);
|
||||
// assert!(available_reader.next().await.is_none())
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ pub(super) async fn run_inbound<F, S>(
|
||||
remote_source_address: String,
|
||||
connection_id: ConnectionId,
|
||||
mix_sender: MixProxySender<S>,
|
||||
available_plaintext_per_mix_packet: usize,
|
||||
adapter_fn: F,
|
||||
shutdown_notify: Arc<Notify>,
|
||||
lane_queue_lengths: Option<LaneQueueLengths>,
|
||||
@@ -176,7 +177,9 @@ where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static,
|
||||
S: Debug,
|
||||
{
|
||||
let mut available_reader = AvailableReader::new(&mut reader);
|
||||
// TODO: this multiplication by 4 is completely arbitrary here
|
||||
let mut available_reader =
|
||||
AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4));
|
||||
let mut message_sender = OrderedMessageSender::new();
|
||||
let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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::connection_controller::ConnectionReceiver;
|
||||
@@ -49,6 +49,8 @@ pub struct ProxyRunner<S> {
|
||||
connection_id: ConnectionId,
|
||||
lane_queue_lengths: Option<LaneQueueLengths>,
|
||||
|
||||
available_plaintext_per_mix_packet: usize,
|
||||
|
||||
// Listens to shutdown commands from higher up
|
||||
shutdown_listener: TaskClient,
|
||||
}
|
||||
@@ -64,6 +66,7 @@ where
|
||||
remote_source_address: String,
|
||||
mix_receiver: ConnectionReceiver,
|
||||
mix_sender: MixProxySender<S>,
|
||||
available_plaintext_per_mix_packet: usize,
|
||||
connection_id: ConnectionId,
|
||||
lane_queue_lengths: Option<LaneQueueLengths>,
|
||||
shutdown_listener: TaskClient,
|
||||
@@ -76,6 +79,7 @@ where
|
||||
remote_source_address,
|
||||
connection_id,
|
||||
lane_queue_lengths,
|
||||
available_plaintext_per_mix_packet,
|
||||
shutdown_listener,
|
||||
}
|
||||
}
|
||||
@@ -96,6 +100,7 @@ where
|
||||
self.remote_source_address.clone(),
|
||||
self.connection_id,
|
||||
self.mix_sender.clone(),
|
||||
self.available_plaintext_per_mix_packet,
|
||||
adapter_fn,
|
||||
Arc::clone(&shutdown_notify),
|
||||
self.lane_queue_lengths.clone(),
|
||||
|
||||
@@ -61,6 +61,10 @@ pub enum PendingEpochEventData {
|
||||
mix_id: MixId,
|
||||
amount: DecCoin,
|
||||
},
|
||||
DecreasePledge {
|
||||
mix_id: MixId,
|
||||
decrease_by: DecCoin,
|
||||
},
|
||||
UnbondMixnode {
|
||||
mix_id: MixId,
|
||||
},
|
||||
@@ -101,6 +105,13 @@ impl PendingEpochEventData {
|
||||
amount: reg.attempt_convert_to_display_dec_coin(amount.into())?,
|
||||
})
|
||||
}
|
||||
MixnetContractPendingEpochEventKind::DecreasePledge {
|
||||
mix_id,
|
||||
decrease_by,
|
||||
} => Ok(PendingEpochEventData::DecreasePledge {
|
||||
mix_id,
|
||||
decrease_by: reg.attempt_convert_to_display_dec_coin(decrease_by.into())?,
|
||||
}),
|
||||
MixnetContractPendingEpochEventKind::UnbondMixnode { mix_id } => {
|
||||
Ok(PendingEpochEventData::UnbondMixnode { mix_id })
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[alias]
|
||||
wasm = "build --target wasm32-unknown-unknown"
|
||||
|
||||
[build]
|
||||
rustflags = ["-C", "link-arg=-s"]
|
||||
#target = "wasm32-unknown-unknown"
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [v1.4.0] (2023-04-25)
|
||||
- Allow mixnode operators to decrease their bond amount without having to rebond (will require a lot of testing EXACT reward values to make sure the "unit delegation" isn't broken afterwards) ([#3233])
|
||||
- Fix a few clippy warnings in contract test code ([#3340])
|
||||
- Add --all-targets to clippy for contracts ([#3337])
|
||||
- A branch with all clippy warnings dealt with in contracts ([#3294])
|
||||
|
||||
[#3233]: https://github.com/nymtech/nym/issues/3233
|
||||
[#3340]: https://github.com/nymtech/nym/pull/3340
|
||||
[#3337]: https://github.com/nymtech/nym/pull/3337
|
||||
[#3294]: https://github.com/nymtech/nym/pull/3294
|
||||
|
||||
## [v1.3.1] (2023-04-18)
|
||||
- Add a query to the vesting contract for the amount of delegated tokens towards a particular `mix_id` (might be needed by NG) ([#3228])
|
||||
|
||||
|
||||
Generated
+20
-4
@@ -949,6 +949,22 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mixnet-vesting-integration-tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-multi-test",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-mixnet-contract",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-vesting-contract",
|
||||
"nym-vesting-contract-common",
|
||||
"rand_chacha 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
@@ -1052,7 +1068,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-derive",
|
||||
@@ -1076,7 +1092,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -1148,7 +1164,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"cosmwasm-crypto",
|
||||
@@ -1171,7 +1187,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
|
||||
@@ -4,6 +4,7 @@ members = [
|
||||
"coconut-dkg",
|
||||
"coconut-test",
|
||||
"mixnet",
|
||||
"mixnet-vesting-integration-tests",
|
||||
"multisig/cw3-flex-multisig",
|
||||
"multisig/cw4-group",
|
||||
"service-provider-directory",
|
||||
|
||||
@@ -86,9 +86,9 @@ mod tests {
|
||||
assert!(res.is_none());
|
||||
|
||||
let mut spend_credential = SpendCredential::new(
|
||||
funds.clone(),
|
||||
funds,
|
||||
blind_serial_number.to_string(),
|
||||
gateway_cosmos_address.clone(),
|
||||
gateway_cosmos_address,
|
||||
);
|
||||
spend_credential.mark_as_spent();
|
||||
|
||||
|
||||
@@ -20,6 +20,6 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
|
||||
};
|
||||
let env = mock_env();
|
||||
let info = mock_info("creator", &[]);
|
||||
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
instantiate(deps.as_mut(), env, info, msg).unwrap();
|
||||
deps
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ mod tests {
|
||||
);
|
||||
let info = mock_info("requester", &[coin]);
|
||||
|
||||
let tx = deposit_funds(deps.as_mut(), env.clone(), info, data).unwrap();
|
||||
let tx = deposit_funds(deps.as_mut(), env, info, data).unwrap();
|
||||
|
||||
let events: Vec<_> = tx
|
||||
.events
|
||||
@@ -246,13 +246,8 @@ mod tests {
|
||||
|
||||
deps.querier
|
||||
.update_balance(env.contract.address.clone(), vec![funds.clone()]);
|
||||
let err = release_funds(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(invalid_admin, &[]),
|
||||
funds.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
let err =
|
||||
release_funds(deps.as_mut(), env, mock_info(invalid_admin, &[]), funds).unwrap_err();
|
||||
assert_eq!(err, ContractError::Admin(AdminError::NotAdmin {}));
|
||||
}
|
||||
|
||||
@@ -294,7 +289,7 @@ mod tests {
|
||||
{
|
||||
assert_eq!(contract_addr, MULTISIG_CONTRACT);
|
||||
assert!(funds.is_empty());
|
||||
let multisig_msg: MultisigExecuteMsg = from_binary(&msg).unwrap();
|
||||
let multisig_msg: MultisigExecuteMsg = from_binary(msg).unwrap();
|
||||
if let MultisigExecuteMsg::Propose {
|
||||
title: _,
|
||||
description,
|
||||
@@ -312,7 +307,7 @@ mod tests {
|
||||
{
|
||||
assert_eq!(*contract_addr, env.contract.address.into_string());
|
||||
assert!(funds.is_empty());
|
||||
let release_funds_req: ExecuteMsg = from_binary(&msg).unwrap();
|
||||
let release_funds_req: ExecuteMsg = from_binary(msg).unwrap();
|
||||
if let ExecuteMsg::ReleaseFunds { funds } = release_funds_req {
|
||||
assert_eq!(funds, *data.funds());
|
||||
} else {
|
||||
|
||||
@@ -216,7 +216,7 @@ mod tests {
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
let res = instantiate(deps.as_mut(), env.clone(), info, msg);
|
||||
let res = instantiate(deps.as_mut(), env, info, msg);
|
||||
assert!(res.is_ok())
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ mod tests {
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(parse_node_index(res), (idx + 1) as u64);
|
||||
@@ -259,7 +259,7 @@ mod tests {
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
&[],
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::AlreadyADealer, err.downcast().unwrap());
|
||||
@@ -269,13 +269,13 @@ mod tests {
|
||||
let err = app
|
||||
.execute_contract(
|
||||
unauthorized_member,
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
coconut_dkg_contract_addr,
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
&[],
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::Unauthorized, err.downcast().unwrap());
|
||||
|
||||
@@ -153,9 +153,9 @@ pub(crate) mod tests {
|
||||
|
||||
let ret = try_add_dealer(
|
||||
deps.as_mut(),
|
||||
info.clone(),
|
||||
bte_key_with_proof.clone(),
|
||||
announce_address.clone(),
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
announce_address,
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
@@ -56,11 +56,11 @@ pub(crate) mod tests {
|
||||
for n in 0..size {
|
||||
let dealing_share = dealing_bytes_fixture();
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
for idx in 0..TOTAL_DEALINGS {
|
||||
(0..TOTAL_DEALINGS).for_each(|idx| {
|
||||
DEALINGS_BYTES[idx]
|
||||
.save(deps.storage, &sender, &dealing_share)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,8 +131,7 @@ pub(crate) mod tests {
|
||||
assert!(ret.is_ok());
|
||||
assert!(dealings.has(deps.as_mut().storage, &owner));
|
||||
}
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true)
|
||||
.unwrap_err();
|
||||
let ret = try_commit_dealings(deps.as_mut(), info, dealing_bytes, true).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::AlreadyCommitted {
|
||||
|
||||
@@ -236,10 +236,10 @@ pub(crate) mod tests {
|
||||
let limit = *limits.next().unwrap();
|
||||
{
|
||||
let mut group_members = GROUP_MEMBERS.lock().unwrap();
|
||||
for i in 0..n as usize {
|
||||
for dealer in dealers.iter() {
|
||||
group_members.push((
|
||||
Member {
|
||||
addr: dealers[i].address.to_string(),
|
||||
addr: dealer.address.to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
@@ -339,7 +339,7 @@ pub(crate) mod tests {
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
for i in 0..3 as u64 {
|
||||
for i in 0..3_u64 {
|
||||
let details = dealer_details_fixture(i + 1);
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, &details)
|
||||
|
||||
@@ -90,6 +90,6 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
|
||||
};
|
||||
let env = mock_env();
|
||||
let info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
instantiate(deps.as_mut(), env, info, msg).unwrap();
|
||||
deps
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user