diff --git a/Cargo.lock b/Cargo.lock index b84d9d3f41..4ff0f9a693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,6 +3575,7 @@ dependencies = [ "console-subscriber", "cosmwasm-std", "cw-utils", + "cw2", "cw3", "cw4", "dirs 4.0.0", @@ -4045,6 +4046,7 @@ dependencies = [ name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ + "nym-api-requests", "nym-contracts-common", "nym-mixnet-contract-common", "schemars", @@ -4319,6 +4321,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -4918,6 +4921,7 @@ dependencies = [ "tokio", "ts-rs", "url", + "wasmtimer", "zeroize", ] 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/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/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 083afb5309..c5aa4a2830 100644 --- a/clients/webassembly/Cargo.lock +++ b/clients/webassembly/Cargo.lock @@ -2733,6 +2733,7 @@ dependencies = [ name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ + "nym-api-requests", "nym-contracts-common", "nym-mixnet-contract-common", "schemars", @@ -2856,6 +2857,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -3160,6 +3162,7 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 05e3809498..733521ea97 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -25,7 +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::QueryWasmRpcNyxdClient; +use nym_validator_client::QueryReqwestRpcNyxdClient; use rand::rngs::OsRng; use rand::RngCore; use std::sync::Arc; @@ -152,7 +152,11 @@ impl NymClientBuilder { let maybe_topology_provider = self.topology_provider(); let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = - BaseClientBuilder::::new(&self.config.base, storage, None); + BaseClientBuilder::::new( + &self.config.base, + storage, + None, + ); if let Some(topology_provider) = maybe_topology_provider { base_builder = base_builder.with_topology_provider(topology_provider); } diff --git a/clients/webassembly/src/tester/mod.rs b/clients/webassembly/src/tester/mod.rs index c7d397f322..ba70062f3b 100644 --- a/clients/webassembly/src/tester/mod.rs +++ b/clients/webassembly/src/tester/mod.rs @@ -26,7 +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::QueryWasmRpcNyxdClient; +use nym_validator_client::QueryReqwestRpcNyxdClient; use rand::rngs::OsRng; use std::collections::HashSet; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; @@ -41,7 +41,8 @@ mod ephemeral_receiver; pub(crate) mod helpers; pub type NodeTestMessage = TestMessage; -type LockedGatewayClient = Arc>>; +type LockedGatewayClient = + Arc>>; pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10); pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20; @@ -77,7 +78,7 @@ pub struct NymNodeTesterBuilder { base_topology: NymTopology, // unimplemented - bandwidth_controller: Option>, + 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 7c372063de..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" } diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index af45a71737..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 diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 37f57fa601..2f2e7d0072 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -25,7 +25,7 @@ use crate::client::topology_control::{ }; use crate::config::{Config, DebugConfig}; use crate::error::ClientCoreError; -use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails}; +use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult}; use crate::{config, spawn_future}; use futures::channel::mpsc; use log::{debug, info}; diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index ef8d324531..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"] } 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 f1cd481f1d..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"] } +url = { workspace = true, features = ["serde"] } tokio = { workspace = true, features = ["sync", "time"] } -futures = "0.3" +futures = { workspace = true } openssl = { version = "^0.10.55", features = ["vendored"], optional = true } nym-coconut-interface = { path = "../../coconut-interface" } @@ -53,14 +53,17 @@ 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", 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]] @@ -72,12 +75,10 @@ required-features = ["http-client"] [[example]] name = "query_service_provider_directory" -# TODO: validate the requirements required-features = ["http-client"] [[example]] name = "query_name_service" -# TODO: validate the requirements required-features = ["http-client"] [features] diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 5106c1c9ae..e70cbdaf2f 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -1,10 +1,13 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::{self, NyxdClient}; -use crate::signing::signer::NoSigner; -use crate::{nym_api, ValidatorClientError}; +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, }; @@ -24,9 +27,7 @@ pub use nym_mixnet_contract_common::{ pub use crate::coconut::CoconutApiClient; #[cfg(feature = "http-client")] -use crate::signing::direct_wallet::DirectSecp256k1HdWallet; -#[cfg(feature = "http-client")] -use tendermint_rpc::HttpClient; +use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient}; #[must_use] #[derive(Debug, Clone)] @@ -89,22 +90,18 @@ pub struct Client { } #[cfg(feature = "http-client")] -impl 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, - )?; + ) -> 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 { - 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> { @@ -113,17 +110,24 @@ impl Client { } } -#[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())?; +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); - Ok(Client { - nym_api: nym_api_client, - nyxd: nyxd_client, - }) + Self::new_signing_with_rpc_client(config, rpc_client, wallet) + } +} + +#[cfg(feature = "http-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> { @@ -132,24 +136,35 @@ impl Client { } } -// nyxd wrappers -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 - - #[deprecated] - pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) { - self.nyxd - .set_mixnet_contract_address(mixnet_contract_address) +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) } +} - #[deprecated] - pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId { - // TODO: deal with the expect - self.nyxd - .mixnet_contract_address() - .expect("mixnet contract address is not available") - .clone() +impl 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()); + + Client { + nym_api: nym_api_client, + nyxd: NyxdClient::new(config.nyxd_config, rpc_client), + } + } +} + +impl Client { + pub fn new_signing_with_rpc_client(config: Config, rpc_client: C, signer: S) -> Self + where + S: OfflineSigner, + { + let nym_api_client = nym_api::Client::new(config.api_url.clone()); + + Client { + nym_api: nym_api_client, + nyxd: NyxdClient::new_signing(config.nyxd_config, rpc_client, signer), + } } } 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 86989833f5..c6391673a0 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 pub mod client; @@ -8,26 +8,24 @@ pub mod connection_tester; pub mod error; pub mod nym_api; pub mod nyxd; +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::*; +#[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 use cosmrs::rpc::HttpClient as HttpRpcClient; - -#[cfg(target_arch = "wasm32")] -pub use crate::nyxd::wasm::WasmRpcClient; - -use crate::signing::direct_wallet::DirectSecp256k1HdWallet; - #[cfg(feature = "http-client")] pub type QueryHttpRpcValidatorClient = Client; #[cfg(feature = "http-client")] @@ -38,15 +36,9 @@ pub type DirectSigningHttpRpcValidatorClient = Client; -// TODO: the same for reqwest client (once implemented) +pub type QueryReqwestRpcValidatorClient = Client; +pub type QueryReqwestRpcNyxdClient = nyxd::NyxdClient; -// TODO: rename it to whatever we end up using in wasm -#[cfg(target_arch = "wasm32")] -pub type QueryWasmRpcValidatorClient = Client; -#[cfg(target_arch = "wasm32")] -pub type QueryWasmRpcNyxdClient = nyxd::NyxdClient; - -#[cfg(target_arch = "wasm32")] -pub type DirectSigningWasmRpcValidatorClient = Client; -#[cfg(target_arch = "wasm32")] -pub type DirectSigningWasmRpcNyxdClient = 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 index e26863fbe9..1737edf613 100644 --- 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 @@ -12,7 +12,8 @@ use nym_coconut_bandwidth_contract_common::spend_credential::{ }; use serde::Deserialize; -#[async_trait] +#[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, @@ -44,7 +45,8 @@ pub trait CoconutBandwidthQueryClient { } } -#[async_trait] +#[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) @@ -54,7 +56,8 @@ pub trait PagedCoconutBandwidthQueryClient: CoconutBandwidthQueryClient { #[async_trait] impl PagedCoconutBandwidthQueryClient for T where T: CoconutBandwidthQueryClient {} -#[async_trait] +#[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, 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 index e9c43e6fca..030d78b29a 100644 --- 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 @@ -12,7 +12,8 @@ use nym_coconut_bandwidth_contract_common::{ deposit::DepositData, msg::ExecuteMsg as CoconutBandwidthExecuteMsg, }; -#[async_trait] +#[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, @@ -82,7 +83,8 @@ pub trait CoconutBandwidthSigningClient { } } -#[async_trait] +#[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, 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 index 2a569127e3..d3ac4807e8 100644 --- 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 @@ -15,7 +15,8 @@ use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplac use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse}; use serde::Deserialize; -#[async_trait] +#[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 @@ -94,7 +95,8 @@ pub trait DkgQueryClient { // extension trait to the query client to deal with the paged queries // (it didn't feel appropriate to combine it with the existing trait -#[async_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) @@ -119,7 +121,8 @@ pub trait PagedDkgQueryClient: DkgQueryClient { #[async_trait] impl PagedDkgQueryClient for T where T: DkgQueryClient {} -#[async_trait] +#[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, 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 index 9ecf476743..73bce467b7 100644 --- 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 @@ -14,7 +14,8 @@ use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof; use nym_coconut_dkg_common::verification_key::VerificationKeyShare; use nym_contracts_common::dealings::ContractSafeBytes; -#[async_trait] +#[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, @@ -107,7 +108,8 @@ pub trait DkgSigningClient { } } -#[async_trait] +#[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, 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 index 580f1ccefd..8513dfa5bb 100644 --- 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 @@ -9,7 +9,8 @@ use cw4::{Member, MemberListResponse, MemberResponse, TotalWeightResponse}; use nym_group_contract_common::msg::QueryMsg as GroupQueryMsg; use serde::Deserialize; -#[async_trait] +#[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 @@ -47,7 +48,8 @@ pub trait GroupQueryClient { } } -#[async_trait] +#[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> { @@ -74,7 +76,8 @@ pub trait PagedGroupQueryClient: GroupQueryClient { #[async_trait] impl PagedGroupQueryClient for T where T: GroupQueryClient {} -#[async_trait] +#[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, 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 index 07d14200d7..5ac1954849 100644 --- 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 @@ -10,7 +10,8 @@ use async_trait::async_trait; use cw4::Member; use nym_group_contract_common::msg::ExecuteMsg as GroupExecuteMsg; -#[async_trait] +#[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, @@ -74,7 +75,8 @@ pub trait GroupSigningClient { } } -#[async_trait] +#[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, 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 index 09793e3eb1..95d692d592 100644 --- 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 @@ -8,22 +8,20 @@ use crate::nyxd::CosmWasmClient; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::Nonce; -use nym_contracts_common::IdentityKeyRef; -use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey}; -use nym_mixnet_contract_common::families::{Family, FamilyHead}; -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, Delegation, EpochEventId, EpochStatus, FamilyByHeadResponse, - FamilyByLabelResponse, FamilyMembersByHeadResponse, FamilyMembersByLabelResponse, GatewayBond, - GatewayBondResponse, GatewayOwnershipResponse, IdentityKey, IntervalEventId, LayerDistribution, + 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, @@ -35,7 +33,8 @@ use nym_mixnet_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 MixnetQueryClient { async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where @@ -467,7 +466,8 @@ pub trait MixnetQueryClient { // extension trait to the query client to deal with the paged queries // (it didn't feel appropriate to combine it with the existing trait -#[async_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) @@ -550,7 +550,8 @@ pub trait PagedMixnetQueryClient: MixnetQueryClient { #[async_trait] impl PagedMixnetQueryClient for T where T: MixnetQueryClient {} -#[async_trait] +#[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, diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs index ad7d03327c..f8b5521e0d 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs @@ -19,7 +19,8 @@ use nym_mixnet_contract_common::{ 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, @@ -695,7 +696,8 @@ pub trait MixnetSigningClient { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl MixnetSigningClient for C where C: SigningCosmWasmClient + NymContractsProvider + Sync, 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 index b7d180f4a1..48fcadc050 100644 --- 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 @@ -13,7 +13,8 @@ use cw_utils::ThresholdResponse; use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg; use serde::Deserialize; -#[async_trait] +#[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 @@ -90,7 +91,8 @@ pub trait MultisigQueryClient { // extension trait to the query client to deal with the paged queries // (it didn't feel appropriate to combine it with the existing trait -#[async_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> { @@ -117,7 +119,8 @@ pub trait PagedMultisigQueryClient: MultisigQueryClient { #[async_trait] impl PagedMultisigQueryClient for T where T: MultisigQueryClient {} -#[async_trait] +#[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, 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 index 0da2fd9de8..f7249f02ea 100644 --- 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 @@ -13,7 +13,8 @@ use cw4::{MemberChangedHookMsg, MemberDiff}; use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; -#[async_trait] +#[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, @@ -124,7 +125,8 @@ pub trait MultisigSigningClient: NymContractsProvider { } } -#[async_trait] +#[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, 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 index d5a65b8e66..60071f674d 100644 --- 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 @@ -14,7 +14,8 @@ use nym_name_service_common::{ }; use serde::Deserialize; -#[async_trait] +#[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 @@ -78,7 +79,8 @@ pub trait NameServiceQueryClient { } } -#[async_trait] +#[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) @@ -88,7 +90,8 @@ pub trait PagedNameServiceQueryClient: NameServiceQueryClient { #[async_trait] impl PagedNameServiceQueryClient for T where T: NameServiceQueryClient {} -#[async_trait] +#[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, diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs index bc5a06e1a9..0a363f991b 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs @@ -11,7 +11,8 @@ use crate::nyxd::{ }; 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, @@ -72,7 +73,8 @@ pub trait NameServiceSigningClient { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl NameServiceSigningClient for C where C: SigningCosmWasmClient + NymContractsProvider + Sync, 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 index 47a56f9080..09279c543a 100644 --- 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 @@ -14,7 +14,8 @@ use serde::Deserialize; use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::{error::NyxdError, CosmWasmClient}; -#[async_trait] +#[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 @@ -78,7 +79,8 @@ pub trait SpDirectoryQueryClient { } } -#[async_trait] +#[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) @@ -88,7 +90,8 @@ pub trait PagedSpDirectoryQueryClient: SpDirectoryQueryClient { #[async_trait] impl PagedSpDirectoryQueryClient for T where T: SpDirectoryQueryClient {} -#[async_trait] +#[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, diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs index 9d0f3f458a..c1efde20f5 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs @@ -12,7 +12,8 @@ use nym_service_provider_directory_common::{ msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId, }; -#[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,7 +82,8 @@ pub trait SpDirectorySigningClient { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl SpDirectorySigningClient for C where C: SigningCosmWasmClient + NymContractsProvider + Sync, diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs index 2a5b587f93..fa6bd643fa 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs @@ -17,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 @@ -282,7 +283,8 @@ pub trait VestingQueryClient { } } -#[async_trait] +#[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> { collect_paged!(self, get_all_vesting_delegations_paged, delegations) @@ -300,7 +302,8 @@ pub trait PagedVestingQueryClient: VestingQueryClient { #[async_trait] impl PagedVestingQueryClient for T where T: VestingQueryClient {} -#[async_trait] +#[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, diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs index b540d3b3f9..11c449fccd 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs @@ -16,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, @@ -399,7 +400,8 @@ pub trait VestingSigningClient { } } -#[async_trait] +#[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, diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs index 46073a2671..69e94b3387 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs @@ -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,6 +39,16 @@ 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); @@ -46,8 +56,9 @@ pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); #[async_trait] impl CosmWasmClient for cosmrs::rpc::HttpClient {} -#[async_trait] -pub trait CosmWasmClient: TendermintClient { +#[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? @@ -236,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`. @@ -244,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`. @@ -255,7 +266,7 @@ 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( @@ -286,13 +297,13 @@ 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) >= timeout { + if Instant::now().duration_since(start) >= timeout { return Err(NyxdError::BroadcastTimeout { hash: tx_hash, timeout, @@ -303,7 +314,7 @@ pub trait CosmWasmClient: TendermintClient { return Ok(poll_res); } - tokio::time::sleep(poll_interval).await; + sleep(poll_interval).await; } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs index 4f6f3a9772..5b3deadb68 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs @@ -53,7 +53,8 @@ fn single_unspecified_signer_auth( .auth_info(empty_fee()) } -#[async_trait] +#[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>, 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 c082468486..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 @@ -3,7 +3,8 @@ use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Config, GasPrice, TendermintClient}; +use crate::nyxd::{Config, GasPrice}; +use crate::rpc::TendermintRpcClient; use crate::signing::{ signer::{NoSigner, OfflineSigner}, AccountData, @@ -77,10 +78,11 @@ impl MaybeSigningClient { } } -#[async_trait] -impl TendermintClient for MaybeSigningClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for MaybeSigningClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: Send + Sync, { async fn perform(&self, request: R) -> Result @@ -113,7 +115,7 @@ where #[async_trait] impl CosmWasmClient for MaybeSigningClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: Send + Sync, { } @@ -121,7 +123,7 @@ where #[async_trait] impl SigningCosmWasmClient for MaybeSigningClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: OfflineSigner + Send + Sync, NyxdError: From, { diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index 102cac80c3..d0cf1e0b3e 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -25,6 +25,7 @@ use tendermint_rpc::Error as TendermintRpcError; 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}; @@ -38,18 +39,20 @@ 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")] +// #[cfg(feature = "http-client")] use crate::signing::direct_wallet::DirectSecp256k1HdWallet; #[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; @@ -57,11 +60,6 @@ pub mod cosmwasm_client; pub mod error; pub mod fee; -#[cfg(target_arch = "wasm32")] -pub mod wasm; - -// TODO: reqwest based for wasm - #[derive(Debug, Clone)] pub struct Config { pub(crate) chain_details: ChainDetails, @@ -71,30 +69,6 @@ pub struct Config { } 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 { Ok(Config { chain_details: details.chain_details.clone(), @@ -110,21 +84,13 @@ impl Config { } } -// so youd have: -// for queries: -// NyxdClient -// NyxdClient -// -// for signing: -// NyxdClient -// NyxdClient - #[derive(Debug)] pub struct NyxdClient { client: MaybeSigningClient, config: 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(config: Config, endpoint: U) -> Result @@ -140,9 +106,32 @@ impl NyxdClient { } } +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 { - // TODO: rename this one pub fn connect_with_mnemonic( config: Config, endpoint: U, @@ -151,32 +140,37 @@ impl NyxdClient { 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) + Ok(Self::connect_with_signer(config, client, wallet)) } } -#[cfg(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, { - pub fn connect_with_signer( - config: Config, - endpoint: U, - signer: S, - ) -> Result, NyxdError> - where - U: TryInto, - { - let client = HttpClient::new(endpoint)?; - Ok(NyxdClient { + pub fn connect_with_signer(config: Config, client: C, signer: S) -> NyxdClient { + NyxdClient { client: MaybeSigningClient::new_signing(client, signer, (&config).into()), config, - }) + } } } @@ -190,15 +184,6 @@ impl NyxdClient { } } -impl NyxdClient { - pub fn new(config: Config, client: C) -> Self { - NyxdClient { - client: MaybeSigningClient::new(client, (&config).into()), - config, - } - } -} - // no trait bounds impl NyxdClient { pub fn new_signing(config: Config, client: C, signer: S) -> Self @@ -289,7 +274,7 @@ impl NymContractsProvider for NyxdClient { // queries impl NyxdClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: Send + Sync, { pub async fn get_account_public_key( @@ -339,7 +324,7 @@ where // signing impl NyxdClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: OfflineSigner + Send + Sync, NyxdError: From<::Error>, { @@ -572,10 +557,11 @@ where // ugh. is there a way to avoid that nasty trait implementation? -#[async_trait] -impl TendermintClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for NyxdClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: Send + Sync, { async fn perform(&self, request: R) -> Result @@ -589,7 +575,7 @@ where #[async_trait] impl CosmWasmClient for NyxdClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: Send + Sync, { } @@ -616,7 +602,7 @@ where #[async_trait] impl SigningCosmWasmClient for NyxdClient where - C: TendermintClient + Send + Sync, + C: TendermintRpcClient + Send + Sync, S: TxSigner + Send + Sync, NyxdError: From, { diff --git a/common/client-libs/validator-client/src/nyxd/wasm.rs b/common/client-libs/validator-client/src/nyxd/wasm.rs deleted file mode 100644 index 4186fc54fa..0000000000 --- a/common/client-libs/validator-client/src/nyxd/wasm.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::TendermintClient; -use async_trait::async_trait; -use tendermint_rpc::{Error, SimpleRequest}; - -pub struct WasmRpcClient { - // -} - -#[async_trait] -impl TendermintClient for WasmRpcClient { - async fn perform(&self, _request: R) -> Result - where - R: SimpleRequest, - { - todo!() - } -} 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/commands/Cargo.toml b/common/commands/Cargo.toml index dd6e8e3895..45c9139fe3 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -19,10 +19,10 @@ 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 } 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/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 2be09b0e70..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")), )) @@ -205,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); @@ -280,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, @@ -308,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/src/tasks.rs b/explorer-api/src/tasks.rs index 7e4ea329fa..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, TendermintClient, ValidatorResponse}; +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; 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/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 f91180fd37..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"] } 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 c6bbe1f81a..e1fa5cf18f 100644 --- a/nym-api/src/nym_contract_cache/cache/refresher.rs +++ b/nym-api/src/nym_contract_cache/cache/refresher.rs @@ -1,12 +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::contract_traits::{ - PagedNameServiceQueryClient, PagedSpDirectoryQueryClient, + 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; @@ -39,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; @@ -56,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", @@ -74,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 861b485307..501be9e581 100644 --- a/nym-api/src/support/nyxd/mod.rs +++ b/nym-api/src/support/nyxd/mod.rs @@ -16,7 +16,7 @@ use nym_coconut_dkg_common::{ types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId}, verification_key::{ContractVKShare, VerificationKeyShare}, }; -use nym_config::defaults::{ChainDetails, NymNetworkDetails}; +use nym_config::defaults::ChainDetails; use nym_contracts_common::dealings::ContractSafeBytes; use nym_mixnet_contract_common::families::FamilyHead; use nym_mixnet_contract_common::mixnode::MixNodeDetails; @@ -47,7 +47,7 @@ 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>); @@ -59,12 +59,9 @@ impl Clone for Client { impl Client { pub(crate) fn new(config: &Config) -> Self { + 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 = nyxd::Config::try_from_nym_network_details(&details) .expect("failed to construct valid validator client config with the provided network"); @@ -80,6 +77,10 @@ impl Client { 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.address() } diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index b1aa136deb..51641a7a86 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -4122,6 +4122,7 @@ dependencies = [ name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ + "nym-api-requests", "nym-contracts-common", "nym-mixnet-contract-common", "schemars", @@ -4245,6 +4246,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -4593,6 +4595,7 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] 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 ba0ebb18e0..fbd4a02504 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", @@ -3448,6 +3449,7 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] @@ -6110,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/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/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/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 }