diff --git a/Cargo.lock b/Cargo.lock index a2b1c5d75f..4ff0f9a693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -460,8 +460,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", - "rand_core 0.6.4", + "rand 0.6.5", + "rand_core 0.4.2", "serde", "unicode-normalization", "zeroize", @@ -3575,6 +3575,7 @@ dependencies = [ "console-subscriber", "cosmwasm-std", "cw-utils", + "cw2", "cw3", "cw4", "dirs 4.0.0", @@ -3965,7 +3966,7 @@ version = "0.1.0" dependencies = [ "async-trait", "log", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -4045,9 +4046,9 @@ dependencies = [ name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ + "nym-api-requests", "nym-contracts-common", "nym-mixnet-contract-common", - "nym-validator-client", "schemars", "serde", "ts-rs", @@ -4092,7 +4093,7 @@ dependencies = [ "rand 0.7.3", "serde", "serde_json", - "sqlx 0.5.13", + "sqlx 0.5.11", "subtle-encoding", "thiserror", "tokio", @@ -4320,6 +4321,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -4384,7 +4386,7 @@ dependencies = [ "pretty_env_logger", "rocket", "serde", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -4801,7 +4803,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -4885,6 +4887,9 @@ dependencies = [ "colored", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -4916,6 +4921,7 @@ dependencies = [ "tokio", "ts-rs", "url", + "wasmtimer", "zeroize", ] @@ -6845,9 +6851,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.5.13" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" +checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826" dependencies = [ "sqlx-core 0.5.13", "sqlx-macros 0.5.13", diff --git a/Cargo.toml b/Cargo.toml index 9d609443f0..8ba5452d11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,6 +134,7 @@ cw3 = { version = "=1.1.0" } cw4 = { version = "=1.1.0" } cw-controllers = { version = "=1.1.0" } dotenvy = "0.15.6" +futures = "0.3.28" generic-array = "0.14.7" getrandom = "0.2.10" k256 = "0.13" @@ -148,7 +149,7 @@ tap = "1.0.1" tendermint-rpc = "0.32" # same version as used by cosmrs thiserror = "1.0.38" tokio = "1.24.1" -url = "2.2" +url = "2.4" zeroize = "1.6.0" # wasm-related dependencies diff --git a/clients/credential/src/client.rs b/clients/credential/src/client.rs index 2db71d8e5e..9eb0a7da75 100644 --- a/clients/credential/src/client.rs +++ b/clients/credential/src/client.rs @@ -4,14 +4,14 @@ use crate::error::Result; use bip39::Mnemonic; use nym_network_defaults::{NymNetworkDetails, VOUCHER_INFO}; -use nym_validator_client::nyxd; -use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient; -use nym_validator_client::nyxd::{Coin, DirectSigningNyxdClient, Fee, NyxdClient}; +use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient; +use nym_validator_client::nyxd::{self, DirectSigningHttpRpcNyxdClient}; +use nym_validator_client::nyxd::{Coin, Fee, NyxdClient}; use std::str::FromStr; use url::Url; pub(crate) struct Client { - nyxd_client: NyxdClient, + nyxd_client: DirectSigningHttpRpcNyxdClient, mix_denom_base: String, } diff --git a/clients/credential/src/commands.rs b/clients/credential/src/commands.rs index 64993d2d7d..da603eb84b 100644 --- a/clients/credential/src/commands.rs +++ b/clients/credential/src/commands.rs @@ -6,7 +6,7 @@ use log::*; use nym_bandwidth_controller::acquire::state::State; use nym_bin_common::completions::ArgShell; use nym_credential_storage::persistent_storage::PersistentStorage; -use nym_validator_client::nyxd::traits::DkgQueryClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use crate::error::Result; use crate::recovery_storage::RecoveryStorage; diff --git a/clients/credential/src/main.rs b/clients/credential/src/main.rs index e37a825403..982ef02823 100644 --- a/clients/credential/src/main.rs +++ b/clients/credential/src/main.rs @@ -17,9 +17,9 @@ use std::time::{Duration, SystemTime}; use clap::{CommandFactory, Parser}; use nym_bin_common::logging::setup_logging; use nym_client_core::config::disk_persistence::CommonClientPaths; -use nym_validator_client::nyxd::traits::DkgQueryClient; -use nym_validator_client::nyxd::{Coin, CosmWasmClient}; -use nym_validator_client::Config; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; +use nym_validator_client::nyxd::{Coin, Config}; +use nym_validator_client::DirectSigningHttpRpcNyxdClient; const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute @@ -34,11 +34,11 @@ struct Cli { pub(crate) command: Command, } -async fn block_until_coconut_is_available( - client: &nym_validator_client::Client, +async fn block_until_coconut_is_available( + client: &C, ) -> Result<()> { loop { - let epoch = client.nyxd.get_current_epoch().await?; + let epoch = client.get_current_epoch().await?; let current_timestamp_secs = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH)? .as_secs(); @@ -89,15 +89,18 @@ async fn main() -> Result<()> { r.amount as u128, network_details.chain_details.mix_denom.base, ); - let client = - nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?; + let endpoint = network_details.endpoints[0].nyxd_url.as_str(); + let client = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + config, + endpoint, + 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 = - nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?; + let state = nym_bandwidth_controller::acquire::deposit(&client, amount).await?; if nym_bandwidth_controller::acquire::get_credential( &state, &client, @@ -117,7 +120,7 @@ async fn main() -> Result<()> { } } } else { - recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?; + recover_credentials(&client, &recovery_storage, &shared_storage).await?; } } Command::Completions(c) => c.generate(&mut Cli::command(), bin_name), diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index e950db03ba..9bf16e89ad 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -14,11 +14,11 @@ path = "src/lib.rs" [dependencies] # dependencies to review: -futures = "0.3" # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed +futures = { workspace = true } # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed # the AsyncRead, AsyncWrite, Stream, Sink, etc. traits could be used from tokio # channels should really be replaced with crossbeam due to that implementation being more efficient # and the single instance of abortable we have should really be refactored anyway -url = "2.2" +url = { workspace = true } clap = { version = "4.0", features = ["cargo", "derive"] } dirs = "4.0" @@ -28,7 +28,7 @@ pretty_env_logger = "0.4" # for formatting log messages rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization serde_json = { workspace = true } -thiserror = "1.0.34" +thiserror = { workspace = true } tap = "1.0.1" tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio-tungstenite = "0.14" # websocket diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index 8457268d79..6f3982038f 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -19,8 +19,7 @@ use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::params::PacketType; use nym_task::connections::TransmissionLane; use nym_task::TaskManager; -use nym_validator_client::nyxd::QueryNyxdClient; -use nym_validator_client::Client; +use nym_validator_client::QueryHttpRpcNyxdClient; use std::error::Error; use tokio::sync::watch::error::SendError; @@ -29,7 +28,7 @@ pub use nym_sphinx::receiver::ReconstructedMessage; pub mod config; -type NativeClientBuilder<'a> = BaseClientBuilder<'a, Client, OnDiskPersistent>; +type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>; pub struct SocketClient { /// Client configuration options, including, among other things, packet sending rates, diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 3e22c2f148..a574f28985 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -14,9 +14,9 @@ pretty_env_logger = "0.4" serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization serde_json = { workspace = true } tap = "1.0.1" -thiserror = "1.0.34" +thiserror = { workspace = true } tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } -url = "2.2" +url = { workspace = true } # internal nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } diff --git a/clients/webassembly/Cargo.lock b/clients/webassembly/Cargo.lock index 3f21d6f932..c5aa4a2830 100644 --- a/clients/webassembly/Cargo.lock +++ b/clients/webassembly/Cargo.lock @@ -2733,9 +2733,9 @@ dependencies = [ name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ + "nym-api-requests", "nym-contracts-common", "nym-mixnet-contract-common", - "nym-validator-client", "schemars", "serde", ] @@ -2857,6 +2857,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -3128,6 +3129,9 @@ dependencies = [ "colored", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -3158,6 +3162,7 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index 7405002cea..d68b5d37f6 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -3,7 +3,7 @@ name = "nym-client-wasm" authors = ["Dave Hrycyszyn ", "Jedrzej Stuczynski "] version = "1.1.1" edition = "2021" -keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] +keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"] license = "Apache-2.0" repository = "https://github.com/nymtech/nym" description = "A webassembly client which can be used to interact with the the Nym privacy platform. Wasm is used for Sphinx packet generation." diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 74af0c2c8e..733521ea97 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -14,7 +14,6 @@ use crate::storage::traits::FullWasmClientStorage; use crate::storage::ClientStorage; use crate::topology::WasmNymTopology; use js_sys::Promise; -use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_client_core::client::base_client::{ BaseClientBuilder, ClientInput, ClientOutput, ClientState, }; @@ -26,6 +25,7 @@ use nym_task::TaskManager; use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider}; use nym_topology::NymTopology; use nym_validator_client::client::IdentityKey; +use nym_validator_client::QueryReqwestRpcNyxdClient; use rand::rngs::OsRng; use rand::RngCore; use std::sync::Arc; @@ -152,7 +152,7 @@ impl NymClientBuilder { let maybe_topology_provider = self.topology_provider(); let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = - BaseClientBuilder::, _>::new( + BaseClientBuilder::::new( &self.config.base, storage, None, diff --git a/clients/webassembly/src/tester/mod.rs b/clients/webassembly/src/tester/mod.rs index 71f985feca..ba70062f3b 100644 --- a/clients/webassembly/src/tester/mod.rs +++ b/clients/webassembly/src/tester/mod.rs @@ -12,7 +12,6 @@ use crate::tester::helpers::{ use crate::topology::WasmNymTopology; use futures::channel::mpsc; use js_sys::Promise; -use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_bandwidth_controller::BandwidthController; use nym_client_core::client::key_manager::ManagedKeys; use nym_client_core::init::{InitialisationDetails, InitialisationResult}; @@ -27,6 +26,7 @@ use nym_sphinx::preparer::PreparedFragment; use nym_task::TaskManager; use nym_topology::NymTopology; use nym_validator_client::client::IdentityKey; +use nym_validator_client::QueryReqwestRpcNyxdClient; use rand::rngs::OsRng; use std::collections::HashSet; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; @@ -42,7 +42,7 @@ pub(crate) mod helpers; pub type NodeTestMessage = TestMessage; type LockedGatewayClient = - Arc, EphemeralStorage>>>; + Arc>>; pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10); pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20; @@ -78,8 +78,7 @@ pub struct NymNodeTesterBuilder { base_topology: NymTopology, // unimplemented - bandwidth_controller: - Option, EphemeralStorage>>, + bandwidth_controller: Option>, } fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient { diff --git a/common/async-file-watcher/Cargo.toml b/common/async-file-watcher/Cargo.toml index 05e7035cf2..0a44f239cf 100644 --- a/common/async-file-watcher/Cargo.toml +++ b/common/async-file-watcher/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" [dependencies] log = "0.4" tokio = { workspace = true, features = ["time"] } -futures = "0.3" +futures = { workspace = true } notify = "5.1.0" diff --git a/common/bandwidth-controller/Cargo.toml b/common/bandwidth-controller/Cargo.toml index 5b168d8b9f..3ce1f01454 100644 --- a/common/bandwidth-controller/Cargo.toml +++ b/common/bandwidth-controller/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" bip39 = { workspace = true } rand = "0.7.3" thiserror = "1.0" -url = "2.2" +url = { workspace = true } nym-coconut-interface = { path = "../coconut-interface" } nym-credential-storage = { path = "../credential-storage" } @@ -20,4 +20,4 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] path = "../client-libs/validator-client" -features = ["signing"] +features = ["http-client"] diff --git a/common/bandwidth-controller/src/acquire/mod.rs b/common/bandwidth-controller/src/acquire/mod.rs index cdf72c3b00..c03ae8e90a 100644 --- a/common/bandwidth-controller/src/acquire/mod.rs +++ b/common/bandwidth-controller/src/acquire/mod.rs @@ -8,11 +8,11 @@ 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::coconut::all_coconut_api_clients; +use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::Coin; use nym_validator_client::nyxd::Hash; -use nym_validator_client::CoconutApiClient; use rand::rngs::OsRng; use state::{KeyPair, State}; use std::str::FromStr; @@ -21,7 +21,7 @@ pub mod state; pub async fn deposit(client: &C, amount: Coin) -> Result where - C: CoconutBandwidthSigningClient, + C: CoconutBandwidthSigningClient + Sync, { let mut rng = OsRng; let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); @@ -70,7 +70,8 @@ where .get_current_epoch_threshold() .await? .ok_or(BandwidthControllerError::NoThreshold)?; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?; + + let coconut_api_clients = all_coconut_api_clients(client, epoch_id).await?; let signature = obtain_aggregate_signature( &state.params, diff --git a/common/bandwidth-controller/src/error.rs b/common/bandwidth-controller/src/error.rs index e9a862ebff..5f464c82e5 100644 --- a/common/bandwidth-controller/src/error.rs +++ b/common/bandwidth-controller/src/error.rs @@ -6,15 +6,18 @@ 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::coconut::CoconutApiError; 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("coconut api query failure: {0}")] + CoconutApiError(#[from] CoconutApiError), + #[error("There was a credential storage error - {0}")] CredentialStorageError(Box), diff --git a/common/bandwidth-controller/src/lib.rs b/common/bandwidth-controller/src/lib.rs index 66a6f0a6d2..d9a714b263 100644 --- a/common/bandwidth-controller/src/lib.rs +++ b/common/bandwidth-controller/src/lib.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BandwidthControllerError; - use nym_credential_storage::error::StorageError; use nym_credential_storage::storage::Storage; - +use nym_validator_client::coconut::all_coconut_api_clients; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::str::FromStr; use { nym_coconut_interface::Base58, @@ -14,17 +14,8 @@ use { }, }; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; - -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::DkgQueryClient; - -#[cfg(not(target_arch = "wasm32"))] pub mod acquire; pub mod error; -#[cfg(target_arch = "wasm32")] -pub mod wasm_mockups; pub struct BandwidthController { storage: St, @@ -64,12 +55,8 @@ impl BandwidthController { let epoch_id = u64::from_str(&bandwidth_credential.epoch_id) .map_err(|_| StorageError::InconsistentData)?; - #[cfg(not(target_arch = "wasm32"))] - let coconut_api_clients = - nym_validator_client::CoconutApiClient::all_coconut_api_clients(&self.client, epoch_id) - .await?; - #[cfg(target_arch = "wasm32")] - let coconut_api_clients = vec![]; + let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?; + let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?; // the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff) diff --git a/common/bandwidth-controller/src/wasm_mockups.rs b/common/bandwidth-controller/src/wasm_mockups.rs deleted file mode 100644 index be7ee06a79..0000000000 --- a/common/bandwidth-controller/src/wasm_mockups.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022-2023 - Nym Technologies SA -// 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 { - _phantom: PhantomData, -} - -impl DkgQueryClient for Client {} diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index 0811b08add..bcb21899fe 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -12,7 +12,7 @@ async-trait = { workspace = true } base64 = "0.21.2" dashmap = "5.4.0" dirs = "4.0" -futures = "0.3" +futures = { workspace = true } humantime-serde = "1.0" log = { workspace = true } rand = { version = "0.7.3", features = ["wasm-bindgen"] } @@ -25,7 +25,7 @@ thiserror = "1.0.34" time = "0.3.17" tokio = { version = "1.24.1", features = ["macros"]} tungstenite = { version = "0.13.0", default-features = false } -url = { version ="2.2", features = ["serde"] } +url = { workspace = true, features = ["serde"] } zeroize = { workspace = true } # internal @@ -45,10 +45,6 @@ nym-task = { path = "../task" } nym-credential-storage = { path = "../credential-storage" } nym-network-defaults = { path = "../network-defaults" } -[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] -path = "../client-libs/validator-client" -features = ["signing", "http-client"] - [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] version = "0.1.11" features = ["time"] diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index c7e47cab0e..2f2e7d0072 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -3,6 +3,7 @@ use super::received_buffer::ReceivedBufferMessage; use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider; +use crate::client::base_client::storage::gateway_details::GatewayDetailsStore; use crate::client::base_client::storage::MixnetClientStorage; use crate::client::cover_traffic_stream::LoopCoverTrafficStream; use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender}; @@ -24,6 +25,7 @@ use crate::client::topology_control::{ }; use crate::config::{Config, DebugConfig}; use crate::error::ClientCoreError; +use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult}; use crate::{config, spawn_future}; use futures::channel::mpsc; use log::{debug, info}; @@ -42,18 +44,11 @@ use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver}; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::{TaskClient, TaskManager}; use nym_topology::provider_trait::TopologyProvider; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::sync::Arc; use tap::TapFallible; use url::Url; -#[cfg(target_arch = "wasm32")] -use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; - -use crate::client::base_client::storage::gateway_details::GatewayDetailsStore; -use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult}; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; - #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] pub mod non_wasm_helpers; diff --git a/common/client-core/src/client/base_client/non_wasm_helpers.rs b/common/client-core/src/client/base_client/non_wasm_helpers.rs index 6b901f0f82..d0d2d5a058 100644 --- a/common/client-core/src/client/base_client/non_wasm_helpers.rs +++ b/common/client-core/src/client/base_client/non_wasm_helpers.rs @@ -10,8 +10,8 @@ use crate::error::ClientCoreError; use log::{error, info}; use nym_bandwidth_controller::BandwidthController; use nym_credential_storage::storage::Storage as CredentialStorage; -use nym_validator_client::nyxd::QueryNyxdClient; -use nym_validator_client::Client; +use nym_validator_client::nyxd; +use nym_validator_client::QueryHttpRpcNyxdClient; use std::path::Path; use std::{fs, io}; use time::OffsetDateTime; @@ -104,48 +104,38 @@ pub async fn setup_fs_reply_surb_backend>( pub fn create_bandwidth_controller( config: &Config, storage: St, -) -> BandwidthController, St> { +) -> BandwidthController { let nyxd_url = config .get_validator_endpoints() .pop() .expect("No nyxd validator endpoint provided"); - let api_url = config - .get_nym_api_endpoints() - .pop() - .expect("No validator api endpoint provided"); - create_bandwidth_controller_with_urls(nyxd_url, api_url, storage) + create_bandwidth_controller_with_urls(nyxd_url, storage) } pub fn create_bandwidth_controller_with_urls( nyxd_url: Url, - nym_api_url: Url, storage: St, -) -> BandwidthController, St> { - let client = default_query_dkg_client(nyxd_url, nym_api_url); +) -> BandwidthController { + let client = default_query_dkg_client(nyxd_url); BandwidthController::new(storage, client) } -pub fn default_query_dkg_client_from_config(config: &Config) -> Client { +pub fn default_query_dkg_client_from_config(config: &Config) -> QueryHttpRpcNyxdClient { let nyxd_url = config .get_validator_endpoints() .pop() .expect("No nyxd validator endpoint provided"); - let api_url = config - .get_nym_api_endpoints() - .pop() - .expect("No validator api endpoint provided"); - default_query_dkg_client(nyxd_url, api_url) + default_query_dkg_client(nyxd_url) } -pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client { +pub fn default_query_dkg_client(nyxd_url: Url) -> QueryHttpRpcNyxdClient { let details = nym_network_defaults::NymNetworkDetails::new_from_env(); - let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details) + let client_config = nyxd::Config::try_from_nym_network_details(&details) .expect("failed to construct validator client config"); // overwrite env configuration with config URLs - client_config = client_config.with_urls(nyxd_url, nym_api_url); - nym_validator_client::Client::new_query(client_config) + QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str()) .expect("Could not construct query client") } diff --git a/common/client-core/src/client/mix_traffic.rs b/common/client-core/src/client/mix_traffic.rs index 8e7f7ce2fb..cb12a55d85 100644 --- a/common/client-core/src/client/mix_traffic.rs +++ b/common/client-core/src/client/mix_traffic.rs @@ -3,15 +3,10 @@ use crate::spawn_future; use log::*; +use nym_credential_storage::storage::Storage; 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_bandwidth_controller::wasm_mockups::DkgQueryClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender>; pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver>; diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 8541bf5ad0..adfd9ad623 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -3,6 +3,7 @@ use crate::config::GatewayEndpointConfig; use crate::error::ClientCoreError; +use crate::init::RegistrationResult; use futures::{SinkExt, StreamExt}; use log::{debug, info, trace, warn}; use nym_crypto::asymmetric::identity; @@ -27,7 +28,6 @@ type WsConn = WebSocketStream>; #[cfg(not(target_arch = "wasm32"))] use tokio::time::sleep; -use crate::init::RegistrationResult; #[cfg(target_arch = "wasm32")] use wasm_utils::websocket::JSWebsocket; #[cfg(target_arch = "wasm32")] diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index 197acdadef..3f8f4ac0e4 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -9,10 +9,10 @@ edition = "2021" [dependencies] # TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than # the entire crate -futures = "0.3" +futures = { workspace = true } log = { workspace = true } -thiserror = "1.0" -url = "2.2" +thiserror = { workspace = true } +url = { workspace = true } rand = { version = "0.7.3", features = ["wasm-bindgen"] } tokio = { version = "1.24.1", features = ["macros"] } @@ -25,7 +25,7 @@ nym-gateway-requests = { path = "../../../gateway/gateway-requests" } nym-network-defaults = { path = "../../network-defaults" } nym-sphinx = { path = "../../nymsphinx" } nym-pemstore = { path = "../../pemstore" } -nym-validator-client = { path = "../validator-client" } +nym-validator-client = { path = "../validator-client", default-features = false } nym-task = { path = "../../task" } serde = { workspace = true, features = ["derive"] } diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index b7d9a2c882..188d84e2c9 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::error::GatewayClientError; @@ -22,21 +22,18 @@ use nym_gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN}; use nym_sphinx::forwarding::packet::MixPacket; use nym_task::TaskClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use rand::rngs::OsRng; use std::convert::TryFrom; use std::sync::Arc; use std::time::Duration; use tungstenite::protocol::Message; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; #[cfg(not(target_arch = "wasm32"))] use tokio::time::sleep; #[cfg(not(target_arch = "wasm32"))] use tokio_tungstenite::connect_async; -#[cfg(target_arch = "wasm32")] -use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; #[cfg(target_arch = "wasm32")] use wasm_utils::websocket::JSWebsocket; #[cfg(target_arch = "wasm32")] @@ -199,7 +196,6 @@ impl GatewayClient { return Ok(()); } - #[cfg(not(target_arch = "wasm32"))] sleep(self.reconnection_backoff).await; } @@ -782,7 +778,7 @@ impl GatewayClient { let shutdown = TaskClient::dummy(); let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone()); - GatewayClient:: { + GatewayClient { authenticated: false, disabled_credentials_mode: true, bandwidth_remaining: 0, diff --git a/common/client-libs/mixnet-client/Cargo.toml b/common/client-libs/mixnet-client/Cargo.toml index fabcca892d..9eede069da 100644 --- a/common/client-libs/mixnet-client/Cargo.toml +++ b/common/client-libs/mixnet-client/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.3" +futures = { workspace = true } log = { workspace = true } tokio = { version = "1.24.1", features = ["time", "net", "rt"] } tokio-util = { version = "0.7.4", features = ["codec"] } diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 793adaf3cb..952d106e97 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -23,11 +23,11 @@ nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } reqwest = { workspace = true, features = ["json"] } -thiserror = "1" +thiserror = { workspace = true } log = { workspace = true } -url = { version = "2.2", features = ["serde"] } -tokio = { version = "1.24.1", features = ["sync", "time"] } -futures = "0.3" +url = { workspace = true, features = ["serde"] } +tokio = { workspace = true, features = ["sync", "time"] } +futures = { workspace = true } openssl = { version = "^0.10.55", features = ["vendored"], optional = true } nym-coconut-interface = { path = "../../coconut-interface" } @@ -35,30 +35,35 @@ nym-network-defaults = { path = "../../network-defaults" } nym-api-requests = { path = "../../../nym-api/nym-api-requests" } async-trait = { workspace = true } -bip39 = { workspace = true, features = ["rand"], optional = true } +bip39 = { workspace = true, features = ["rand"] } nym-config = { path = "../../config" } cosmrs = { workspace = true, features = ["bip32", "cosmwasm"] } -#cosmrs = { workspace = true, features = ["bip32", "rpc", "cosmwasm"], optional = true } # note that this has the same version as used by cosmrs # import it just for the `Client` trait tendermint-rpc = { workspace = true } -eyre = { version = "0.6", optional = true } +eyre = { version = "0.6" } +cw-utils = { workspace = true } +cw2 = { workspace = true } cw3 = { workspace = true } cw4 = { workspace = true } +cw-controllers = { workspace = true } prost = { version = "0.11", default-features = false } flate2 = { version = "1.0.20" } sha2 = { version = "0.9.5" } itertools = { version = "0.10" } -zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] } +zeroize = { workspace = true, features = ["zeroize_derive"] } cosmwasm-std = { workspace = true } +# required for polling for broadcast result +[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] +workspace = true +features = ["tokio"] + [dev-dependencies] bip39 = { workspace = true } -#cosmrs = { workspace = true, features = ["rpc", "bip32"] } cosmrs = { workspace = true, features = ["bip32"] } -tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] } ts-rs = "6.1.2" [[example]] @@ -66,24 +71,18 @@ name = "offline_signing" # it should only really require the "signing" feature, # but that would require another round of refactoring to make it possible # (traits would need to be moved around and refactored themselves) -required-features = ["http-client", "signing"] +required-features = ["http-client"] [[example]] name = "query_service_provider_directory" -# TODO: validate the requirements -required-features = ["http-client", "signing"] +required-features = ["http-client"] [[example]] name = "query_name_service" -# TODO: validate the requirements -required-features = ["http-client", "signing"] +required-features = ["http-client"] [features] +default = ["http-client"] http-client = ["cosmrs/rpc", "openssl"] -signing = [ - "bip39", - "eyre", - "zeroize" -] generate-ts = [] diff --git a/common/client-libs/validator-client/examples/offline_signing.rs b/common/client-libs/validator-client/examples/offline_signing.rs index a21bfa4475..baa2d10035 100644 --- a/common/client-libs/validator-client/examples/offline_signing.rs +++ b/common/client-libs/validator-client/examples/offline_signing.rs @@ -23,7 +23,7 @@ async fn main() { let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone(); // local 'client' ONLY signing messages - let tx_signer = TxSigner::new(signer); + let tx_signer = signer; // possibly remote client that doesn't do ANY signing // (only broadcasts + queries for sequence numbers) diff --git a/common/client-libs/validator-client/examples/query_name_service.rs b/common/client-libs/validator-client/examples/query_name_service.rs index 8ea026ce05..68e4da4cf2 100644 --- a/common/client-libs/validator-client/examples/query_name_service.rs +++ b/common/client-libs/validator-client/examples/query_name_service.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use cosmrs::AccountId; use nym_name_service_common::Address; use nym_network_defaults::{setup_env, NymNetworkDetails}; -use nym_validator_client::nyxd::traits::NameServiceQueryClient; +use nym_validator_client::nyxd::contract_traits::{ + NameServiceQueryClient, PagedNameServiceQueryClient, +}; #[tokio::main] async fn main() { diff --git a/common/client-libs/validator-client/examples/query_service_provider_directory.rs b/common/client-libs/validator-client/examples/query_service_provider_directory.rs index 12369b2696..d0f6f7ce46 100644 --- a/common/client-libs/validator-client/examples/query_service_provider_directory.rs +++ b/common/client-libs/validator-client/examples/query_service_provider_directory.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use cosmrs::AccountId; use nym_network_defaults::{setup_env, NymNetworkDetails}; use nym_service_provider_directory_common::NymAddress; -use nym_validator_client::nyxd::traits::SpDirectoryQueryClient; +use nym_validator_client::nyxd::contract_traits::{ + PagedSpDirectoryQueryClient, SpDirectoryQueryClient, +}; #[tokio::main] async fn main() { diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 54539403fb..e70cbdaf2f 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -1,41 +1,33 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{nym_api, ValidatorClientError}; +use crate::nyxd::{self, NyxdClient}; +use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +use crate::signing::signer::{NoSigner, OfflineSigner}; +use crate::{ + nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, + ReqwestRpcClient, ValidatorClientError, +}; use nym_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, }; +use nym_api_requests::models::MixNodeBondAnnotated; use nym_api_requests::models::{ GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, }; -use nym_coconut_dkg_common::types::NodeIndex; -use nym_coconut_interface::VerificationKey; +use nym_network_defaults::NymNetworkDetails; +use url::Url; + pub use nym_mixnet_contract_common::{ mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId, }; -use url::Url; -use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient}; +// re-export the type to not break existing imports +pub use crate::coconut::CoconutApiClient; + #[cfg(feature = "http-client")] -use crate::nyxd::QueryNyxdClient; -use crate::nyxd::{self, CosmWasmClient, NyxdClient}; -use nym_api_requests::models::MixNodeBondAnnotated; -use nym_coconut_dkg_common::{types::EpochId, verification_key::ContractVKShare}; -use nym_coconut_interface::Base58; -use nym_mixnet_contract_common::{ - families::{Family, FamilyHead}, - mixnode::MixNodeBond, - pending_events::{PendingEpochEvent, PendingIntervalEvent}, - Delegation, RewardedSetNodeStatus, UnbondedMixnode, -}; -use nym_network_defaults::NymNetworkDetails; -use std::str::FromStr; - -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::nyxd::SigningNyxdClient; -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient}; #[must_use] #[derive(Debug, Clone)] @@ -44,13 +36,7 @@ pub struct Config { nyxd_url: Url, // TODO: until refactored, this is a dead field under some features - #[allow(dead_code)] nyxd_config: nyxd::Config, - - mixnode_page_limit: Option, - gateway_page_limit: Option, - mixnode_delegations_page_limit: Option, - rewarded_set_page_limit: Option, } impl Config { @@ -75,10 +61,6 @@ impl Config { .parse() .map_err(ValidatorClientError::MalformedUrlProvided)?, nyxd_config: nyxd::Config::try_from_nym_network_details(details)?, - mixnode_page_limit: None, - gateway_page_limit: None, - mixnode_delegations_page_limit: None, - rewarded_set_page_limit: None, }) } @@ -95,87 +77,57 @@ impl Config { self } - pub fn with_mixnode_page_limit(mut self, limit: Option) -> Config { - self.mixnode_page_limit = limit; - self - } - - pub fn with_gateway_page_limit(mut self, limit: Option) -> Config { - self.gateway_page_limit = limit; - self - } - - pub fn with_mixnode_delegations_page_limit(mut self, limit: Option) -> Config { - self.mixnode_delegations_page_limit = limit; - self - } - - pub fn with_rewarded_set_page_limit(mut self, limit: Option) -> Config { - self.rewarded_set_page_limit = limit; + pub fn with_simulated_gas_multiplier(mut self, gas_multiplier: f32) -> Self { + self.nyxd_config.simulated_gas_multiplier = gas_multiplier; self } } -pub struct Client { - mixnode_page_limit: Option, - gateway_page_limit: Option, - mixnode_delegations_page_limit: Option, - rewarded_set_page_limit: Option, - +pub struct Client { // ideally they would have been read-only, but unfortunately rust doesn't have such features pub nym_api: nym_api::Client, - pub nyxd: NyxdClient, + pub nyxd: NyxdClient, } -#[cfg(all(feature = "signing", feature = "http-client"))] -impl Client> { +#[cfg(feature = "http-client")] +impl Client { pub fn new_signing( config: Config, mnemonic: bip39::Mnemonic, - ) -> Result>, ValidatorClientError> { - let nym_api_client = nym_api::Client::new(config.api_url.clone()); - let nyxd_client = NyxdClient::connect_with_mnemonic( - config.nyxd_config.clone(), - config.nyxd_url.as_str(), - mnemonic, - None, - )?; + ) -> Result { + let rpc_client = HttpRpcClient::new(config.nyxd_url.as_str())?; + let prefix = &config.nyxd_config.chain_details.bech32_account_prefix; + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); - Ok(Client { - mixnode_page_limit: config.mixnode_page_limit, - gateway_page_limit: config.gateway_page_limit, - mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, - rewarded_set_page_limit: config.rewarded_set_page_limit, - nym_api: nym_api_client, - nyxd: nyxd_client, - }) + Ok(Self::new_signing_with_rpc_client( + config, rpc_client, wallet, + )) } pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { self.nyxd.change_endpoint(new_endpoint.as_ref())?; Ok(()) } +} - pub fn set_nyxd_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.nyxd.set_simulated_gas_multiplier(multiplier) +impl Client { + pub fn new_reqwest_signing( + config: Config, + mnemonic: bip39::Mnemonic, + ) -> DirectSigningReqwestRpcValidatorClient { + let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone()); + let prefix = &config.nyxd_config.chain_details.bech32_account_prefix; + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); + + Self::new_signing_with_rpc_client(config, rpc_client, wallet) } } #[cfg(feature = "http-client")] -impl Client { - pub fn new_query(config: Config) -> Result, ValidatorClientError> { - let nym_api_client = nym_api::Client::new(config.api_url.clone()); - let nyxd_client = - NyxdClient::connect(config.nyxd_config.clone(), config.nyxd_url.as_str())?; - - Ok(Client { - mixnode_page_limit: config.mixnode_page_limit, - gateway_page_limit: config.gateway_page_limit, - mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, - rewarded_set_page_limit: config.rewarded_set_page_limit, - nym_api: nym_api_client, - nyxd: nyxd_client, - }) +impl Client { + pub fn new_query(config: Config) -> Result { + let rpc_client = HttpRpcClient::new(config.nyxd_url.as_str())?; + Ok(Self::new_with_rpc_client(config, rpc_client)) } pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { @@ -184,390 +136,40 @@ impl Client { } } -// nyxd wrappers +impl Client { + pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient { + let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone()); + Self::new_with_rpc_client(config, rpc_client) + } +} + impl Client { - // use case: somebody initialised client without a contract in order to upload and initialise one - // and now they want to actually use it without making new client + pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self { + let nym_api_client = nym_api::Client::new(config.api_url.clone()); - pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) { - self.nyxd - .set_mixnet_contract_address(mixnet_contract_address) - } - - pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId { - self.nyxd.mixnet_contract_address().clone() - } - - pub async fn get_all_node_families(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut families = Vec::new(); - let mut start_after = None; - - loop { - let paged_response = self - .nyxd - .get_all_node_families_paged(start_after.take(), None) - .await?; - families.extend(paged_response.families); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } + Client { + nym_api: nym_api_client, + nyxd: NyxdClient::new(config.nyxd_config, rpc_client), } - - Ok(families) } +} - pub async fn get_all_family_members( - &self, - ) -> Result, ValidatorClientError> +impl Client { + pub fn new_signing_with_rpc_client(config: Config, rpc_client: C, signer: S) -> Self where - C: CosmWasmClient + Sync + Send, + S: OfflineSigner, { - let mut members = Vec::new(); - let mut start_after = None; + let nym_api_client = nym_api::Client::new(config.api_url.clone()); - loop { - let paged_response = self - .nyxd - .get_all_family_members_paged(start_after.take(), None) - .await?; - members.extend(paged_response.members); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } + Client { + nym_api: nym_api_client, + nyxd: NyxdClient::new_signing(config.nyxd_config, rpc_client, signer), } - - Ok(members) - } - - // basically handles paging for us - pub async fn get_all_nyxd_rewarded_set_mixnodes( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut identities = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - identities.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(identities) - } - - pub async fn get_all_nyxd_mixnode_bonds(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_mixnodes_detailed( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes_by_owner( - &self, - owner: &cosmrs::AccountId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes_by_identity( - &self, - identity_key: String, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_by_identity_paged( - identity_key.clone(), - self.mixnode_page_limit, - start_after.take(), - ) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_gateways(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut gateways = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_gateways_paged(start_after.take(), self.gateway_page_limit) - .await?; - gateways.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(gateways) - } - - pub async fn get_all_nyxd_single_mixnode_delegations( - &self, - mix_id: MixId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnode_delegations_paged( - mix_id, - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_delegator_delegations( - &self, - delegation_owner: &cosmrs::AccountId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_delegator_delegations_paged( - delegation_owner.to_string(), - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_network_delegations(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_all_network_delegations_paged( - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_nyxd_pending_epoch_events( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut events = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_pending_epoch_events_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - events.append(&mut paged_response.events); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(events) - } - - pub async fn get_all_nyxd_pending_interval_events( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut events = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_pending_interval_events_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - events.append(&mut paged_response.events); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(events) } } // validator-api wrappers -impl Client { +impl Client { pub fn change_nym_api(&mut self, new_endpoint: Url) { self.nym_api.change_url(new_endpoint) } @@ -624,85 +226,42 @@ impl Client { } } -#[derive(Clone)] -pub struct CoconutApiClient { - pub api_client: NymApiClient, - pub verification_key: VerificationKey, - pub node_id: NodeIndex, - pub cosmos_address: cosmrs::AccountId, -} - -impl CoconutApiClient { - pub async fn all_coconut_api_clients( - client: &C, - epoch_id: EpochId, - ) -> Result, ValidatorClientError> - where - C: DkgQueryClient + Sync + Send, - { - Ok(client - .get_all_verification_key_shares(epoch_id) - .await? - .into_iter() - .filter_map(Self::try_from) - .collect()) - } - - fn try_from(share: ContractVKShare) -> Option { - if share.verified { - if let Ok(url_address) = Url::parse(&share.announce_address) { - if let Ok(verification_key) = VerificationKey::try_from_bs58(&share.share) { - if let Ok(cosmos_address) = cosmrs::AccountId::from_str(share.owner.as_str()) { - return Some(CoconutApiClient { - api_client: NymApiClient::new(url_address), - verification_key, - node_id: share.node_index, - cosmos_address, - }); - } - } - } - } - None - } -} - #[derive(Clone)] pub struct NymApiClient { - pub nym_api_client: nym_api::Client, + pub nym_api: nym_api::Client, // TODO: perhaps if we really need it at some (currently I don't see any reasons for it) // we could re-implement the communication with the REST API on port 1317 } impl NymApiClient { pub fn new(api_url: Url) -> Self { - let nym_api_client = nym_api::Client::new(api_url); + let nym_api = nym_api::Client::new(api_url); - NymApiClient { nym_api_client } + NymApiClient { nym_api } } pub fn change_nym_api(&mut self, new_endpoint: Url) { - self.nym_api_client.change_url(new_endpoint); + self.nym_api.change_url(new_endpoint); } pub async fn get_cached_active_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_active_mixnodes().await?) + Ok(self.nym_api.get_active_mixnodes().await?) } pub async fn get_cached_rewarded_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_rewarded_mixnodes().await?) + Ok(self.nym_api.get_rewarded_mixnodes().await?) } pub async fn get_cached_mixnodes(&self) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_mixnodes().await?) + Ok(self.nym_api.get_mixnodes().await?) } pub async fn get_cached_gateways(&self) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_gateways().await?) + Ok(self.nym_api.get_gateways().await?) } pub async fn get_gateway_core_status_count( @@ -711,7 +270,7 @@ impl NymApiClient { since: Option, ) -> Result { Ok(self - .nym_api_client + .nym_api .get_gateway_core_status_count(identity, since) .await?) } @@ -722,7 +281,7 @@ impl NymApiClient { since: Option, ) -> Result { Ok(self - .nym_api_client + .nym_api .get_mixnode_core_status_count(mix_id, since) .await?) } @@ -731,34 +290,28 @@ impl NymApiClient { &self, mix_id: MixId, ) -> Result { - Ok(self.nym_api_client.get_mixnode_status(mix_id).await?) + Ok(self.nym_api.get_mixnode_status(mix_id).await?) } pub async fn get_mixnode_reward_estimation( &self, mix_id: MixId, ) -> Result { - Ok(self - .nym_api_client - .get_mixnode_reward_estimation(mix_id) - .await?) + Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?) } pub async fn get_mixnode_stake_saturation( &self, mix_id: MixId, ) -> Result { - Ok(self - .nym_api_client - .get_mixnode_stake_saturation(mix_id) - .await?) + Ok(self.nym_api.get_mixnode_stake_saturation(mix_id).await?) } pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, ) -> Result { - Ok(self.nym_api_client.blind_sign(request_body).await?) + Ok(self.nym_api.blind_sign(request_body).await?) } pub async fn verify_bandwidth_credential( @@ -766,7 +319,7 @@ impl NymApiClient { request_body: &VerifyCredentialBody, ) -> Result { Ok(self - .nym_api_client + .nym_api .verify_bandwidth_credential(request_body) .await?) } diff --git a/common/client-libs/validator-client/src/coconut/mod.rs b/common/client-libs/validator-client/src/coconut/mod.rs new file mode 100644 index 0000000000..97ac95cad7 --- /dev/null +++ b/common/client-libs/validator-client/src/coconut/mod.rs @@ -0,0 +1,97 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::{DkgQueryClient, PagedDkgQueryClient}; +use crate::nyxd::error::NyxdError; +use crate::NymApiClient; +use nym_coconut_dkg_common::types::{EpochId, NodeIndex}; +use nym_coconut_dkg_common::verification_key::ContractVKShare; +use nym_coconut_interface::{Base58, CoconutError, VerificationKey}; +use thiserror::Error; +use url::Url; + +// TODO: it really doesn't feel like this should live in this crate. +#[derive(Clone)] +pub struct CoconutApiClient { + pub api_client: NymApiClient, + pub verification_key: VerificationKey, + pub node_id: NodeIndex, + pub cosmos_address: cosmrs::AccountId, +} + +// TODO: this should be using the coconut error +// (which is in different crate; perhaps this client should be moved there?) + +#[derive(Debug, Error)] +pub enum CoconutApiError { + // TODO: ask @BN whether this is a correct error message + #[error("the provided key share hasn't been verified")] + UnverifiedShare, + + #[error("failed to query the contract: {source}")] + ContractQueryFailure { + #[from] + source: NyxdError, + }, + + #[error("the provided announce address is malformed: {source}")] + MalformedAnnounceAddress { + #[from] + source: url::ParseError, + }, + + #[error("the provided verification key is malformed: {source}")] + MalformedVerificationKey { + #[from] + source: CoconutError, + }, + + #[error("the provided account address is malformed: {source}")] + MalformedAccountAddress { + #[from] + source: cosmrs::ErrorReport, + }, +} + +impl TryFrom for CoconutApiClient { + type Error = CoconutApiError; + + fn try_from(share: ContractVKShare) -> Result { + if !share.verified { + return Err(CoconutApiError::UnverifiedShare); + } + + let url_address = Url::parse(&share.announce_address)?; + + Ok(CoconutApiClient { + api_client: NymApiClient::new(url_address), + verification_key: VerificationKey::try_from_bs58(&share.share)?, + node_id: share.node_index, + cosmos_address: share.owner.as_str().parse()?, + }) + } +} + +pub async fn all_coconut_api_clients( + client: &C, + epoch_id: EpochId, +) -> Result, CoconutApiError> +where + C: DkgQueryClient + Sync + Send, +{ + // TODO: this will error out if there's an invalid share out there. is that what we want? + client + .get_all_verification_key_shares(epoch_id) + .await? + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() + + // ... if not, let's switch to the below: + // client + // .get_all_verification_key_shares(epoch_id) + // .await? + // .into_iter() + // .filter_map(TryInto::try_into) + // .collect::, _>>() +} diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index 800399d415..15d2efe250 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,8 +1,7 @@ +use crate::nyxd::contract_traits::MixnetQueryClient; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient}; -use crate::{NymApiClient, ValidatorClientError}; - -use crate::nyxd::traits::MixnetQueryClient; +use crate::nyxd::Config as ClientConfig; +use crate::{NymApiClient, QueryHttpRpcNyxdClient, ValidatorClientError}; use colored::Colorize; use core::fmt; use itertools::Itertools; @@ -53,7 +52,7 @@ pub async fn test_nyxd_url_connection( let config = ClientConfig::try_from_nym_network_details(&network) .expect("failed to create valid nyxd client config"); - let mut nyxd_client = NyxdClient::::connect(config, nyxd_url.as_str())?; + let mut nyxd_client = QueryHttpRpcNyxdClient::connect(config, nyxd_url.as_str())?; // possibly redundant, but lets just leave it here nyxd_client.set_mixnet_contract_address(address); match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await { @@ -75,7 +74,7 @@ fn setup_connection_tests( let config = ClientConfig::try_from_nym_network_details(&network) .expect("failed to create valid nyxd client config"); - if let Ok(mut client) = NyxdClient::::connect(config, url.as_str()) { + if let Ok(mut client) = QueryHttpRpcNyxdClient::connect(config, url.as_str()) { // possibly redundant, but lets just leave it here client.set_mixnet_contract_address(address); Some(ClientForConnectionTest::Nyxd( @@ -112,7 +111,7 @@ fn extract_and_collect_results_into_map( async fn test_nyxd_connection( network: NymNetworkDetails, url: &Url, - client: &NyxdClient, + client: &QueryHttpRpcNyxdClient, ) -> ConnectionResult { let result = match timeout( Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC), @@ -186,7 +185,7 @@ async fn test_nym_api_connection( } enum ClientForConnectionTest { - Nyxd(NymNetworkDetails, Url, Box>), + Nyxd(NymNetworkDetails, Url, Box), Api(NymNetworkDetails, Url, NymApiClient), } diff --git a/common/client-libs/validator-client/src/error.rs b/common/client-libs/validator-client/src/error.rs index a675a27717..d439a86848 100644 --- a/common/client-libs/validator-client/src/error.rs +++ b/common/client-libs/validator-client/src/error.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nym_api; +pub use tendermint_rpc::error::Error as TendermintRpcError; use thiserror::Error; #[derive(Error, Debug)] @@ -12,6 +13,9 @@ pub enum ValidatorClientError { source: nym_api::error::NymAPIError, }, + #[error("Tendermint RPC request failure: {0}")] + TendermintErrorRpc(#[from] TendermintRpcError), + #[error("One of the provided URLs was malformed - {0}")] MalformedUrlProvided(#[from] url::ParseError), diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index ab0cff646e..c6391673a0 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -1,18 +1,44 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 pub mod client; +pub mod coconut; #[cfg(feature = "http-client")] pub mod connection_tester; pub mod error; pub mod nym_api; pub mod nyxd; - -#[cfg(feature = "signing")] +pub mod rpc; pub mod signing; pub use crate::error::ValidatorClientError; +pub use crate::rpc::reqwest::ReqwestRpcClient; +pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet; pub use client::NymApiClient; +pub use client::{Client, CoconutApiClient, Config}; pub use nym_api_requests::*; -pub use client::{Client, CoconutApiClient, Config}; +#[cfg(feature = "http-client")] +pub use cosmrs::rpc::HttpClient as HttpRpcClient; + +// some type aliasing + +pub type ValidatorClient = Client; +pub type SigningValidatorClient = Client; + +#[cfg(feature = "http-client")] +pub type QueryHttpRpcValidatorClient = Client; +#[cfg(feature = "http-client")] +pub type QueryHttpRpcNyxdClient = nyxd::NyxdClient; + +#[cfg(feature = "http-client")] +pub type DirectSigningHttpRpcValidatorClient = Client; +#[cfg(feature = "http-client")] +pub type DirectSigningHttpRpcNyxdClient = nyxd::NyxdClient; + +pub type QueryReqwestRpcValidatorClient = Client; +pub type QueryReqwestRpcNyxdClient = nyxd::NyxdClient; + +pub type DirectSigningReqwestRpcValidatorClient = Client; +pub type DirectSigningReqwestRpcNyxdClient = + nyxd::NyxdClient; diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs new file mode 100644 index 0000000000..1737edf613 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs @@ -0,0 +1,100 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use nym_coconut_bandwidth_contract_common::msg::QueryMsg as CoconutBandwidthQueryMsg; +use nym_coconut_bandwidth_contract_common::spend_credential::{ + PagedSpendCredentialResponse, SpendCredential, SpendCredentialResponse, +}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait CoconutBandwidthQueryClient { + async fn query_coconut_bandwidth_contract( + &self, + query: CoconutBandwidthQueryMsg, + ) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_spent_credential( + &self, + blinded_serial_number: String, + ) -> Result { + self.query_coconut_bandwidth_contract(CoconutBandwidthQueryMsg::GetSpentCredential { + blinded_serial_number, + }) + .await + } + + async fn get_all_spent_credential_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_coconut_bandwidth_contract(CoconutBandwidthQueryMsg::GetAllSpentCredentials { + limit, + start_after, + }) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedCoconutBandwidthQueryClient: CoconutBandwidthQueryClient { + async fn get_all_spent_credentials(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_spent_credential_paged, spend_credentials) + } +} + +#[async_trait] +impl PagedCoconutBandwidthQueryClient for T where T: CoconutBandwidthQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl CoconutBandwidthQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_coconut_bandwidth_contract( + &self, + query: CoconutBandwidthQueryMsg, + ) -> Result + where + for<'a> T: Deserialize<'a>, + { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + self.query_contract_smart(coconut_bandwidth_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: CoconutBandwidthQueryMsg, + ) { + match msg { + CoconutBandwidthQueryMsg::GetSpentCredential { + blinded_serial_number, + } => client.get_spent_credential(blinded_serial_number).ignore(), + CoconutBandwidthQueryMsg::GetAllSpentCredentials { limit, start_after } => client + .get_all_spent_credential_paged(start_after, limit) + .ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_signing_client.rs new file mode 100644 index 0000000000..030d78b29a --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_signing_client.rs @@ -0,0 +1,153 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialData; +use nym_coconut_bandwidth_contract_common::{ + deposit::DepositData, msg::ExecuteMsg as CoconutBandwidthExecuteMsg, +}; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait CoconutBandwidthSigningClient { + async fn execute_coconut_bandwidth_contract( + &self, + fee: Option, + msg: CoconutBandwidthExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn deposit( + &self, + amount: Coin, + info: String, + verification_key: String, + encryption_key: String, + fee: Option, + ) -> Result { + let req = CoconutBandwidthExecuteMsg::DepositFunds { + data: DepositData::new(info.to_string(), verification_key, encryption_key), + }; + self.execute_coconut_bandwidth_contract( + fee, + req, + "CoconutBandwidth::Deposit".to_string(), + vec![amount], + ) + .await + } + + async fn spend_credential( + &self, + funds: Coin, + blinded_serial_number: String, + gateway_cosmos_address: String, + fee: Option, + ) -> Result { + let req = CoconutBandwidthExecuteMsg::SpendCredential { + data: SpendCredentialData::new( + funds.into(), + blinded_serial_number, + gateway_cosmos_address, + ), + }; + self.execute_coconut_bandwidth_contract( + fee, + req, + "CoconutBandwidth::SpendCredential".to_string(), + vec![], + ) + .await + } + + async fn release_funds( + &self, + amount: Coin, + fee: Option, + ) -> Result { + self.execute_coconut_bandwidth_contract( + fee, + CoconutBandwidthExecuteMsg::ReleaseFunds { + funds: amount.into(), + }, + "CoconutBandwidth::ReleaseFunds".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl CoconutBandwidthSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_coconut_bandwidth_contract( + &self, + fee: Option, + msg: CoconutBandwidthExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + let signer_address = &self.signer_addresses()?[0]; + + self.execute( + signer_address, + coconut_bandwidth_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: CoconutBandwidthExecuteMsg, + ) { + match msg { + CoconutBandwidthExecuteMsg::DepositFunds { data } => client + .deposit( + mock_coin(), + data.deposit_info().to_string(), + data.identity_key().to_string(), + data.encryption_key().to_string(), + None, + ) + .ignore(), + CoconutBandwidthExecuteMsg::SpendCredential { data } => client + .spend_credential( + mock_coin(), + data.blinded_serial_number().to_string(), + data.gateway_cosmos_address().to_string(), + None, + ) + .ignore(), + CoconutBandwidthExecuteMsg::ReleaseFunds { funds } => { + client.release_funds(funds.into(), None).ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs new file mode 100644 index 0000000000..d3ac4807e8 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs @@ -0,0 +1,182 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_coconut_dkg_common::dealer::{ + ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse, +}; +use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; +use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData}; +use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait DkgQueryClient { + async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_current_epoch(&self) -> Result { + let request = DkgQueryMsg::GetCurrentEpochState {}; + self.query_dkg_contract(request).await + } + async fn get_current_epoch_threshold(&self) -> Result, NyxdError> { + let request = DkgQueryMsg::GetCurrentEpochThreshold {}; + self.query_dkg_contract(request).await + } + + async fn get_initial_dealers(&self) -> Result, NyxdError> { + let request = DkgQueryMsg::GetInitialDealers {}; + self.query_dkg_contract(request).await + } + + async fn get_dealer_details( + &self, + address: &AccountId, + ) -> Result { + let request = DkgQueryMsg::GetDealerDetails { + dealer_address: address.to_string(), + }; + self.query_dkg_contract(request).await + } + + async fn get_current_dealers_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetCurrentDealers { start_after, limit }; + self.query_dkg_contract(request).await + } + + async fn get_past_dealers_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetPastDealers { start_after, limit }; + self.query_dkg_contract(request).await + } + + async fn get_dealings_paged( + &self, + idx: u64, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetDealing { + idx, + limit, + start_after, + }; + self.query_dkg_contract(request).await + } + + async fn get_vk_shares_paged( + &self, + epoch_id: EpochId, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetVerificationKeys { + epoch_id, + limit, + start_after, + }; + self.query_dkg_contract(request).await + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedDkgQueryClient: DkgQueryClient { + async fn get_all_current_dealers(&self) -> Result, NyxdError> { + collect_paged!(self, get_current_dealers_paged, dealers) + } + + async fn get_all_past_dealers(&self) -> Result, NyxdError> { + collect_paged!(self, get_past_dealers_paged, dealers) + } + + async fn get_all_epoch_dealings(&self, idx: u64) -> Result, NyxdError> { + collect_paged!(self, get_dealings_paged, dealings, idx) + } + + async fn get_all_verification_key_shares( + &self, + epoch_id: EpochId, + ) -> Result, NyxdError> { + collect_paged!(self, get_vk_shares_paged, shares, epoch_id) + } +} + +#[async_trait] +impl PagedDkgQueryClient for T where T: DkgQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl DkgQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let dkg_contract_address = &self + .dkg_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?; + self.query_contract_smart(dkg_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: DkgQueryMsg, + ) { + match msg { + DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(), + DkgQueryMsg::GetCurrentEpochThreshold {} => { + client.get_current_epoch_threshold().ignore() + } + DkgQueryMsg::GetInitialDealers {} => client.get_initial_dealers().ignore(), + DkgQueryMsg::GetDealerDetails { dealer_address } => client + .get_dealer_details(&dealer_address.parse().unwrap()) + .ignore(), + DkgQueryMsg::GetCurrentDealers { limit, start_after } => client + .get_current_dealers_paged(start_after, limit) + .ignore(), + DkgQueryMsg::GetPastDealers { limit, start_after } => { + client.get_past_dealers_paged(start_after, limit).ignore() + } + DkgQueryMsg::GetDealing { + idx, + limit, + start_after, + } => client.get_dealings_paged(idx, start_after, limit).ignore(), + DkgQueryMsg::GetVerificationKeys { + epoch_id, + limit, + start_after, + } => client + .get_vk_shares_paged(epoch_id, start_after, limit) + .ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_signing_client.rs new file mode 100644 index 0000000000..73bce467b7 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_signing_client.rs @@ -0,0 +1,176 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use cosmrs::AccountId; +use cosmwasm_std::Addr; +use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; +use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof; +use nym_coconut_dkg_common::verification_key::VerificationKeyShare; +use nym_contracts_common::dealings::ContractSafeBytes; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait DkgSigningClient { + async fn execute_dkg_contract( + &self, + fee: Option, + msg: DkgExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn advance_dkg_epoch_state(&self, fee: Option) -> Result { + let req = DkgExecuteMsg::AdvanceEpochState {}; + + self.execute_dkg_contract(fee, req, "advancing DKG state".to_string(), vec![]) + .await + } + + async fn surpass_threshold(&self, fee: Option) -> Result { + let req = DkgExecuteMsg::SurpassedThreshold {}; + + self.execute_dkg_contract(fee, req, "surpass DKG threshold".to_string(), vec![]) + .await + } + + async fn register_dealer( + &self, + bte_key: EncodedBTEPublicKeyWithProof, + announce_address: String, + resharing: bool, + fee: Option, + ) -> Result { + let req = DkgExecuteMsg::RegisterDealer { + bte_key_with_proof: bte_key, + announce_address, + resharing, + }; + + self.execute_dkg_contract(fee, req, "registering as a dealer".to_string(), vec![]) + .await + } + + async fn submit_dealing_bytes( + &self, + dealing_bytes: ContractSafeBytes, + resharing: bool, + fee: Option, + ) -> Result { + let req = DkgExecuteMsg::CommitDealing { + dealing_bytes, + resharing, + }; + + self.execute_dkg_contract(fee, req, "dealing commitment".to_string(), vec![]) + .await + } + + async fn submit_verification_key_share( + &self, + share: VerificationKeyShare, + resharing: bool, + fee: Option, + ) -> Result { + let req = DkgExecuteMsg::CommitVerificationKeyShare { share, resharing }; + + self.execute_dkg_contract( + fee, + req, + "verification key share commitment".to_string(), + vec![], + ) + .await + } + + async fn verify_verification_key_share( + &self, + owner: &AccountId, + resharing: bool, + fee: Option, + ) -> Result { + // the call to unchecked is fine as we're converting from pre-validated `AccountId` + let owner = Addr::unchecked(owner.to_string()); + let req = DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing }; + + self.execute_dkg_contract( + fee, + req, + "verification key VerifyVerificationKeyShare".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl DkgSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_dkg_contract( + &self, + fee: Option, + msg: DkgExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let dkg_contract_address = self + .dkg_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + let signer_address = &self.signer_addresses()?[0]; + + self.execute(signer_address, dkg_contract_address, &msg, fee, memo, funds) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: DkgExecuteMsg, + ) { + match msg { + DkgExecuteMsg::RegisterDealer { + bte_key_with_proof, + announce_address, + resharing, + } => client + .register_dealer(bte_key_with_proof, announce_address, resharing, None) + .ignore(), + DkgExecuteMsg::CommitDealing { + dealing_bytes, + resharing, + } => client + .submit_dealing_bytes(dealing_bytes, resharing, None) + .ignore(), + DkgExecuteMsg::CommitVerificationKeyShare { share, resharing } => client + .submit_verification_key_share(share, resharing, None) + .ignore(), + DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => client + .verify_verification_key_share( + &owner.into_string().parse().unwrap(), + resharing, + None, + ) + .ignore(), + DkgExecuteMsg::SurpassedThreshold {} => client.surpass_threshold(None).ignore(), + DkgExecuteMsg::AdvanceEpochState {} => client.advance_dkg_epoch_state(None).ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/group_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/group_query_client.rs new file mode 100644 index 0000000000..8513dfa5bb --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/group_query_client.rs @@ -0,0 +1,118 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cw4::{Member, MemberListResponse, MemberResponse, TotalWeightResponse}; +use nym_group_contract_common::msg::QueryMsg as GroupQueryMsg; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait GroupQueryClient { + async fn query_group_contract(&self, query: GroupQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn admin(&self) -> Result { + self.query_group_contract(GroupQueryMsg::Admin {}).await + } + + async fn total_weight(&self, at_height: Option) -> Result { + self.query_group_contract(GroupQueryMsg::TotalWeight { at_height }) + .await + } + + async fn list_members_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_group_contract(GroupQueryMsg::ListMembers { start_after, limit }) + .await + } + + async fn member( + &self, + addr: String, + at_height: Option, + ) -> Result { + self.query_group_contract(GroupQueryMsg::Member { addr, at_height }) + .await + } + + async fn hooks(&self) -> Result { + self.query_group_contract(GroupQueryMsg::Hooks {}).await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedGroupQueryClient: GroupQueryClient { + // can't use the macro due to different paging behaviour + async fn get_all_members(&self) -> Result, NyxdError> { + let mut members = Vec::new(); + let mut start_after = None; + + loop { + let mut paged_response = self.list_members_paged(start_after.take(), None).await?; + + let last_id = paged_response.members.last().map(|mem| mem.addr.clone()); + members.append(&mut paged_response.members); + + if let Some(start_after_res) = last_id { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(members) + } +} + +#[async_trait] +impl PagedGroupQueryClient for T where T: GroupQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GroupQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_group_contract(&self, query: GroupQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let group_contract_address = &self + .group_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("group contract"))?; + self.query_contract_smart(group_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: GroupQueryMsg, + ) { + match msg { + GroupQueryMsg::Admin {} => client.admin().ignore(), + GroupQueryMsg::TotalWeight { at_height } => client.total_weight(at_height).ignore(), + GroupQueryMsg::ListMembers { start_after, limit } => { + client.list_members_paged(start_after, limit).ignore() + } + GroupQueryMsg::Member { addr, at_height } => client.member(addr, at_height).ignore(), + GroupQueryMsg::Hooks {} => client.hooks().ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/group_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/group_signing_client.rs new file mode 100644 index 0000000000..5ac1954849 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/group_signing_client.rs @@ -0,0 +1,131 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use cw4::Member; +use nym_group_contract_common::msg::ExecuteMsg as GroupExecuteMsg; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait GroupSigningClient { + async fn execute_group_contract( + &self, + fee: Option, + msg: GroupExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn update_admin( + &self, + admin: Option, + fee: Option, + ) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::UpdateAdmin { admin }, + "GroupExecuteMsg::UpdateAdmin".to_string(), + vec![], + ) + .await + } + + async fn update_members( + &self, + add: Vec, + remove: Vec, + fee: Option, + ) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::UpdateMembers { add, remove }, + "GroupExecuteMsg::UpdateMembers".to_string(), + vec![], + ) + .await + } + + async fn add_hook(&self, addr: String, fee: Option) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::AddHook { addr }, + "GroupExecuteMsg::AddHook".to_string(), + vec![], + ) + .await + } + + async fn remove_hook( + &self, + addr: String, + fee: Option, + ) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::RemoveHook { addr }, + "GroupExecuteMsg::RemoveHook".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GroupSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_group_contract( + &self, + fee: Option, + msg: GroupExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let group_contract_address = self + .group_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("group contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + group_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: GroupExecuteMsg, + ) { + match msg { + GroupExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(), + GroupExecuteMsg::UpdateMembers { remove, add } => { + client.update_members(add, remove, None).ignore() + } + GroupExecuteMsg::AddHook { addr } => client.add_hook(addr, None).ignore(), + GroupExecuteMsg::RemoveHook { addr } => client.remove_hook(addr, None).ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs new file mode 100644 index 0000000000..95d692d592 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs @@ -0,0 +1,745 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::signing::Nonce; +use nym_mixnet_contract_common::{ + delegation, + delegation::{MixNodeDelegationResponse, OwnerProxySubKey}, + families::{Family, FamilyHead}, + mixnode::{ + MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse, + PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse, + }, + reward_params::{Performance, RewardingParams}, + rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse}, + ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse, + Delegation, EpochEventId, EpochStatus, FamilyByHeadResponse, FamilyByLabelResponse, + FamilyMembersByHeadResponse, FamilyMembersByLabelResponse, GatewayBond, GatewayBondResponse, + GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, LayerDistribution, + MixId, MixNodeBond, MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, + MixnodeDetailsResponse, NumberOfPendingEventsResponse, PagedAllDelegationsResponse, + PagedDelegatorDelegationsResponse, PagedFamiliesResponse, PagedGatewayResponse, + PagedMembersResponse, PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, + PagedRewardedSetResponse, PendingEpochEvent, PendingEpochEventResponse, + PendingEpochEventsResponse, PendingIntervalEvent, PendingIntervalEventResponse, + PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg, RewardedSetNodeStatus, + UnbondedMixnode, +}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait MixnetQueryClient { + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + // state/sys-params-related + + async fn get_mixnet_contract_version(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {}) + .await + } + + async fn get_mixnet_contract_cw2_version(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetCW2ContractVersion {}) + .await + } + + async fn get_rewarding_validator_address(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {}) + .await + } + + async fn get_mixnet_contract_settings(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) + .await + } + + async fn get_mixnet_contract_state_params(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) + .await + } + + async fn get_mixnet_contract_state(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetState {}) + .await + } + + async fn get_rewarding_parameters(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {}) + .await + } + + async fn get_current_epoch_status(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {}) + .await + } + + async fn get_current_interval_details(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {}) + .await + } + + async fn get_rewarded_set_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after }) + .await + } + + async fn get_all_node_families_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after }) + .await + } + + async fn get_all_family_members_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after }) + .await + } + + async fn get_family_members_by_head + Send>( + &self, + head: S, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByHead { head: head.into() }) + .await + } + + async fn get_family_members_by_label + Send>( + &self, + label: S, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByLabel { + label: label.into(), + }) + .await + } + + // mixnode-related: + + async fn get_mixnode_bonds_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after }) + .await + } + + async fn get_mixnodes_detailed_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after }) + .await + } + + async fn get_unbonded_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after }) + .await + } + + async fn get_unbonded_by_owner_paged( + &self, + owner: &AccountId, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner { + owner: owner.to_string(), + limit, + start_after, + }) + .await + } + + async fn get_unbonded_by_identity_paged( + &self, + identity_key: IdentityKeyRef<'_>, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { + identity_key: identity_key.to_string(), + limit, + start_after, + }) + .await + } + + async fn get_owned_mixnode( + &self, + address: &AccountId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode { + address: address.to_string(), + }) + .await + } + + async fn get_mixnode_details( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id }) + .await + } + + async fn get_mixnode_details_by_identity( + &self, + mix_identity: IdentityKey, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { + mix_identity, + }) + .await + } + + async fn get_mixnode_rewarding_details( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id }) + .await + } + + async fn get_mixnode_stake_saturation( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id }) + .await + } + + async fn get_unbonded_mixnode_information( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id }) + .await + } + + async fn get_layer_distribution(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {}) + .await + } + + // gateway-related: + + async fn get_gateways_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit }) + .await + } + + /// Checks whether there is a bonded gateway associated with the provided identity key + async fn get_gateway_bond( + &self, + identity: IdentityKey, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity }) + .await + } + + /// Checks whether there is a bonded gateway associated with the provided client's address + async fn get_owned_gateway( + &self, + address: &AccountId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway { + address: address.to_string(), + }) + .await + } + + // delegation-related: + + /// Gets list of all delegations towards particular mixnode on particular page. + async fn get_mixnode_delegations_paged( + &self, + mix_id: MixId, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations { + mix_id, + start_after, + limit, + }) + .await + } + + /// Gets list of all the mixnodes to which a particular address delegated. + async fn get_delegator_delegations_paged( + &self, + delegator: &AccountId, + start_after: Option<(MixId, OwnerProxySubKey)>, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations { + delegator: delegator.to_string(), + start_after, + limit, + }) + .await + } + + /// Checks value of delegation of given client towards particular mixnode. + async fn get_delegation_details( + &self, + mix_id: MixId, + delegator: &AccountId, + proxy: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails { + mix_id, + delegator: delegator.to_string(), + proxy, + }) + .await + } + + /// Gets all the delegations on the entire network + async fn get_all_network_delegations_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit }) + .await + } + + // rewards related + async fn get_pending_operator_reward( + &self, + operator: &AccountId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward { + address: operator.to_string(), + }) + .await + } + + async fn get_pending_mixnode_operator_reward( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id }) + .await + } + + async fn get_pending_delegator_reward( + &self, + delegator: &AccountId, + mix_id: MixId, + proxy: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward { + address: delegator.to_string(), + mix_id, + proxy, + }) + .await + } + + // given the provided performance, estimate the reward at the end of the current epoch + async fn get_estimated_current_epoch_operator_reward( + &self, + mix_id: MixId, + estimated_performance: Performance, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { + mix_id, + estimated_performance, + }) + .await + } + + // given the provided performance, estimate the reward at the end of the current epoch + async fn get_estimated_current_epoch_delegator_reward( + &self, + delegator: &AccountId, + mix_id: MixId, + proxy: Option, + estimated_performance: Performance, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { + address: delegator.to_string(), + mix_id, + proxy, + estimated_performance, + }) + .await + } + + // interval-related + + async fn get_pending_epoch_events_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit }) + .await + } + + async fn get_pending_interval_events_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit }) + .await + } + + async fn get_pending_epoch_event( + &self, + event_id: EpochEventId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id }) + .await + } + + async fn get_pending_interval_event( + &self, + event_id: IntervalEventId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id }) + .await + } + + async fn get_number_of_pending_events( + &self, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {}) + .await + } + + async fn get_signing_nonce(&self, address: &AccountId) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce { + address: address.to_string(), + }) + .await + } + + async fn get_node_family_by_label( + &self, + label: String, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { label }) + .await + } + + async fn get_node_family_by_head( + &self, + head: String, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { head }) + .await + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedMixnetQueryClient: MixnetQueryClient { + async fn get_all_node_families(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_node_families_paged, families) + } + + async fn get_all_family_members(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_family_members_paged, members) + } + + async fn get_all_rewarded_set_mixnodes( + &self, + ) -> Result, NyxdError> { + collect_paged!(self, get_rewarded_set_paged, nodes) + } + + async fn get_all_mixnode_bonds(&self) -> Result, NyxdError> { + collect_paged!(self, get_mixnode_bonds_paged, nodes) + } + + async fn get_all_mixnodes_detailed(&self) -> Result, NyxdError> { + collect_paged!(self, get_mixnodes_detailed_paged, nodes) + } + + async fn get_all_unbonded_mixnodes(&self) -> Result, NyxdError> { + collect_paged!(self, get_unbonded_paged, nodes) + } + + async fn get_all_unbonded_mixnodes_by_owner( + &self, + owner: &AccountId, + ) -> Result, NyxdError> { + collect_paged!(self, get_unbonded_by_owner_paged, nodes, owner) + } + + async fn get_all_unbonded_mixnodes_by_identity( + &self, + identity_key: IdentityKeyRef<'_>, + ) -> Result, NyxdError> { + collect_paged!(self, get_unbonded_by_identity_paged, nodes, identity_key) + } + + async fn get_all_gateways(&self) -> Result, NyxdError> { + collect_paged!(self, get_gateways_paged, nodes) + } + + async fn get_all_single_mixnode_delegations( + &self, + mix_id: MixId, + ) -> Result, NyxdError> { + collect_paged!(self, get_mixnode_delegations_paged, delegations, mix_id) + } + + async fn get_all_delegator_delegations( + &self, + delegation_owner: &AccountId, + ) -> Result, NyxdError> { + collect_paged!( + self, + get_delegator_delegations_paged, + delegations, + delegation_owner + ) + } + + async fn get_all_network_delegations(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_network_delegations_paged, delegations) + } + + async fn get_all_pending_epoch_events(&self) -> Result, NyxdError> { + collect_paged!(self, get_pending_epoch_events_paged, events) + } + + async fn get_all_pending_interval_events( + &self, + ) -> Result, NyxdError> { + collect_paged!(self, get_pending_interval_events_paged, events) + } +} + +#[async_trait] +impl PagedMixnetQueryClient for T where T: MixnetQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MixnetQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let mixnet_contract_address = &self + .mixnet_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?; + self.query_contract_smart(mixnet_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: MixnetQueryMsg, + ) -> u32 { + match msg { + MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after } => client + .get_all_family_members_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetAllMembersPaged { limit, start_after } => client + .get_all_family_members_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetFamilyByHead { head } => { + client.get_node_family_by_head(head).ignore() + } + MixnetQueryMsg::GetFamilyByLabel { label } => { + client.get_node_family_by_label(label).ignore() + } + MixnetQueryMsg::GetFamilyMembersByHead { head } => { + client.get_family_members_by_head(head).ignore() + } + MixnetQueryMsg::GetFamilyMembersByLabel { label } => { + client.get_family_members_by_label(label).ignore() + } + MixnetQueryMsg::GetContractVersion {} => client.get_mixnet_contract_version().ignore(), + MixnetQueryMsg::GetCW2ContractVersion {} => { + client.get_mixnet_contract_cw2_version().ignore() + } + MixnetQueryMsg::GetRewardingValidatorAddress {} => { + client.get_rewarding_validator_address().ignore() + } + MixnetQueryMsg::GetStateParams {} => client.get_mixnet_contract_state_params().ignore(), + MixnetQueryMsg::GetState {} => client.get_mixnet_contract_state().ignore(), + MixnetQueryMsg::GetRewardingParams {} => client.get_rewarding_parameters().ignore(), + MixnetQueryMsg::GetEpochStatus {} => client.get_current_epoch_status().ignore(), + MixnetQueryMsg::GetCurrentIntervalDetails {} => { + client.get_current_interval_details().ignore() + } + MixnetQueryMsg::GetRewardedSet { limit, start_after } => { + client.get_rewarded_set_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetMixNodeBonds { limit, start_after } => { + client.get_mixnode_bonds_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetMixNodesDetailed { limit, start_after } => client + .get_mixnodes_detailed_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after } => { + client.get_unbonded_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetUnbondedMixNodesByOwner { + owner, + limit, + start_after, + } => client + .get_unbonded_by_owner_paged(&owner.parse().unwrap(), start_after, limit) + .ignore(), + MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { + identity_key, + limit, + start_after, + } => client + .get_unbonded_by_identity_paged(&identity_key, start_after, limit) + .ignore(), + MixnetQueryMsg::GetOwnedMixnode { address } => { + client.get_owned_mixnode(&address.parse().unwrap()).ignore() + } + MixnetQueryMsg::GetMixnodeDetails { mix_id } => { + client.get_mixnode_details(mix_id).ignore() + } + MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id } => { + client.get_mixnode_rewarding_details(mix_id).ignore() + } + MixnetQueryMsg::GetStakeSaturation { mix_id } => { + client.get_mixnode_stake_saturation(mix_id).ignore() + } + MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id } => { + client.get_unbonded_mixnode_information(mix_id).ignore() + } + MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => client + .get_mixnode_details_by_identity(mix_identity) + .ignore(), + MixnetQueryMsg::GetLayerDistribution {} => client.get_layer_distribution().ignore(), + MixnetQueryMsg::GetGateways { start_after, limit } => { + client.get_gateways_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetGatewayBond { identity } => { + client.get_gateway_bond(identity).ignore() + } + MixnetQueryMsg::GetOwnedGateway { address } => { + client.get_owned_gateway(&address.parse().unwrap()).ignore() + } + MixnetQueryMsg::GetMixnodeDelegations { + mix_id, + start_after, + limit, + } => client + .get_mixnode_delegations_paged(mix_id, start_after, limit) + .ignore(), + MixnetQueryMsg::GetDelegatorDelegations { + delegator, + start_after, + limit, + } => client + .get_delegator_delegations_paged(&delegator.parse().unwrap(), start_after, limit) + .ignore(), + MixnetQueryMsg::GetDelegationDetails { + mix_id, + delegator, + proxy, + } => client + .get_delegation_details(mix_id, &delegator.parse().unwrap(), proxy) + .ignore(), + MixnetQueryMsg::GetAllDelegations { start_after, limit } => client + .get_all_network_delegations_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetPendingOperatorReward { address } => client + .get_pending_operator_reward(&address.parse().unwrap()) + .ignore(), + MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id } => { + client.get_pending_mixnode_operator_reward(mix_id).ignore() + } + MixnetQueryMsg::GetPendingDelegatorReward { + address, + mix_id, + proxy, + } => client + .get_pending_delegator_reward(&address.parse().unwrap(), mix_id, proxy) + .ignore(), + MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { + mix_id, + estimated_performance, + } => client + .get_estimated_current_epoch_operator_reward(mix_id, estimated_performance) + .ignore(), + MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { + address, + mix_id, + proxy, + estimated_performance, + } => client + .get_estimated_current_epoch_delegator_reward( + &address.parse().unwrap(), + mix_id, + proxy, + estimated_performance, + ) + .ignore(), + MixnetQueryMsg::GetPendingEpochEvents { limit, start_after } => client + .get_pending_epoch_events_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetPendingIntervalEvents { limit, start_after } => client + .get_pending_interval_events_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetPendingEpochEvent { event_id } => { + client.get_pending_epoch_event(event_id).ignore() + } + MixnetQueryMsg::GetPendingIntervalEvent { event_id } => { + client.get_pending_interval_event(event_id).ignore() + } + MixnetQueryMsg::GetNumberOfPendingEvents {} => { + client.get_number_of_pending_events().ignore() + } + MixnetQueryMsg::GetSigningNonce { address } => { + client.get_signing_nonce(&address.parse().unwrap()).ignore() + } + } + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs similarity index 62% rename from common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs index 49fc651d5d..f8b5521e0d 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::coin::Coin; -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::cosmwasm_client::types::ExecuteResult; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; +use crate::nyxd::{Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::MessageSignature; @@ -14,10 +15,12 @@ use nym_mixnet_contract_common::gateway::GatewayConfigUpdate; use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance}; use nym_mixnet_contract_common::{ - ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, LayerAssignment, MixId, MixNode, + ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, Layer, LayerAssignment, MixId, + MixNode, }; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait MixnetSigningClient { async fn execute_mixnet_contract( &self, @@ -131,6 +134,20 @@ pub trait MixnetSigningClient { .await } + async fn assign_node_layer( + &self, + mix_id: MixId, + layer: Layer, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::AssignNodeLayer { mix_id, layer }, + vec![], + ) + .await + } + async fn reconcile_epoch_events( &self, limit: Option, @@ -665,12 +682,26 @@ pub trait MixnetSigningClient { ) .await } + + #[cfg(feature = "nym_mixnet_contract_common/contract-testing")] + async fn testing_resolve_all_pending_events( + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::TestingResolveAllPendingEvents {}, + vec![], + ) + .await + } } -#[async_trait] -impl MixnetSigningClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MixnetSigningClient for C where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, { async fn execute_mixnet_contract( &self, @@ -678,32 +709,229 @@ where msg: MixnetExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let mixnet_contract_address = &self + .mixnet_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.default_memo(); - self.client - .execute( - self.address(), - self.mixnet_contract_address(), - &msg, - fee, - memo, - funds, - ) - .await + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + mixnet_contract_address, + &msg, + fee, + memo, + funds, + ) + .await } } -#[async_trait] -impl MixnetSigningClient for crate::Client -where - C: SigningCosmWasmClient + Sync + Send + Clone, -{ - async fn execute_mixnet_contract( - &self, - fee: Option, +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, msg: MixnetExecuteMsg, - funds: Vec, - ) -> Result { - self.nyxd.execute_mixnet_contract(fee, msg, funds).await + ) { + match msg { + MixnetExecuteMsg::AssignNodeLayer { mix_id, layer } => { + client.assign_node_layer(mix_id, layer, None).ignore() + } + MixnetExecuteMsg::CreateFamily { label } => client.create_family(label, None).ignore(), + MixnetExecuteMsg::JoinFamily { + join_permit, + family_head, + } => client.join_family(join_permit, family_head, None).ignore(), + MixnetExecuteMsg::LeaveFamily { family_head } => { + client.leave_family(family_head, None).ignore() + } + MixnetExecuteMsg::KickFamilyMember { member } => { + client.kick_family_member(member, None).ignore() + } + MixnetExecuteMsg::CreateFamilyOnBehalf { + owner_address, + label, + } => client + .create_family_on_behalf(owner_address, label, None) + .ignore(), + MixnetExecuteMsg::JoinFamilyOnBehalf { + member_address, + join_permit, + family_head, + } => client + .join_family_on_behalf(member_address, join_permit, family_head, None) + .ignore(), + MixnetExecuteMsg::LeaveFamilyOnBehalf { + member_address, + family_head, + } => client + .leave_family_on_behalf(member_address, family_head, None) + .ignore(), + MixnetExecuteMsg::KickFamilyMemberOnBehalf { + head_address, + member, + } => client + .kick_family_member_on_behalf(head_address, member, None) + .ignore(), + MixnetExecuteMsg::UpdateRewardingValidatorAddress { address } => client + .update_rewarding_validator_address(address.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::UpdateContractStateParams { updated_parameters } => client + .update_contract_state_params(updated_parameters, None) + .ignore(), + MixnetExecuteMsg::UpdateActiveSetSize { + active_set_size, + force_immediately, + } => client + .update_active_set_size(active_set_size, force_immediately, None) + .ignore(), + MixnetExecuteMsg::UpdateRewardingParams { + updated_params, + force_immediately, + } => client + .update_rewarding_parameters(updated_params, force_immediately, None) + .ignore(), + MixnetExecuteMsg::UpdateIntervalConfig { + epochs_in_interval, + epoch_duration_secs, + force_immediately, + } => client + .update_interval_config( + epochs_in_interval, + epoch_duration_secs, + force_immediately, + None, + ) + .ignore(), + MixnetExecuteMsg::BeginEpochTransition {} => { + client.begin_epoch_transition(None).ignore() + } + MixnetExecuteMsg::AdvanceCurrentEpoch { + new_rewarded_set, + expected_active_set_size, + } => client + .advance_current_epoch(new_rewarded_set, expected_active_set_size, None) + .ignore(), + MixnetExecuteMsg::ReconcileEpochEvents { limit } => { + client.reconcile_epoch_events(limit, None).ignore() + } + MixnetExecuteMsg::BondMixnode { + mix_node, + cost_params, + owner_signature, + } => client + .bond_mixnode(mix_node, cost_params, owner_signature, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::BondMixnodeOnBehalf { + mix_node, + cost_params, + owner_signature, + owner, + } => client + .bond_mixnode_on_behalf( + owner.parse().unwrap(), + mix_node, + cost_params, + owner_signature, + mock_coin(), + None, + ) + .ignore(), + MixnetExecuteMsg::PledgeMore {} => client.pledge_more(mock_coin(), None).ignore(), + MixnetExecuteMsg::PledgeMoreOnBehalf { owner } => client + .pledge_more_on_behalf(owner.parse().unwrap(), mock_coin(), None) + .ignore(), + MixnetExecuteMsg::DecreasePledge { decrease_by } => { + client.decrease_pledge(decrease_by.into(), None).ignore() + } + MixnetExecuteMsg::DecreasePledgeOnBehalf { owner, decrease_by } => client + .decrease_pledge_on_behalf(owner.parse().unwrap(), decrease_by.into(), None) + .ignore(), + MixnetExecuteMsg::UnbondMixnode {} => client.unbond_mixnode(None).ignore(), + MixnetExecuteMsg::UnbondMixnodeOnBehalf { owner } => client + .unbond_mixnode_on_behalf(owner.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs } => { + client.update_mixnode_cost_params(new_costs, None).ignore() + } + MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => client + .update_mixnode_cost_params_on_behalf(owner.parse().unwrap(), new_costs, None) + .ignore(), + MixnetExecuteMsg::UpdateMixnodeConfig { new_config } => { + client.update_mixnode_config(new_config, None).ignore() + } + MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner } => client + .update_mixnode_config_on_behalf(owner.parse().unwrap(), new_config, None) + .ignore(), + MixnetExecuteMsg::BondGateway { + gateway, + owner_signature, + } => client + .bond_gateway(gateway, owner_signature, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::BondGatewayOnBehalf { + gateway, + owner, + owner_signature, + } => client + .bond_gateway_on_behalf( + owner.parse().unwrap(), + gateway, + owner_signature, + mock_coin(), + None, + ) + .ignore(), + MixnetExecuteMsg::UnbondGateway {} => client.unbond_gateway(None).ignore(), + MixnetExecuteMsg::UnbondGatewayOnBehalf { owner } => client + .unbond_gateway_on_behalf(owner.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::UpdateGatewayConfig { new_config } => { + client.update_gateway_config(new_config, None).ignore() + } + MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => client + .update_gateway_config_on_behalf(owner.parse().unwrap(), new_config, None) + .ignore(), + MixnetExecuteMsg::DelegateToMixnode { mix_id } => client + .delegate_to_mixnode(mix_id, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => client + .delegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::UndelegateFromMixnode { mix_id } => { + client.undelegate_from_mixnode(mix_id, None).ignore() + } + MixnetExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => client + .undelegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, None) + .ignore(), + MixnetExecuteMsg::RewardMixnode { + mix_id, + performance, + } => client.reward_mixnode(mix_id, performance, None).ignore(), + MixnetExecuteMsg::WithdrawOperatorReward {} => { + client.withdraw_operator_reward(None).ignore() + } + MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => client + .withdraw_operator_reward_on_behalf(owner.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::WithdrawDelegatorReward { mix_id } => { + client.withdraw_delegator_reward(mix_id, None).ignore() + } + MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client + .withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None) + .ignore(), + + #[cfg(feature = "nym_mixnet_contract_common/contract-testing")] + MixnetExecuteMsg::TestingResolveAllPendingEvents {} => { + client.testing_resolve_all_pending_events(None).ignore() + } + }; } } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs new file mode 100644 index 0000000000..545fdf05ec --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs @@ -0,0 +1,179 @@ +// Copyright 2021-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmrs::AccountId; +use nym_network_defaults::NymContracts; +use std::str::FromStr; + +// TODO: all of those could/should be derived via a macro + +// query clients +mod coconut_bandwidth_query_client; +mod dkg_query_client; +mod group_query_client; +mod mixnet_query_client; +mod multisig_query_client; +mod name_service_query_client; +mod sp_directory_query_client; +mod vesting_query_client; + +// signing clients +mod coconut_bandwidth_signing_client; +mod dkg_signing_client; +mod group_signing_client; +mod mixnet_signing_client; +mod multisig_signing_client; +mod name_service_signing_client; +mod sp_directory_signing_client; +mod vesting_signing_client; + +// re-export query traits +pub use coconut_bandwidth_query_client::{ + CoconutBandwidthQueryClient, PagedCoconutBandwidthQueryClient, +}; +pub use dkg_query_client::{DkgQueryClient, PagedDkgQueryClient}; +pub use group_query_client::{GroupQueryClient, PagedGroupQueryClient}; +pub use mixnet_query_client::{MixnetQueryClient, PagedMixnetQueryClient}; +pub use multisig_query_client::{MultisigQueryClient, PagedMultisigQueryClient}; +pub use name_service_query_client::{NameServiceQueryClient, PagedNameServiceQueryClient}; +pub use sp_directory_query_client::{PagedSpDirectoryQueryClient, SpDirectoryQueryClient}; +pub use vesting_query_client::{PagedVestingQueryClient, VestingQueryClient}; + +// re-export signing traits +pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; +pub use dkg_signing_client::DkgSigningClient; +pub use group_signing_client::GroupSigningClient; +pub use mixnet_signing_client::MixnetSigningClient; +pub use multisig_signing_client::MultisigSigningClient; +pub use name_service_signing_client::NameServiceSigningClient; +pub use sp_directory_signing_client::SpDirectorySigningClient; +pub use vesting_signing_client::VestingSigningClient; + +// helper for providing blanket implementation for query clients +pub trait NymContractsProvider { + // main + fn mixnet_contract_address(&self) -> Option<&AccountId>; + fn vesting_contract_address(&self) -> Option<&AccountId>; + + // coconut-related + fn coconut_bandwidth_contract_address(&self) -> Option<&AccountId>; + fn dkg_contract_address(&self) -> Option<&AccountId>; + fn group_contract_address(&self) -> Option<&AccountId>; + fn multisig_contract_address(&self) -> Option<&AccountId>; + + // SPs + fn name_service_contract_address(&self) -> Option<&AccountId>; + fn service_provider_contract_address(&self) -> Option<&AccountId>; +} + +#[derive(Debug, Clone)] +pub struct TypedNymContracts { + pub mixnet_contract_address: Option, + pub vesting_contract_address: Option, + + pub coconut_bandwidth_contract_address: Option, + pub group_contract_address: Option, + pub multisig_contract_address: Option, + pub coconut_dkg_contract_address: Option, + + pub service_provider_directory_contract_address: Option, + pub name_service_contract_address: Option, +} + +impl TryFrom for TypedNymContracts { + type Error = ::Err; + + fn try_from(value: NymContracts) -> Result { + Ok(TypedNymContracts { + mixnet_contract_address: value + .mixnet_contract_address + .map(|addr| addr.parse()) + .transpose()?, + vesting_contract_address: value + .vesting_contract_address + .map(|addr| addr.parse()) + .transpose()?, + coconut_bandwidth_contract_address: value + .coconut_bandwidth_contract_address + .map(|addr| addr.parse()) + .transpose()?, + group_contract_address: value + .group_contract_address + .map(|addr| addr.parse()) + .transpose()?, + multisig_contract_address: value + .multisig_contract_address + .map(|addr| addr.parse()) + .transpose()?, + coconut_dkg_contract_address: value + .coconut_dkg_contract_address + .map(|addr| addr.parse()) + .transpose()?, + service_provider_directory_contract_address: value + .service_provider_directory_contract_address + .map(|addr| addr.parse()) + .transpose()?, + name_service_contract_address: value + .name_service_contract_address + .map(|addr| addr.parse()) + .transpose()?, + }) + } +} + +// a simple helper macro to define to repeatedly call a paged query until a full response is constructed +#[macro_export] +macro_rules! collect_paged { + // TODO: deal with the args in a nicer way + ( $self:ident, $f: ident, $field: ident ) => {{ + let mut res = Vec::new(); + let mut start_after = None; + loop { + let paged_response = $self.$f(start_after.take(), None).await?; + res.extend(paged_response.$field); + + if let Some(start_next_after) = paged_response.start_next_after { + start_after = Some(start_next_after.into()) + } else { + break Ok(res); + } + } + }}; + + ( $self:ident, $f: ident, $field: ident, $($args:tt),*) => {{ + let mut res = Vec::new(); + let mut start_after = None; + loop { + let paged_response = $self.$f($($args),*, start_after.take(), None).await?; + res.extend(paged_response.$field); + + if let Some(start_next_after) = paged_response.start_next_after { + start_after = Some(start_next_after.into()) + } else { + break Ok(res); + } + } + }}; +} + +#[cfg(test)] +mod tests { + use crate::nyxd::Coin; + + pub(crate) trait IgnoreValue { + fn ignore(self) -> u32 + where + Self: Sized, + { + 42 + // reason we're returning a value as opposed to just `()` is that whenever we match on all enums + // we don't want to accidentally miss a variant because compiler will treat it the same way + } + } + + impl IgnoreValue for T {} + + pub(crate) fn mock_coin() -> Coin { + Coin::new(42, "ufoomp") + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_query_client.rs new file mode 100644 index 0000000000..48fcadc050 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_query_client.rs @@ -0,0 +1,178 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cw3::{ + ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterListResponse, + VoterResponse, +}; +use cw_utils::ThresholdResponse; +use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait MultisigQueryClient { + async fn query_multisig_contract(&self, query: MultisigQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn query_threshold(&self) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Threshold {}) + .await + } + + async fn query_proposal(&self, proposal_id: u64) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Proposal { proposal_id }) + .await + } + + async fn list_proposals( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListProposals { start_after, limit }) + .await + } + + async fn reverse_proposals( + &self, + start_before: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ReverseProposals { + start_before, + limit, + }) + .await + } + + async fn query_vote(&self, proposal_id: u64, voter: String) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Vote { proposal_id, voter }) + .await + } + + async fn list_votes( + &self, + proposal_id: u64, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListVotes { + proposal_id, + start_after, + limit, + }) + .await + } + + async fn query_voter(&self, address: String) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Voter { address }) + .await + } + + async fn list_voters( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListVoters { start_after, limit }) + .await + } + + async fn query_config(&self) -> Result<(), NyxdError> { + unimplemented!("requires exporting state::Config type") + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedMultisigQueryClient: MultisigQueryClient { + // can't use the macro due to different paging behaviour + async fn get_all_proposals(&self) -> Result, NyxdError> { + let mut proposals = Vec::new(); + let mut start_after = None; + + loop { + let mut paged_response = self.list_proposals(start_after.take(), None).await?; + + let last_id = paged_response.proposals.last().map(|prop| prop.id); + proposals.append(&mut paged_response.proposals); + + if let Some(start_after_res) = last_id { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(proposals) + } +} + +#[async_trait] +impl PagedMultisigQueryClient for T where T: MultisigQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MultisigQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_multisig_contract(&self, query: MultisigQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let multisig_contract_address = &self + .multisig_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("multisig contract"))?; + self.query_contract_smart(multisig_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: MultisigQueryMsg, + ) { + match msg { + MultisigQueryMsg::Threshold {} => client.query_threshold().ignore(), + MultisigQueryMsg::Proposal { proposal_id } => { + client.query_proposal(proposal_id).ignore() + } + MultisigQueryMsg::ListProposals { start_after, limit } => { + client.list_proposals(start_after, limit).ignore() + } + MultisigQueryMsg::ReverseProposals { + start_before, + limit, + } => client.reverse_proposals(start_before, limit).ignore(), + MultisigQueryMsg::Vote { proposal_id, voter } => { + client.query_vote(proposal_id, voter).ignore() + } + MultisigQueryMsg::ListVotes { + proposal_id, + start_after, + limit, + } => client.list_votes(proposal_id, start_after, limit).ignore(), + MultisigQueryMsg::Voter { address } => client.query_voter(address).ignore(), + MultisigQueryMsg::ListVoters { start_after, limit } => { + client.list_voters(start_after, limit).ignore() + } + MultisigQueryMsg::Config {} => client.query_config().ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs new file mode 100644 index 0000000000..f7249f02ea --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs @@ -0,0 +1,192 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use cosmwasm_std::{to_binary, CosmosMsg, WasmMsg}; +use cw3::Vote; +use cw4::{MemberChangedHookMsg, MemberDiff}; +use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; +use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait MultisigSigningClient: NymContractsProvider { + async fn execute_multisig_contract( + &self, + fee: Option, + msg: MultisigExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn propose_release_funds( + &self, + title: String, + blinded_serial_number: String, + voucher_value: Coin, + fee: Option, + ) -> Result { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + + let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { + funds: voucher_value.into(), + }; + let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: coconut_bandwidth_contract_address.to_string(), + msg: to_binary(&release_funds_req)?, + funds: vec![], + }); + let req = MultisigExecuteMsg::Propose { + title, + description: blinded_serial_number, + msgs: vec![release_funds_msg], + latest: None, + }; + self.execute_multisig_contract( + fee, + req, + "Multisig::Propose::Execute::ReleaseFunds".to_string(), + vec![], + ) + .await + } + + async fn vote_proposal( + &self, + proposal_id: u64, + vote_yes: bool, + fee: Option, + ) -> Result { + let vote = if vote_yes { Vote::Yes } else { Vote::No }; + let req = MultisigExecuteMsg::Vote { proposal_id, vote }; + self.execute_multisig_contract(fee, req, "Multisig::Vote".to_string(), vec![]) + .await + } + + // alternative variant to vote_proposal that lets you to abstain and veto a proposal + async fn vote( + &self, + proposal_id: u64, + vote: Vote, + fee: Option, + ) -> Result { + self.execute_multisig_contract( + fee, + MultisigExecuteMsg::Vote { proposal_id, vote }, + "Multisig::Vote".to_string(), + vec![], + ) + .await + } + + async fn execute_proposal( + &self, + proposal_id: u64, + fee: Option, + ) -> Result { + let req = MultisigExecuteMsg::Execute { proposal_id }; + self.execute_multisig_contract(fee, req, "Multisig::Execute".to_string(), vec![]) + .await + } + + async fn close_proposal( + &self, + proposal_id: u64, + fee: Option, + ) -> Result { + self.execute_multisig_contract( + fee, + MultisigExecuteMsg::Close { proposal_id }, + "Multisig::Close".to_string(), + vec![], + ) + .await + } + + async fn changed_member_hook( + &self, + member_diff: Vec, + fee: Option, + ) -> Result { + self.execute_multisig_contract( + fee, + MultisigExecuteMsg::MemberChangedHook(MemberChangedHookMsg::new(member_diff)), + "Multisig::MemberChangedHook".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MultisigSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_multisig_contract( + &self, + fee: Option, + msg: MultisigExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let multisig_contract_address = self + .multisig_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("multisig contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + multisig_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: MultisigExecuteMsg, + ) { + match msg { + MultisigExecuteMsg::Propose { + title, description, .. + } => client + .propose_release_funds(title, description, mock_coin(), None) + .ignore(), + MultisigExecuteMsg::Vote { proposal_id, vote } => { + client.vote(proposal_id, vote, None).ignore() + } + MultisigExecuteMsg::Execute { proposal_id } => { + client.execute_proposal(proposal_id, None).ignore() + } + MultisigExecuteMsg::Close { proposal_id } => { + client.close_proposal(proposal_id, None).ignore() + } + MultisigExecuteMsg::MemberChangedHook(hook_msg) => { + client.changed_member_hook(hook_msg.diffs, None).ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_query_client.rs new file mode 100644 index 0000000000..60071f674d --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_query_client.rs @@ -0,0 +1,144 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{error::NyxdError, CosmWasmClient}; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; +use nym_name_service_common::{ + msg::QueryMsg as NameQueryMsg, + response::{ConfigResponse, NamesListResponse, PagedNamesListResponse}, + Address, NameId, NymName, RegisteredName, +}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait NameServiceQueryClient { + async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_name_service_config(&self) -> Result { + self.query_name_service_contract(NameQueryMsg::Config {}) + .await + } + + async fn get_name_entry(&self, name_id: NameId) -> Result { + self.query_name_service_contract(NameQueryMsg::NameId { name_id }) + .await + } + + async fn get_names_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_name_service_contract(NameQueryMsg::All { limit, start_after }) + .await + } + + async fn get_name_signing_nonce(&self, address: &AccountId) -> Result { + self.query_name_service_contract(NameQueryMsg::SigningNonce { + address: address.to_string(), + }) + .await + } + + async fn get_names_by_owner(&self, owner: AccountId) -> Result { + self.query_name_service_contract(NameQueryMsg::ByOwner { + owner: owner.to_string(), + }) + .await + } + + async fn get_names_by_nym_name(&self, name: NymName) -> Result { + self.query_name_service_contract(NameQueryMsg::ByName { name }) + .await + } + + async fn get_names_by_address(&self, address: Address) -> Result { + self.query_name_service_contract(NameQueryMsg::ByAddress { address }) + .await + } + + async fn get_name_service_contract_version( + &self, + ) -> Result { + self.query_name_service_contract(NameQueryMsg::GetContractVersion {}) + .await + } + + async fn get_name_service_contract_cw2_version( + &self, + ) -> Result { + self.query_name_service_contract(NameQueryMsg::GetCW2ContractVersion {}) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedNameServiceQueryClient: NameServiceQueryClient { + async fn get_all_names(&self) -> Result, NyxdError> { + collect_paged!(self, get_names_paged, names) + } +} + +#[async_trait] +impl PagedNameServiceQueryClient for T where T: NameServiceQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl NameServiceQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let name_service_contract_address = &self + .name_service_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?; + self.query_contract_smart(name_service_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: NameQueryMsg, + ) { + match msg { + NameQueryMsg::NameId { name_id } => client.get_name_entry(name_id).ignore(), + NameQueryMsg::ByOwner { owner } => { + client.get_names_by_owner(owner.parse().unwrap()).ignore() + } + NameQueryMsg::ByName { name } => client.get_names_by_nym_name(name).ignore(), + NameQueryMsg::ByAddress { address } => client.get_names_by_address(address).ignore(), + NameQueryMsg::All { limit, start_after } => { + client.get_names_paged(limit, start_after).ignore() + } + NameQueryMsg::SigningNonce { address } => client + .get_name_signing_nonce(&address.parse().unwrap()) + .ignore(), + NameQueryMsg::Config {} => client.get_name_service_config().ignore(), + NameQueryMsg::GetContractVersion {} => { + client.get_name_service_contract_version().ignore() + } + NameQueryMsg::GetCW2ContractVersion {} => { + client.get_name_service_contract_cw2_version().ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/name_service_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs similarity index 52% rename from common/client-libs/validator-client/src/nyxd/traits/name_service_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs index 1235043094..0a363f991b 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/name_service_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs @@ -5,12 +5,14 @@ use async_trait::async_trait; use nym_contracts_common::signing::MessageSignature; use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, NameDetails, NameId, NymName}; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::{ - coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient, - SigningCosmWasmClient, + coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient, }; +use crate::signing::signer::OfflineSigner; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait NameServiceSigningClient { async fn execute_name_service_contract( &self, @@ -71,10 +73,12 @@ pub trait NameServiceSigningClient { } } -#[async_trait] -impl NameServiceSigningClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl NameServiceSigningClient for C where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, { async fn execute_name_service_contract( &self, @@ -82,19 +86,53 @@ where msg: NameExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let name_service_contract_address = &self + .name_service_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.default_memo(); - self.client - .execute( - self.address(), - self.name_service_contract_address().ok_or( - NyxdError::NoContractAddressAvailable("name service contract".to_string()), - )?, - &msg, - fee, - memo, - funds, - ) - .await + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + name_service_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: NameExecuteMsg, + ) { + match msg { + NameExecuteMsg::Register { + name, + owner_signature, + } => client + .register_name(name, owner_signature, mock_coin(), None) + .ignore(), + NameExecuteMsg::DeleteId { name_id } => { + client.delete_name_by_id(name_id, None).ignore() + } + NameExecuteMsg::DeleteName { name } => { + client.delete_service_provider_by_name(name, None).ignore() + } + NameExecuteMsg::UpdateDepositRequired { deposit_required } => client + .update_deposit_required(deposit_required.into(), None) + .ignore(), + }; } } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_query_client.rs new file mode 100644 index 0000000000..09279c543a --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_query_client.rs @@ -0,0 +1,142 @@ +use crate::collect_paged; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; +use nym_service_provider_directory_common::{ + msg::QueryMsg as SpQueryMsg, + response::{ + ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse, + }, + NymAddress, Service, ServiceId, +}; +use serde::Deserialize; + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{error::NyxdError, CosmWasmClient}; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait SpDirectoryQueryClient { + async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_service_config(&self) -> Result { + self.query_service_provider_contract(SpQueryMsg::Config {}) + .await + } + + async fn get_service_info( + &self, + service_id: ServiceId, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id }) + .await + } + + async fn get_services_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::All { limit, start_after }) + .await + } + + async fn get_services_by_announcer( + &self, + announcer: AccountId, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::ByAnnouncer { + announcer: announcer.to_string(), + }) + .await + } + + async fn get_services_by_nym_address( + &self, + nym_address: NymAddress, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address }) + .await + } + + async fn get_sp_contract_version(&self) -> Result { + self.query_service_provider_contract(SpQueryMsg::GetContractVersion {}) + .await + } + + async fn get_sp_contract_cw2_version(&self) -> Result { + self.query_service_provider_contract(SpQueryMsg::GetCW2ContractVersion {}) + .await + } + + async fn get_service_signing_nonce(&self, address: &AccountId) -> Result { + self.query_service_provider_contract(SpQueryMsg::SigningNonce { + address: address.to_string(), + }) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedSpDirectoryQueryClient: SpDirectoryQueryClient { + async fn get_all_services(&self) -> Result, NyxdError> { + collect_paged!(self, get_services_paged, services) + } +} + +#[async_trait] +impl PagedSpDirectoryQueryClient for T where T: SpDirectoryQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl SpDirectoryQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let sp_directory_contract_address = + &self.service_provider_contract_address().ok_or_else(|| { + NyxdError::unavailable_contract_address("service provider directory contract") + })?; + self.query_contract_smart(sp_directory_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: SpQueryMsg, + ) { + match msg { + SpQueryMsg::ServiceId { service_id } => client.get_service_info(service_id).ignore(), + SpQueryMsg::ByAnnouncer { announcer } => client + .get_services_by_announcer(announcer.parse().unwrap()) + .ignore(), + SpQueryMsg::ByNymAddress { nym_address } => { + client.get_services_by_nym_address(nym_address).ignore() + } + SpQueryMsg::All { limit, start_after } => { + client.get_services_paged(start_after, limit).ignore() + } + SpQueryMsg::SigningNonce { address } => client + .get_service_signing_nonce(&address.parse().unwrap()) + .ignore(), + SpQueryMsg::Config {} => client.get_service_config().ignore(), + SpQueryMsg::GetContractVersion {} => client.get_sp_contract_version().ignore(), + SpQueryMsg::GetCW2ContractVersion {} => client.get_sp_contract_cw2_version().ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs similarity index 54% rename from common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs index 31cf30839a..c1efde20f5 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs @@ -1,18 +1,19 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{ + coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient, +}; +use crate::signing::signer::OfflineSigner; use async_trait::async_trait; use nym_contracts_common::signing::MessageSignature; use nym_service_provider_directory_common::{ msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId, }; -use crate::nyxd::{ - coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient, - SigningCosmWasmClient, -}; - -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait SpDirectorySigningClient { async fn execute_service_provider_directory_contract( &self, @@ -81,10 +82,12 @@ pub trait SpDirectorySigningClient { } } -#[async_trait] -impl SpDirectorySigningClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl SpDirectorySigningClient for C where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, { async fn execute_service_provider_directory_contract( &self, @@ -92,21 +95,54 @@ where msg: SpExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let sp_directory_contract_address = + &self.service_provider_contract_address().ok_or_else(|| { + NyxdError::unavailable_contract_address("service provider directory contract") + })?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.default_memo(); - self.client - .execute( - self.address(), - self.service_provider_contract_address().ok_or( - NyxdError::NoContractAddressAvailable( - "service provider directory contract".to_string(), - ), - )?, - &msg, - fee, - memo, - funds, - ) - .await + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + sp_directory_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: SpExecuteMsg, + ) { + match msg { + SpExecuteMsg::Announce { + service, + owner_signature, + } => client + .announce_service_provider(service, owner_signature, mock_coin(), None) + .ignore(), + SpExecuteMsg::DeleteId { service_id } => client + .delete_service_provider_by_id(service_id, None) + .ignore(), + SpExecuteMsg::DeleteNymAddress { nym_address } => client + .delete_service_provider_by_nym_address(nym_address, None) + .ignore(), + SpExecuteMsg::UpdateDepositRequired { deposit_required } => client + .update_deposit_required(deposit_required.into(), None) + .ignore(), + }; } } diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs similarity index 58% rename from common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs index c70f0a78ca..fa6bd643fa 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs @@ -1,10 +1,11 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::collect_paged; use crate::nyxd::coin::Coin; -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; -use crate::nyxd::NyxdClient; +use crate::nyxd::CosmWasmClient; use async_trait::async_trait; use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp}; use nym_contracts_common::ContractBuildInformation; @@ -16,7 +17,8 @@ use nym_vesting_contract_common::{ }; use serde::Deserialize; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait VestingQueryClient { async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where @@ -27,6 +29,11 @@ pub trait VestingQueryClient { .await } + async fn get_vesting_contract_cw2_version(&self) -> Result { + self.query_vesting_contract(VestingQueryMsg::GetCW2ContractVersion {}) + .await + } + async fn get_all_accounts_paged( &self, start_next_after: Option, @@ -274,73 +281,161 @@ pub trait VestingQueryClient { self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit }) .await } +} +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedVestingQueryClient: VestingQueryClient { async fn get_all_vesting_delegations(&self) -> Result, NyxdError> { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_all_vesting_delegations_paged(start_after.take(), None) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) + collect_paged!(self, get_all_vesting_delegations_paged, delegations) } async fn get_all_accounts_info(&self) -> Result, NyxdError> { - let mut accounts = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_all_accounts_paged(start_after.take(), None) - .await?; - accounts.append(&mut paged_response.accounts); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(accounts) + collect_paged!(self, get_all_accounts_paged, accounts) } async fn get_all_accounts_vesting_coins(&self) -> Result, NyxdError> { - let mut accounts = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_all_accounts_vesting_coins_paged(start_after.take(), None) - .await?; - accounts.append(&mut paged_response.accounts); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(accounts) + collect_paged!(self, get_all_accounts_vesting_coins_paged, accounts) } } #[async_trait] -impl VestingQueryClient for NyxdClient { +impl PagedVestingQueryClient for T where T: VestingQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl VestingQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.client - .query_contract_smart(self.vesting_contract_address(), &query) + let vesting_contract_address = &self + .vesting_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?; + self.query_contract_smart(vesting_contract_address, &query) .await } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: VestingQueryMsg, + ) { + match msg { + VestingQueryMsg::GetContractVersion {} => { + client.get_vesting_contract_version().ignore() + } + VestingQueryMsg::GetCW2ContractVersion {} => { + client.get_vesting_contract_cw2_version().ignore() + } + VestingQueryMsg::GetAccountsPaged { + start_next_after, + limit, + } => client + .get_all_accounts_paged(start_next_after, limit) + .ignore(), + VestingQueryMsg::GetAccountsVestingCoinsPaged { + start_next_after, + limit, + } => client + .get_all_accounts_vesting_coins_paged(start_next_after, limit) + .ignore(), + VestingQueryMsg::LockedCoins { + vesting_account_address, + block_time, + } => client + .locked_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::SpendableCoins { + vesting_account_address, + block_time, + } => client + .spendable_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::GetVestedCoins { + vesting_account_address, + block_time, + } => client + .vested_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::GetVestingCoins { + vesting_account_address, + block_time, + } => client + .vesting_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::GetStartTime { + vesting_account_address, + } => client.vesting_start_time(&vesting_account_address).ignore(), + VestingQueryMsg::GetEndTime { + vesting_account_address, + } => client.vesting_end_time(&vesting_account_address).ignore(), + VestingQueryMsg::GetOriginalVesting { + vesting_account_address, + } => client.original_vesting(&vesting_account_address).ignore(), + VestingQueryMsg::GetHistoricalVestingStakingReward { + vesting_account_address, + } => client + .get_historical_vesting_staking_reward(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetSpendableVestedCoins { + vesting_account_address, + } => client + .get_spendable_vested_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetSpendableRewardCoins { + vesting_account_address, + } => client + .get_spendable_reward_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetDelegatedCoins { + vesting_account_address, + } => client + .get_delegated_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetPledgedCoins { + vesting_account_address, + } => client.get_pledged_coins(&vesting_account_address).ignore(), + VestingQueryMsg::GetStakedCoins { + vesting_account_address, + } => client.get_staked_coins(&vesting_account_address).ignore(), + VestingQueryMsg::GetWithdrawnCoins { + vesting_account_address, + } => client + .get_withdrawn_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetAccount { address } => client.get_account(&address).ignore(), + VestingQueryMsg::GetMixnode { address } => client.get_mixnode_pledge(&address).ignore(), + VestingQueryMsg::GetGateway { address } => client.get_gateway_pledge(&address).ignore(), + VestingQueryMsg::GetCurrentVestingPeriod { address } => { + client.get_current_vesting_period(&address).ignore() + } + VestingQueryMsg::GetDelegation { + address, + mix_id, + block_timestamp_secs, + } => client + .get_vesting_delegation(&address, mix_id, block_timestamp_secs) + .ignore(), + VestingQueryMsg::GetTotalDelegationAmount { address, mix_id } => client + .get_total_delegation_amount(&address, mix_id) + .ignore(), + VestingQueryMsg::GetDelegationTimes { address, mix_id } => { + client.get_delegation_timestamps(&address, mix_id).ignore() + } + VestingQueryMsg::GetAllDelegations { start_after, limit } => client + .get_all_vesting_delegations_paged(start_after, limit) + .ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs similarity index 52% rename from common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs index f1354566dd..11c449fccd 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs @@ -1,10 +1,11 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::cosmwasm_client::types::ExecuteResult; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Coin, Fee, NyxdClient}; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::MessageSignature; @@ -15,7 +16,8 @@ use nym_mixnet_contract_common::{Gateway, MixId, MixNode}; use nym_vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg; use nym_vesting_contract_common::{PledgeCap, VestingSpecification}; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait VestingSigningClient { async fn execute_vesting_contract( &self, @@ -28,25 +30,64 @@ pub trait VestingSigningClient { &self, new_costs: MixNodeCostParams, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateMixnodeCostParams { new_costs }, + vec![], + ) + .await + } async fn vesting_update_mixnode_config( &self, new_config: MixNodeConfigUpdate, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_update_gateway_config( &self, new_config: GatewayConfigUpdate, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateGatewayConfig { new_config }, + vec![], + ) + .await + } async fn update_mixnet_address( &self, address: &str, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::UpdateMixnetAddress { + address: address.to_string(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } + + async fn vesting_track_decrease_pledge( + &self, + owner: String, + amount: Coin, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TrackDecreasePledge { + owner, + amount: amount.into(), + }, + Vec::new(), + ) + .await + } async fn vesting_bond_gateway( &self, @@ -54,16 +95,31 @@ pub trait VestingSigningClient { owner_signature: MessageSignature, pledge: Coin, fee: Option, - ) -> Result; - - async fn vesting_unbond_gateway(&self, fee: Option) -> Result; + ) -> Result { + let req = VestingExecuteMsg::BondGateway { + gateway, + owner_signature, + amount: pledge.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } + async fn vesting_unbond_gateway(&self, fee: Option) -> Result { + let req = VestingExecuteMsg::UnbondGateway {}; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_track_unbond_gateway( &self, owner: &str, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::TrackUnbondGateway { + owner: owner.to_string(), + amount: amount.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_bond_mixnode( &self, @@ -72,7 +128,19 @@ pub trait VestingSigningClient { owner_signature: MessageSignature, pledge: Coin, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::BondMixnode { + mix_node, + cost_params, + owner_signature, + amount: pledge.into(), + }, + vec![], + ) + .await + } async fn vesting_pledge_more( &self, @@ -104,20 +172,34 @@ pub trait VestingSigningClient { .await } - async fn vesting_unbond_mixnode(&self, fee: Option) -> Result; + async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { + let req = VestingExecuteMsg::UnbondMixnode {}; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_track_unbond_mixnode( &self, owner: &str, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::TrackUnbondMixnode { + owner: owner.to_string(), + amount: amount.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn withdraw_vested_coins( &self, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::WithdrawVestedCoins { + amount: amount.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_track_undelegation( &self, @@ -125,7 +207,18 @@ pub trait VestingSigningClient { mix_id: MixId, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TrackUndelegation { + owner: address.to_string(), + mix_id, + amount: amount.into(), + }, + vec![], + ) + .await + } async fn vesting_delegate_to_mixnode( &self, @@ -133,14 +226,35 @@ pub trait VestingSigningClient { amount: Coin, on_behalf_of: Option, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::DelegateToMixnode { + mix_id, + amount: amount.into(), + on_behalf_of, + }, + vec![], + ) + .await + } async fn vesting_undelegate_from_mixnode( &self, mix_id: MixId, on_behalf_of: Option, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UndelegateFromMixnode { + mix_id, + on_behalf_of, + }, + vec![], + ) + .await + } async fn create_periodic_vesting_account( &self, @@ -150,7 +264,32 @@ pub trait VestingSigningClient { amount: Coin, cap: Option, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::CreateAccount { + owner_address: owner_address.to_string(), + staking_address, + vesting_spec, + cap, + }; + self.execute_vesting_contract(fee, req, vec![amount]).await + } + + async fn vesting_track_reward( + &self, + amount: Coin, + address: String, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TrackReward { + amount: amount.into(), + address, + }, + Vec::new(), + ) + .await + } async fn vesting_withdraw_operator_reward( &self, @@ -173,6 +312,32 @@ pub trait VestingSigningClient { .await } + async fn vesting_transfer_ownership( + &self, + to_address: String, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TransferOwnership { to_address }, + Vec::new(), + ) + .await + } + + async fn update_staking_address( + &self, + to_address: Option, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateStakingAddress { to_address }, + Vec::new(), + ) + .await + } + async fn update_locked_pledge_cap( &self, address: AccountId, @@ -235,317 +400,166 @@ pub trait VestingSigningClient { } } -#[async_trait] -impl VestingSigningClient for NyxdClient { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl VestingSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ async fn execute_vesting_contract( &self, fee: Option, msg: VestingExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let vesting_contract_address = &self + .vesting_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.name().to_string(); - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &msg, - fee, - memo, - funds, - ) - .await - } - async fn vesting_update_mixnode_cost_params( - &self, - new_costs: MixNodeCostParams, - fee: Option, - ) -> Result { - self.execute_vesting_contract( + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + vesting_contract_address, + &msg, fee, - VestingExecuteMsg::UpdateMixnodeCostParams { new_costs }, - vec![], + memo, + funds, ) .await } +} - async fn vesting_update_mixnode_config( - &self, - new_config: MixNodeConfigUpdate, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UpdateMixnetConfig", - vec![], - ) - .await - } +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; - async fn vesting_update_gateway_config( - &self, - new_config: GatewayConfigUpdate, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::UpdateGatewayConfig { new_config }, - vec![], - ) - .await - } - - async fn update_mixnet_address( - &self, - address: &str, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UpdateMixnetAddress { - address: address.to_string(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UpdateMixnetAddress", - vec![], - ) - .await - } - - async fn vesting_bond_gateway( - &self, - gateway: Gateway, - owner_signature: MessageSignature, - pledge: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::BondGateway { - gateway, - owner_signature, - amount: pledge.into(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::BondGateway", - vec![], - ) - .await - } - - async fn vesting_unbond_gateway(&self, fee: Option) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UnbondGateway {}; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UnbondGateway", - vec![], - ) - .await - } - - async fn vesting_track_unbond_gateway( - &self, - owner: &str, - amount: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::TrackUnbondGateway { - owner: owner.to_string(), - amount: amount.into(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::TrackUnbondGateway", - vec![], - ) - .await - } - - async fn vesting_bond_mixnode( - &self, - mix_node: MixNode, - cost_params: MixNodeCostParams, - owner_signature: MessageSignature, - pledge: Coin, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: VestingExecuteMsg, + ) { + match msg { + VestingExecuteMsg::CreateFamily { label } => { + client.vesting_create_family(label, None).ignore() + } + VestingExecuteMsg::JoinFamily { + join_permit, + family_head, + } => client + .vesting_join_family(join_permit, family_head, None) + .ignore(), + VestingExecuteMsg::LeaveFamily { family_head } => { + client.vesting_leave_family(family_head, None).ignore() + } + VestingExecuteMsg::KickFamilyMember { member } => { + client.vesting_kick_family_member(member, None).ignore() + } + VestingExecuteMsg::TrackReward { amount, address } => client + .vesting_track_reward(amount.into(), address, None) + .ignore(), + VestingExecuteMsg::ClaimOperatorReward {} => { + client.vesting_withdraw_operator_reward(None).ignore() + } + VestingExecuteMsg::ClaimDelegatorReward { mix_id } => client + .vesting_withdraw_delegator_reward(mix_id, None) + .ignore(), + VestingExecuteMsg::UpdateMixnodeCostParams { new_costs } => client + .vesting_update_mixnode_cost_params(new_costs, None) + .ignore(), + VestingExecuteMsg::UpdateMixnodeConfig { new_config } => client + .vesting_update_mixnode_config(new_config, None) + .ignore(), + VestingExecuteMsg::UpdateMixnetAddress { address } => { + client.update_mixnet_address(&address, None).ignore() + } + VestingExecuteMsg::DelegateToMixnode { + mix_id, + amount, + on_behalf_of, + } => client + .vesting_delegate_to_mixnode(mix_id, amount.into(), on_behalf_of, None) + .ignore(), + VestingExecuteMsg::UndelegateFromMixnode { + mix_id, + on_behalf_of, + } => client + .vesting_undelegate_from_mixnode(mix_id, on_behalf_of, None) + .ignore(), + VestingExecuteMsg::CreateAccount { + owner_address, + staking_address, + vesting_spec, + cap, + } => client + .create_periodic_vesting_account( + &owner_address, + staking_address, + vesting_spec, + mock_coin(), + cap, + None, + ) + .ignore(), + VestingExecuteMsg::WithdrawVestedCoins { amount } => { + client.withdraw_vested_coins(amount.into(), None).ignore() + } + VestingExecuteMsg::TrackUndelegation { + owner, + mix_id, + amount, + } => client + .vesting_track_undelegation(&owner, mix_id, amount.into(), None) + .ignore(), VestingExecuteMsg::BondMixnode { mix_node, cost_params, owner_signature, - amount: pledge.into(), - }, - vec![], - ) - .await - } - - async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UnbondMixnode {}; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UnbondMixnode", - vec![], - ) - .await - } - - async fn vesting_track_unbond_mixnode( - &self, - owner: &str, - amount: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::TrackUnbondMixnode { - owner: owner.to_string(), - amount: amount.into(), + amount, + } => client + .vesting_bond_mixnode(mix_node, cost_params, owner_signature, amount.into(), None) + .ignore(), + VestingExecuteMsg::PledgeMore { amount } => { + client.vesting_pledge_more(amount.into(), None).ignore() + } + VestingExecuteMsg::DecreasePledge { amount } => { + client.vesting_decrease_pledge(amount.into(), None).ignore() + } + VestingExecuteMsg::UnbondMixnode {} => client.vesting_unbond_mixnode(None).ignore(), + VestingExecuteMsg::TrackUnbondMixnode { owner, amount } => client + .vesting_track_unbond_mixnode(&owner, amount.into(), None) + .ignore(), + VestingExecuteMsg::TrackDecreasePledge { owner, amount } => client + .vesting_track_decrease_pledge(owner, amount.into(), None) + .ignore(), + VestingExecuteMsg::BondGateway { + gateway, + owner_signature, + amount, + } => client + .vesting_bond_gateway(gateway, owner_signature, amount.into(), None) + .ignore(), + VestingExecuteMsg::UnbondGateway {} => client.vesting_unbond_gateway(None).ignore(), + VestingExecuteMsg::TrackUnbondGateway { owner, amount } => client + .vesting_track_unbond_gateway(&owner, amount.into(), None) + .ignore(), + VestingExecuteMsg::UpdateGatewayConfig { new_config } => client + .vesting_update_gateway_config(new_config, None) + .ignore(), + VestingExecuteMsg::TransferOwnership { to_address } => { + client.vesting_transfer_ownership(to_address, None).ignore() + } + VestingExecuteMsg::UpdateStakingAddress { to_address } => { + client.update_staking_address(to_address, None).ignore() + } + VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client + .update_locked_pledge_cap(address.parse().unwrap(), cap, None) + .ignore(), }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::TrackUnbondMixnode", - vec![], - ) - .await - } - - async fn withdraw_vested_coins( - &self, - amount: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::WithdrawVestedCoins { - amount: amount.into(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::WithdrawVested", - vec![], - ) - .await - } - - async fn vesting_track_undelegation( - &self, - address: &str, - mix_id: MixId, - amount: Coin, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::TrackUndelegation { - owner: address.to_string(), - mix_id, - amount: amount.into(), - }, - vec![], - ) - .await - } - - async fn vesting_delegate_to_mixnode( - &self, - mix_id: MixId, - amount: Coin, - on_behalf_of: Option, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::DelegateToMixnode { - mix_id, - amount: amount.into(), - on_behalf_of, - }, - vec![], - ) - .await - } - - async fn vesting_undelegate_from_mixnode( - &self, - mix_id: MixId, - on_behalf_of: Option, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::UndelegateFromMixnode { - mix_id, - on_behalf_of, - }, - vec![], - ) - .await - } - - async fn create_periodic_vesting_account( - &self, - owner_address: &str, - staking_address: Option, - vesting_spec: Option, - amount: Coin, - cap: Option, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::CreateAccount { - owner_address: owner_address.to_string(), - staking_address, - vesting_spec, - cap, - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::CreatePeriodicVestingAccount", - vec![amount], - ) - .await } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs new file mode 100644 index 0000000000..bfa126ba37 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs @@ -0,0 +1,8 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod query_client; +pub mod signing_client; + +pub use query_client::CosmWasmClient; +pub use signing_client::SigningCosmWasmClient; diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs similarity index 91% rename from common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs index 9ca497e1c8..69e94b3387 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs @@ -1,4 +1,4 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::nyxd; @@ -8,7 +8,7 @@ use crate::nyxd::cosmwasm_client::types::{ Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse, }; use crate::nyxd::error::NyxdError; -use crate::nyxd::TendermintClient; +use crate::rpc::TendermintRpcClient; use async_trait::async_trait; use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry}; use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; @@ -39,25 +39,26 @@ use tendermint_rpc::{ Order, }; +#[cfg(not(target_arch = "wasm32"))] +use tokio::time::sleep; +#[cfg(not(target_arch = "wasm32"))] +use tokio::time::Instant; + +#[cfg(target_arch = "wasm32")] +use wasmtimer::std::Instant; +#[cfg(target_arch = "wasm32")] +use wasmtimer::tokio::sleep; + +pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); +pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); + #[cfg(feature = "http-client")] #[async_trait] -impl CosmWasmClient for cosmrs::rpc::HttpClient { - fn broadcast_polling_rate(&self) -> Duration { - Duration::from_secs(4) - } - - fn broadcast_timeout(&self) -> Duration { - Duration::from_secs(60) - } -} - -#[async_trait] -pub trait CosmWasmClient: TendermintClient { - // this should probably get redesigned, but I'm leaving those like that temporarily to fix - // the underlying issue more quickly - fn broadcast_polling_rate(&self) -> Duration; - fn broadcast_timeout(&self) -> Duration; +impl CosmWasmClient for cosmrs::rpc::HttpClient {} +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait CosmWasmClient: TendermintRpcClient { // helper method to remove duplicate code involved in making abci requests with protobuf messages // TODO: perhaps it should have an additional argument to determine whether the response should // require proof? @@ -246,7 +247,7 @@ pub trait CosmWasmClient: TendermintClient { where T: Into> + Send, { - Ok(tendermint_rpc::client::Client::broadcast_tx_async(self, tx).await?) + Ok(TendermintRpcClient::broadcast_tx_async(self, tx).await?) } /// Broadcast a transaction, returning the response from `CheckTx`. @@ -254,7 +255,7 @@ pub trait CosmWasmClient: TendermintClient { where T: Into> + Send, { - Ok(tendermint_rpc::client::Client::broadcast_tx_sync(self, tx).await?) + Ok(TendermintRpcClient::broadcast_tx_sync(self, tx).await?) } /// Broadcast a transaction, returning the response from `DeliverTx`. @@ -265,13 +266,23 @@ pub trait CosmWasmClient: TendermintClient { where T: Into> + Send, { - Ok(tendermint_rpc::client::Client::broadcast_tx_commit(self, tx).await?) + Ok(TendermintRpcClient::broadcast_tx_commit(self, tx).await?) } - async fn broadcast_tx(&self, tx: T) -> Result + async fn broadcast_tx( + &self, + tx: T, + timeout: impl Into> + Send, + poll_interval: impl Into> + Send, + ) -> Result where T: Into> + Send, { + let timeout = timeout.into().unwrap_or(DEFAULT_BROADCAST_TIMEOUT); + let poll_interval = poll_interval + .into() + .unwrap_or(DEFAULT_BROADCAST_POLLING_RATE); + let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?; if broadcasted.code.is_err() { @@ -286,16 +297,16 @@ pub trait CosmWasmClient: TendermintClient { let tx_hash = broadcasted.hash; - let start = tokio::time::Instant::now(); + let start = Instant::now(); loop { log::debug!( "Polling for result of including {} in a block...", broadcasted.hash ); - if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() { + if Instant::now().duration_since(start) >= timeout { return Err(NyxdError::BroadcastTimeout { hash: tx_hash, - timeout: self.broadcast_timeout(), + timeout, }); } @@ -303,7 +314,7 @@ pub trait CosmWasmClient: TendermintClient { return Ok(poll_res); } - tokio::time::sleep(self.broadcast_polling_rate()).await; + sleep(poll_interval).await; } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs similarity index 85% rename from common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs index f4d6a41829..5b3deadb68 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs @@ -1,7 +1,7 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::cosmwasm_client::client_traits::CosmWasmClient; use crate::nyxd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; use crate::nyxd::cosmwasm_client::logs::{self, parse_raw_logs}; use crate::nyxd::cosmwasm_client::types::*; @@ -9,6 +9,7 @@ use crate::nyxd::error::NyxdError; use crate::nyxd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; use crate::nyxd::{Coin, GasAdjustable, GasPrice, TxResponse}; use crate::signing::signer::OfflineSigner; +use crate::signing::tx_signer::TxSigner; use crate::signing::SignerData; use async_trait::async_trait; use cosmrs::abci::GasInfo; @@ -26,21 +27,9 @@ use serde::Serialize; use sha2::Digest; use sha2::Sha256; use std::convert::TryInto; -use std::time::{Duration, SystemTime}; +use std::time::SystemTime; use tendermint_rpc::endpoint::broadcast; -#[cfg(feature = "http-client")] -use crate::signing::tx_signer::TxSigner; - -#[cfg(feature = "http-client")] -use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest}; - -#[cfg(feature = "http-client")] -use cosmrs::rpc::{HttpClient, HttpClientUrl}; - -pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); -pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); - fn empty_fee() -> tx::Fee { tx::Fee { amount: vec![], @@ -64,18 +53,17 @@ fn single_unspecified_signer_auth( .auth_info(empty_fee()) } -#[async_trait] -pub trait SigningCosmWasmClient: CosmWasmClient { - type Signer: OfflineSigner + Send + Sync; - - fn signer(&self) -> &Self::Signer; - +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait SigningCosmWasmClient: CosmWasmClient + TxSigner +where + NyxdError: From<::Error>, +{ + // TODO: would it somehow be possible to get rid of this method and allow for + // blanket implementation for anything that provides CosmWasmClient + TxSigner? fn gas_price(&self) -> &GasPrice; - fn signer_public_key(&self, signer_address: &AccountId) -> Option { - let account = self.signer().find_account(signer_address).ok()?; - Some(account.public_key().into()) - } + fn simulated_gas_multiplier(&self) -> f32; async fn simulate( &self, @@ -587,9 +575,9 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER); let gas = gas_estimation.adjust_gas(multiplier); - debug!("Gas estimation: {}", gas_estimation); - debug!("Multiplying the estimation by {}", multiplier); - debug!("Final gas limit used: {}", gas); + debug!("Gas estimation: {gas_estimation}"); + debug!("Multiplying the estimation by {multiplier}"); + debug!("Final gas limit used: {gas}"); let fee = self.gas_price() * gas; Ok::(tx::Fee::from_amount_and_gas(fee, gas)) @@ -687,7 +675,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - self.broadcast_tx(tx_bytes).await + self.broadcast_tx(tx_bytes, None, None).await } async fn sign( @@ -710,161 +698,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { } }; - self.sign_direct(signer_address, messages, fee, memo, signer_data) - } - - fn sign_amino( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result; - - fn sign_direct( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result; -} - -#[cfg(feature = "http-client")] -#[derive(Debug)] -pub struct Client { - // TODO: somehow nicely hide this guy if we decide to use our client in offline mode, - // maybe just convert it into an option? - // or maybe we need another level of indirection. tbd. - rpc_client: HttpClient, - tx_signer: TxSigner, - gas_price: GasPrice, - - broadcast_polling_rate: Duration, - broadcast_timeout: Duration, -} - -#[cfg(feature = "http-client")] -impl Client { - pub fn connect_with_signer( - endpoint: U, - signer: S, - gas_price: GasPrice, - ) -> Result - where - U: TryInto, - { - let rpc_client = HttpClient::new(endpoint)?; - Ok(Client { - rpc_client, - tx_signer: TxSigner::new(signer), - gas_price, - broadcast_polling_rate: DEFAULT_BROADCAST_POLLING_RATE, - broadcast_timeout: DEFAULT_BROADCAST_TIMEOUT, - }) - } - - pub fn offline(signer: S) -> TxSigner - where - S: OfflineSigner, - { - TxSigner::new(signer) - } - - pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> - where - U: TryInto, - { - let new_rpc_client = HttpClient::new(new_endpoint)?; - self.rpc_client = new_rpc_client; - Ok(()) - } - - pub fn into_signer(self) -> S { - self.tx_signer.into_inner_signer() - } - - pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) { - self.broadcast_polling_rate = broadcast_polling_rate - } - - pub fn set_broadcast_timeout(&mut self, broadcast_timeout: Duration) { - self.broadcast_timeout = broadcast_timeout - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl tendermint_rpc::client::Client for Client -where - S: Send + Sync, -{ - async fn perform(&self, request: R) -> Result - where - R: SimpleRequest, - { - self.rpc_client.perform(request).await - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl CosmWasmClient for Client -where - S: Send + Sync, -{ - fn broadcast_polling_rate(&self) -> Duration { - self.broadcast_polling_rate - } - - fn broadcast_timeout(&self) -> Duration { - self.broadcast_timeout - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl SigningCosmWasmClient for Client -where - S: OfflineSigner + Send + Sync, - NyxdError: From, -{ - type Signer = S; - - fn signer(&self) -> &Self::Signer { - self.tx_signer.signer() - } - - fn gas_price(&self) -> &GasPrice { - &self.gas_price - } - - fn sign_amino( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result { - Ok(self - .tx_signer - .sign_amino(signer_address, messages, fee, memo, signer_data)?) - } - - fn sign_direct( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result { - Ok(self - .tx_signer - .sign_direct(signer_address, messages, fee, memo, signer_data)?) + Ok(::sign_direct( + self, + signer_address, + messages, + fee, + memo, + signer_data, + )?) } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs index 64c2fd25fa..b639b223e3 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs @@ -48,7 +48,6 @@ impl CheckResponse for crate::nyxd::TxResponse { } } -#[cfg(feature = "signing")] pub(crate) fn compress_wasm_code(code: &[u8]) -> Result, NyxdError> { use flate2::write::GzEncoder; use flate2::Compression; diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs index 0a2d2c7c32..7ebff2ef16 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs @@ -1,37 +1,158 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "http-client")] +use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; use crate::nyxd::error::NyxdError; -#[cfg(feature = "http-client")] -use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl}; -#[cfg(feature = "http-client")] -use std::convert::TryInto; +use crate::nyxd::{Config, GasPrice}; +use crate::rpc::TendermintRpcClient; +use crate::signing::{ + signer::{NoSigner, OfflineSigner}, + AccountData, +}; +use async_trait::async_trait; +use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest}; -pub mod client; +#[cfg(feature = "http-client")] +use cosmrs::rpc::{HttpClient, HttpClientUrl}; +use cosmrs::tx::{Raw, SignDoc}; + +pub mod client_traits; mod helpers; pub mod logs; pub mod types; -#[cfg(feature = "signing")] -pub mod signing_client; +#[derive(Debug)] +pub(crate) struct SigningClientOptions { + gas_price: GasPrice, + simulated_gas_multiplier: f32, +} + +impl<'a> From<&'a Config> for SigningClientOptions { + fn from(value: &'a Config) -> Self { + SigningClientOptions { + gas_price: value.gas_price.clone(), + simulated_gas_multiplier: value.simulated_gas_multiplier, + } + } +} + +// convenience wrapper around query client to allow for optional signing +#[derive(Debug)] +pub(crate) struct MaybeSigningClient { + client: C, + signer: S, + opts: SigningClientOptions, +} + +impl MaybeSigningClient { + pub(crate) fn new(client: C, opts: SigningClientOptions) -> Self { + MaybeSigningClient { + client, + signer: Default::default(), + opts, + } + } +} + +impl MaybeSigningClient { + pub(crate) fn new_signing(client: C, signer: S, opts: SigningClientOptions) -> Self + where + S: OfflineSigner, + { + MaybeSigningClient { + client, + signer, + opts, + } + } +} #[cfg(feature = "http-client")] -pub fn connect(endpoint: U) -> Result -where - U: TryInto, -{ - Ok(HttpClient::new(endpoint)?) +impl MaybeSigningClient { + pub(crate) fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> + where + U: TryInto, + { + self.client = HttpClient::new(new_endpoint)?; + Ok(()) + } } -#[cfg(all(feature = "signing", feature = "http-client"))] -pub fn connect_with_signer( - endpoint: U, - signer: S, - gas_price: crate::nyxd::GasPrice, -) -> Result, NyxdError> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for MaybeSigningClient where - U: TryInto, + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, { - signing_client::Client::connect_with_signer(endpoint, signer, gas_price) + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.client.perform(request).await + } } + +impl OfflineSigner for MaybeSigningClient +where + S: OfflineSigner, +{ + type Error = S::Error; + + fn get_accounts(&self) -> Result, Self::Error> { + self.signer.get_accounts() + } + + fn sign_direct_with_account( + &self, + signer: &AccountData, + sign_doc: SignDoc, + ) -> Result { + self.signer.sign_direct_with_account(signer, sign_doc) + } +} + +#[async_trait] +impl CosmWasmClient for MaybeSigningClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ +} + +#[async_trait] +impl SigningCosmWasmClient for MaybeSigningClient +where + C: TendermintRpcClient + Send + Sync, + S: OfflineSigner + Send + Sync, + NyxdError: From, +{ + fn gas_price(&self) -> &GasPrice { + &self.opts.gas_price + } + + fn simulated_gas_multiplier(&self) -> f32 { + self.opts.simulated_gas_multiplier + } +} + +// +// #[cfg(feature = "http-client")] +// pub fn connect(endpoint: U) -> Result, NyxdError> +// where +// U: TryInto, +// { +// Ok(HttpClient::new(endpoint)?) +// } +// +// #[cfg(all(feature = "signing", feature = "http-client"))] +// pub fn connect_with_signer( +// endpoint: U, +// signer: S, +// gas_price: crate::nyxd::GasPrice, +// ) -> Result, NyxdError> +// where +// U: TryInto, +// { +// signing_client::Client::connect_with_signer(endpoint, signer, gas_price) +// } diff --git a/common/client-libs/validator-client/src/nyxd/error.rs b/common/client-libs/validator-client/src/nyxd/error.rs index b63be6e75c..e247dff6a1 100644 --- a/common/client-libs/validator-client/src/nyxd/error.rs +++ b/common/client-libs/validator-client/src/nyxd/error.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::cosmwasm_client::types::ContractCodeId; +use crate::signing::direct_wallet::DirectSecp256k1HdWalletError; use cosmrs::tendermint::Hash; use cosmrs::{ tendermint::{abci::Code as AbciCode, block}, @@ -11,9 +12,6 @@ use std::{io, time::Duration}; use tendermint_rpc::endpoint::abci_query::AbciQuery; use thiserror::Error; -#[cfg(feature = "signing")] -use crate::signing::direct_wallet::DirectSecp256k1HdWalletError; - pub use cosmrs::tendermint::error::Error as TendermintError; pub use tendermint_rpc::{ error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail}, @@ -25,7 +23,6 @@ pub enum NyxdError { #[error("No contract address is available to perform the call: {0}")] NoContractAddressAvailable(String), - #[cfg(feature = "signing")] #[error(transparent)] WalletError(#[from] DirectSecp256k1HdWalletError), @@ -224,4 +221,8 @@ impl NyxdError { _ => false, } } + + pub fn unavailable_contract_address>(contract_type: S) -> Self { + NyxdError::NoContractAddressAvailable(contract_type.into()) + } } diff --git a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs index caccceb436..6f68dbbcf2 100644 --- a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs @@ -6,6 +6,7 @@ use cosmrs::Coin; use cosmrs::Gas; use cosmwasm_std::{Decimal, Fraction, Uint128}; use nym_config::defaults; +use nym_network_defaults::NymNetworkDetails; use std::ops::Mul; use std::str::FromStr; @@ -73,6 +74,19 @@ impl FromStr for GasPrice { } } +impl<'a> TryFrom<&'a NymNetworkDetails> for GasPrice { + type Error = NyxdError; + + fn try_from(value: &'a NymNetworkDetails) -> Result { + format!( + "{}{}", + value.default_gas_price_amount(), + value.chain_details.mix_denom.base + ) + .parse() + } +} + impl GasPrice { pub fn new_with_default_price(denom: &str) -> Result { format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse() diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index 9a4ee1eba4..d0cf1e0b3e 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -1,18 +1,31 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::cosmwasm_client::types::Account; +use crate::nyxd::contract_traits::{NymContractsProvider, TypedNymContracts}; +use crate::nyxd::cosmwasm_client::types::{ + ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, + MigrateResult, SequenceResponse, SimulateResponse, UploadResult, +}; +use crate::nyxd::cosmwasm_client::MaybeSigningClient; use crate::nyxd::error::NyxdError; -use log::{debug, trace}; +use crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; +use crate::signing::signer::NoSigner; +use crate::signing::signer::OfflineSigner; +use crate::signing::tx_signer::TxSigner; +use crate::signing::AccountData; +use async_trait::async_trait; +use cosmrs::cosmwasm; +use cosmrs::tx::{Msg, Raw, SignDoc}; +use cosmwasm_std::Addr; use nym_network_defaults::{ChainDetails, NymNetworkDetails}; -use serde::{Deserialize, Serialize}; -use tendermint_rpc::{endpoint::block::Response as BlockResponse, query::Query}; - -#[cfg(feature = "http-client")] +use serde::Serialize; +use std::time::SystemTime; +use tendermint_rpc::endpoint::block::Response as BlockResponse; use tendermint_rpc::Error as TendermintRpcError; -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; pub use crate::nyxd::fee::Fee; +pub use crate::rpc::TendermintRpcClient; pub use coin::Coin; pub use cosmrs::bank::MsgSend; pub use cosmrs::tendermint::abci::{response::DeliverTx, Event, EventAttribute}; @@ -26,269 +39,312 @@ pub use cosmrs::Gas; pub use cosmrs::{bip32, AccountId, Denom}; pub use cosmwasm_std::Coin as CosmWasmCoin; pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment}; -pub use tendermint_rpc::{client::Client as TendermintClient, Request, Response, SimpleRequest}; pub use tendermint_rpc::{ endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse}, Paging, }; +pub use tendermint_rpc::{Request, Response, SimpleRequest}; -#[cfg(feature = "http-client")] -pub use cosmrs::rpc::{HttpClient as QueryNyxdClient, HttpClientUrl}; - -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::nyxd::cosmwasm_client::signing_client; -#[cfg(feature = "signing")] -use crate::nyxd::cosmwasm_client::types::{ - ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, - MigrateResult, SequenceResponse, SimulateResponse, UploadResult, -}; -#[cfg(all(feature = "signing", feature = "http-client"))] +// #[cfg(feature = "http-client")] use crate::signing::direct_wallet::DirectSecp256k1HdWallet; -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::signing::signer::OfflineSigner; -#[cfg(feature = "signing")] -use cosmrs::cosmwasm; -#[cfg(feature = "signing")] -use cosmrs::tx::Msg; -#[cfg(feature = "signing")] -use cosmwasm_std::Addr; -#[cfg(feature = "signing")] -use std::time::SystemTime; - -#[cfg(feature = "signing")] -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; - -#[cfg(all(feature = "signing", feature = "http-client"))] -pub use signing_client::Client as SigningNyxdClient; - -#[cfg(all(feature = "signing", feature = "http-client"))] -pub type DirectSigningNyxdClient = SigningNyxdClient; +#[cfg(feature = "http-client")] +use crate::{DirectSigningHttpRpcNyxdClient, QueryHttpRpcNyxdClient}; +use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient}; +#[cfg(feature = "http-client")] +use cosmrs::rpc::{HttpClient, HttpClientUrl}; +use url::Url; pub mod coin; +pub mod contract_traits; pub mod cosmwasm_client; pub mod error; pub mod fee; -pub mod traits; #[derive(Debug, Clone)] pub struct Config { pub(crate) chain_details: ChainDetails, - - // I'd love to have used `NymContracts` struct directly here instead, - // however, I'd really prefer to use something more strongly typed (i.e. AccountId vs String) - pub(crate) mixnet_contract_address: Option, - pub(crate) vesting_contract_address: Option, - pub(crate) coconut_bandwidth_contract_address: Option, - pub(crate) group_contract_address: Option, - pub(crate) multisig_contract_address: Option, - pub(crate) coconut_dkg_contract_address: Option, - pub(crate) service_provider_contract_address: Option, - pub(crate) name_service_contract_address: Option, - // TODO: add this in later commits - // pub(crate) gas_price: GasPrice, + pub(crate) contracts: TypedNymContracts, + pub(crate) gas_price: GasPrice, + pub(crate) simulated_gas_multiplier: f32, } impl Config { - fn parse_optional_account( - raw: Option<&String>, - expected_prefix: &str, - ) -> Result, NyxdError> { - if let Some(address) = raw { - trace!("Raw address:{:?}", raw); - trace!("Expected prefix:{:?}", expected_prefix); - let parsed: AccountId = address - .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(address.clone()))?; - debug!("Parsed prefix:{:?}", parsed); - if parsed.prefix() != expected_prefix { - Err(NyxdError::UnexpectedBech32Prefix { - got: parsed.prefix().into(), - expected: expected_prefix.into(), - }) - } else { - Ok(Some(parsed)) - } - } else { - Ok(None) - } - } - pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result { - let prefix = &details.chain_details.bech32_account_prefix; Ok(Config { chain_details: details.chain_details.clone(), - mixnet_contract_address: Self::parse_optional_account( - details.contracts.mixnet_contract_address.as_ref(), - prefix, - )?, - vesting_contract_address: Self::parse_optional_account( - details.contracts.vesting_contract_address.as_ref(), - prefix, - )?, - coconut_bandwidth_contract_address: Self::parse_optional_account( - details - .contracts - .coconut_bandwidth_contract_address - .as_ref(), - prefix, - )?, - group_contract_address: Self::parse_optional_account( - details.contracts.group_contract_address.as_ref(), - prefix, - )?, - multisig_contract_address: Self::parse_optional_account( - details.contracts.multisig_contract_address.as_ref(), - prefix, - )?, - coconut_dkg_contract_address: Self::parse_optional_account( - details.contracts.coconut_dkg_contract_address.as_ref(), - prefix, - )?, - service_provider_contract_address: Self::parse_optional_account( - details - .contracts - .service_provider_directory_contract_address - .as_ref(), - prefix, - )?, - name_service_contract_address: Self::parse_optional_account( - details.contracts.name_service_contract_address.as_ref(), - prefix, - )?, + contracts: TypedNymContracts::try_from(details.contracts.clone())?, + gas_price: details.try_into()?, + simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } + + pub fn with_simulated_gas_multplier(mut self, simulated_gas_multiplier: f32) -> Self { + self.simulated_gas_multiplier = simulated_gas_multiplier; + self + } } #[derive(Debug)] -pub struct NyxdClient { - client: C, +pub struct NyxdClient { + client: MaybeSigningClient, config: Config, - // TODO: refactor because that field is only really used for signing - #[allow(dead_code)] - client_address: Option>, - simulated_gas_multiplier: f32, } +// terrible name, but can't really change it because it will break so many uses #[cfg(feature = "http-client")] -impl NyxdClient { - pub fn connect(config: Config, endpoint: U) -> Result, NyxdError> +impl NyxdClient { + pub fn connect(config: Config, endpoint: U) -> Result where U: TryInto, { + let client = HttpClient::new(endpoint)?; + Ok(NyxdClient { - client: QueryNyxdClient::new(endpoint)?, + client: MaybeSigningClient::new(client, (&config).into()), config, - client_address: None, - simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } } -#[cfg(all(feature = "signing", feature = "http-client"))] -impl NyxdClient> { - // TODO: rename this one - pub fn connect_with_mnemonic( +impl NyxdClient { + pub fn connect_reqwest( + config: Config, + endpoint: Url, + ) -> Result { + let client = ReqwestRpcClient::new(endpoint); + + Ok(NyxdClient { + client: MaybeSigningClient::new(client, (&config).into()), + config, + }) + } +} + +impl NyxdClient { + pub fn new(config: Config, client: C) -> Self { + NyxdClient { + client: MaybeSigningClient::new(client, (&config).into()), + config, + } + } +} + +// terrible name, but can't really change it because it will break so many uses +#[cfg(feature = "http-client")] +impl NyxdClient { + pub fn connect_with_mnemonic( config: Config, endpoint: U, mnemonic: bip39::Mnemonic, - gas_price: Option, - ) -> Result>, NyxdError> + ) -> Result where U: TryInto, { + let client = HttpClient::new(endpoint)?; + let prefix = &config.chain_details.bech32_account_prefix; let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); - Self::connect_with_signer(config, endpoint, wallet, gas_price) + Ok(Self::connect_with_signer(config, client, wallet)) } } -#[cfg(all(feature = "signing", feature = "http-client"))] -impl NyxdClient> +impl NyxdClient { + pub fn connect_reqwest_with_mnemonic( + config: Config, + endpoint: Url, + mnemonic: bip39::Mnemonic, + ) -> DirectSigningReqwestRpcNyxdClient { + let client = ReqwestRpcClient::new(endpoint); + + let prefix = &config.chain_details.bech32_account_prefix; + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); + Self::connect_with_signer(config, client, wallet) + } +} + +impl NyxdClient where S: OfflineSigner, - // I have no idea why S::Error: Into bound wouldn't do the trick - NyxdError: From, { - #[cfg(feature = "http-client")] - pub fn connect_with_signer( - config: Config, - endpoint: U, - signer: S, - gas_price: Option, - ) -> Result>, NyxdError> - where - U: TryInto, - { - let denom = &config.chain_details.mix_denom.base; - let client_address = signer - .get_accounts()? - .into_iter() - .map(|account| account.address) - .collect(); - let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?); - - Ok(NyxdClient { - client: SigningNyxdClient::connect_with_signer(endpoint, signer, gas_price)?, + pub fn connect_with_signer(config: Config, client: C, signer: S) -> NyxdClient { + NyxdClient { + client: MaybeSigningClient::new_signing(client, signer, (&config).into()), config, - client_address: Some(client_address), - simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, - }) + } } +} - #[cfg(feature = "http-client")] +#[cfg(feature = "http-client")] +impl NyxdClient { pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> where U: TryInto, { self.client.change_endpoint(new_endpoint) } +} - pub fn into_signer(self) -> S { - self.client.into_signer() +// no trait bounds +impl NyxdClient { + pub fn new_signing(config: Config, client: C, signer: S) -> Self + where + S: OfflineSigner, + { + NyxdClient { + client: MaybeSigningClient::new_signing(client, signer, (&config).into()), + config, + } + } + + pub fn current_config(&self) -> &Config { + &self.config + } + + pub fn current_chain_details(&self) -> &ChainDetails { + &self.config.chain_details + } + + pub fn set_mixnet_contract_address(&mut self, address: AccountId) { + self.config.contracts.mixnet_contract_address = Some(address); + } + + pub fn set_vesting_contract_address(&mut self, address: AccountId) { + self.config.contracts.vesting_contract_address = Some(address); + } + + pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { + self.config.contracts.coconut_bandwidth_contract_address = Some(address); + } + + pub fn set_multisig_contract_address(&mut self, address: AccountId) { + self.config.contracts.multisig_contract_address = Some(address); + } + + pub fn set_service_provider_contract_address(&mut self, address: AccountId) { + self.config + .contracts + .service_provider_directory_contract_address = Some(address); + } + + pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { + self.config.simulated_gas_multiplier = multiplier; } } -#[cfg(feature = "signing")] -impl NyxdClient -where - C: SigningCosmWasmClient + Sync, -{ - pub fn address(&self) -> &AccountId - where - C: SigningCosmWasmClient, - { - // if this is a signing client (as required by the trait bound), it must have the address set - &self.client_address.as_ref().unwrap()[0] +impl NymContractsProvider for NyxdClient { + fn mixnet_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.mixnet_contract_address.as_ref() } - pub fn cw_address(&self) -> Addr - where - C: SigningCosmWasmClient, - { + fn vesting_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.vesting_contract_address.as_ref() + } + + fn coconut_bandwidth_contract_address(&self) -> Option<&AccountId> { + self.config + .contracts + .coconut_bandwidth_contract_address + .as_ref() + } + + fn dkg_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.coconut_dkg_contract_address.as_ref() + } + + fn group_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.group_contract_address.as_ref() + } + + fn multisig_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.multisig_contract_address.as_ref() + } + + fn name_service_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.name_service_contract_address.as_ref() + } + + fn service_provider_contract_address(&self) -> Option<&AccountId> { + self.config + .contracts + .service_provider_directory_contract_address + .as_ref() + } +} + +// queries +impl NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ + pub async fn get_account_public_key( + &self, + address: &AccountId, + ) -> Result, NyxdError> { + if let Some(account) = self.client.get_account(address).await? { + let base_account = account.try_get_base_account()?; + return Ok(base_account.pubkey); + } + + Ok(None) + } + + pub async fn get_current_block_timestamp(&self) -> Result { + self.get_block_timestamp(None).await + } + + pub async fn get_block_timestamp( + &self, + height: Option, + ) -> Result { + Ok(self.client.get_block(height).await?.block.header.time) + } + + pub async fn get_block(&self, height: Option) -> Result { + self.client.get_block(height).await + } + + pub async fn get_current_block_height(&self) -> Result { + self.client.get_height().await + } + + /// Obtains the hash of a block specified by the provided height. + /// + /// # Arguments + /// + /// * `height`: height of the block for which we want to obtain the hash. + pub async fn get_block_hash(&self, height: u32) -> Result { + self.client + .get_block(Some(height)) + .await + .map(|block| block.block_id.hash) + } +} + +// signing +impl NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: OfflineSigner + Send + Sync, + NyxdError: From<::Error>, +{ + pub fn address(&self) -> AccountId { + match self.client.signer_addresses() { + Ok(addresses) => addresses[0].clone(), + Err(_) => { + panic!("key derivation failure") + } + } + } + + pub fn cw_address(&self) -> Addr { // the call to unchecked is fine here as we're converting directly from `AccountId` // which must have been a valid bech32 address Addr::unchecked(self.address().as_ref()) } - pub async fn account_sequence(&self) -> Result - where - C: SigningCosmWasmClient + Sync, - { - self.client.get_sequence(self.address()).await - } - - pub fn signer(&self) -> &::Signer - where - C: SigningCosmWasmClient, - { - self.client.signer() - } - - pub fn gas_price(&self) -> &GasPrice - where - C: SigningCosmWasmClient, - { - self.client.gas_price() + pub async fn account_sequence(&self) -> Result { + self.client.get_sequence(&self.address()).await } pub fn wrap_contract_execute_message( @@ -298,11 +354,10 @@ where funds: Vec, ) -> Result where - C: SigningCosmWasmClient, M: ?Sized + Serialize, { Ok(cosmwasm::MsgExecuteContract { - sender: self.address().clone(), + sender: self.address(), contract: contract_address.clone(), msg: serde_json::to_vec(msg)?, funds: funds.into_iter().map(Into::into).collect(), @@ -311,13 +366,12 @@ where pub async fn simulate(&self, messages: I) -> Result where - C: SigningCosmWasmClient + Sync, I: IntoIterator + Send, M: Msg, { self.client .simulate( - self.address(), + &self.address(), messages .into_iter() .map(|msg| msg.into_any()) @@ -337,13 +391,10 @@ where amount: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .send_tokens(self.address(), recipient, amount, fee, memo) + .send_tokens(&self.address(), recipient, amount, fee, memo) .await } @@ -353,13 +404,10 @@ where msgs: Vec<(AccountId, Vec)>, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .send_tokens_multiple(self.address(), msgs, fee, memo) + .send_tokens_multiple(&self.address(), msgs, fee, memo) .await } @@ -372,14 +420,11 @@ where allowed_messages: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client .grant_allowance( - self.address(), + &self.address(), grantee, spend_limit, expiration, @@ -396,13 +441,10 @@ where grantee: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .revoke_allowance(self.address(), grantee, fee, memo) + .revoke_allowance(&self.address(), grantee, fee, memo) .await } @@ -415,12 +457,11 @@ where funds: Vec, ) -> Result where - C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .execute(self.address(), contract_address, msg, fee, memo, funds) + .execute(&self.address(), contract_address, msg, fee, memo, funds) .await } @@ -432,13 +473,12 @@ where memo: impl Into + Send + 'static, ) -> Result where - C: SigningCosmWasmClient + Sync, I: IntoIterator)> + Send, M: Serialize, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .execute_multiple(self.address(), contract_address, msgs, fee, memo) + .execute_multiple(&self.address(), contract_address, msgs, fee, memo) .await } @@ -447,13 +487,10 @@ where wasm_code: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .upload(self.address(), wasm_code, fee, memo) + .upload(&self.address(), wasm_code, fee, memo) .await } @@ -467,12 +504,11 @@ where fee: Option, ) -> Result where - C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .instantiate(self.address(), code_id, msg, label, fee, memo, options) + .instantiate(&self.address(), code_id, msg, label, fee, memo, options) .await } @@ -482,13 +518,10 @@ where new_admin: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .update_admin(self.address(), contract_address, new_admin, fee, memo) + .update_admin(&self.address(), contract_address, new_admin, fee, memo) .await } @@ -497,13 +530,10 @@ where contract_address: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .clear_admin(self.address(), contract_address, fee, memo) + .clear_admin(&self.address(), contract_address, fee, memo) .await } @@ -516,258 +546,71 @@ where fee: Option, ) -> Result where - C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .migrate(self.address(), contract_address, code_id, fee, msg, memo) + .migrate(&self.address(), contract_address, code_id, fee, msg, memo) .await } } -impl NyxdClient { - pub fn current_config(&self) -> &Config { - &self.config - } +// ugh. is there a way to avoid that nasty trait implementation? - pub fn current_chain_details(&self) -> &ChainDetails { - &self.config.chain_details - } - - pub fn set_mixnet_contract_address(&mut self, address: AccountId) { - self.config.mixnet_contract_address = Some(address); - } - - pub fn set_vesting_contract_address(&mut self, address: AccountId) { - self.config.vesting_contract_address = Some(address); - } - - pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { - self.config.coconut_bandwidth_contract_address = Some(address); - } - - pub fn set_multisig_contract_address(&mut self, address: AccountId) { - self.config.multisig_contract_address = Some(address); - } - - pub fn set_service_provider_contract_address(&mut self, address: AccountId) { - self.config.service_provider_contract_address = Some(address); - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn mixnet_contract_address(&self) -> &AccountId { - self.config.mixnet_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn vesting_contract_address(&self) -> &AccountId { - self.config.vesting_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn coconut_bandwidth_contract_address(&self) -> &AccountId { - self.config - .coconut_bandwidth_contract_address - .as_ref() - .unwrap() - } - - pub fn group_contract_address(&self) -> &AccountId { - self.config.group_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn multisig_contract_address(&self) -> &AccountId { - self.config.multisig_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn coconut_dkg_contract_address(&self) -> &AccountId { - self.config.coconut_dkg_contract_address.as_ref().unwrap() - } - - // The service provider directory contract is optional, so we return an Option not a Result - pub fn service_provider_contract_address(&self) -> Option<&AccountId> { - self.config.service_provider_contract_address.as_ref() - } - - // The name service contract is optional, so we return an Option not a Result - pub fn name_service_contract_address(&self) -> Option<&AccountId> { - self.config.name_service_contract_address.as_ref() - } - - pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.simulated_gas_multiplier = multiplier; - } - - pub async fn query_contract_smart( - &self, - contract: &AccountId, - query_msg: &M, - ) -> Result +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ + async fn perform(&self, request: R) -> Result where - C: CosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - for<'a> T: Deserialize<'a>, + R: SimpleRequest, { - self.client.query_contract_smart(contract, query_msg).await - } - - pub async fn query_contract_raw( - &self, - contract: &AccountId, - query_data: Vec, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.query_contract_raw(contract, query_data).await - } - - pub fn gas_adjustment(&self) -> GasAdjustment { - self.simulated_gas_multiplier - } - - // ============= - // CHAIN RELATED - // ============= - - // CHAIN QUERIES - - pub async fn get_account_details( - &self, - address: &AccountId, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_account(address).await - } - - pub async fn get_account_public_key( - &self, - address: &AccountId, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - if let Some(account) = self.client.get_account(address).await? { - let base_account = account.try_get_base_account()?; - return Ok(base_account.pubkey); - } - - Ok(None) - } - - pub async fn get_current_block_timestamp(&self) -> Result - where - C: CosmWasmClient + Sync, - { - self.get_block_timestamp(None).await - } - - pub async fn get_block_timestamp( - &self, - height: Option, - ) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.client.get_block(height).await?.block.header.time) - } - - pub async fn get_block(&self, height: Option) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_block(height).await - } - - pub async fn get_current_block_height(&self) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_height().await - } - - /// Obtains the hash of a block specified by the provided height. - /// - /// # Arguments - /// - /// * `height`: height of the block for which we want to obtain the hash. - pub async fn get_block_hash(&self, height: u32) -> Result - where - C: CosmWasmClient + Sync, - { - self.client - .get_block(Some(height)) - .await - .map(|block| block.block_id.hash) - } - - pub async fn get_validators( - &self, - height: u64, - paging: Paging, - ) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.client.validators(height as u32, paging).await?) - } - - pub async fn get_balance( - &self, - address: &AccountId, - denom: String, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_balance(address, denom).await - } - - pub async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_all_balances(address).await - } - - pub async fn get_tx(&self, id: Hash) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_tx(id).await - } - - pub async fn search_tx(&self, query: Query) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.search_tx(query).await - } - - pub async fn get_total_supply(&self) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_total_supply().await + self.client.perform(request).await + } +} + +#[async_trait] +impl CosmWasmClient for NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ +} + +impl OfflineSigner for NyxdClient +where + S: OfflineSigner, +{ + type Error = S::Error; + + fn get_accounts(&self) -> Result, Self::Error> { + self.client.get_accounts() + } + + fn sign_direct_with_account( + &self, + signer: &AccountData, + sign_doc: SignDoc, + ) -> Result { + self.client.sign_direct_with_account(signer, sign_doc) + } +} + +#[async_trait] +impl SigningCosmWasmClient for NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: TxSigner + Send + Sync, + NyxdError: From, +{ + fn gas_price(&self) -> &GasPrice { + self.client.gas_price() + } + + fn simulated_gas_multiplier(&self) -> f32 { + self.client.simulated_gas_multiplier() } } diff --git a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs deleted file mode 100644 index 66ac21947b..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use nym_coconut_bandwidth_contract_common::msg::QueryMsg; -use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse; - -use async_trait::async_trait; - -#[async_trait] -pub trait CoconutBandwidthQueryClient { - async fn get_spent_credential( - &self, - blinded_serial_number: String, - ) -> Result; -} - -#[async_trait] -impl CoconutBandwidthQueryClient for NyxdClient { - async fn get_spent_credential( - &self, - blinded_serial_number: String, - ) -> Result { - let request = QueryMsg::GetSpentCredential { - blinded_serial_number, - }; - self.client - .query_contract_smart(self.coconut_bandwidth_contract_address(), &request) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs deleted file mode 100644 index 1efb4bf494..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nyxd::cosmwasm_client::types::ExecuteResult; -use crate::nyxd::error::NyxdError; -use crate::nyxd::{Coin, Fee, NyxdClient}; -use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialData; -use nym_coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg}; - -use async_trait::async_trait; - -#[async_trait] -pub trait CoconutBandwidthSigningClient { - async fn deposit( - &self, - amount: Coin, - info: String, - verification_key: String, - encryption_key: String, - fee: Option, - ) -> Result; - async fn spend_credential( - &self, - funds: Coin, - blinded_serial_number: String, - gateway_cosmos_address: String, - fee: Option, - ) -> Result; -} - -#[async_trait] -impl CoconutBandwidthSigningClient for NyxdClient { - async fn deposit( - &self, - amount: Coin, - info: String, - verification_key: String, - encryption_key: String, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = ExecuteMsg::DepositFunds { - data: DepositData::new(info.to_string(), verification_key, encryption_key), - }; - self.client - .execute( - self.address(), - self.coconut_bandwidth_contract_address(), - &req, - fee, - "CoconutBandwidth::Deposit", - vec![amount], - ) - .await - } - async fn spend_credential( - &self, - funds: Coin, - blinded_serial_number: String, - gateway_cosmos_address: String, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = ExecuteMsg::SpendCredential { - data: SpendCredentialData::new( - funds.into(), - blinded_serial_number, - gateway_cosmos_address, - ), - }; - self.client - .execute( - self.address(), - self.coconut_bandwidth_contract_address(), - &req, - fee, - "CoconutBandwidth::SpendCredential", - vec![], - ) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs deleted file mode 100644 index 92334424c2..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_coconut_dkg_common::dealer::{ - ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse, -}; -use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; -use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData}; -use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse}; -use serde::Deserialize; - -#[async_trait] -pub trait DkgQueryClient { - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - async fn get_current_epoch(&self) -> Result { - let request = DkgQueryMsg::GetCurrentEpochState {}; - self.query_dkg_contract(request).await - } - async fn get_current_epoch_threshold(&self) -> Result, NyxdError> { - let request = DkgQueryMsg::GetCurrentEpochThreshold {}; - self.query_dkg_contract(request).await - } - - async fn get_initial_dealers(&self) -> Result, NyxdError> { - let request = DkgQueryMsg::GetInitialDealers {}; - self.query_dkg_contract(request).await - } - - async fn get_dealer_details( - &self, - address: &AccountId, - ) -> Result { - let request = DkgQueryMsg::GetDealerDetails { - dealer_address: address.to_string(), - }; - self.query_dkg_contract(request).await - } - - async fn get_current_dealers_paged( - &self, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetCurrentDealers { - start_after, - limit: page_limit, - }; - self.query_dkg_contract(request).await - } - - async fn get_past_dealers_paged( - &self, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetPastDealers { - start_after, - limit: page_limit, - }; - self.query_dkg_contract(request).await - } - - async fn get_dealings_paged( - &self, - idx: usize, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetDealing { - idx: idx as u64, - limit: page_limit, - start_after, - }; - self.query_dkg_contract(request).await - } - - async fn get_vk_shares_paged( - &self, - epoch_id: EpochId, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetVerificationKeys { - epoch_id, - limit: page_limit, - start_after, - }; - self.query_dkg_contract(request).await - } - - async fn get_all_current_dealers(&self) -> Result, NyxdError> { - let mut dealers = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_current_dealers_paged(start_after.take(), None) - .await?; - dealers.append(&mut paged_response.dealers); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(dealers) - } - - async fn get_all_past_dealers(&self) -> Result, NyxdError> { - let mut dealers = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_past_dealers_paged(start_after.take(), None) - .await?; - dealers.append(&mut paged_response.dealers); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(dealers) - } - - async fn get_all_epoch_dealings(&self, idx: usize) -> Result, NyxdError> { - let mut dealings = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_dealings_paged(idx, start_after.take(), None) - .await?; - dealings.append(&mut paged_response.dealings); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(dealings) - } - - async fn get_all_verification_key_shares( - &self, - epoch_id: EpochId, - ) -> Result, NyxdError> { - let mut shares = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_vk_shares_paged(epoch_id, start_after.take(), None) - .await?; - shares.append(&mut paged_response.shares); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(shares) - } -} - -#[async_trait] -impl DkgQueryClient for NyxdClient -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart(self.coconut_dkg_contract_address(), &query) - .await - } -} - -#[async_trait] -impl DkgQueryClient for crate::Client -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_dkg_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs deleted file mode 100644 index c35747f40f..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::cosmwasm_client::types::ExecuteResult; -use crate::nyxd::error::NyxdError; -use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; -use async_trait::async_trait; -use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; -use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof; -use nym_coconut_dkg_common::verification_key::VerificationKeyShare; -use nym_contracts_common::dealings::ContractSafeBytes; - -#[async_trait] -pub trait DkgSigningClient { - async fn advance_dkg_epoch_state(&self, fee: Option) -> Result; - async fn register_dealer( - &self, - bte_key: EncodedBTEPublicKeyWithProof, - announce_address: String, - resharing: bool, - fee: Option, - ) -> Result; - - async fn submit_dealing_bytes( - &self, - commitment: ContractSafeBytes, - resharing: bool, - fee: Option, - ) -> Result; - - async fn submit_verification_key_share( - &self, - share: VerificationKeyShare, - resharing: bool, - fee: Option, - ) -> Result; -} - -#[async_trait] -impl DkgSigningClient for NyxdClient -where - C: SigningCosmWasmClient + Send + Sync, -{ - async fn advance_dkg_epoch_state(&self, fee: Option) -> Result { - let req = DkgExecuteMsg::AdvanceEpochState {}; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - "advancing DKG state", - vec![], - ) - .await - } - - async fn register_dealer( - &self, - bte_key: EncodedBTEPublicKeyWithProof, - announce_address: String, - resharing: bool, - fee: Option, - ) -> Result { - let req = DkgExecuteMsg::RegisterDealer { - bte_key_with_proof: bte_key, - announce_address, - resharing, - }; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - format!("registering {} as a dealer", self.address()), - vec![], - ) - .await - } - - async fn submit_dealing_bytes( - &self, - dealing_bytes: ContractSafeBytes, - resharing: bool, - fee: Option, - ) -> Result { - let req = DkgExecuteMsg::CommitDealing { - dealing_bytes, - resharing, - }; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - "dealing commitment", - vec![], - ) - .await - } - - async fn submit_verification_key_share( - &self, - share: VerificationKeyShare, - resharing: bool, - fee: Option, - ) -> Result { - let req = DkgExecuteMsg::CommitVerificationKeyShare { share, resharing }; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - "verification key share commitment", - vec![], - ) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs deleted file mode 100644 index b5b4c1d1ee..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use nym_group_contract_common::msg::QueryMsg; - -use async_trait::async_trait; -use cw4::MemberResponse; - -#[async_trait] -pub trait GroupQueryClient { - async fn member(&self, addr: String) -> Result; -} - -#[async_trait] -impl GroupQueryClient for NyxdClient { - async fn member(&self, addr: String) -> Result { - let request = QueryMsg::Member { - addr, - at_height: None, - }; - self.client - .query_contract_smart(self.group_contract_address(), &request) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs deleted file mode 100644 index 0297b90452..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; -use crate::nyxd::error::NyxdError; -use crate::nyxd::NyxdClient; -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_contracts_common::signing::Nonce; -use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey}; -use nym_mixnet_contract_common::mixnode::{ - MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse, PagedUnbondedMixnodesResponse, - StakeSaturationResponse, UnbondedMixnodeResponse, -}; -use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams}; -use nym_mixnet_contract_common::rewarding::{ - EstimatedCurrentEpochRewardResponse, PendingRewardResponse, -}; -use nym_mixnet_contract_common::{ - delegation, ContractBuildInformation, ContractState, ContractStateParams, - CurrentIntervalResponse, EpochEventId, EpochStatus, FamilyByHeadResponse, - FamilyByLabelResponse, FamilyMembersByHeadResponse, FamilyMembersByLabelResponse, - GatewayBondResponse, GatewayOwnershipResponse, IdentityKey, IntervalEventId, LayerDistribution, - MixId, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, - NumberOfPendingEventsResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, - PagedFamiliesResponse, PagedGatewayResponse, PagedMembersResponse, - PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, PagedRewardedSetResponse, - PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEventResponse, - PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg, -}; -use serde::Deserialize; - -#[async_trait] -pub trait MixnetQueryClient { - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - // state/sys-params-related - - async fn get_mixnet_contract_version(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {}) - .await - } - - async fn get_rewarding_validator_address(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {}) - .await - } - - async fn get_mixnet_contract_settings(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) - .await - } - - async fn get_mixnet_contract_state(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetState {}) - .await - } - - async fn get_rewarding_parameters(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {}) - .await - } - - async fn get_current_epoch_status(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {}) - .await - } - - async fn get_current_interval_details(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {}) - .await - } - - async fn get_rewarded_set_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after }) - .await - } - - async fn get_all_node_families_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after }) - .await - } - - async fn get_all_family_members_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after }) - .await - } - - async fn get_family_members_by_head + Send>( - &self, - head: S, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByHead { head: head.into() }) - .await - } - - async fn get_family_members_by_label + Send>( - &self, - label: S, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByLabel { - label: label.into(), - }) - .await - } - - // mixnode-related: - - async fn get_mixnode_bonds_paged( - &self, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after }) - .await - } - - async fn get_mixnodes_detailed_paged( - &self, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after }) - .await - } - - async fn get_unbonded_paged( - &self, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after }) - .await - } - - async fn get_unbonded_by_owner_paged( - &self, - owner: &AccountId, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner { - owner: owner.to_string(), - limit, - start_after, - }) - .await - } - - async fn get_unbonded_by_identity_paged( - &self, - identity_key: String, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { - identity_key, - limit, - start_after, - }) - .await - } - - async fn get_owned_mixnode( - &self, - address: &AccountId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode { - address: address.to_string(), - }) - .await - } - - async fn get_mixnode_details( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id }) - .await - } - - async fn get_mixnode_details_by_identity( - &self, - mix_identity: IdentityKey, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { - mix_identity, - }) - .await - } - - async fn get_mixnode_rewarding_details( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id }) - .await - } - - async fn get_mixnode_stake_saturation( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id }) - .await - } - - async fn get_unbonded_mixnode_information( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id }) - .await - } - - async fn get_layer_distribution(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {}) - .await - } - - // gateway-related: - - async fn get_gateways_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit }) - .await - } - - /// Checks whether there is a bonded gateway associated with the provided identity key - async fn get_gateway_bond( - &self, - identity: IdentityKey, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity }) - .await - } - - /// Checks whether there is a bonded gateway associated with the provided client's address - async fn get_owned_gateway( - &self, - address: &AccountId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway { - address: address.to_string(), - }) - .await - } - - // delegation-related: - - /// Gets list of all delegations towards particular mixnode on particular page. - async fn get_mixnode_delegations_paged( - &self, - mix_id: MixId, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations { - mix_id, - start_after, - limit, - }) - .await - } - - /// Gets list of all the mixnodes to which a particular address delegated. - async fn get_delegator_delegations_paged( - &self, - delegator: String, - start_after: Option<(MixId, OwnerProxySubKey)>, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations { - delegator, - start_after, - limit, - }) - .await - } - - /// Checks value of delegation of given client towards particular mixnode. - async fn get_delegation_details( - &self, - mix_id: MixId, - delegator: &AccountId, - proxy: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails { - mix_id, - delegator: delegator.to_string(), - proxy, - }) - .await - } - - /// Gets all the delegations on the entire network - async fn get_all_network_delegations_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit }) - .await - } - - // rewards related - async fn get_pending_operator_reward( - &self, - operator: &AccountId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward { - address: operator.to_string(), - }) - .await - } - - async fn get_pending_mixnode_operator_reward( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id }) - .await - } - - async fn get_pending_delegator_reward( - &self, - delegator: &AccountId, - mix_id: MixId, - proxy: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward { - address: delegator.to_string(), - mix_id, - proxy, - }) - .await - } - - // given the provided performance, estimate the reward at the end of the current epoch - async fn get_estimated_current_epoch_operator_reward( - &self, - mix_id: MixId, - estimated_performance: Performance, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { - mix_id, - estimated_performance, - }) - .await - } - - // given the provided performance, estimate the reward at the end of the current epoch - async fn get_estimated_current_epoch_delegator_reward( - &self, - delegator: &AccountId, - mix_id: MixId, - proxy: Option, - estimated_performance: Performance, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { - address: delegator.to_string(), - mix_id, - proxy, - estimated_performance, - }) - .await - } - - // interval-related - - async fn get_pending_epoch_events_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit }) - .await - } - - async fn get_pending_interval_events_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit }) - .await - } - - async fn get_pending_epoch_event( - &self, - event_id: EpochEventId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id }) - .await - } - - async fn get_pending_interval_event( - &self, - event_id: IntervalEventId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id }) - .await - } - - async fn get_number_of_pending_events( - &self, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {}) - .await - } - - async fn get_signing_nonce(&self, address: &AccountId) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce { - address: address.to_string(), - }) - .await - } - - async fn get_node_family_by_label( - &self, - label: &str, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { - label: label.to_string(), - }) - .await - } - - async fn get_node_family_by_head(&self, head: &str) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { - head: head.to_string(), - }) - .await - } -} - -#[async_trait] -impl MixnetQueryClient for NyxdClient -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart(self.mixnet_contract_address(), &query) - .await - } -} - -#[async_trait] -impl MixnetQueryClient for crate::Client -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_mixnet_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mod.rs b/common/client-libs/validator-client/src/nyxd/traits/mod.rs deleted file mode 100644 index 9f6dac5354..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021-2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -// TODO: expose query-related capabilities to wasm client... - -mod coconut_bandwidth_query_client; -mod dkg_query_client; -mod group_query_client; -mod mixnet_query_client; -mod multisig_query_client; -mod name_service_query_client; -mod sp_directory_query_client; -mod vesting_query_client; - -#[cfg(feature = "signing")] -mod coconut_bandwidth_signing_client; -#[cfg(feature = "signing")] -mod dkg_signing_client; -#[cfg(feature = "signing")] -mod mixnet_signing_client; -#[cfg(feature = "signing")] -mod multisig_signing_client; -#[cfg(feature = "signing")] -mod name_service_signing_client; -#[cfg(feature = "signing")] -mod sp_directory_signing_client; -#[cfg(feature = "signing")] -mod vesting_signing_client; - -pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient; -pub use dkg_query_client::DkgQueryClient; -pub use group_query_client::GroupQueryClient; -pub use mixnet_query_client::MixnetQueryClient; -pub use multisig_query_client::MultisigQueryClient; -pub use name_service_query_client::NameServiceQueryClient; -pub use sp_directory_query_client::SpDirectoryQueryClient; -pub use vesting_query_client::VestingQueryClient; - -#[cfg(feature = "signing")] -pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; -#[cfg(feature = "signing")] -pub use dkg_signing_client::DkgSigningClient; -#[cfg(feature = "signing")] -pub use mixnet_signing_client::MixnetSigningClient; -#[cfg(feature = "signing")] -pub use multisig_signing_client::MultisigSigningClient; -#[cfg(feature = "signing")] -pub use name_service_signing_client::NameServiceSigningClient; -#[cfg(feature = "signing")] -pub use sp_directory_signing_client::SpDirectorySigningClient; -#[cfg(feature = "signing")] -pub use vesting_signing_client::VestingSigningClient; diff --git a/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs deleted file mode 100644 index ea461d1080..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use cw3::{ProposalListResponse, ProposalResponse}; -use nym_multisig_contract_common::msg::QueryMsg; - -use async_trait::async_trait; - -#[async_trait] -pub trait MultisigQueryClient { - async fn get_proposal(&self, proposal_id: u64) -> Result; - - async fn list_proposals( - &self, - start_after: Option, - limit: Option, - ) -> Result; - - async fn get_all_proposals(&self) -> Result, NyxdError> { - let mut proposals = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self.list_proposals(start_after.take(), None).await?; - - let last_id = paged_response.proposals.last().map(|prop| prop.id); - proposals.append(&mut paged_response.proposals); - - if let Some(start_after_res) = last_id { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(proposals) - } -} - -#[async_trait] -impl MultisigQueryClient for NyxdClient { - async fn get_proposal(&self, proposal_id: u64) -> Result { - let request = QueryMsg::Proposal { proposal_id }; - self.client - .query_contract_smart(self.multisig_contract_address(), &request) - .await - } - - async fn list_proposals( - &self, - start_after: Option, - limit: Option, - ) -> Result { - let request = QueryMsg::ListProposals { start_after, limit }; - self.client - .query_contract_smart(self.multisig_contract_address(), &request) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs deleted file mode 100644 index b5ad0a8e96..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nyxd::cosmwasm_client::types::ExecuteResult; -use crate::nyxd::error::NyxdError; -use crate::nyxd::{Fee, NyxdClient}; - -use cw3::Vote; -use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; -use nym_multisig_contract_common::msg::ExecuteMsg; - -use async_trait::async_trait; -use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg}; - -#[async_trait] -pub trait MultisigSigningClient { - async fn propose_release_funds( - &self, - title: String, - blinded_serial_number: String, - voucher_value: u128, - fee: Option, - ) -> Result; - - async fn vote_proposal( - &self, - proposal_id: u64, - yes: bool, - fee: Option, - ) -> Result; - - async fn execute_proposal( - &self, - proposal_id: u64, - fee: Option, - ) -> Result; -} - -#[async_trait] -impl MultisigSigningClient for NyxdClient { - async fn propose_release_funds( - &self, - title: String, - blinded_serial_number: String, - voucher_value: u128, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { - funds: Coin::new( - voucher_value, - self.config.chain_details.mix_denom.base.clone(), - ), - }; - let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: self.coconut_bandwidth_contract_address().to_string(), - msg: to_binary(&release_funds_req)?, - funds: vec![], - }); - let req = ExecuteMsg::Propose { - title, - description: blinded_serial_number, - msgs: vec![release_funds_msg], - latest: None, - }; - self.client - .execute( - self.address(), - self.multisig_contract_address(), - &req, - fee, - "Multisig::Propose::Execute::ReleaseFunds", - vec![], - ) - .await - } - - async fn vote_proposal( - &self, - proposal_id: u64, - vote_yes: bool, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let vote = if vote_yes { Vote::Yes } else { Vote::No }; - let req = ExecuteMsg::Vote { proposal_id, vote }; - self.client - .execute( - self.address(), - self.multisig_contract_address(), - &req, - fee, - "Multisig::Vote", - vec![], - ) - .await - } - - async fn execute_proposal( - &self, - proposal_id: u64, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = ExecuteMsg::Execute { proposal_id }; - self.client - .execute( - self.address(), - self.multisig_contract_address(), - &req, - fee, - "Multisig::Execute", - vec![], - ) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/name_service_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/name_service_query_client.rs deleted file mode 100644 index 1cb59711ff..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/name_service_query_client.rs +++ /dev/null @@ -1,116 +0,0 @@ -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; -use nym_name_service_common::{ - msg::QueryMsg as NameQueryMsg, - response::{ConfigResponse, NamesListResponse, PagedNamesListResponse}, - Address, NameId, RegisteredName, -}; -use serde::Deserialize; - -use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; - -#[async_trait] -pub trait NameServiceQueryClient { - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - async fn get_name_service_config(&self) -> Result { - self.query_name_service_contract(NameQueryMsg::Config {}) - .await - } - - async fn get_name_entry(&self, name_id: NameId) -> Result { - self.query_name_service_contract(NameQueryMsg::NameId { name_id }) - .await - } - - async fn get_names_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_name_service_contract(NameQueryMsg::All { limit, start_after }) - .await - } - - async fn get_names_by_owner(&self, owner: AccountId) -> Result { - self.query_name_service_contract(NameQueryMsg::ByOwner { - owner: owner.to_string(), - }) - .await - } - - async fn get_names_by_address(&self, address: Address) -> Result { - self.query_name_service_contract(NameQueryMsg::ByAddress { address }) - .await - } - - async fn get_name_service_contract_version( - &self, - ) -> Result { - self.query_name_service_contract(NameQueryMsg::GetContractVersion {}) - .await - } - - async fn get_all_names(&self) -> Result, NyxdError> { - let mut services = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self.get_names_paged(start_after.take(), None).await?; - - let last_id = paged_response.names.last().map(|serv| serv.id); - services.append(&mut paged_response.names); - - if let Some(start_after_res) = last_id { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(services) - } - - async fn get_name_signing_nonce(&self, address: &AccountId) -> Result { - self.query_name_service_contract(NameQueryMsg::SigningNonce { - address: address.to_string(), - }) - .await - } -} - -#[async_trait] -impl NameServiceQueryClient for NyxdClient -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart( - self.name_service_contract_address().ok_or( - NyxdError::NoContractAddressAvailable("name service contract".to_string()), - )?, - &query, - ) - .await - } -} - -#[async_trait] -impl NameServiceQueryClient for crate::Client -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_name_service_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs deleted file mode 100644 index 0ed99d5c1c..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs +++ /dev/null @@ -1,124 +0,0 @@ -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; -use nym_service_provider_directory_common::{ - msg::QueryMsg as SpQueryMsg, - response::{ - ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse, - }, - NymAddress, Service, ServiceId, -}; -use serde::Deserialize; - -use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; - -#[async_trait] -pub trait SpDirectoryQueryClient { - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - async fn get_service_config(&self) -> Result { - self.query_service_provider_contract(SpQueryMsg::Config {}) - .await - } - - async fn get_service_info( - &self, - service_id: ServiceId, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id }) - .await - } - - async fn get_services_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::All { limit, start_after }) - .await - } - - async fn get_services_by_announcer( - &self, - announcer: AccountId, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::ByAnnouncer { - announcer: announcer.to_string(), - }) - .await - } - - async fn get_services_by_nym_address( - &self, - nym_address: NymAddress, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address }) - .await - } - - async fn get_sp_contract_version(&self) -> Result { - self.query_service_provider_contract(SpQueryMsg::GetContractVersion {}) - .await - } - - async fn get_all_services(&self) -> Result, NyxdError> { - let mut services = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self.get_services_paged(start_after.take(), None).await?; - services.append(&mut paged_response.services); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(services) - } - - async fn get_service_signing_nonce(&self, address: &AccountId) -> Result { - self.query_service_provider_contract(SpQueryMsg::SigningNonce { - address: address.to_string(), - }) - .await - } -} - -#[async_trait] -impl SpDirectoryQueryClient for NyxdClient -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart( - self.service_provider_contract_address().ok_or( - NyxdError::NoContractAddressAvailable( - "service provider directory contract".to_string(), - ), - )?, - &query, - ) - .await - } -} - -#[async_trait] -impl SpDirectoryQueryClient for crate::Client -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_service_provider_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/rpc/mod.rs b/common/client-libs/validator-client/src/rpc/mod.rs new file mode 100644 index 0000000000..f483f1067c --- /dev/null +++ b/common/client-libs/validator-client/src/rpc/mod.rs @@ -0,0 +1,514 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use cosmrs::tendermint::{self, abci, block::Height, evidence::Evidence, Genesis, Hash}; +use serde::{de::DeserializeOwned, Serialize}; +use std::fmt; +use tendermint_rpc::{ + endpoint::{validators::DEFAULT_VALIDATORS_PER_PAGE, *}, + query::Query, + Error, Order, Paging, SimpleRequest, +}; + +pub mod reqwest; + +// we have to create a sealed trait since `TendermintClient` needs T: Send (due to how async trait is created) +// which we can't do in wasm +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait TendermintRpcClient { + /// `/abci_info`: get information about the ABCI application. + async fn abci_info(&self) -> Result { + Ok(self.perform(abci_info::Request).await?.response) + } + + /// `/abci_query`: query the ABCI application + async fn abci_query( + &self, + path: Option, + data: V, + height: Option, + prove: bool, + ) -> Result + where + V: Into> + Send, + { + Ok(self + .perform(abci_query::Request::new(path, data, height, prove)) + .await? + .response) + } + + /// `/block`: get block at a given height. + async fn block(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(block::Request::new(height.into())).await + } + + /// `/block_by_hash`: get block by hash. + async fn block_by_hash( + &self, + hash: tendermint::Hash, + ) -> Result { + self.perform(block_by_hash::Request::new(hash)).await + } + + /// `/block`: get the latest block. + async fn latest_block(&self) -> Result { + self.perform(block::Request::default()).await + } + + /// `/header`: get block header at a given height. + async fn header(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(header::Request::new(height.into())).await + } + + /// `/header_by_hash`: get block by hash. + async fn header_by_hash( + &self, + hash: tendermint::Hash, + ) -> Result { + self.perform(header_by_hash::Request::new(hash)).await + } + + /// `/block_results`: get ABCI results for a block at a particular height. + async fn block_results(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(block_results::Request::new(height.into())) + .await + } + + /// `/block_results`: get ABCI results for the latest block. + async fn latest_block_results(&self) -> Result { + self.perform(block_results::Request::default()).await + } + + /// `/block_search`: search for blocks by BeginBlock and EndBlock events. + async fn block_search( + &self, + query: Query, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.perform(block_search::Request::new(query, page, per_page, order)) + .await + } + + /// `/blockchain`: get block headers for `min` <= `height` <= `max`. + /// + /// Block headers are returned in descending order (highest first). + /// + /// Returns at most 20 items. + async fn blockchain(&self, min: H, max: H) -> Result + where + H: Into + Send, + { + // TODO(tarcieri): return errors for invalid params before making request? + self.perform(blockchain::Request::new(min.into(), max.into())) + .await + } + + /// `/broadcast_tx_async`: broadcast a transaction, returning immediately. + async fn broadcast_tx_async(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.perform(broadcast::tx_async::Request::new(tx)).await + } + + /// `/broadcast_tx_sync`: broadcast a transaction, returning the response + /// from `CheckTx`. + async fn broadcast_tx_sync(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.perform(broadcast::tx_sync::Request::new(tx)).await + } + + /// `/broadcast_tx_commit`: broadcast a transaction, returning the response + /// from `DeliverTx`. + async fn broadcast_tx_commit(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.perform(broadcast::tx_commit::Request::new(tx)).await + } + + /// `/commit`: get block commit at a given height. + async fn commit(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(commit::Request::new(height.into())).await + } + + /// `/consensus_params`: get current consensus parameters at the specified + /// height. + async fn consensus_params(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(consensus_params::Request::new(Some(height.into()))) + .await + } + + /// `/consensus_state`: get current consensus state + async fn consensus_state(&self) -> Result { + self.perform(consensus_state::Request::new()).await + } + + // TODO(thane): Simplify once validators endpoint removes pagination. + /// `/validators`: get validators a given height. + async fn validators(&self, height: H, paging: Paging) -> Result + where + H: Into + Send, + { + let height = height.into(); + match paging { + Paging::Default => { + self.perform(validators::Request::new(Some(height), None, None)) + .await + } + Paging::Specific { + page_number, + per_page, + } => { + self.perform(validators::Request::new( + Some(height), + Some(page_number), + Some(per_page), + )) + .await + } + Paging::All => { + let mut page_num = 1_usize; + let mut validators = Vec::new(); + let per_page = DEFAULT_VALIDATORS_PER_PAGE.into(); + loop { + let response = self + .perform(validators::Request::new( + Some(height), + Some(page_num.into()), + Some(per_page), + )) + .await?; + validators.extend(response.validators); + if validators.len() as i32 == response.total { + return Ok(validators::Response::new( + response.block_height, + validators, + response.total, + )); + } + page_num += 1; + } + } + } + } + + /// `/consensus_params`: get the latest consensus parameters. + async fn latest_consensus_params(&self) -> Result { + self.perform(consensus_params::Request::new(None)).await + } + + /// `/commit`: get the latest block commit + async fn latest_commit(&self) -> Result { + self.perform(commit::Request::default()).await + } + + /// `/health`: get node health. + /// + /// Returns empty result (200 OK) on success, no response in case of an error. + async fn health(&self) -> Result<(), Error> { + self.perform(health::Request).await?; + Ok(()) + } + + /// `/genesis`: get genesis file. + async fn genesis(&self) -> Result, Error> + where + AppState: fmt::Debug + Serialize + DeserializeOwned + Send, + { + Ok(self.perform(genesis::Request::default()).await?.genesis) + } + + /// `/net_info`: obtain information about P2P and other network connections. + async fn net_info(&self) -> Result { + self.perform(net_info::Request).await + } + + /// `/status`: get Tendermint status including node info, pubkey, latest + /// block hash, app hash, block height and time. + async fn status(&self) -> Result { + self.perform(status::Request).await + } + + /// `/broadcast_evidence`: broadcast an evidence. + async fn broadcast_evidence(&self, e: Evidence) -> Result { + self.perform(evidence::Request::new(e)).await + } + + /// `/tx`: find transaction by hash. + async fn tx(&self, hash: Hash, prove: bool) -> Result { + self.perform(tx::Request::new(hash, prove)).await + } + + /// `/tx_search`: search for transactions with their results. + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.perform(tx_search::Request::new(query, prove, page, per_page, order)) + .await + } + + #[cfg(any( + feature = "tendermint-rpc/http-client", + feature = "tendermint-rpc/websocket-client" + ))] + /// Poll the `/health` endpoint until it returns a successful result or + /// the given `timeout` has elapsed. + async fn wait_until_healthy(&self, timeout: T) -> Result<(), Error> + where + T: Into + Send, + { + let timeout = timeout.into(); + let poll_interval = core::time::Duration::from_millis(200); + let mut attempts_remaining = timeout.as_millis() / poll_interval.as_millis(); + + while self.health().await.is_err() { + if attempts_remaining == 0 { + return Err(Error::timeout(timeout)); + } + + attempts_remaining -= 1; + tokio::time::sleep(poll_interval).await; + } + + Ok(()) + } + + /// Perform a request against the RPC endpoint. + /// + /// This method is used by the default implementations of specific + /// endpoint methods. The latest protocol dialect is assumed to be invoked. + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest; +} + +#[cfg(not(target_arch = "wasm32"))] +mod non_wasm { + use super::*; + use cosmrs::tendermint::abci::response::Info; + use std::fmt::Debug; + use tendermint_rpc::endpoint::abci_query::AbciQuery; + use tendermint_rpc::endpoint::block::Response; + + #[async_trait] + impl TendermintRpcClient for C + where + C: tendermint_rpc::client::Client + Sync, + { + async fn abci_info(&self) -> Result { + self.abci_info().await + } + + async fn abci_query( + &self, + path: Option, + data: V, + height: Option, + prove: bool, + ) -> Result + where + V: Into> + Send, + { + self.abci_query(path, data, height, prove).await + } + + async fn block(&self, height: H) -> Result + where + H: Into + Send, + { + self.block(height).await + } + + async fn block_by_hash(&self, hash: Hash) -> Result { + self.block_by_hash(hash).await + } + + async fn latest_block(&self) -> Result { + self.latest_block().await + } + + async fn header(&self, height: H) -> Result + where + H: Into + Send, + { + self.header(height).await + } + + async fn header_by_hash(&self, hash: Hash) -> Result { + self.header_by_hash(hash).await + } + + async fn block_results(&self, height: H) -> Result + where + H: Into + Send, + { + self.block_results(height).await + } + + async fn latest_block_results(&self) -> Result { + self.latest_block_results().await + } + + async fn block_search( + &self, + query: Query, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.block_search(query, page, per_page, order).await + } + + async fn blockchain(&self, min: H, max: H) -> Result + where + H: Into + Send, + { + self.blockchain(min, max).await + } + + async fn broadcast_tx_async(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.broadcast_tx_async(tx).await + } + + async fn broadcast_tx_sync(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.broadcast_tx_sync(tx).await + } + + async fn broadcast_tx_commit( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + self.broadcast_tx_commit(tx).await + } + + async fn commit(&self, height: H) -> Result + where + H: Into + Send, + { + self.commit(height).await + } + + async fn consensus_params(&self, height: H) -> Result + where + H: Into + Send, + { + self.consensus_params(height).await + } + + async fn consensus_state(&self) -> Result { + self.consensus_state().await + } + + async fn validators( + &self, + height: H, + paging: Paging, + ) -> Result + where + H: Into + Send, + { + self.validators(height, paging).await + } + + async fn latest_consensus_params(&self) -> Result { + self.latest_consensus_params().await + } + + async fn latest_commit(&self) -> Result { + self.latest_commit().await + } + + async fn health(&self) -> Result<(), Error> { + self.health().await + } + + async fn genesis(&self) -> Result, Error> + where + AppState: Debug + Serialize + DeserializeOwned + Send, + { + self.genesis().await + } + + async fn net_info(&self) -> Result { + self.net_info().await + } + + async fn status(&self) -> Result { + self.status().await + } + + async fn broadcast_evidence(&self, e: Evidence) -> Result { + self.broadcast_evidence(e).await + } + + async fn tx(&self, hash: Hash, prove: bool) -> Result { + self.tx(hash, prove).await + } + + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.tx_search(query, prove, page, per_page, order).await + } + + #[cfg(any( + feature = "tendermint-rpc/http-client", + feature = "tendermint-rpc/websocket-client" + ))] + async fn wait_until_healthy(&self, timeout: T) -> Result<(), Error> + where + T: Into + Send, + { + self.wait_until_healthy(timeout).await + } + + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.perform(request).await + } + } +} diff --git a/common/client-libs/validator-client/src/rpc/reqwest.rs b/common/client-libs/validator-client/src/rpc/reqwest.rs new file mode 100644 index 0000000000..9c8411d666 --- /dev/null +++ b/common/client-libs/validator-client/src/rpc/reqwest.rs @@ -0,0 +1,118 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::rpc::TendermintRpcClient; +use async_trait::async_trait; +use reqwest::header::HeaderMap; +use reqwest::{header, RequestBuilder}; +use tendermint_rpc::{Error, Response, SimpleRequest}; +use url::Url; + +pub struct ReqwestRpcClient { + inner: reqwest::Client, + url: Url, +} + +impl ReqwestRpcClient { + pub fn new(url: Url) -> Self { + ReqwestRpcClient { + inner: reqwest::Client::new(), + url, + } + } + + fn build_request(&self, request: R) -> RequestBuilder { + let mut headers = HeaderMap::new(); + headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap()); + headers.insert( + header::USER_AGENT, + format!("nym-reqwest-rpc-client/{}", env!("CARGO_PKG_VERSION")) + .parse() + .unwrap(), + ); + if let Some(auth) = extract_authorization(&self.url) { + headers.insert(header::AUTHORIZATION, auth.parse().unwrap()); + } + + self.inner + .post(self.url.clone()) + .body(request.into_json().into_bytes()) + .headers(headers) + } +} + +trait TendermintRpcErrorMap { + fn into_rpc_err(self) -> Error; +} + +impl TendermintRpcErrorMap for reqwest::Error { + fn into_rpc_err(self) -> Error { + todo!() + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for ReqwestRpcClient { + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + let request = self.build_request(request); + // that's extremely unfortunate. the trait requires returning tendermint rpc error so we have to make best effort error mapping + let response = request + .send() + .await + .map_err(TendermintRpcErrorMap::into_rpc_err)?; + let bytes = response + .bytes() + .await + .map_err(TendermintRpcErrorMap::into_rpc_err)?; + R::Response::from_string(bytes).map(Into::into) + } +} + +// essentially https://github.com/informalsystems/tendermint-rs/blob/v0.32.0/rpc/src/client/transport/auth.rs#L31 +pub fn extract_authorization(url: &Url) -> Option { + if !url.has_authority() { + return None; + } + + let authority = url.authority(); + if let Some((userpass, _)) = authority.split_once('@') { + Some(base64::encode(userpass)) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(test)] + mod extracting_url_authorization { + use super::*; + use std::str::FromStr; + + #[test] + fn extract_auth_absent() { + let uri = Url::from_str("http://example.com").unwrap(); + assert_eq!(extract_authorization(&uri), None); + } + + #[test] + fn extract_auth_username_only() { + let uri = Url::from_str("http://toto@example.com").unwrap(); + let base64 = "dG90bw==".to_string(); + assert_eq!(extract_authorization(&uri), Some(base64)); + } + + #[test] + fn extract_auth_username_password() { + let uri = Url::from_str("http://toto:tata@example.com").unwrap(); + let base64 = "dG90bzp0YXRh".to_string(); + assert_eq!(extract_authorization(&uri), Some(base64)); + } + } +} diff --git a/common/client-libs/validator-client/src/signing/direct_wallet.rs b/common/client-libs/validator-client/src/signing/direct_wallet.rs index 177e50a2f2..b26ac39ada 100644 --- a/common/client-libs/validator-client/src/signing/direct_wallet.rs +++ b/common/client-libs/validator-client/src/signing/direct_wallet.rs @@ -35,6 +35,7 @@ pub enum DirectSecp256k1HdWalletError { AccountDerivationError { source: eyre::Report }, } +// TODO: maybe lock this one behind feature flag? #[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)] pub struct DirectSecp256k1HdWallet { /// Base secret diff --git a/common/client-libs/validator-client/src/signing/signer.rs b/common/client-libs/validator-client/src/signing/signer.rs index 074924a0cd..4b620291fe 100644 --- a/common/client-libs/validator-client/src/signing/signer.rs +++ b/common/client-libs/validator-client/src/signing/signer.rs @@ -33,6 +33,16 @@ pub enum SignerType { pub trait OfflineSigner { type Error: From; + // I really dislike existence of this function because it makes you re-derive your key **twice** for each contract transaction + fn signer_addresses(&self) -> Result, Self::Error> { + let derived_addresses = self + .get_accounts()? + .into_iter() + .map(|account| account.address) + .collect(); + Ok(derived_addresses) + } + fn get_accounts(&self) -> Result, Self::Error>; fn find_account(&self, signer_address: &AccountId) -> Result { @@ -94,3 +104,25 @@ pub trait OfflineSigner { // fn sign_amino_with_account(&self, signer: &AccountData, sign_doc: AminoSignDoc) -> Result; } + +#[derive(Debug, Default, Copy, Clone)] +pub struct NoSigner; + +// #[derive(Debug, Copy, Clone, Error)] +// #[error("no signer is available")] +// struct SignerUnavailable; +// +// // trait bound requirements +// impl From for SignerUnavailable { +// fn from(_: SigningError) -> Self { +// SignerUnavailable +// } +// } +// +// impl OfflineSigner for NoSigner { +// type Error = SignerUnavailable; +// +// fn get_accounts(&self) -> Result, Self::Error> { +// return Err(SignerUnavailable); +// } +// } diff --git a/common/client-libs/validator-client/src/signing/tx_signer.rs b/common/client-libs/validator-client/src/signing/tx_signer.rs index f14f640793..f04047c39e 100644 --- a/common/client-libs/validator-client/src/signing/tx_signer.rs +++ b/common/client-libs/validator-client/src/signing/tx_signer.rs @@ -6,53 +6,33 @@ use crate::signing::SignerData; use cosmrs::tx::{SignDoc, SignerInfo}; use cosmrs::{tx, AccountId, Any}; -#[derive(Debug)] -/// A client that has only one responsibility - sign transactions -/// and not touch chain. -pub struct TxSigner { - signer: S, -} - -impl TxSigner { - pub fn new(signer: S) -> Self { - TxSigner { signer } +// extension trait for the OfflineSigner to allow to sign transactions +pub trait TxSigner: OfflineSigner { + fn signer_public_key(&self, signer_address: &AccountId) -> Option { + let account = self.find_account(signer_address).ok()?; + Some(account.public_key().into()) } - pub fn signer(&self) -> &S { - &self.signer - } - - pub fn into_inner_signer(self) -> S { - self.signer - } - - pub fn sign_amino( + fn sign_amino( &self, _signer_address: &AccountId, _messages: Vec, _fee: tx::Fee, _memo: impl Into + Send + 'static, _signer_data: SignerData, - ) -> Result - where - S: OfflineSigner, - { + ) -> Result::Error> { unimplemented!() } - // TODO: change this sucker to use the trait better - pub fn sign_direct( + fn sign_direct( &self, signer_address: &AccountId, messages: Vec, fee: tx::Fee, memo: impl Into + Send + 'static, signer_data: SignerData, - ) -> Result - where - S: OfflineSigner, - { - let account_from_signer = self.signer.find_account(signer_address)?; + ) -> Result::Error> { + let account_from_signer = self.find_account(signer_address)?; // TODO: experiment with this field let timeout_height = 0u32; @@ -70,7 +50,8 @@ impl TxSigner { ) .map_err(|source| SigningError::SignDocFailure { source })?; - self.signer - .sign_direct_with_account(&account_from_signer, sign_doc) + self.sign_direct_with_account(&account_from_signer, sign_doc) } } + +impl TxSigner for T where T: OfflineSigner {} diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index 28f319b875..45c9139fe3 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -19,16 +19,16 @@ log = { workspace = true } rand = {version = "0.6", features = ["std"] } serde = { version = "1.0", features = ["derive"] } serde_json = { workspace = true } -thiserror = "1" +thiserror = { workspace = true } time = { version = "0.3.6", features = ["parsing", "formatting"] } toml = "0.5.6" -url = "2.2" +url = { workspace = true } tap = "1" cosmrs = { workspace = true } cosmwasm-std = { workspace = true } -nym-validator-client = { path = "../client-libs/validator-client", features = ["signing", "http-client"] } +nym-validator-client = { path = "../client-libs/validator-client" } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] } nym-network-defaults = { path = "../network-defaults" } diff --git a/common/commands/src/context/mod.rs b/common/commands/src/context/mod.rs index e2d648fbf0..db20ed0ac3 100644 --- a/common/commands/src/context/mod.rs +++ b/common/commands/src/context/mod.rs @@ -8,17 +8,19 @@ use nym_network_defaults::{ NymNetworkDetails, }; pub use nym_validator_client::nym_api::Client as NymApiClient; -use nym_validator_client::nyxd::{ - self, AccountId, DirectSigningNyxdClient, NyxdClient, QueryNyxdClient, +use nym_validator_client::nyxd::{self, AccountId, NyxdClient}; +use nym_validator_client::{ + DirectSigningHttpRpcNyxdClient, DirectSigningHttpRpcValidatorClient, QueryHttpRpcNyxdClient, + QueryHttpRpcValidatorClient, }; use tap::prelude::*; pub mod errors; -pub type SigningClient = nym_validator_client::nyxd::NyxdClient; -pub type QueryClient = nym_validator_client::nyxd::NyxdClient; -pub type SigningClientWithNyxd = nym_validator_client::Client; -pub type QueryClientWithNyxd = nym_validator_client::Client; +pub type SigningClient = DirectSigningHttpRpcNyxdClient; +pub type QueryClient = QueryHttpRpcNyxdClient; +pub type SigningClientWithNyxd = DirectSigningHttpRpcValidatorClient; +pub type QueryClientWithNyxd = QueryHttpRpcValidatorClient; #[derive(Debug)] pub struct ClientArgs { @@ -79,7 +81,7 @@ pub fn create_signing_client( .nyxd_url .as_str(); - match NyxdClient::connect_with_mnemonic(client_config, nyxd_url, mnemonic, None) { + match NyxdClient::connect_with_mnemonic(client_config, nyxd_url, mnemonic) { Ok(client) => Ok(client), Err(e) => Err(ContextError::NyxdError(format!("{e}"))), } diff --git a/common/commands/src/validator/account/balance.rs b/common/commands/src/validator/account/balance.rs index 8786470bd1..4e109ad7b3 100644 --- a/common/commands/src/validator/account/balance.rs +++ b/common/commands/src/validator/account/balance.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::{error, info}; -use nym_validator_client::nyxd::AccountId; +use nym_validator_client::nyxd::{AccountId, CosmWasmClient}; use crate::context::QueryClient; use crate::utils::{pretty_coin, show_error}; diff --git a/common/commands/src/validator/account/pubkey.rs b/common/commands/src/validator/account/pubkey.rs index b1ed260568..6a7c3383d6 100644 --- a/common/commands/src/validator/account/pubkey.rs +++ b/common/commands/src/validator/account/pubkey.rs @@ -3,7 +3,7 @@ use clap::Parser; use log::{error, info}; -use nym_validator_client::nyxd::AccountId; +use nym_validator_client::nyxd::{AccountId, CosmWasmClient}; use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; use crate::context::QueryClient; @@ -68,7 +68,7 @@ pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip3 pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) { info!("Getting public key for address {} from chain...", address); - match client.get_account_details(&address).await { + match client.get_account(&address).await { Ok(Some(account)) => { if let Ok(base_account) = account.try_get_base_account() { if let Some(pubkey) = base_account.pubkey { diff --git a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs index f07cd21d5f..54b2516d6e 100644 --- a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::{Coin, MixId}; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs b/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs index 4e6d91926b..f29dae6db3 100644 --- a/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs +++ b/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs @@ -10,6 +10,7 @@ use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough}; use comfy_table::Table; use cosmwasm_std::Addr; use nym_mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventKind}; +use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient; #[derive(Debug, Parser)] pub struct Args {} @@ -21,12 +22,14 @@ pub async fn execute(_args: Args, client: SigningClientWithNyxd) { ); let delegations = client - .get_all_delegator_delegations(client.nyxd.address()) + .nyxd + .get_all_delegator_delegations(&client.nyxd.address()) .await .map_err(show_error_passthrough); let mixnet_contract_events = client - .get_all_nyxd_pending_epoch_events() + .nyxd + .get_all_pending_epoch_events() .await .map_err(show_error_passthrough); diff --git a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs index 61265c9ac5..327a3c0fd3 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs index 6d9e0bcbf7..aa6b8dbe44 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs index 790f518267..cadb6c272f 100644 --- a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs index 41a992f0ed..45c4ffa5c3 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs @@ -5,8 +5,8 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::{Coin, MixId}; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs index 42493f493d..13bae0b7a0 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs @@ -4,8 +4,8 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs index 76b6e93fa0..28fe160070 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs @@ -7,7 +7,7 @@ use log::{info, warn}; use nym_contracts_common::signing::MessageSignature; use nym_mixnet_contract_common::Coin; use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs b/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs index ff4f8eaf32..3845a6c7ef 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs @@ -8,7 +8,7 @@ use cosmwasm_std::Coin; use nym_bin_common::output_format::OutputFormat; use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload; use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; #[derive(Debug, Parser)] pub struct Args { @@ -62,7 +62,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { let coin = Coin::new(args.amount, denom); - let nonce = match client.get_signing_nonce(client.address()).await { + let nonce = match client.get_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -73,9 +73,11 @@ pub async fn create_payload(args: Args, client: SigningClient) { } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let proxy = if args.with_vesting_account { - Some(account_id_to_cw_addr(client.vesting_contract_address())) + Some(account_id_to_cw_addr( + client.vesting_contract_address().unwrap(), + )) } else { None }; diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs index cce3d26dfe..c0cf879147 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::GatewayConfigUpdate; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,7 +29,7 @@ pub async fn update_config(args: Args, client: SigningClient) { info!("Update gateway config!"); let current_details = match client - .get_owned_gateway(client.address()) + .get_owned_gateway(&client.address()) .await .expect("failed to query the chain for gateway details") .gateway diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs index e734ddc97d..346b652b2f 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::GatewayConfigUpdate; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -30,7 +30,7 @@ pub async fn vesting_update_config(args: Args, client: SigningClient) { info!("Update vesting gateway config!"); let current_details = match client - .get_owned_gateway(client.address()) + .get_owned_gateway(&client.address()) .await .expect("failed to query the chain for gateway details") .gateway diff --git a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs index 7603db1a6e..af9c6c8bb4 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs index 21f771c0b3..91c2817be8 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs @@ -7,7 +7,7 @@ use log::{info, warn}; use nym_contracts_common::signing::MessageSignature; use nym_mixnet_contract_common::{Coin, Gateway}; use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs index 8e79dd7bc7..1c4312424c 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs index 8fe6bb8040..862a797566 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs @@ -10,7 +10,7 @@ use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent}; use nym_network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; use nym_validator_client::nyxd::CosmWasmCoin; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs index 969e40024a..556021dcf9 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::Coin; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs index f5ff9e3419..6acaca80d2 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs @@ -4,8 +4,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs index 7b02731b30..c81dc35f69 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs @@ -10,7 +10,7 @@ use nym_bin_common::output_format::OutputFormat; use nym_crypto::asymmetric::identity; use nym_mixnet_contract_common::construct_family_join_permit; use nym_mixnet_contract_common::families::FamilyHead; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; #[derive(Debug, Parser)] pub struct Args { @@ -47,7 +47,7 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli // make sure this mixnode is actually a family head if client - .get_node_family_by_head(mixnode.bond_information.identity()) + .get_node_family_by_head(mixnode.bond_information.identity().to_string()) .await .unwrap() .family @@ -70,7 +70,9 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli // let address = account_id_to_cw_addr(&args.address); let proxy = if args.with_vesting_account { - Some(account_id_to_cw_addr(client.vesting_contract_address())) + Some(account_id_to_cw_addr( + client.vesting_contract_address().unwrap(), + )) } else { None }; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs index b69dd7a4b2..411a8412b7 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs @@ -7,8 +7,8 @@ use log::info; use nym_contracts_common::signing::MessageSignature; use nym_crypto::asymmetric::identity; use nym_mixnet_contract_common::families::FamilyHead; -use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs index 9305c56918..27b5d71d7d 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_crypto::asymmetric::identity; -use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs index 193c003ab4..0673be508a 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs @@ -6,8 +6,8 @@ use clap::Parser; use log::info; use nym_crypto::asymmetric::identity; use nym_mixnet_contract_common::families::FamilyHead; -use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs b/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs index b44d159398..332de7614e 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs @@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode use nym_network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; use nym_validator_client::nyxd::CosmWasmCoin; #[derive(Debug, Parser)] @@ -88,7 +88,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { }, }; - let nonce = match client.get_signing_nonce(client.address()).await { + let nonce = match client.get_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -99,9 +99,11 @@ pub async fn create_payload(args: Args, client: SigningClient) { } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let proxy = if args.with_vesting_account { - Some(account_id_to_cw_addr(client.vesting_contract_address())) + Some(account_id_to_cw_addr( + client.vesting_contract_address().unwrap(), + )) } else { None }; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs b/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs index 1fbf38eb0c..1989725907 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::Coin; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs index 729a5e8c03..a5f8d65236 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs index 8c1d43e091..3b88c6cf5c 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs index 69fd8531be..733dea04a1 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixNodeConfigUpdate; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,7 +29,7 @@ pub async fn update_config(args: Args, client: SigningClient) { info!("Update mix node config!"); let current_details = match client - .get_owned_mixnode(client.address()) + .get_owned_mixnode(&client.address()) .await .expect("failed to query the chain for mixnode details") .mixnode_details diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs index 5b956b929f..a495c8f4b5 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixNodeConfigUpdate; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -30,7 +30,7 @@ pub async fn vesting_update_config(client: SigningClient, args: Args) { info!("Update vesting mix node config!"); let current_details = match client - .get_owned_mixnode(client.address()) + .get_owned_mixnode(&client.address()) .await .expect("failed to query the chain for mixnode details") .mixnode_details diff --git a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs index ecfc3652e1..fe6f3df223 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs @@ -3,7 +3,7 @@ use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs index 9928dec18f..415f9fe2b3 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs @@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{MixNode, Percent}; use nym_network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use nym_validator_client::nyxd::{traits::VestingSigningClient, CosmWasmCoin}; +use nym_validator_client::nyxd::{contract_traits::VestingSigningClient, CosmWasmCoin}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs index d38d7f61dc..bb83e1ffaa 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::Coin; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs index 0774fd89d6..9917e02860 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::Coin; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs index 49f717a5af..3e63846430 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/name/delete.rs b/common/commands/src/validator/mixnet/operators/name/delete.rs index 558dbf190c..d93b432108 100644 --- a/common/commands/src/validator/mixnet/operators/name/delete.rs +++ b/common/commands/src/validator/mixnet/operators/name/delete.rs @@ -1,7 +1,7 @@ use clap::Parser; use log::{error, info}; use nym_name_service_common::NameId; -use nym_validator_client::nyxd::{error::NyxdError, traits::NameServiceSigningClient}; +use nym_validator_client::nyxd::{contract_traits::NameServiceSigningClient, error::NyxdError}; use tap::TapFallible; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/name/register.rs b/common/commands/src/validator/mixnet/operators/name/register.rs index 85afd3012a..54a4b1a20d 100644 --- a/common/commands/src/validator/mixnet/operators/name/register.rs +++ b/common/commands/src/validator/mixnet/operators/name/register.rs @@ -3,7 +3,7 @@ use log::{error, info}; use nym_contracts_common::signing::MessageSignature; use nym_name_service_common::{Address, Coin, NameDetails, NymName}; use nym_sphinx::addressing::clients::Recipient; -use nym_validator_client::nyxd::{error::NyxdError, traits::NameServiceSigningClient}; +use nym_validator_client::nyxd::{contract_traits::NameServiceSigningClient, error::NyxdError}; use tap::TapFallible; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs b/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs index 05774ac30b..e293f23c2e 100644 --- a/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs @@ -14,7 +14,7 @@ use nym_name_service_common::{ signing_types::construct_name_register_sign_payload, Address, NymName, }; use nym_sphinx::addressing::clients::Recipient; -use nym_validator_client::nyxd::{error::NyxdError, traits::NameServiceQueryClient}; +use nym_validator_client::nyxd::{contract_traits::NameServiceQueryClient, error::NyxdError}; #[derive(Debug, Parser)] pub struct Args { @@ -45,7 +45,7 @@ pub async fn create_payload(args: Args, client: SigningClient) -> Result<(), Nyx let denom = client.current_chain_details().mix_denom.base.as_str(); let deposit = Coin::new(args.amount, denom); - let nonce = match client.get_name_signing_nonce(client.address()).await { + let nonce = match client.get_name_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -56,7 +56,7 @@ pub async fn create_payload(args: Args, client: SigningClient) -> Result<(), Nyx } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let payload = construct_name_register_sign_payload(nonce, address, deposit, name); let wrapper = DataWrapper::new(payload.to_base58_string().unwrap()); println!("{}", args.output.format(&wrapper)); diff --git a/common/commands/src/validator/mixnet/operators/service/announce.rs b/common/commands/src/validator/mixnet/operators/service/announce.rs index a3e86bc674..b61652254e 100644 --- a/common/commands/src/validator/mixnet/operators/service/announce.rs +++ b/common/commands/src/validator/mixnet/operators/service/announce.rs @@ -2,7 +2,7 @@ use clap::Parser; use log::info; use nym_contracts_common::signing::MessageSignature; use nym_service_provider_directory_common::{Coin, NymAddress, ServiceDetails, ServiceType}; -use nym_validator_client::nyxd::traits::SpDirectorySigningClient; +use nym_validator_client::nyxd::contract_traits::SpDirectorySigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs b/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs index f8ac587f9b..f3dc4e5e66 100644 --- a/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs @@ -15,7 +15,7 @@ use nym_service_provider_directory_common::{ ServiceType::NetworkRequester, }; use nym_sphinx::addressing::clients::Recipient; -use nym_validator_client::nyxd::traits::SpDirectoryQueryClient; +use nym_validator_client::nyxd::contract_traits::SpDirectoryQueryClient; #[derive(Debug, Parser)] pub struct Args { @@ -42,7 +42,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { let denom = client.current_chain_details().mix_denom.base.as_str(); let deposit = Coin::new(args.amount, denom); - let nonce = match client.get_service_signing_nonce(client.address()).await { + let nonce = match client.get_service_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -53,7 +53,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let payload = construct_service_provider_announce_sign_payload(nonce, address, deposit, service); let wrapper = DataWrapper::new(payload.to_base58_string().unwrap()); diff --git a/common/commands/src/validator/mixnet/operators/service/delete.rs b/common/commands/src/validator/mixnet/operators/service/delete.rs index eb86f4028f..3d0ca4a676 100644 --- a/common/commands/src/validator/mixnet/operators/service/delete.rs +++ b/common/commands/src/validator/mixnet/operators/service/delete.rs @@ -1,7 +1,7 @@ use clap::Parser; use log::info; use nym_service_provider_directory_common::ServiceId; -use nym_validator_client::nyxd::traits::SpDirectorySigningClient; +use nym_validator_client::nyxd::contract_traits::SpDirectorySigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/transactions/get_transaction.rs b/common/commands/src/validator/transactions/get_transaction.rs index 6ad1bbbd2f..62d829d52b 100644 --- a/common/commands/src/validator/transactions/get_transaction.rs +++ b/common/commands/src/validator/transactions/get_transaction.rs @@ -7,6 +7,7 @@ use std::str::FromStr; use crate::context::QueryClient; use crate::utils::show_error; use cosmrs::tendermint::Hash; +use nym_validator_client::nyxd::CosmWasmClient; use serde_json::json; #[derive(Debug, Parser)] diff --git a/common/commands/src/validator/transactions/query_transactions.rs b/common/commands/src/validator/transactions/query_transactions.rs index b8b341ec57..f109d9a07a 100644 --- a/common/commands/src/validator/transactions/query_transactions.rs +++ b/common/commands/src/validator/transactions/query_transactions.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use clap::Parser; use cosmrs::rpc::query::Query; +use nym_validator_client::nyxd::CosmWasmClient; use serde_json::json; use crate::context::QueryClient; diff --git a/common/commands/src/validator/vesting/balance.rs b/common/commands/src/validator/vesting/balance.rs index d5f5af20fe..ff8e2ee28c 100644 --- a/common/commands/src/validator/vesting/balance.rs +++ b/common/commands/src/validator/vesting/balance.rs @@ -5,7 +5,7 @@ use clap::Parser; use cosmrs::AccountId; use log::{error, info}; -use nym_validator_client::nyxd::{traits::VestingQueryClient, Coin}; +use nym_validator_client::nyxd::{contract_traits::VestingQueryClient, Coin, CosmWasmClient}; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/vesting/create_vesting_schedule.rs b/common/commands/src/validator/vesting/create_vesting_schedule.rs index f7397bf14c..151350bf2c 100644 --- a/common/commands/src/validator/vesting/create_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/create_vesting_schedule.rs @@ -8,7 +8,7 @@ use log::info; use nym_mixnet_contract_common::Coin; use nym_network_defaults::NymNetworkDetails; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use nym_validator_client::nyxd::AccountId; use nym_validator_client::nyxd::{CosmosCoin, Denom}; use nym_vesting_contract_common::types::VestingSpecification; diff --git a/common/commands/src/validator/vesting/query_vesting_schedule.rs b/common/commands/src/validator/vesting/query_vesting_schedule.rs index 9c8facfb70..0baca858f1 100644 --- a/common/commands/src/validator/vesting/query_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/query_vesting_schedule.rs @@ -6,7 +6,7 @@ use cosmrs::AccountId; use cosmwasm_std::Coin as CosmWasmCoin; use log::{error, info}; -use nym_validator_client::nyxd::{traits::VestingQueryClient, Coin}; +use nym_validator_client::nyxd::{contract_traits::VestingQueryClient, Coin, CosmWasmClient}; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/vesting/withdraw_vested.rs b/common/commands/src/validator/vesting/withdraw_vested.rs index a85e960a1a..2c2bf254b9 100644 --- a/common/commands/src/validator/vesting/withdraw_vested.rs +++ b/common/commands/src/validator/vesting/withdraw_vested.rs @@ -5,8 +5,8 @@ use clap::Parser; use log::info; use nym_validator_client::nyxd::{ - traits::{VestingQueryClient, VestingSigningClient}, - Coin, + contract_traits::{VestingQueryClient, VestingSigningClient}, + Coin, CosmWasmClient, }; use crate::context::SigningClient; @@ -39,7 +39,7 @@ pub async fn execute(args: Args, client: SigningClient) { .await .unwrap_or_else(|_| Coin::new(0u128, denom)); let liquid_account_balance = client - .get_balance(account_id, denom.to_string()) + .get_balance(&account_id, denom.to_string()) .await .unwrap_or(None) .unwrap_or_else(|| Coin::new(0u128, denom)); @@ -97,7 +97,7 @@ pub async fn execute(args: Args, client: SigningClient) { .unwrap_or_else(|_| Coin::new(0u128, denom)); let liquid_account_balance = client - .get_balance(account_id, denom.to_string()) + .get_balance(&account_id, denom.to_string()) .await .unwrap_or(None) .unwrap_or_else(|| Coin::new(0u128, denom)); diff --git a/common/config/Cargo.toml b/common/config/Cargo.toml index ed894e808b..d7ca649e60 100644 --- a/common/config/Cargo.toml +++ b/common/config/Cargo.toml @@ -12,7 +12,7 @@ handlebars = "3.5.5" log = { workspace = true } serde = { workspace = true, features = ["derive"] } toml = "0.7.4" -url = "2.2" +url = { workspace = true } nym-network-defaults = { path = "../network-defaults" } diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs index 076f84291c..b986b77f41 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs @@ -42,6 +42,7 @@ pub enum ExecuteMsg { }, VerifyVerificationKeyShare { + // TODO: this should be using a String... owner: Addr, resharing: bool, }, diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 6bb10d5cb6..1f4c444b31 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -14,7 +14,7 @@ thiserror = "1.0" nym-coconut-interface = { path = "../coconut-interface" } nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] } nym-api-requests = { path = "../../nym-api/nym-api-requests" } -nym-validator-client = { path = "../client-libs/validator-client" } +nym-validator-client = { path = "../client-libs/validator-client", default-features = false } [dev-dependencies] rand = "0.7.3" diff --git a/common/mixnode-common/Cargo.toml b/common/mixnode-common/Cargo.toml index 62fec9422a..b2fcf6e2a3 100644 --- a/common/mixnode-common/Cargo.toml +++ b/common/mixnode-common/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] bytes = "1.0" -futures = "0.3" +futures = { workspace = true } humantime-serde = "1.0" log = { workspace = true } rand = "0.8" @@ -21,8 +21,8 @@ tokio = { version = "1.24.1", features = [ "io-util", ] } tokio-util = { version = "0.7.4", features = ["codec"] } -url = "2.2" -thiserror = "1.0.37" +url = { workspace = true } +thiserror = { workspace = true } ## tracing tracing = { version = "0.1.37", optional = true } diff --git a/common/network-defaults/Cargo.toml b/common/network-defaults/Cargo.toml index f2878ad16a..bc5bddb497 100644 --- a/common/network-defaults/Cargo.toml +++ b/common/network-defaults/Cargo.toml @@ -12,6 +12,7 @@ cfg-if = { workspace = true } dotenvy = { workspace = true } hex-literal = "0.3.3" once_cell = { workspace = true } +schemars = { version = "0.8", features = ["preserve_order"] } serde = { workspace = true, features = ["derive"]} thiserror = { workspace = true } url = { workspace = true } diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 77b1761d1c..33373dcc52 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD}; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::{ env::{var, VarError}, @@ -14,14 +15,14 @@ use url::Url; pub mod mainnet; pub mod var_names; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct ChainDetails { pub bech32_account_prefix: String, pub mix_denom: DenomDetailsOwned, pub stake_denom: DenomDetailsOwned, } -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct NymContracts { pub mixnet_contract_address: Option, pub vesting_contract_address: Option, @@ -35,7 +36,7 @@ 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, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct NymNetworkDetails { pub network_name: String, pub chain_details: ChainDetails, @@ -104,7 +105,7 @@ impl NymNetworkDetails { .parse() .expect("denomination exponent is not u32"), }) - .with_validator_endpoint(ValidatorDetails::new( + .with_additional_validator_endpoint(ValidatorDetails::new( var(var_names::NYXD).expect("nyxd validator not set"), Some(var(var_names::NYM_API).expect("nym api not set")), )) @@ -164,6 +165,10 @@ impl NymNetworkDetails { } } + pub fn default_gas_price_amount(&self) -> f64 { + GAS_PRICE_AMOUNT + } + #[must_use] pub fn with_network_name(mut self, network_name: String) -> Self { self.network_name = network_name; @@ -201,11 +206,17 @@ impl NymNetworkDetails { } #[must_use] - pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { + pub fn with_additional_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { self.endpoints.push(endpoint); self } + #[must_use] + pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { + self.endpoints = vec![endpoint]; + self + } + #[must_use] pub fn with_mixnet_contract>(mut self, contract: Option) -> Self { self.contracts.mixnet_contract_address = contract.map(Into::into); @@ -276,7 +287,7 @@ impl DenomDetails { } } -#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq)] +#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)] pub struct DenomDetailsOwned { pub base: String, pub display: String, @@ -304,7 +315,7 @@ impl DenomDetailsOwned { } } -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct ValidatorDetails { // it is assumed those values are always valid since they're being provided in our defaults file pub nyxd_url: String, diff --git a/common/node-tester-utils/Cargo.toml b/common/node-tester-utils/Cargo.toml index 39a93d4a9f..32ec845723 100644 --- a/common/node-tester-utils/Cargo.toml +++ b/common/node-tester-utils/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.3.28" +futures = { workspace = true } rand = "0.7.3" serde = { workspace = true } diff --git a/common/socks5-client-core/Cargo.toml b/common/socks5-client-core/Cargo.toml index 4c7df38c8e..6a5221bba0 100644 --- a/common/socks5-client-core/Cargo.toml +++ b/common/socks5-client-core/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] dirs = "4.0" -futures = "0.3" +futures = { workspace = true } log = { workspace = true } pin-project = "1.0" rand = { version = "0.7.3", features = ["wasm-bindgen"] } @@ -17,7 +17,7 @@ serde = { workspace = true, features = ["derive"] } # for config serialization/d tap = "1.0.1" thiserror = "1.0.34" tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } -url = "2.2" +url = { workspace = true } nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] } diff --git a/common/socks5/proxy-helpers/Cargo.toml b/common/socks5/proxy-helpers/Cargo.toml index 88157a22c4..6ebaaa03c9 100644 --- a/common/socks5/proxy-helpers/Cargo.toml +++ b/common/socks5/proxy-helpers/Cargo.toml @@ -12,7 +12,7 @@ tokio = { version = "1.24.1", features = [ "net", "io-util", "sync", "macros", " tokio-util = { version = "0.7.4", features = [ "io" ] } # reason for getting this guy is to to able to port to tokio 1.X more quickly by being able to use # their `read_buf` [from the util crate] replacement rather than having to rethink/reimplement `AvailableReader` with the new AsyncRead trait definition. # In the long run, the dependency should probably get removed in favour of pure-tokio implementation, but for time being it's fine. -futures = "0.3" +futures = { workspace = true } log = { workspace = true } # internal diff --git a/common/task/Cargo.toml b/common/task/Cargo.toml index 7e94b5442b..500cdb2a53 100644 --- a/common/task/Cargo.toml +++ b/common/task/Cargo.toml @@ -8,10 +8,10 @@ license.workspace = true repository.workspace = true [dependencies] -futures = "0.3" +futures = { workspace = true } log = { workspace = true } -thiserror = "1.0.37" -tokio = { version = "1.24.1", features = ["macros", "sync"] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["macros", "sync"] } [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] version = "1.24.1" diff --git a/common/types/Cargo.toml b/common/types/Cargo.toml index 35c7cd228a..5515ba5248 100644 --- a/common/types/Cargo.toml +++ b/common/types/Cargo.toml @@ -15,8 +15,8 @@ schemars = "0.8" serde = { version = "1.0", features = ["derive"] } serde_json = { workspace = true } strum = { version = "0.23", features = ["derive"] } -thiserror = "1.0" -url = "2.2" +thiserror = { workspace = true } +url = { workspace = true } ts-rs = "6.1.2" cosmwasm-std = { workspace = true } diff --git a/common/types/src/error.rs b/common/types/src/error.rs index 15b938da9a..4546731ff4 100644 --- a/common/types/src/error.rs +++ b/common/types/src/error.rs @@ -1,3 +1,4 @@ +use nym_validator_client::error::TendermintRpcError; use nym_validator_client::nym_api::error::NymAPIError; use nym_validator_client::{nyxd::error::NyxdError, ValidatorClientError}; use serde::{Serialize, Serializer}; @@ -18,6 +19,11 @@ pub enum TypesError { source: cosmwasm_std::StdError, }, #[error("{source}")] + TendermintRpcError { + #[from] + source: TendermintRpcError, + }, + #[error("{source}")] ErrorReport { #[from] source: eyre::Report, @@ -92,6 +98,7 @@ impl From for TypesError { ValidatorClientError::MalformedUrlProvided(e) => e.into(), ValidatorClientError::NyxdError(e) => e.into(), ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured, + ValidatorClientError::TendermintErrorRpc(err) => err.into(), } } } diff --git a/common/wasm-utils/Cargo.toml b/common/wasm-utils/Cargo.toml index 2964e32110..fb4b25c5d8 100644 --- a/common/wasm-utils/Cargo.toml +++ b/common/wasm-utils/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.3" +futures = { workspace = true } js-sys = "^0.3.51" wasm-bindgen = "=0.2.83" wasm-bindgen-futures = "0.4" diff --git a/explorer-api/explorer-api-requests/Cargo.toml b/explorer-api/explorer-api-requests/Cargo.toml index 93c8faaada..7d8dc2f803 100644 --- a/explorer-api/explorer-api-requests/Cargo.toml +++ b/explorer-api/explorer-api-requests/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" } nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } -nym-validator-client = { path = "../../common/client-libs/validator-client" } +nym-api-requests = { path = "../../nym-api/nym-api-requests" } schemars = { version = "0.8", features = ["preserve_order"] } serde = { version = "1.0", features = ["derive"] } ts-rs = { version = "6.1.2", optional = true } diff --git a/explorer-api/explorer-api-requests/src/lib.rs b/explorer-api/explorer-api-requests/src/lib.rs index bfcafa2860..6180d65f11 100644 --- a/explorer-api/explorer-api-requests/src/lib.rs +++ b/explorer-api/explorer-api-requests/src/lib.rs @@ -1,6 +1,6 @@ +use nym_api_requests::models::NodePerformance; use nym_contracts_common::Percent; use nym_mixnet_contract_common::{Addr, Coin, Layer, MixId, MixNode}; -use nym_validator_client::models::NodePerformance; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/explorer-api/src/client.rs b/explorer-api/src/client.rs index 9b345e4cf6..1b565fcd60 100644 --- a/explorer-api/src/client.rs +++ b/explorer-api/src/client.rs @@ -2,7 +2,7 @@ use nym_network_defaults::{ var_names::{NYM_API, NYXD}, NymNetworkDetails, }; -use nym_validator_client::nyxd::QueryNyxdClient; +use nym_validator_client::QueryHttpRpcValidatorClient; use reqwest::Url; use std::{str::FromStr, sync::Arc}; @@ -11,9 +11,7 @@ use std::{str::FromStr, sync::Arc}; // when that becomes a requirement, we would simply put an extra RwLock (or Mutex) in here #[derive(Clone)] -pub(crate) struct ThreadsafeValidatorClient( - pub(crate) Arc>, -); +pub(crate) struct ThreadsafeValidatorClient(pub(crate) Arc); impl ThreadsafeValidatorClient { pub(crate) fn new() -> Self { diff --git a/explorer-api/src/mix_node/delegations.rs b/explorer-api/src/mix_node/delegations.rs index d9c151214d..feaefcf5c0 100644 --- a/explorer-api/src/mix_node/delegations.rs +++ b/explorer-api/src/mix_node/delegations.rs @@ -5,6 +5,7 @@ use super::models::SummedDelegations; use crate::client::ThreadsafeValidatorClient; use itertools::Itertools; use nym_mixnet_contract_common::{Delegation, MixId}; +use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient; pub(crate) async fn get_single_mixnode_delegations( client: &ThreadsafeValidatorClient, @@ -12,7 +13,8 @@ pub(crate) async fn get_single_mixnode_delegations( ) -> Vec { match client .0 - .get_all_nyxd_single_mixnode_delegations(mix_id) + .nyxd + .get_all_single_mixnode_delegations(mix_id) .await { Ok(result) => result, diff --git a/explorer-api/src/tasks.rs b/explorer-api/src/tasks.rs index 548d1f1a0a..b0b9a9701b 100644 --- a/explorer-api/src/tasks.rs +++ b/explorer-api/src/tasks.rs @@ -1,14 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::future::Future; - use nym_mixnet_contract_common::GatewayBond; use nym_task::TaskClient; use nym_validator_client::models::MixNodeBondAnnotated; use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::nyxd::{Paging, QueryNyxdClient, ValidatorResponse}; -use nym_validator_client::ValidatorClientError; +use nym_validator_client::nyxd::{Paging, TendermintRpcClient, ValidatorResponse}; +use nym_validator_client::{QueryHttpRpcValidatorClient, ValidatorClientError}; +use std::future::Future; use crate::mix_nodes::CACHE_REFRESH_RATE; use crate::state::ExplorerApiStateContext; @@ -26,7 +25,7 @@ impl ExplorerApiTasks { // a helper to remove duplicate code when grabbing active/rewarded/all mixnodes async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec where - F: FnOnce(&'a nym_validator_client::Client) -> Fut, + F: FnOnce(&'a QueryHttpRpcValidatorClient) -> Fut, Fut: Future, ValidatorClientError>>, { let bonds = match f(&self.state.inner.validator_client.0).await { @@ -75,7 +74,7 @@ impl ExplorerApiTasks { .validator_client .0 .nyxd - .get_validators(height.value(), Paging::All) + .validators(height, Paging::All) .await?; info!("Fetched {} validators", response.validators.len()); Ok(response) diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 589c65ec8f..25e2b62375 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -25,7 +25,7 @@ colored = "2.0" dashmap = "4.0" dirs = "4.0" dotenvy = { workspace = true } -futures = "0.3" +futures = { workspace = true } humantime-serde = "1.0.1" lazy_static = "1.4.0" log = { workspace = true } diff --git a/gateway/gateway-requests/Cargo.toml b/gateway/gateway-requests/Cargo.toml index 92b2a7c8e4..dcd8ad3743 100644 --- a/gateway/gateway-requests/Cargo.toml +++ b/gateway/gateway-requests/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] bs58 = "0.4.0" -futures = "0.3.15" +futures = { workspace = true } generic-array = { workspace = true, features = ["serde"] } log = { workspace = true } rand = { version = "0.7.3", features = ["wasm-bindgen"] } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index 1a41937c4b..0fe930a96d 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -22,6 +22,7 @@ use crate::node::client_handling::bandwidth::Bandwidth; use crate::node::client_handling::FREE_TESTNET_BANDWIDTH_VALUE; use nym_gateway_requests::iv::IV; use nym_task::TaskClient; +use nym_validator_client::coconut::CoconutApiError; #[derive(Debug, Error)] pub(crate) enum RequestHandlingError { @@ -64,6 +65,9 @@ pub(crate) enum RequestHandlingError { #[error("Coconut interface error - {0}")] CoconutInterfaceError(#[from] nym_coconut_interface::error::CoconutInterfaceError), + #[error("coconut api query failure: {0}")] + CoconutApiError(#[from] CoconutApiError), + #[error("Credential error - {0}")] CredentialError(#[from] nym_credentials::error::Error), } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index e19f4a14fe..356a884161 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -4,14 +4,17 @@ use super::authenticated::RequestHandlingError; use log::*; use nym_coconut_interface::Credential; -use nym_validator_client::nyxd::traits::DkgQueryClient; +use nym_validator_client::coconut::all_coconut_api_clients; use nym_validator_client::{ nyxd::{ + contract_traits::{ + CoconutBandwidthSigningClient, DkgQueryClient, MultisigQueryClient, + MultisigSigningClient, + }, cosmwasm_client::logs::{find_attribute, BANDWIDTH_PROPOSAL_ID}, - traits::{CoconutBandwidthSigningClient, MultisigQueryClient, MultisigSigningClient}, - Coin, DirectSigningNyxdClient, Fee, + Coin, Fee, }, - Client, CoconutApiClient, + CoconutApiClient, DirectSigningHttpRpcNyxdClient, }; use std::time::{Duration, SystemTime}; @@ -19,18 +22,13 @@ const ONE_HOUR_SEC: u64 = 3600; const MAX_FEEGRANT_UNYM: u128 = 10000; pub(crate) struct CoconutVerifier { - nyxd_client: Client, + nyxd_client: DirectSigningHttpRpcNyxdClient, mix_denom_base: String, } impl CoconutVerifier { - pub fn new(nyxd_client: Client) -> Self { - let mix_denom_base = nyxd_client - .nyxd - .current_chain_details() - .mix_denom - .base - .clone(); + pub fn new(nyxd_client: DirectSigningHttpRpcNyxdClient) -> Self { + let mix_denom_base = nyxd_client.current_chain_details().mix_denom.base.clone(); CoconutVerifier { nyxd_client, @@ -41,7 +39,7 @@ impl CoconutVerifier { pub async fn all_current_coconut_api_clients( &self, ) -> Result, RequestHandlingError> { - let epoch_id = self.nyxd_client.nyxd.get_current_epoch().await?.epoch_id; + let epoch_id = self.nyxd_client.get_current_epoch().await?.epoch_id; self.all_coconut_api_clients(epoch_id).await } @@ -49,7 +47,7 @@ impl CoconutVerifier { &self, epoch_id: u64, ) -> Result, RequestHandlingError> { - Ok(CoconutApiClient::all_coconut_api_clients(&self.nyxd_client, epoch_id).await?) + Ok(all_coconut_api_clients(&self.nyxd_client, epoch_id).await?) } pub async fn release_funds( @@ -63,14 +61,13 @@ impl CoconutVerifier { let res = self .nyxd_client - .nyxd .spend_credential( Coin::new( credential.voucher_value().into(), self.mix_denom_base.clone(), ), credential.blinded_serial_number(), - self.nyxd_client.nyxd.address().to_string(), + self.nyxd_client.address().to_string(), None, ) .await?; @@ -84,7 +81,7 @@ impl CoconutVerifier { reason: String::from("proposal id could not be parsed to u64"), })?; - let proposal = self.nyxd_client.nyxd.get_proposal(proposal_id).await?; + let proposal = self.nyxd_client.query_proposal(proposal_id).await?; if !credential.has_blinded_serial_number(&proposal.description)? { return Err(RequestHandlingError::ProposalIdError { reason: String::from("proposal has different serial number"), @@ -94,11 +91,10 @@ impl CoconutVerifier { let req = nym_api_requests::coconut::VerifyCredentialBody::new( credential.clone(), proposal_id, - self.nyxd_client.nyxd.address().clone(), + self.nyxd_client.address().clone(), ); for client in api_clients { self.nyxd_client - .nyxd .grant_allowance( &client.cosmos_address, vec![Coin::new(MAX_FEEGRANT_UNYM, self.mix_denom_base.clone())], @@ -111,7 +107,6 @@ impl CoconutVerifier { .await?; let ret = client.api_client.verify_bandwidth_credential(&req).await; self.nyxd_client - .nyxd .revoke_allowance( &client.cosmos_address, "Cleanup the previous allowance for releasing funds".to_string(), @@ -121,19 +116,16 @@ impl CoconutVerifier { match ret { Ok(res) => { if !res.verification_result { - debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.api_client.nym_api_client.current_url()); + debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.api_client.nym_api.current_url()); } } Err(e) => { - warn!("Validator {} could not be reached. There might be a problem with the coconut endpoint - {:?}", client.api_client.nym_api_client.current_url(), e); + warn!("Validator {} could not be reached. There might be a problem with the coconut endpoint - {:?}", client.api_client.nym_api.current_url(), e); } } } - self.nyxd_client - .nyxd - .execute_proposal(proposal_id, None) - .await?; + self.nyxd_client.execute_proposal(proposal_id, None).await?; Ok(()) } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index 8487e6c99f..93bce3a6fd 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -17,7 +17,7 @@ use nym_mixnet_client::forwarder::{MixForwardingSender, PacketForwarder}; use nym_network_defaults::NymNetworkDetails; use nym_statistics_common::collector::StatisticsSender; use nym_task::{TaskClient, TaskManager}; -use nym_validator_client::Client; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; use rand::seq::SliceRandom; use rand::thread_rng; use std::error::Error; @@ -212,26 +212,22 @@ impl Gateway { nym_validator_client::NymApiClient::new(nym_api.clone()) } - fn random_nyxd_client( - &self, - ) -> nym_validator_client::Client { + fn random_nyxd_client(&self) -> DirectSigningHttpRpcNyxdClient { let endpoints = self.config.get_nyxd_urls(); let validator_nyxd = endpoints .choose(&mut thread_rng()) .expect("The list of validators is empty"); let network_details = NymNetworkDetails::new_from_env(); - let client_config = nym_validator_client::Config::try_from_nym_network_details( - &network_details, - ) - .expect("failed to construct valid validator client config with the provided network"); + let client_config = nyxd::Config::try_from_nym_network_details(&network_details) + .expect("failed to construct valid validator client config with the provided network"); - let mut client = Client::new_signing(client_config, self.config.get_cosmos_mnemonic()) - .expect("Could not connect with mnemonic"); - client - .change_nyxd(validator_nyxd.clone()) - .expect("Could not use the random nyxd URL"); - client + DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + client_config, + validator_nyxd.as_ref(), + self.config.get_cosmos_mnemonic(), + ) + .expect("Could not connect with mnemonic") } async fn check_if_bonded(&self) -> Result { diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index a670dd4f81..101ac4d513 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -22,7 +22,7 @@ clap = { version = "4.0", features = ["cargo", "derive"] } colored = "2.0" cupid = "0.6.1" dirs = "4.0" -futures = "0.3.0" +futures = { workspace = true } humantime-serde = "1.0" lazy_static = "1.4.0" log = { workspace = true } diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 10f1017cb4..5e7a9488d1 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -22,7 +22,7 @@ cfg-if = "1.0" clap = { version = "4.0", features = ["cargo", "derive"] } console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable" dirs = "4.0" -futures = "0.3.24" +futures = { workspace = true } humantime-serde = "1.0" lazy_static = "1.4.0" log = { workspace = true } @@ -33,10 +33,10 @@ rand-07 = { package = "rand", version = "0.7.3" } # required for compatibility reqwest = { workspace = true, features = ["json"] } rocket = { version = "0.5.0-rc.2", features = ["json"] } rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "dfd3662c49e2f6fc37df35091cb94d82f7fb5915" } -serde = "1.0" +serde = { workspace = true } serde_json = { workspace = true } tap = "1.0" -thiserror = "1.0" +thiserror = { workspace = true } time = { version = "0.3.14", features = ["serde-human-readable", "parsing"] } tokio = { version = "1.24.1", features = [ "rt-multi-thread", @@ -45,7 +45,7 @@ tokio = { version = "1.24.1", features = [ "time", ] } tokio-stream = "0.1.11" -url = "2.2" +url = { workspace = true } ts-rs = {version = "6.1", optional = true} @@ -74,6 +74,7 @@ cosmwasm-std = { workspace = true } nym-credential-storage = { path = "../common/credential-storage" } nym-credentials = { path = "../common/credentials" } nym-crypto = { path = "../common/crypto" } +cw2 = { workspace = true } cw3 = { workspace = true } cw4 = { workspace = true } nym-dkg = { path = "../common/dkg", features = ["cw-types"] } @@ -91,9 +92,7 @@ nym-pemstore = { path = "../common/pemstore" } nym-task = { path = "../common/task" } nym-topology = { path = "../common/topology" } nym-api-requests = { path = "nym-api-requests" } -nym-validator-client = { path = "../common/client-libs/validator-client", features = [ - "http-client", "signing" -] } +nym-validator-client = { path = "../common/client-libs/validator-client" } nym-bin-common = { path = "../common/bin-common" } nym-node-tester-utils = { path = "../common/node-tester-utils" } diff --git a/nym-api/src/circulating_supply_api/cache/mod.rs b/nym-api/src/circulating_supply_api/cache/mod.rs index 003d25feb4..70794bc72e 100644 --- a/nym-api/src/circulating_supply_api/cache/mod.rs +++ b/nym-api/src/circulating_supply_api/cache/mod.rs @@ -4,8 +4,8 @@ use self::data::CirculatingSupplyCacheData; use cosmwasm_std::Addr; use nym_api_requests::models::CirculatingSupplyResponse; +use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::nyxd::Coin; -use nym_validator_client::ValidatorClientError; use rocket::fairing::AdHoc; use std::ops::Deref; use std::{ @@ -34,7 +34,7 @@ enum CirculatingSupplyCacheError { #[error(transparent)] ClientError { #[from] - source: ValidatorClientError, + source: NyxdError, }, } diff --git a/nym-api/src/coconut/comm.rs b/nym-api/src/coconut/comm.rs index 04e53c2c02..cf671defa1 100644 --- a/nym-api/src/coconut/comm.rs +++ b/nym-api/src/coconut/comm.rs @@ -6,7 +6,8 @@ use crate::nyxd; use nym_coconut_dkg_common::types::EpochId; use nym_coconut_interface::VerificationKey; use nym_credentials::coconut::utils::obtain_aggregate_verification_key; -use nym_validator_client::CoconutApiClient; +use nym_validator_client::coconut::all_coconut_api_clients; +use std::ops::Deref; #[async_trait] pub trait APICommunicationChannel { @@ -27,8 +28,7 @@ impl QueryCommunicationChannel { impl APICommunicationChannel for QueryCommunicationChannel { async fn aggregated_verification_key(&self, epoch_id: EpochId) -> Result { let client = self.nyxd_client.0.read().await; - let coconut_api_clients = - CoconutApiClient::all_coconut_api_clients(&client.nyxd, epoch_id).await?; + let coconut_api_clients = all_coconut_api_clients(client.deref(), epoch_id).await?; let vk = obtain_aggregate_verification_key(&coconut_api_clients).await?; Ok(vk) } diff --git a/nym-api/src/coconut/error.rs b/nym-api/src/coconut/error.rs index c540bc62d1..e038e74723 100644 --- a/nym-api/src/coconut/error.rs +++ b/nym-api/src/coconut/error.rs @@ -12,6 +12,7 @@ use nym_crypto::asymmetric::{ identity::{Ed25519RecoveryError, SignatureError}, }; use nym_dkg::error::DkgError; +use nym_validator_client::coconut::CoconutApiError; use nym_validator_client::nyxd::error::NyxdError; use crate::node_status_api::models::NymApiStorageError; @@ -23,6 +24,9 @@ pub enum CoconutError { #[error(transparent)] IOError(#[from] std::io::Error), + #[error("coconut api query failure: {0}")] + CoconutApiError(#[from] CoconutApiError), + #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), diff --git a/nym-api/src/main.rs b/nym-api/src/main.rs index 39719ba0fe..5784247e7f 100644 --- a/nym-api/src/main.rs +++ b/nym-api/src/main.rs @@ -5,6 +5,7 @@ extern crate rocket; use crate::epoch_operations::RewardedSetUpdater; +use crate::network::models::NetworkDetails; use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater; use crate::support::cli; use crate::support::cli::CliArgs; @@ -29,6 +30,7 @@ use support::{http, nyxd}; mod circulating_supply_api; mod coconut; mod epoch_operations; +pub(crate) mod network; mod network_monitor; pub(crate) mod node_status_api; pub(crate) mod nym_contract_cache; @@ -58,14 +60,16 @@ async fn start_nym_api_tasks( config: Config, ) -> Result> { let nyxd_client = nyxd::Client::new(&config); - let mix_denom = nyxd_client.chain_details().await.mix_denom.base; + let connected_nyxd = config.get_nyxd_url(); + let nym_network_details = config.get_network_details(); + let network_details = NetworkDetails::new(connected_nyxd.to_string(), nym_network_details); let coconut_keypair = coconut::keypair::KeyPair::new(); // let's build our rocket! let rocket = http::setup_rocket( &config, - mix_denom, + network_details, nyxd_client.clone(), coconut_keypair.clone(), ) diff --git a/nym-api/src/network/mod.rs b/nym-api/src/network/mod.rs new file mode 100644 index 0000000000..f06bc5bf16 --- /dev/null +++ b/nym-api/src/network/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use okapi::openapi3::OpenApi; +use rocket::Route; +use rocket_okapi::openapi_get_routes_spec; +use rocket_okapi::settings::OpenApiSettings; + +pub(crate) mod models; +mod routes; + +pub(crate) fn network_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { + openapi_get_routes_spec![ + settings: routes::network_details, routes::nym_contracts, routes::nym_contracts_detailed + ] +} diff --git a/nym-api/src/network/models.rs b/nym-api/src/network/models.rs new file mode 100644 index 0000000000..518b4352f1 --- /dev/null +++ b/nym-api/src/network/models.rs @@ -0,0 +1,28 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_config::defaults::NymNetworkDetails; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct NetworkDetails { + pub(crate) connected_nyxd: String, + pub(crate) network: NymNetworkDetails, +} + +impl NetworkDetails { + pub fn new(connected_nyxd: String, network: NymNetworkDetails) -> Self { + Self { + connected_nyxd, + network, + } + } +} + +#[derive(Serialize, Deserialize, Clone, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct ContractInformation { + pub(crate) address: Option, + pub(crate) details: Option, +} diff --git a/nym-api/src/network/routes.rs b/nym-api/src/network/routes.rs new file mode 100644 index 0000000000..77f60f0fa9 --- /dev/null +++ b/nym-api/src/network/routes.rs @@ -0,0 +1,63 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::network::models::{ContractInformation, NetworkDetails}; +use crate::nym_contract_cache::cache::NymContractCache; +use nym_contracts_common::ContractBuildInformation; +use rocket::serde::json::Json; +use rocket::State; +use rocket_okapi::openapi; +use std::collections::HashMap; +use std::ops::Deref; + +#[openapi(tag = "network")] +#[get("/details")] +pub(crate) fn network_details(details: &State) -> Json { + Json(details.deref().clone()) +} + +// I agree, it feels weird to be pulling contract cache here, but I feel like it makes +// more sense to return this information here rather than in the generic cache route +#[openapi(tag = "network")] +#[get("/nym-contracts")] +pub(crate) async fn nym_contracts( + cache: &State, +) -> Json>> { + let info = cache.contract_details().await; + Json( + info.value + .into_iter() + .map(|(contract, info)| { + ( + contract, + ContractInformation { + address: info.address.map(|a| a.to_string()), + details: info.base, + }, + ) + }) + .collect(), + ) +} + +#[openapi(tag = "network")] +#[get("/nym-contracts-detailed")] +pub(crate) async fn nym_contracts_detailed( + cache: &State, +) -> Json>> { + let info = cache.contract_details().await; + Json( + info.value + .into_iter() + .map(|(contract, info)| { + ( + contract, + ContractInformation { + address: info.address.map(|a| a.to_string()), + details: info.detailed, + }, + ) + }) + .collect(), + ) +} diff --git a/nym-api/src/nym_contract_cache/cache/data.rs b/nym-api/src/nym_contract_cache/cache/data.rs index 27285db41a..ede4bc6838 100644 --- a/nym-api/src/nym_contract_cache/cache/data.rs +++ b/nym-api/src/nym_contract_cache/cache/data.rs @@ -2,13 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::support::caching::Cache; +use nym_contracts_common::ContractBuildInformation; use nym_mixnet_contract_common::{ families::FamilyHead, GatewayBond, IdentityKey, Interval, MixId, MixNodeDetails, RewardingParams, }; use nym_name_service_common::RegisteredName; use nym_service_provider_directory_common::Service; -use std::collections::HashSet; +use nym_validator_client::nyxd::AccountId; +use std::collections::{HashMap, HashSet}; pub(crate) struct ValidatorCacheData { pub(crate) mixnodes: Cache>, @@ -27,6 +29,8 @@ pub(crate) struct ValidatorCacheData { pub(crate) service_providers: Cache>, pub(crate) registered_names: Cache>, + + pub(crate) contracts_info: Cache, } impl ValidatorCacheData { @@ -43,6 +47,31 @@ impl ValidatorCacheData { mix_to_family: Cache::default(), service_providers: Cache::default(), registered_names: Cache::default(), + contracts_info: Cache::default(), + } + } +} + +type ContractAddress = String; +pub type CachedContractsInfo = HashMap; + +#[derive(Clone)] +pub struct CachedContractInfo { + pub(crate) address: Option, + pub(crate) base: Option, + pub(crate) detailed: Option, +} + +impl CachedContractInfo { + pub fn new( + address: Option<&AccountId>, + base: Option, + detailed: Option, + ) -> Self { + Self { + address: address.cloned(), + base, + detailed, } } } diff --git a/nym-api/src/nym_contract_cache/cache/mod.rs b/nym-api/src/nym_contract_cache/cache/mod.rs index 2eac4f0c61..ec0ce7f8e5 100644 --- a/nym-api/src/nym_contract_cache/cache/mod.rs +++ b/nym-api/src/nym_contract_cache/cache/mod.rs @@ -1,3 +1,4 @@ +use crate::nym_contract_cache::cache::data::CachedContractsInfo; use crate::support::caching::Cache; use data::ValidatorCacheData; use nym_api_requests::models::MixnodeStatus; @@ -54,6 +55,7 @@ impl NymContractCache { mix_to_family: Vec<(IdentityKey, FamilyHead)>, services: Option>, names: Option>, + nym_contracts_info: CachedContractsInfo, ) { match time::timeout(Duration::from_millis(100), self.inner.write()).await { Ok(mut cache) => { @@ -67,6 +69,7 @@ impl NymContractCache { // Just return empty lists when these are not available cache.service_providers.update(services.unwrap_or_default()); cache.registered_names.update(names.unwrap_or_default()); + cache.contracts_info.update(nym_contracts_info) } Err(err) => { error!("{err}"); @@ -267,6 +270,16 @@ impl NymContractCache { } } + pub(crate) async fn contract_details(&self) -> Cache { + match time::timeout(Duration::from_millis(100), self.inner.read()).await { + Ok(cache) => cache.contracts_info.clone(), + Err(err) => { + error!("{err}"); + Cache::default() + } + } + } + pub async fn mixnode_details(&self, mix_id: MixId) -> (Option, MixnodeStatus) { // it might not be the most optimal to possibly iterate the entire vector to find (or not) // the relevant value. However, the vectors are relatively small (< 10_000 elements, < 1000 for active set) diff --git a/nym-api/src/nym_contract_cache/cache/refresher.rs b/nym-api/src/nym_contract_cache/cache/refresher.rs index 93ddabf3a1..e1fa5cf18f 100644 --- a/nym-api/src/nym_contract_cache/cache/refresher.rs +++ b/nym-api/src/nym_contract_cache/cache/refresher.rs @@ -1,10 +1,16 @@ use super::NymContractCache; +use crate::nym_contract_cache::cache::data::{CachedContractInfo, CachedContractsInfo}; use crate::nyxd::Client; use crate::support::caching::CacheNotification; use anyhow::Result; +use futures::future::join_all; use nym_mixnet_contract_common::{MixId, MixNodeDetails, RewardedSetNodeStatus}; use nym_task::TaskClient; -use nym_validator_client::nyxd::traits::{NameServiceQueryClient, SpDirectoryQueryClient}; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, NameServiceQueryClient, NymContractsProvider, PagedNameServiceQueryClient, + PagedSpDirectoryQueryClient, SpDirectoryQueryClient, VestingQueryClient, +}; +use nym_validator_client::nyxd::CosmWasmClient; use std::{collections::HashMap, sync::atomic::Ordering, time::Duration}; use tokio::sync::watch; use tokio::time; @@ -37,6 +43,129 @@ impl NymContractCacheRefresher { self.update_notifier.subscribe() } + async fn get_nym_contracts_info(&self) -> Result { + let mut updated = HashMap::new(); + + let client_guard = self.nyxd_client.read().await; + + let mixnet = client_guard.mixnet_contract_address(); + let vesting = client_guard.vesting_contract_address(); + let name_service = client_guard.name_service_contract_address(); + let service_provider = client_guard.service_provider_contract_address(); + let coconut_bandwidth = client_guard.coconut_bandwidth_contract_address(); + let coconut_dkg = client_guard.dkg_contract_address(); + let group = client_guard.group_contract_address(); + let multisig = client_guard.multisig_contract_address(); + + // get cw2 versions + let mixnet_cw2_future = client_guard.get_mixnet_contract_cw2_version(); + let vesting_cw2_future = client_guard.get_vesting_contract_cw2_version(); + let service_provider_cw2_future = client_guard.get_name_service_contract_cw2_version(); + let name_service_cw2_future = client_guard.get_name_service_contract_cw2_version(); + + // group and multisig contract save that information in their storage but don't expose it via queries + // so a temporary workaround... + let multisig_cw2 = if let Some(multisig_contract) = multisig { + client_guard + .query_contract_raw(multisig_contract, b"contract_info".to_vec()) + .await + .map(|r| serde_json::from_slice(&r).ok()) + .ok() + .flatten() + } else { + None + }; + let group_cw2 = if let Some(group_contract) = group { + client_guard + .query_contract_raw(group_contract, b"contract_info".to_vec()) + .await + .map(|r| serde_json::from_slice(&r).ok()) + .ok() + .flatten() + } else { + None + }; + + let mut cw2_info = join_all(vec![ + mixnet_cw2_future, + vesting_cw2_future, + service_provider_cw2_future, + name_service_cw2_future, + ]) + .await; + + // get detailed build info + let mixnet_detailed_future = client_guard.get_mixnet_contract_version(); + let vesting_detailed_future = client_guard.get_vesting_contract_version(); + let service_provider_detailed_future = client_guard.get_sp_contract_version(); + let name_service_detailed_future = client_guard.get_name_service_contract_version(); + + let mut build_info = join_all(vec![ + mixnet_detailed_future, + vesting_detailed_future, + service_provider_detailed_future, + name_service_detailed_future, + ]) + .await; + + // the below unwraps are fine as we definitely have the specified number of entries + // Note to whoever updates this code in the future: `pop` removes **LAST** element, + // so make sure you call them in correct order, depending on what's specified in the `join_all` + updated.insert( + "nym-name-service-contract".to_string(), + CachedContractInfo::new( + name_service, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + + updated.insert( + "nym-service-provider-directory-contract".to_string(), + CachedContractInfo::new( + service_provider, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + + updated.insert( + "nym-vesting-contract".to_string(), + CachedContractInfo::new( + vesting, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + updated.insert( + "nym-mixnet-contract".to_string(), + CachedContractInfo::new( + mixnet, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + + updated.insert( + "nym-coconut-bandwidth-contract".to_string(), + CachedContractInfo::new(coconut_bandwidth, None, None), + ); + updated.insert( + "nym-coconut-dkg-contract".to_string(), + CachedContractInfo::new(coconut_dkg, None, None), + ); + updated.insert( + "nym-cw3-multisig-contract".to_string(), + CachedContractInfo::new(multisig, multisig_cw2, None), + ); + updated.insert( + "nym-cw4-group-contract".to_string(), + CachedContractInfo::new(group, group_cw2, None), + ); + + Ok(updated) + } + async fn refresh(&self) -> Result<()> { let rewarding_params = self.nyxd_client.get_current_rewarding_parameters().await?; let current_interval = self.nyxd_client.get_current_interval().await?.interval; @@ -54,6 +183,7 @@ impl NymContractCacheRefresher { // The service providers and names are optional let services = self.nyxd_client.get_all_services().await.ok(); let names = self.nyxd_client.get_all_names().await.ok(); + let contract_info = self.get_nym_contracts_info().await?; info!( "Updating validator cache. There are {} mixnodes and {} gateways", @@ -72,6 +202,7 @@ impl NymContractCacheRefresher { mix_to_family, services, names, + contract_info, ) .await; diff --git a/nym-api/src/support/caching/mod.rs b/nym-api/src/support/caching/mod.rs index ad2e40e80d..0d2615d327 100644 --- a/nym-api/src/support/caching/mod.rs +++ b/nym-api/src/support/caching/mod.rs @@ -2,13 +2,13 @@ use serde::Serialize; use std::ops::Deref; use time::OffsetDateTime; -#[derive(Default, Serialize, Clone)] +#[derive(Serialize, Clone)] pub struct Cache { pub value: T, as_at: i64, } -impl Cache { +impl Cache { pub fn new(value: T) -> Self { Cache { value, @@ -38,6 +38,18 @@ impl Deref for Cache { } } +impl Default for Cache +where + T: Default, +{ + fn default() -> Self { + Cache { + value: T::default(), + as_at: 0, + } + } +} + fn current_unix_timestamp() -> i64 { let now = OffsetDateTime::now_utc(); now.unix_timestamp() diff --git a/nym-api/src/support/config/mod.rs b/nym-api/src/support/config/mod.rs index 51f2c6726c..163eaa44db 100644 --- a/nym-api/src/support/config/mod.rs +++ b/nym-api/src/support/config/mod.rs @@ -5,7 +5,7 @@ use crate::support::config::persistence::{ CoconutSignerPaths, NetworkMonitorPaths, NodeStatusAPIPaths, }; use crate::support::config::template::CONFIG_TEMPLATE; -use nym_config::defaults::mainnet; +use nym_config::defaults::{mainnet, NymNetworkDetails}; use nym_config::{ must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR, @@ -98,6 +98,16 @@ pub struct Config { pub coconut_signer: CoconutSigner, } +impl<'a> From<&'a Config> for NymNetworkDetails { + fn from(value: &'a Config) -> Self { + // we get the current environmental details and then overwrite whatever is appropriate with + // the values from the config + NymNetworkDetails::new_from_env() + .with_mixnet_contract(Some(value.get_mixnet_contract_address().as_ref())) + .with_vesting_contract(Some(value.get_vesting_contract_address().as_ref())) + } +} + impl NymConfigTemplate for Config { fn template() -> &'static str { CONFIG_TEMPLATE @@ -134,6 +144,10 @@ impl Config { save_formatted_config_to_file(self, config_save_location) } + pub fn get_network_details(&self) -> NymNetworkDetails { + self.into() + } + pub fn with_network_monitor_enabled(mut self, enabled: bool) -> Self { self.network_monitor.enabled = enabled; self diff --git a/nym-api/src/support/http/mod.rs b/nym-api/src/support/http/mod.rs index 572d1c281b..b21ccdb381 100644 --- a/nym-api/src/support/http/mod.rs +++ b/nym-api/src/support/http/mod.rs @@ -3,6 +3,8 @@ use crate::circulating_supply_api::cache::CirculatingSupplyCache; use crate::coconut::{self, comm::QueryCommunicationChannel, InternalSignRequest}; +use crate::network::models::NetworkDetails; +use crate::network::network_routes; use crate::node_status_api::{self, NodeStatusCache}; use crate::nym_contract_cache::cache::NymContractCache; use crate::support::config::Config; @@ -19,13 +21,15 @@ pub(crate) mod openapi; pub(crate) async fn setup_rocket( config: &Config, - mix_denom: String, + network_details: NetworkDetails, _nyxd_client: nyxd::Client, coconut_keypair: coconut::keypair::KeyPair, ) -> anyhow::Result> { let openapi_settings = rocket_okapi::settings::OpenApiSettings::default(); let mut rocket = rocket::build(); + let mix_denom = network_details.network.chain_details.mix_denom.base.clone(); + mount_endpoints_and_merged_docs! { rocket, "/v1".to_owned(), @@ -34,9 +38,11 @@ pub(crate) async fn setup_rocket( "" => circulating_supply_api::circulating_supply_routes(&openapi_settings), "" => nym_contract_cache::nym_contract_cache_routes(&openapi_settings), "/status" => node_status_api::node_status_routes(&openapi_settings, config.network_monitor.enabled), + "/network" => network_routes(&openapi_settings), } let rocket = rocket + .manage(network_details) .mount("/swagger", make_swagger_ui(&openapi::get_docs())) .attach(setup_cors()?) .attach(NymContractCache::stage()) diff --git a/nym-api/src/support/nyxd/mod.rs b/nym-api/src/support/nyxd/mod.rs index e35098ee1f..501be9e581 100644 --- a/nym-api/src/support/nyxd/mod.rs +++ b/nym-api/src/support/nyxd/mod.rs @@ -16,9 +16,9 @@ use nym_coconut_dkg_common::{ types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId}, verification_key::{ContractVKShare, VerificationKeyShare}, }; -use nym_config::defaults::{ChainDetails, NymNetworkDetails, DEFAULT_NYM_API_PORT}; +use nym_config::defaults::ChainDetails; use nym_contracts_common::dealings::ContractSafeBytes; -use nym_mixnet_contract_common::families::{Family, FamilyHead}; +use nym_mixnet_contract_common::families::FamilyHead; use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::reward_params::RewardingParams; use nym_mixnet_contract_common::{ @@ -27,31 +27,29 @@ use nym_mixnet_contract_common::{ }; use nym_name_service_common::msg::QueryMsg as NameServiceQueryMsg; use nym_service_provider_directory_common::msg::QueryMsg as SpQueryMsg; +use nym_validator_client::nyxd::contract_traits::{NameServiceQueryClient, PagedDkgQueryClient}; use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::nyxd::traits::{ - MixnetQueryClient, MixnetSigningClient, SpDirectoryQueryClient, -}; use nym_validator_client::nyxd::{ - cosmwasm_client::types::ExecuteResult, - traits::{ + contract_traits::{ CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, GroupQueryClient, - MultisigQueryClient, MultisigSigningClient, NameServiceQueryClient, VestingQueryClient, + MixnetQueryClient, MixnetSigningClient, MultisigQueryClient, MultisigSigningClient, + NymContractsProvider, PagedMixnetQueryClient, PagedMultisigQueryClient, + PagedVestingQueryClient, SpDirectoryQueryClient, }, - Fee, + cosmwasm_client::types::ExecuteResult, + CosmWasmClient, Fee, }; use nym_validator_client::nyxd::{ hash::{Hash, SHA256_HASH_SIZE}, - AccountId, Coin, DirectSigningNyxdClient, TendermintTime, + AccountId, Coin, TendermintTime, }; -use nym_validator_client::ValidatorClientError; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; use nym_vesting_contract_common::AccountVestingCoins; use serde::Deserialize; use std::sync::Arc; -use tokio::sync::RwLock; +use tokio::sync::{RwLock, RwLockReadGuard}; -pub(crate) struct Client( - pub(crate) Arc>>, -); +pub(crate) struct Client(pub(crate) Arc>); impl Clone for Client { fn clone(&self) -> Self { @@ -61,45 +59,41 @@ impl Clone for Client { impl Client { pub(crate) fn new(config: &Config) -> Self { - // the api address is irrelevant here as **WE ARE THE API** - // and we won't be talking on the socket here. - let api_url = format!("http://localhost:{}", DEFAULT_NYM_API_PORT) - .parse() - .unwrap(); + let details = config.get_network_details(); let nyxd_url = config.get_nyxd_url(); - let details = NymNetworkDetails::new_from_env() - .with_mixnet_contract(Some(config.get_mixnet_contract_address().as_ref())) - .with_vesting_contract(Some(config.get_vesting_contract_address().as_ref())); - - let client_config = nym_validator_client::Config::try_from_nym_network_details(&details) - .expect("failed to construct valid validator client config with the provided network") - .with_urls(nyxd_url, api_url); + let client_config = nyxd::Config::try_from_nym_network_details(&details) + .expect("failed to construct valid validator client config with the provided network"); let mnemonic = config.get_mnemonic(); - let inner = nym_validator_client::Client::new_signing(client_config, mnemonic) - .expect("Failed to connect to nyxd!"); + let inner = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + client_config, + nyxd_url.as_str(), + mnemonic, + ) + .expect("Failed to connect to nyxd!"); Client(Arc::new(RwLock::new(inner))) } + pub(crate) async fn read(&self) -> RwLockReadGuard<'_, DirectSigningHttpRpcNyxdClient> { + self.0.read().await + } + pub(crate) async fn client_address(&self) -> AccountId { - self.0.read().await.nyxd.address().clone() + self.0.read().await.address() } pub(crate) async fn chain_details(&self) -> ChainDetails { - self.0.read().await.nyxd.current_chain_details().clone() + self.0.read().await.current_chain_details().clone() } - pub(crate) async fn get_rewarding_validator_address( - &self, - ) -> Result { + pub(crate) async fn get_rewarding_validator_address(&self) -> Result { let cosmwasm_addr = self .0 .read() .await - .nyxd .get_mixnet_contract_state() .await? .rewarding_validator_address @@ -113,21 +107,13 @@ impl Client { cosmwasm_addr .clone() .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(cosmwasm_addr).into()) + .map_err(|_| NyxdError::MalformedAccountAddress(cosmwasm_addr)) } // a helper function for the future to obtain the current block timestamp #[allow(dead_code)] - pub(crate) async fn current_block_timestamp( - &self, - ) -> Result { - let time = self - .0 - .read() - .await - .nyxd - .get_current_block_timestamp() - .await?; + pub(crate) async fn current_block_timestamp(&self) -> Result { + let time = self.0.read().await.get_current_block_timestamp().await?; Ok(time) } @@ -142,8 +128,8 @@ impl Client { pub(crate) async fn get_block_hash( &self, height: u32, - ) -> Result, ValidatorClientError> { - let hash = match self.0.read().await.nyxd.get_block_hash(height).await? { + ) -> Result, NyxdError> { + let hash = match self.0.read().await.get_block_hash(height).await? { Hash::Sha256(hash) => Some(hash), Hash::None => None, }; @@ -151,101 +137,78 @@ impl Client { Ok(hash) } - pub(crate) async fn get_mixnodes(&self) -> Result, ValidatorClientError> { - self.0.read().await.get_all_nyxd_mixnodes_detailed().await + pub(crate) async fn get_mixnodes(&self) -> Result, NyxdError> { + self.0.read().await.get_all_mixnodes_detailed().await } - pub(crate) async fn get_gateways(&self) -> Result, ValidatorClientError> { - self.0.read().await.get_all_nyxd_gateways().await + pub(crate) async fn get_gateways(&self) -> Result, NyxdError> { + self.0.read().await.get_all_gateways().await } - pub(crate) async fn get_current_interval( - &self, - ) -> Result { - Ok(self.0.read().await.get_current_interval_details().await?) + pub(crate) async fn get_current_interval(&self) -> Result { + self.0.read().await.get_current_interval_details().await } - pub(crate) async fn get_current_epoch_status( - &self, - ) -> Result { - Ok(self.0.read().await.nyxd.get_current_epoch_status().await?) + pub(crate) async fn get_current_epoch_status(&self) -> Result { + self.0.read().await.get_current_epoch_status().await } pub(crate) async fn get_current_rewarding_parameters( &self, - ) -> Result { - Ok(self.0.read().await.get_rewarding_parameters().await?) + ) -> Result { + self.0.read().await.get_rewarding_parameters().await } pub(crate) async fn get_rewarded_set_mixnodes( &self, - ) -> Result, ValidatorClientError> { - self.0 - .read() - .await - .get_all_nyxd_rewarded_set_mixnodes() - .await + ) -> Result, NyxdError> { + self.0.read().await.get_all_rewarded_set_mixnodes().await } - pub(crate) async fn get_current_vesting_account_storage_key( - &self, - ) -> Result { + pub(crate) async fn get_current_vesting_account_storage_key(&self) -> Result { let guard = self.0.read().await; - let vesting_contract = guard.nyxd.vesting_contract_address(); + + // the expect is fine as we always construct the client with the vesting contract explicitly set + let vesting_contract = guard + .vesting_contract_address() + .expect("vesting contract address is not available"); // TODO: I don't like the usage of the hardcoded value here let res = guard - .nyxd .query_contract_raw(vesting_contract, b"key".to_vec()) .await?; if res.is_empty() { return Ok(0); } - Ok(serde_json::from_slice(&res).map_err(NyxdError::from)?) + serde_json::from_slice(&res).map_err(NyxdError::from) } pub(crate) async fn get_all_vesting_coins( &self, - ) -> Result, ValidatorClientError> { - Ok(self - .0 - .read() - .await - .nyxd - .get_all_accounts_vesting_coins() - .await?) - } - - #[allow(dead_code)] - pub(crate) async fn get_all_node_families(&self) -> Result, ValidatorClientError> { - self.0.read().await.get_all_node_families().await + ) -> Result, NyxdError> { + self.0.read().await.get_all_accounts_vesting_coins().await } pub(crate) async fn get_all_family_members( &self, - ) -> Result, ValidatorClientError> { + ) -> Result, NyxdError> { self.0.read().await.get_all_family_members().await } - pub(crate) async fn get_pending_events_count(&self) -> Result { + pub(crate) async fn get_pending_events_count(&self) -> Result { let pending = self.0.read().await.get_number_of_pending_events().await?; Ok(pending.epoch_events + pending.interval_events) } - pub(crate) async fn begin_epoch_transition(&self) -> Result<(), ValidatorClientError> { - self.0 - .write() - .await - .nyxd - .begin_epoch_transition(None) - .await?; + pub(crate) async fn begin_epoch_transition(&self) -> Result<(), NyxdError> { + self.0.write().await.begin_epoch_transition(None).await?; Ok(()) } pub(crate) async fn send_rewarding_messages( &self, nodes: &[MixnodeWithPerformance], - ) -> Result<(), ValidatorClientError> { + ) -> Result<(), NyxdError> { // for some reason, compiler complains if this is explicitly inline in code ¯\_(ツ)_/¯ #[inline] #[allow(unused_variables)] @@ -265,16 +228,21 @@ impl Client { } } - let contract = self.0.read().await.get_mixnet_contract_address(); + // "technically" we don't need a write access to the client, + // but we REALLY don't want to accidentally send any transactions while we're sending rewarding messages + // as that would have messed up sequence numbers + let guard = self.0.write().await; + + // the expect is fine as we always construct the client with the mixnet contract explicitly set + let mixnet_contract = guard + .mixnet_contract_address() + .expect("mixnet contract address is not available"); let msgs = generate_reward_messages(nodes); - self.0 - .write() - .await - .nyxd + guard .execute_multiple( - &contract, + mixnet_contract, msgs, Default::default(), format!("rewarding {} mixnodes", nodes.len()), @@ -287,24 +255,19 @@ impl Client { &self, new_rewarded_set: Vec, expected_active_set_size: u32, - ) -> Result<(), ValidatorClientError> { + ) -> Result<(), NyxdError> { self.0 .write() .await - .nyxd .advance_current_epoch(new_rewarded_set, expected_active_set_size, None) .await?; Ok(()) } - pub(crate) async fn reconcile_epoch_events( - &self, - limit: Option, - ) -> Result<(), ValidatorClientError> { + pub(crate) async fn reconcile_epoch_events(&self, limit: Option) -> Result<(), NyxdError> { self.0 .write() .await - .nyxd .reconcile_epoch_events(limit, None) .await?; Ok(()) @@ -317,25 +280,22 @@ impl crate::coconut::client::Client for Client { self.client_address().await } - async fn get_tx( - &self, - tx_hash: &str, - ) -> crate::coconut::error::Result { + async fn get_tx(&self, tx_hash: &str) -> crate::coconut::error::Result { let tx_hash: Hash = tx_hash .parse() .map_err(|_| CoconutError::TxHashParseError)?; - Ok(self.0.read().await.nyxd.get_tx(tx_hash).await?) + Ok(self.0.read().await.get_tx(tx_hash).await?) } async fn get_proposal( &self, proposal_id: u64, ) -> crate::coconut::error::Result { - Ok(self.0.read().await.nyxd.get_proposal(proposal_id).await?) + Ok(self.0.read().await.query_proposal(proposal_id).await?) } async fn list_proposals(&self) -> crate::coconut::error::Result> { - Ok(self.0.read().await.nyxd.get_all_proposals().await?) + Ok(self.0.read().await.get_all_proposals().await?) } async fn get_spent_credential( @@ -346,48 +306,35 @@ impl crate::coconut::client::Client for Client { .0 .read() .await - .nyxd .get_spent_credential(blinded_serial_number) .await?) } async fn get_current_epoch(&self) -> crate::coconut::error::Result { - Ok(self.0.read().await.nyxd.get_current_epoch().await?) + Ok(self.0.read().await.get_current_epoch().await?) } async fn group_member(&self, addr: String) -> crate::coconut::error::Result { - Ok(self.0.read().await.nyxd.member(addr).await?) + Ok(self.0.read().await.member(addr, None).await?) } async fn get_current_epoch_threshold( &self, ) -> crate::coconut::error::Result> { - Ok(self - .0 - .read() - .await - .nyxd - .get_current_epoch_threshold() - .await?) + Ok(self.0.read().await.get_current_epoch_threshold().await?) } async fn get_initial_dealers( &self, ) -> crate::coconut::error::Result> { - Ok(self.0.read().await.nyxd.get_initial_dealers().await?) + Ok(self.0.read().await.get_initial_dealers().await?) } async fn get_self_registered_dealer_details( &self, ) -> crate::coconut::error::Result { let self_address = &self.address().await; - Ok(self - .0 - .read() - .await - .nyxd - .get_dealer_details(self_address) - .await?) + Ok(self.0.read().await.get_dealer_details(self_address).await?) } async fn get_current_dealers(&self) -> crate::coconut::error::Result> { @@ -398,7 +345,12 @@ impl crate::coconut::client::Client for Client { &self, idx: usize, ) -> crate::coconut::error::Result> { - Ok(self.0.read().await.get_all_epoch_dealings(idx).await?) + Ok(self + .0 + .read() + .await + .get_all_epoch_dealings(idx as u64) + .await?) } async fn get_verification_key_shares( @@ -422,7 +374,6 @@ impl crate::coconut::client::Client for Client { self.0 .read() .await - .nyxd .vote_proposal(proposal_id, vote_yes, fee) .await?; Ok(()) @@ -432,19 +383,13 @@ impl crate::coconut::client::Client for Client { self.0 .read() .await - .nyxd .execute_proposal(proposal_id, None) .await?; Ok(()) } async fn advance_epoch_state(&self) -> crate::coconut::error::Result<()> { - self.0 - .write() - .await - .nyxd - .advance_dkg_epoch_state(None) - .await?; + self.0.write().await.advance_dkg_epoch_state(None).await?; Ok(()) } @@ -458,7 +403,6 @@ impl crate::coconut::client::Client for Client { .0 .write() .await - .nyxd .register_dealer(bte_key, announce_address, resharing, None) .await?) } @@ -472,7 +416,6 @@ impl crate::coconut::client::Client for Client { .0 .write() .await - .nyxd .submit_dealing_bytes(dealing_bytes, resharing, None) .await?) } @@ -486,7 +429,6 @@ impl crate::coconut::client::Client for Client { .0 .write() .await - .nyxd .submit_verification_key_share(share, resharing, None) .await?) } @@ -498,7 +440,7 @@ impl DkgQueryClient for Client { where for<'a> T: Deserialize<'a>, { - self.0.read().await.nyxd.query_dkg_contract(query).await + self.0.read().await.query_dkg_contract(query).await } } @@ -514,7 +456,6 @@ impl SpDirectoryQueryClient for Client { self.0 .read() .await - .nyxd .query_service_provider_contract(query) .await } @@ -529,11 +470,6 @@ impl NameServiceQueryClient for Client { where for<'a> T: Deserialize<'a>, { - self.0 - .read() - .await - .nyxd - .query_name_service_contract(query) - .await + self.0.read().await.query_name_service_contract(query).await } } diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index ef73c6c55e..51641a7a86 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -4056,7 +4056,7 @@ version = "0.1.0" dependencies = [ "async-trait", "log", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -4122,9 +4122,9 @@ dependencies = [ name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ + "nym-api-requests", "nym-contracts-common", "nym-mixnet-contract-common", - "nym-validator-client", "schemars", "serde", ] @@ -4246,6 +4246,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -4561,6 +4562,9 @@ dependencies = [ "colored 2.0.4", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -4591,6 +4595,7 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] @@ -6486,9 +6491,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.5.13" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" +checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826" dependencies = [ "sqlx-core 0.5.13", "sqlx-macros 0.5.13", diff --git a/nym-connect/desktop/src-tauri/Cargo.toml b/nym-connect/desktop/src-tauri/Cargo.toml index f1176cdc7c..192b1bb391 100644 --- a/nym-connect/desktop/src-tauri/Cargo.toml +++ b/nym-connect/desktop/src-tauri/Cargo.toml @@ -41,7 +41,7 @@ tauri = { version = "^1.2.2", features = ["clipboard-write-text", "macos-private thiserror = "1.0" time = { version = "0.3.17", features = ["local-offset"] } tokio = { version = "1.24.1", features = ["sync", "time"] } -url = "2.2" +url = "2.4" yaml-rust = "0.4" toml = "0.7" sentry = { version = "0.31.5", features = [ "anyhow" ] } diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index bc7f8ae20f..72ea6b5683 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -3334,6 +3334,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -3415,6 +3416,9 @@ dependencies = [ "colored 2.0.4", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -3445,6 +3449,7 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] @@ -3496,7 +3501,7 @@ dependencies = [ [[package]] name = "nym_wallet" -version = "1.2.6" +version = "1.2.7" dependencies = [ "async-trait", "base64 0.13.1", @@ -6107,6 +6112,20 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasmtimer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.64" diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index b34190a6d2..2297a1ac90 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -51,9 +51,7 @@ zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] } cosmwasm-std = "1.3.0" cosmrs = "=0.14.0" -nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "signing", "http-client" -] } +nym-validator-client = { path = "../../common/client-libs/validator-client" } nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] } nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" } nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 3d9374c22a..7f542d257c 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -196,11 +196,6 @@ impl From for BackendError { impl From for BackendError { fn from(e: ValidatorClientError) -> Self { - match e { - ValidatorClientError::NymAPIError { source } => source.into(), - ValidatorClientError::MalformedUrlProvided(e) => e.into(), - ValidatorClientError::NyxdError(e) => e.into(), - ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured.into(), - } + TypesError::from(e).into() } } diff --git a/nym-wallet/src-tauri/src/operations/helpers.rs b/nym-wallet/src-tauri/src/operations/helpers.rs index 57fcb5d1bb..35dacbb190 100644 --- a/nym-wallet/src-tauri/src/operations/helpers.rs +++ b/nym-wallet/src-tauri/src/operations/helpers.rs @@ -12,10 +12,10 @@ use nym_mixnet_contract_common::{ construct_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode, MixNodeCostParams, SignableGatewayBondingMsg, SignableMixNodeBondingMsg, }; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::{Coin, DirectSigningNyxdClient}; -use nym_validator_client::Client; +use nym_validator_client::nyxd::Coin; +use nym_validator_client::DirectSigningHttpRpcValidatorClient; // define this as a separate trait for mocking purposes #[async_trait] @@ -26,15 +26,20 @@ pub(crate) trait AddressAndNonceProvider { } #[async_trait] -impl AddressAndNonceProvider for Client { +impl AddressAndNonceProvider for DirectSigningHttpRpcValidatorClient { async fn get_signing_nonce(&self) -> Result { - self.nyxd.get_signing_nonce(self.nyxd.address()).await + self.nyxd.get_signing_nonce(&self.nyxd.address()).await } fn vesting_contract_address(&self) -> Addr { // the call to unchecked is fine here as we're converting directly from `AccountId` // which must have been a valid bech32 address - Addr::unchecked(self.nyxd.vesting_contract_address().as_ref()) + Addr::unchecked( + self.nyxd + .vesting_contract_address() + .expect("unknown vesting contract address") + .as_ref(), + ) } fn cw_address(&self) -> Addr { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 07f939ce95..4060b1e810 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -9,9 +9,10 @@ use cosmrs::bip32::DerivationPath; use itertools::Itertools; use nym_config::defaults::{NymNetworkDetails, COSMOS_DERIVATION_PATH}; use nym_types::account::{Account, AccountEntry, Balance}; +use nym_validator_client::nyxd::CosmWasmClient; use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; use nym_validator_client::signing::AccountData; -use nym_validator_client::{nyxd::DirectSigningNyxdClient, Client}; +use nym_validator_client::DirectSigningHttpRpcValidatorClient; use nym_wallet_types::network::Network as WalletNetwork; use std::collections::HashMap; use strum::IntoEnumIterator; @@ -35,7 +36,7 @@ pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result { @@ -251,7 +252,7 @@ fn create_clients( default_api_urls: &HashMap, config: &Config, mnemonic: &Mnemonic, -) -> Result)>, BackendError> { +) -> Result, BackendError> { let mut clients = Vec::new(); for network in WalletNetwork::iter() { let nyxd_url = if let Some(url) = config.get_selected_validator_nyxd_url(network) { @@ -285,10 +286,10 @@ fn create_clients( .with_vesting_contract(Some(config.get_vesting_contract_address(network).as_ref())); let config = nym_validator_client::Config::try_from_nym_network_details(&network_details)? - .with_urls(nyxd_url, api_url); + .with_urls(nyxd_url, api_url) + .with_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); - let mut client = nym_validator_client::Client::new_signing(config, mnemonic.clone())?; - client.set_nyxd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); + let client = nym_validator_client::Client::new_signing(config, mnemonic.clone())?; clients.push((network, client)); } Ok(clients) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs index d8824e41d8..2670ff360c 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs @@ -5,7 +5,7 @@ use crate::error::BackendError; use crate::state::WalletState; use nym_mixnet_contract_common::ContractStateParams; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; use nym_validator_client::nyxd::Fee; use nym_wallet_types::admin::TauriContractStateParams; diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index 4acd1aecfc..896b62175a 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -14,7 +14,7 @@ use nym_types::currency::DecCoin; use nym_types::gateway::GatewayBond; use nym_types::mixnode::{MixNodeCostParams, MixNodeDetails}; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; use nym_validator_client::nyxd::Fee; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; @@ -337,7 +337,10 @@ pub async fn get_mixnode_avg_uptime( log::info!(">>> Get mixnode bond details"); let guard = state.read().await; let client = guard.current_client()?; - let res = client.nyxd.get_owned_mixnode(client.nyxd.address()).await?; + let res = client + .nyxd + .get_owned_mixnode(&client.nyxd.address()) + .await?; match res.mixnode_details { Some(details) => { @@ -363,7 +366,10 @@ pub async fn mixnode_bond_details( log::info!(">>> Get mixnode bond details"); let guard = state.read().await; let client = guard.current_client()?; - let res = client.nyxd.get_owned_mixnode(client.nyxd.address()).await?; + let res = client + .nyxd + .get_owned_mixnode(&client.nyxd.address()) + .await?; let details = res .mixnode_details .map(|details| { @@ -391,7 +397,10 @@ pub async fn gateway_bond_details( log::info!(">>> Get gateway bond details"); let guard = state.read().await; let client = guard.current_client()?; - let bond = client.nyxd.get_owned_gateway(client.nyxd.address()).await?; + let bond = client + .nyxd + .get_owned_gateway(&client.nyxd.address()) + .await?; let res = bond .gateway .map(|bond| { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 8a4a82e158..18bd72551f 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -13,7 +13,9 @@ use nym_types::deprecated::{ use nym_types::mixnode::MixNodeCostParams; use nym_types::pending_events::PendingEpochEvent; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, MixnetSigningClient, NymContractsProvider, PagedMixnetQueryClient, +}; use nym_validator_client::nyxd::Fee; #[tauri::command] @@ -25,7 +27,7 @@ pub async fn get_pending_delegation_events( let reg = guard.registered_coins()?; let client = guard.current_client()?; - let events = client.get_all_nyxd_pending_epoch_events().await?; + let events = client.nyxd.get_all_pending_epoch_events().await?; let converted = events .into_iter() .map(|e| PendingEpochEvent::try_from_mixnet_contract(e, reg)) @@ -156,10 +158,13 @@ pub async fn get_all_mix_delegations( let address = client.nyxd.address(); let network = guard.current_network(); let base_mix_denom = network.base_mix_denom().to_string(); - let vesting_contract = client.nyxd.vesting_contract_address(); + let vesting_contract = client + .nyxd + .vesting_contract_address() + .expect("vesting contract address is not available"); log::info!(" >>> Get delegations"); - let delegations = client.get_all_delegator_delegations(address).await?; + let delegations = client.nyxd.get_all_delegator_delegations(&address).await?; log::info!(" <<< {} delegations", delegations.len()); let pending_events_for_account = get_pending_delegation_events(state.clone()).await?; @@ -185,7 +190,11 @@ pub async fn get_all_mix_delegations( d.amount ); - let mixnode = client.get_mixnode_details(d.mix_id).await?.mixnode_details; + let mixnode = client + .nyxd + .get_mixnode_details(d.mix_id) + .await? + .mixnode_details; let accumulated_by_operator = mixnode .as_ref() @@ -214,7 +223,7 @@ pub async fn get_all_mix_delegations( log::trace!(" >>> Get accumulated rewards: address = {}", address); let pending_reward = client .nyxd - .get_pending_delegator_reward(address, d.mix_id, d.proxy.clone()) + .get_pending_delegator_reward(&address, d.mix_id, d.proxy.clone()) .await?; let accumulated_rewards = match &pending_reward.amount_earned { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs index e18fa1e44a..05151d8a53 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs @@ -5,7 +5,7 @@ use crate::error::BackendError; use crate::nyxd_client; use crate::state::WalletState; use nym_types::pending_events::{PendingEpochEvent, PendingIntervalEvent}; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, PagedMixnetQueryClient}; use nym_wallet_types::interval::Interval; #[tauri::command] @@ -26,7 +26,7 @@ pub async fn get_pending_epoch_events( let guard = state.read().await; let reg = guard.registered_coins()?; let client = guard.current_client()?; - let res = client.get_all_nyxd_pending_epoch_events().await?; + let res = client.nyxd.get_all_pending_epoch_events().await?; log::info!("<<< got = {:?} events", res.len()); @@ -46,7 +46,7 @@ pub async fn get_pending_interval_events( let guard = state.read().await; let reg = guard.registered_coins()?; let client = guard.current_client()?; - let res = client.get_all_nyxd_pending_interval_events().await?; + let res = client.nyxd.get_all_pending_interval_events().await?; log::info!("<<< got = {:?} events", res.len()); diff --git a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs index 62ef534d02..266295c6e1 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs @@ -3,7 +3,9 @@ use crate::state::WalletState; use crate::vesting::rewards::vesting_claim_delegator_reward; use nym_mixnet_contract_common::{MixId, RewardingParams}; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, MixnetSigningClient, NymContractsProvider, PagedMixnetQueryClient, +}; use nym_validator_client::nyxd::Fee; #[tauri::command] @@ -64,13 +66,21 @@ pub async fn claim_locked_and_unlocked_delegator_reward( log::trace!(">>> Get delegations: mix_id = {}", mix_id); let address = client.nyxd.address(); - let delegations = client.get_all_delegator_delegations(address).await?; + let delegations = client.nyxd.get_all_delegator_delegations(&address).await?; log::trace!("<<< {} delegations", delegations.len()); - let vesting_contract = client.nyxd.vesting_contract_address().to_string(); - let liquid_delegation = client.get_delegation_details(mix_id, address, None).await?; + let vesting_contract = client + .nyxd + .vesting_contract_address() + .expect("vesting contract address is not available") + .to_string(); + let liquid_delegation = client + .nyxd + .get_delegation_details(mix_id, &address, None) + .await?; let vesting_delegation = client - .get_delegation_details(mix_id, address, Some(vesting_contract)) + .nyxd + .get_delegation_details(mix_id, &address, Some(vesting_contract)) .await?; drop(guard); diff --git a/nym-wallet/src-tauri/src/operations/signatures/sign.rs b/nym-wallet/src-tauri/src/operations/signatures/sign.rs index 24da5a0ca1..c494bc5b4f 100644 --- a/nym-wallet/src-tauri/src/operations/signatures/sign.rs +++ b/nym-wallet/src-tauri/src/operations/signatures/sign.rs @@ -4,6 +4,7 @@ use cosmrs::crypto::secp256k1::{Signature, VerifyingKey}; use cosmrs::crypto::PublicKey; use cosmrs::AccountId; use k256::ecdsa::signature::Verifier; +use nym_validator_client::nyxd::CosmWasmClient; use nym_validator_client::signing::signer::OfflineSigner; use serde::Serialize; use serde_json::json; @@ -23,15 +24,16 @@ pub async fn sign( ) -> Result { let guard = state.read().await; let client = guard.current_client()?; - let wallet = client.nyxd.signer(); - let derived_accounts = wallet.try_derive_accounts()?; + let derived_accounts = client.nyxd.get_accounts()?; let account = derived_accounts.first().ok_or_else(|| { log::error!(">>> Unable to derive account"); BackendError::SignatureError("unable to derive account".to_string()) })?; log::info!("<<< Signing message"); - let signature = wallet.sign_raw_with_account(account, message.as_bytes())?; + let signature = client + .nyxd + .sign_raw_with_account(account, message.as_bytes())?; let output = SignatureOutputJson { account_id: account.address().to_string(), public_key: account.public_key(), @@ -49,14 +51,10 @@ async fn get_pubkey_from_account_address( log::info!("Getting public key for address {} from chain...", address); let guard = state.read().await; let client = guard.current_client()?; - let account = client - .nyxd - .get_account_details(address) - .await? - .ok_or_else(|| { - log::error!("No account associated with address {}", address); - BackendError::SignatureError(format!("No account associated with address {address}")) - })?; + let account = client.nyxd.get_account(address).await?.ok_or_else(|| { + log::error!("No account associated with address {}", address); + BackendError::SignatureError(format!("No account associated with address {address}")) + })?; let base_account = account.try_get_base_account()?; base_account.pubkey.ok_or_else(|| { @@ -116,7 +114,7 @@ pub async fn verify( // get public key from current account address let guard = state.read().await; let client = guard.current_client()?; - let address = client.nyxd.address(); + let address = &client.nyxd.address(); get_pubkey_from_account_address(address, &state).await? } }; diff --git a/nym-wallet/src-tauri/src/operations/simulate/admin.rs b/nym-wallet/src-tauri/src/operations/simulate/admin.rs index f47b44e454..1884c7472e 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/admin.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/admin.rs @@ -5,6 +5,7 @@ use crate::error::BackendError; use crate::operations::simulate::FeeDetails; use crate::WalletState; use nym_mixnet_contract_common::{ContractStateParams, ExecuteMsg}; +use nym_validator_client::nyxd::contract_traits::NymContractsProvider; use nym_wallet_types::admin::TauriContractStateParams; #[tauri::command] @@ -18,7 +19,10 @@ pub async fn simulate_update_contract_settings( params.try_convert_to_mixnet_contract_params(reg)?; let client = guard.current_client()?; - let mixnet_contract = client.nyxd.mixnet_contract_address(); + let mixnet_contract = client + .nyxd + .mixnet_contract_address() + .expect("mixnet contract address is not available"); let msg = client.nyxd.wrap_contract_execute_message( mixnet_contract, diff --git a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs index 332f0eeb43..11d360ef71 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs @@ -11,6 +11,7 @@ use nym_mixnet_contract_common::{ExecuteMsg, Gateway, MixId, MixNode}; use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; +use nym_validator_client::nyxd::contract_traits::NymContractsProvider; async fn simulate_mixnet_operation( msg: ExecuteMsg, @@ -28,7 +29,10 @@ async fn simulate_mixnet_operation( }; let client = guard.current_client()?; - let mixnet_contract = client.nyxd.mixnet_contract_address(); + let mixnet_contract = client + .nyxd + .mixnet_contract_address() + .expect("mixnet contract address is not available"); let msg = client .nyxd diff --git a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs index 79e40ec792..b225aa9ff3 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs @@ -11,6 +11,7 @@ use nym_mixnet_contract_common::{Gateway, MixId, MixNode}; use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; +use nym_validator_client::nyxd::contract_traits::NymContractsProvider; use nym_vesting_contract_common::ExecuteMsg; async fn simulate_vesting_operation( @@ -29,7 +30,10 @@ async fn simulate_vesting_operation( }; let client = guard.current_client()?; - let vesting_contract = client.nyxd.vesting_contract_address(); + let vesting_contract = client + .nyxd + .vesting_contract_address() + .expect("vesting contract address is not available"); let msg = client .nyxd diff --git a/nym-wallet/src-tauri/src/operations/vesting/bond.rs b/nym-wallet/src-tauri/src/operations/vesting/bond.rs index f2077e5694..8e47953109 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/bond.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/bond.rs @@ -10,7 +10,7 @@ use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::{traits::VestingSigningClient, Fee}; +use nym_validator_client::nyxd::{contract_traits::VestingSigningClient, Fee}; use std::cmp::Ordering; #[tauri::command] diff --git a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs index ff7c33710f..64ac3ed736 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs @@ -6,7 +6,7 @@ use crate::state::WalletState; use nym_mixnet_contract_common::MixId; use nym_types::currency::DecCoin; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::{traits::VestingSigningClient, Fee}; +use nym_validator_client::nyxd::{contract_traits::VestingSigningClient, Fee}; #[tauri::command] pub async fn vesting_delegate_to_mixnode( diff --git a/nym-wallet/src-tauri/src/operations/vesting/queries.rs b/nym-wallet/src-tauri/src/operations/vesting/queries.rs index a24e26d001..f3377d5ed5 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/queries.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/queries.rs @@ -8,7 +8,7 @@ use cosmwasm_std::Timestamp; use nym_types::currency::DecCoin; use nym_types::vesting::VestingAccountInfo; use nym_types::vesting::{OriginalVestingResponse, PledgeData}; -use nym_validator_client::nyxd::traits::VestingQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingQueryClient; use nym_vesting_contract_common::Period; #[tauri::command] diff --git a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs index dbedb302e3..d990b2c1c5 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs @@ -5,7 +5,7 @@ use crate::error::BackendError; use crate::state::WalletState; use nym_mixnet_contract_common::MixId; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use nym_validator_client::nyxd::Fee; #[tauri::command] diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index bfc2d85cb6..5fd4485e18 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -8,10 +8,8 @@ use log::warn; use nym_types::currency::{DecCoin, Denom, RegisteredCoins}; use nym_types::fees::FeeDetails; use nym_validator_client::nyxd::cosmwasm_client::types::SimulateResponse; -use nym_validator_client::nyxd::{ - AccountId as CosmosAccountId, Coin, DirectSigningNyxdClient, Fee, -}; -use nym_validator_client::Client; +use nym_validator_client::nyxd::{AccountId as CosmosAccountId, Coin, Fee, SigningCosmWasmClient}; +use nym_validator_client::DirectSigningHttpRpcValidatorClient; use nym_wallet_types::network::Network; use nym_wallet_types::network_config; use once_cell::sync::Lazy; @@ -67,7 +65,7 @@ impl WalletState { #[derive(Default)] pub struct WalletStateInner { config: config::Config, - signing_clients: HashMap>, + signing_clients: HashMap, current_network: Network, // All the accounts the we get from decrypting the wallet. We hold on to these for being able to @@ -181,7 +179,7 @@ impl WalletStateInner { // this MUST succeed as we just used it before let client = self.current_client()?; let gas_price = client.nyxd.gas_price().clone(); - let gas_adjustment = client.nyxd.gas_adjustment(); + let gas_adjustment = client.nyxd.simulated_gas_multiplier(); let res = SimulateResult::new(simulate_response.gas_info, gas_price, gas_adjustment); @@ -196,7 +194,7 @@ impl WalletStateInner { pub fn client( &self, network: Network, - ) -> Result<&Client, BackendError> { + ) -> Result<&DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get(&network) .ok_or(BackendError::ClientNotInitialized) @@ -205,13 +203,13 @@ impl WalletStateInner { pub fn client_mut( &mut self, network: Network, - ) -> Result<&mut Client, BackendError> { + ) -> Result<&mut DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get_mut(&network) .ok_or(BackendError::ClientNotInitialized) } - pub fn current_client(&self) -> Result<&Client, BackendError> { + pub fn current_client(&self) -> Result<&DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get(&self.current_network) .ok_or(BackendError::ClientNotInitialized) @@ -220,7 +218,7 @@ impl WalletStateInner { #[allow(unused)] pub fn current_client_mut( &mut self, - ) -> Result<&mut Client, BackendError> { + ) -> Result<&mut DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get_mut(&self.current_network) .ok_or(BackendError::ClientNotInitialized) @@ -240,7 +238,7 @@ impl WalletStateInner { Ok(self.config.save_to_files()?) } - pub fn add_client(&mut self, network: Network, client: Client) { + pub fn add_client(&mut self, network: Network, client: DirectSigningHttpRpcValidatorClient) { self.signing_clients.insert(network, client); } diff --git a/nym-wallet/src-tauri/src/utils.rs b/nym-wallet/src-tauri/src/utils.rs index 579ccc5add..2a340cc5dd 100644 --- a/nym-wallet/src-tauri/src/utils.rs +++ b/nym-wallet/src-tauri/src/utils.rs @@ -8,7 +8,7 @@ use cosmwasm_std::Decimal; use nym_mixnet_contract_common::{IdentityKey, MixId, Percent}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; use nym_wallet_types::app::AppEnv; fn get_env_as_option(key: &str) -> Option { @@ -30,7 +30,7 @@ pub fn get_env() -> AppEnv { #[tauri::command] pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result { Ok(nyxd_client!(state) - .get_owned_mixnode(nyxd_client!(state).address()) + .get_owned_mixnode(&nyxd_client!(state).address()) .await? .mixnode_details .is_some()) @@ -39,7 +39,7 @@ pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result) -> Result { Ok(nyxd_client!(state) - .get_owned_gateway(nyxd_client!(state).address()) + .get_owned_gateway(&nyxd_client!(state).address()) .await? .gateway .is_some()) diff --git a/sdk/lib/socks5-listener/Cargo.toml b/sdk/lib/socks5-listener/Cargo.toml index 6960745920..fe2ff08134 100644 --- a/sdk/lib/socks5-listener/Cargo.toml +++ b/sdk/lib/socks5-listener/Cargo.toml @@ -18,7 +18,7 @@ codegen-units = 1 [dependencies] anyhow = { workspace = true } -futures = "0.3" +futures = { workspace = true } lazy_static = "1.4.0" nym-bin-common = { path = "../../../common/bin-common"} nym-client-core = { path = "../../../common/client-core", default-features = false } diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index 97751d4e72..c034e733a1 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -20,12 +20,12 @@ nym-topology = { path = "../../../common/topology" } nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-validator-client = { path = "../../../common/client-libs/validator-client", features = ["http-client"] } -futures = "0.3" +futures = { workspace = true } log = { workspace = true } rand = { version = "0.7.3" } tap = "1.0.1" -thiserror = "1.0.38" -url = "2.2" +thiserror = { workspace = true } +url = { workspace = true } toml = "0.5.10" [dev-dependencies] diff --git a/sdk/rust/nym-sdk/src/bandwidth/client.rs b/sdk/rust/nym-sdk/src/bandwidth/client.rs index 3be519bf49..7171282a71 100644 --- a/sdk/rust/nym-sdk/src/bandwidth/client.rs +++ b/sdk/rust/nym-sdk/src/bandwidth/client.rs @@ -6,9 +6,8 @@ use nym_bandwidth_controller::acquire::state::State; use nym_credential_storage::storage::Storage; use nym_credentials::coconut::bandwidth::BandwidthVoucher; use nym_network_defaults::NymNetworkDetails; -use nym_validator_client::nyxd::{Coin, SigningNyxdClient}; -use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; -use nym_validator_client::{Client, Config}; +use nym_validator_client::nyxd::Coin; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; /// The serialized version of the yet untransformed bandwidth voucher. It can be used to complete /// the acquirement process of a bandwidth credential. @@ -22,7 +21,7 @@ pub type VoucherBlob = Vec; /// client. pub struct BandwidthAcquireClient<'a, St: Storage> { network_details: NymNetworkDetails, - client: Client>, + client: DirectSigningHttpRpcNyxdClient, storage: &'a St, } @@ -36,8 +35,14 @@ where mnemonic: String, storage: &'a St, ) -> Result { - let config = Config::try_from_nym_network_details(&network_details)?; - let client = nym_validator_client::Client::new_signing(config, mnemonic.parse()?)?; + let nyxd_url = network_details.endpoints[0].nyxd_url.as_str(); + let config = nyxd::Config::try_from_nym_network_details(&network_details)?; + + let client = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + config, + nyxd_url, + mnemonic.parse()?, + )?; Ok(Self { network_details, client, @@ -51,7 +56,7 @@ where /// associated bandwidth credential, using [`Self::recover`]. pub async fn acquire(&self, amount: u128) -> Result<()> { let amount = Coin::new(amount, &self.network_details.chain_details.mix_denom.base); - let state = nym_bandwidth_controller::acquire::deposit(&self.client.nyxd, amount).await?; + let state = nym_bandwidth_controller::acquire::deposit(&self.client, amount).await?; nym_bandwidth_controller::acquire::get_credential(&state, &self.client, self.storage) .await .map_err(|reason| Error::UnconvertedDeposit { diff --git a/sdk/rust/nym-sdk/src/error.rs b/sdk/rust/nym-sdk/src/error.rs index 48469271e4..84283bc9a2 100644 --- a/sdk/rust/nym-sdk/src/error.rs +++ b/sdk/rust/nym-sdk/src/error.rs @@ -1,3 +1,4 @@ +use nym_validator_client::nyxd::error::NyxdError; use std::path::PathBuf; /// Top-level Error enum for the mixnet client and its relevant types. @@ -46,7 +47,7 @@ pub enum Error { DisabledCredentialsMode, #[error("bad validator details: {0}")] - BadValidatorDetails(#[from] nym_validator_client::ValidatorClientError), + BadValidatorDetails(#[from] NyxdError), #[error("socks5 configuration set: {}, but expected to be {}", set, !set)] Socks5Config { set: bool }, diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 8f19074605..212c192f72 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -24,8 +24,7 @@ use nym_network_defaults::NymNetworkDetails; use nym_socks5_client_core::config::Socks5; use nym_task::manager::TaskStatus; use nym_topology::provider_trait::TopologyProvider; -use nym_validator_client::nyxd::QueryNyxdClient; -use nym_validator_client::Client; +use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient}; use std::path::Path; use std::path::PathBuf; use url::Url; @@ -214,7 +213,7 @@ where /// In the case of enabled credentials, a client instance responsible for querying the state of the /// dkg and coconut contracts - dkg_query_client: Option>, + dkg_query_client: Option, /// Alternative provider of network topology used for constructing sphinx packets. custom_topology_provider: Option>, @@ -244,10 +243,12 @@ where ) -> Result> { // don't create dkg client for the bandwidth controller if credentials are disabled let dkg_query_client = if config.enabled_credentials_mode { - let client_config = nym_validator_client::Config::try_from_nym_network_details( - &config.network_details, + let client_config = + nyxd::Config::try_from_nym_network_details(&config.network_details)?; + let client = QueryHttpRpcNyxdClient::connect( + client_config, + config.network_details.endpoints[0].nyxd_url.as_str(), )?; - let client = nym_validator_client::Client::new_query(client_config)?; Some(client) } else { None diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index c76662dfde..713552f812 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -15,7 +15,7 @@ async-trait = { workspace = true } bs58 = "0.4.0" clap = {version = "4.0", features = ["cargo", "derive"]} dirs = "4.0" -futures = "0.3.24" +futures = { workspace = true } humantime-serde = "1.1.1" ipnetwork = "0.20.0" lazy_static = { workspace = true }