diff --git a/Cargo.lock b/Cargo.lock index a2b1c5d75f..47bd947773 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4885,6 +4885,7 @@ dependencies = [ "colored", "cosmrs", "cosmwasm-std", + "cw-utils", "cw3", "cw4", "eyre", diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 793adaf3cb..bead36057c 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -45,6 +45,7 @@ cosmrs = { workspace = true, features = ["bip32", "cosmwasm"] } tendermint-rpc = { workspace = true } eyre = { version = "0.6", optional = true } +cw-utils = { workspace = true } cw3 = { workspace = true } cw4 = { workspace = true } prost = { version = "0.11", default-features = false } diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 70511efe61..854648bcc9 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -16,9 +16,8 @@ pub use nym_mixnet_contract_common::{ }; use url::Url; -use crate::nyxd::contract_traits::{DkgQueryClient, MixnetQueryClient}; -#[cfg(feature = "http-client")] -use crate::nyxd::QueryNyxdClient; +// use crate::nyxd::contract_traits::{DkgQueryClient, MixnetQueryClient}; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::{self, CosmWasmClient, NyxdClient}; use nym_api_requests::models::MixNodeBondAnnotated; use nym_coconut_dkg_common::{types::EpochId, verification_key::ContractVKShare}; @@ -31,11 +30,11 @@ use nym_mixnet_contract_common::{ }; use nym_network_defaults::NymNetworkDetails; use std::str::FromStr; +use tendermint_rpc::HttpClient; -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::nyxd::SigningNyxdClient; #[cfg(all(feature = "signing", feature = "http-client"))] use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +use crate::signing::signer::NoSigner; #[must_use] #[derive(Debug, Clone)] @@ -116,29 +115,32 @@ impl Config { } } -pub struct Client { +pub struct Client { + #[deprecated] mixnode_page_limit: Option, + #[deprecated] gateway_page_limit: Option, + #[deprecated] mixnode_delegations_page_limit: Option, + #[deprecated] rewarded_set_page_limit: Option, // ideally they would have been read-only, but unfortunately rust doesn't have such features pub nym_api: nym_api::Client, - pub nyxd: NyxdClient, + pub nyxd: NyxdClient, } #[cfg(all(feature = "signing", feature = "http-client"))] -impl Client> { +impl Client { pub fn new_signing( config: Config, mnemonic: bip39::Mnemonic, - ) -> Result>, ValidatorClientError> { + ) -> Result, ValidatorClientError> { let nym_api_client = nym_api::Client::new(config.api_url.clone()); let nyxd_client = NyxdClient::connect_with_mnemonic( config.nyxd_config.clone(), config.nyxd_url.as_str(), mnemonic, - None, )?; Ok(Client { @@ -157,13 +159,14 @@ impl Client> { } pub fn set_nyxd_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.nyxd.set_simulated_gas_multiplier(multiplier) + todo!() + // self.nyxd.set_simulated_gas_multiplier(multiplier) } } #[cfg(feature = "http-client")] -impl Client { - pub fn new_query(config: Config) -> Result, ValidatorClientError> { +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())?; @@ -195,375 +198,379 @@ impl Client { } pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId { - self.nyxd.mixnet_contract_address().clone() + // TODO: deal with the expect + self.nyxd + .mixnet_contract_address() + .expect("mixnet contract address is not available") + .clone() } - pub async fn get_all_node_families(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut families = Vec::new(); - let mut start_after = None; - - loop { - let paged_response = self - .nyxd - .get_all_node_families_paged(start_after.take(), None) - .await?; - families.extend(paged_response.families); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(families) - } - - pub async fn get_all_family_members( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut members = Vec::new(); - let mut start_after = None; - - loop { - let paged_response = self - .nyxd - .get_all_family_members_paged(start_after.take(), None) - .await?; - members.extend(paged_response.members); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(members) - } - - // basically handles paging for us - pub async fn get_all_nyxd_rewarded_set_mixnodes( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut identities = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - identities.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(identities) - } - - pub async fn get_all_nyxd_mixnode_bonds(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_mixnodes_detailed( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes_by_owner( - &self, - owner: &cosmrs::AccountId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes_by_identity( - &self, - identity_key: String, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_by_identity_paged( - identity_key.clone(), - self.mixnode_page_limit, - start_after.take(), - ) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_gateways(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut gateways = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_gateways_paged(start_after.take(), self.gateway_page_limit) - .await?; - gateways.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(gateways) - } - - pub async fn get_all_nyxd_single_mixnode_delegations( - &self, - mix_id: MixId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnode_delegations_paged( - mix_id, - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_delegator_delegations( - &self, - delegation_owner: &cosmrs::AccountId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_delegator_delegations_paged( - delegation_owner.to_string(), - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_network_delegations(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_all_network_delegations_paged( - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_nyxd_pending_epoch_events( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut events = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_pending_epoch_events_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - events.append(&mut paged_response.events); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(events) - } - - pub async fn get_all_nyxd_pending_interval_events( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut events = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_pending_interval_events_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - events.append(&mut paged_response.events); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(events) - } + // pub async fn get_all_node_families(&self) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut families = Vec::new(); + // let mut start_after = None; + // + // loop { + // let paged_response = self + // .nyxd + // .get_all_node_families_paged(start_after.take(), None) + // .await?; + // families.extend(paged_response.families); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(families) + // } + // + // pub async fn get_all_family_members( + // &self, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut members = Vec::new(); + // let mut start_after = None; + // + // loop { + // let paged_response = self + // .nyxd + // .get_all_family_members_paged(start_after.take(), None) + // .await?; + // members.extend(paged_response.members); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(members) + // } + // + // // basically handles paging for us + // pub async fn get_all_nyxd_rewarded_set_mixnodes( + // &self, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut identities = Vec::new(); + // let mut start_after = None; + // + // loop { + // let mut paged_response = self + // .nyxd + // .get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit) + // .await?; + // identities.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(identities) + // } + // + // pub async fn get_all_nyxd_mixnode_bonds(&self) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut mixnodes = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take()) + // .await?; + // mixnodes.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(mixnodes) + // } + // + // pub async fn get_all_nyxd_mixnodes_detailed( + // &self, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut mixnodes = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take()) + // .await?; + // mixnodes.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(mixnodes) + // } + // + // pub async fn get_all_nyxd_unbonded_mixnodes( + // &self, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut mixnodes = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_unbonded_paged(self.mixnode_page_limit, start_after.take()) + // .await?; + // mixnodes.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(mixnodes) + // } + // + // pub async fn get_all_nyxd_unbonded_mixnodes_by_owner( + // &self, + // owner: &cosmrs::AccountId, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut mixnodes = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take()) + // .await?; + // mixnodes.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(mixnodes) + // } + // + // pub async fn get_all_nyxd_unbonded_mixnodes_by_identity( + // &self, + // identity_key: String, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut mixnodes = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_unbonded_by_identity_paged( + // identity_key.clone(), + // self.mixnode_page_limit, + // start_after.take(), + // ) + // .await?; + // mixnodes.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(mixnodes) + // } + // + // pub async fn get_all_nyxd_gateways(&self) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut gateways = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_gateways_paged(start_after.take(), self.gateway_page_limit) + // .await?; + // gateways.append(&mut paged_response.nodes); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(gateways) + // } + // + // pub async fn get_all_nyxd_single_mixnode_delegations( + // &self, + // mix_id: MixId, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut delegations = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_mixnode_delegations_paged( + // mix_id, + // start_after.take(), + // self.mixnode_delegations_page_limit, + // ) + // .await?; + // delegations.append(&mut paged_response.delegations); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(delegations) + // } + // + // pub async fn get_all_delegator_delegations( + // &self, + // delegation_owner: &cosmrs::AccountId, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut delegations = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_delegator_delegations_paged( + // delegation_owner.to_string(), + // start_after.take(), + // self.mixnode_delegations_page_limit, + // ) + // .await?; + // delegations.append(&mut paged_response.delegations); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(delegations) + // } + // + // pub async fn get_all_network_delegations(&self) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut delegations = Vec::new(); + // let mut start_after = None; + // loop { + // let mut paged_response = self + // .nyxd + // .get_all_network_delegations_paged( + // start_after.take(), + // self.mixnode_delegations_page_limit, + // ) + // .await?; + // delegations.append(&mut paged_response.delegations); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(delegations) + // } + // + // pub async fn get_all_nyxd_pending_epoch_events( + // &self, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut events = Vec::new(); + // let mut start_after = None; + // + // loop { + // let mut paged_response = self + // .nyxd + // .get_pending_epoch_events_paged(start_after.take(), self.rewarded_set_page_limit) + // .await?; + // events.append(&mut paged_response.events); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(events) + // } + // + // pub async fn get_all_nyxd_pending_interval_events( + // &self, + // ) -> Result, ValidatorClientError> + // where + // C: CosmWasmClient + Sync + Send, + // { + // let mut events = Vec::new(); + // let mut start_after = None; + // + // loop { + // let mut paged_response = self + // .nyxd + // .get_pending_interval_events_paged(start_after.take(), self.rewarded_set_page_limit) + // .await?; + // events.append(&mut paged_response.events); + // + // if let Some(start_after_res) = paged_response.start_next_after { + // start_after = Some(start_after_res) + // } else { + // break; + // } + // } + // + // Ok(events) + // } } // validator-api wrappers @@ -633,20 +640,20 @@ pub struct CoconutApiClient { } impl CoconutApiClient { - pub async fn all_coconut_api_clients( - client: &C, - epoch_id: EpochId, - ) -> Result, ValidatorClientError> - where - C: DkgQueryClient + Sync + Send, - { - Ok(client - .get_all_verification_key_shares(epoch_id) - .await? - .into_iter() - .filter_map(Self::try_from) - .collect()) - } + // pub async fn all_coconut_api_clients( + // client: &C, + // epoch_id: EpochId, + // ) -> Result, ValidatorClientError> + // where + // C: DkgQueryClient + Sync + Send, + // { + // Ok(client + // .get_all_verification_key_shares(epoch_id) + // .await? + // .into_iter() + // .filter_map(Self::try_from) + // .collect()) + // } fn try_from(share: ContractVKShare) -> Option { if share.verified { diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index b1d2293850..1eb0c50914 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,7 +1,8 @@ use crate::nyxd::error::NyxdError; -use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient}; +use crate::nyxd::{Config as ClientConfig, QueryHttpNyxdClient}; use crate::{NymApiClient, ValidatorClientError}; +// use crate::nyxd::contract_traits::MixnetQueryClient; use crate::nyxd::contract_traits::MixnetQueryClient; use colored::Colorize; use core::fmt; @@ -53,7 +54,7 @@ pub async fn test_nyxd_url_connection( let config = ClientConfig::try_from_nym_network_details(&network) .expect("failed to create valid nyxd client config"); - let mut nyxd_client = NyxdClient::::connect(config, nyxd_url.as_str())?; + let mut nyxd_client = QueryHttpNyxdClient::connect(config, nyxd_url.as_str())?; // possibly redundant, but lets just leave it here nyxd_client.set_mixnet_contract_address(address); match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await { @@ -75,7 +76,7 @@ fn setup_connection_tests( let config = ClientConfig::try_from_nym_network_details(&network) .expect("failed to create valid nyxd client config"); - if let Ok(mut client) = NyxdClient::::connect(config, url.as_str()) { + if let Ok(mut client) = QueryHttpNyxdClient::connect(config, url.as_str()) { // possibly redundant, but lets just leave it here client.set_mixnet_contract_address(address); Some(ClientForConnectionTest::Nyxd( @@ -112,7 +113,7 @@ fn extract_and_collect_results_into_map( async fn test_nyxd_connection( network: NymNetworkDetails, url: &Url, - client: &NyxdClient, + client: &QueryHttpNyxdClient, ) -> ConnectionResult { let result = match timeout( Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC), @@ -186,7 +187,7 @@ async fn test_nym_api_connection( } enum ClientForConnectionTest { - Nyxd(NymNetworkDetails, Url, Box>), + Nyxd(NymNetworkDetails, Url, Box), Api(NymNetworkDetails, Url, NymApiClient), } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs index 66ac21947b..b1ecc7a18c 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 @@ -1,33 +1,65 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::error::NyxdError; use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use nym_coconut_bandwidth_contract_common::msg::QueryMsg; +use nym_coconut_bandwidth_contract_common::msg::QueryMsg as CoconutBandwidthQueryMsg; use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse; +use crate::nyxd::contract_traits::NymContractsProvider; use async_trait::async_trait; +use serde::Deserialize; #[async_trait] pub trait CoconutBandwidthQueryClient { - async fn get_spent_credential( + async fn query_coconut_bandwidth_contract( &self, - blinded_serial_number: String, - ) -> Result; -} + query: CoconutBandwidthQueryMsg, + ) -> Result + where + for<'a> T: Deserialize<'a>; -#[async_trait] -impl CoconutBandwidthQueryClient for NyxdClient { async fn get_spent_credential( &self, blinded_serial_number: String, ) -> Result { - let request = QueryMsg::GetSpentCredential { + self.query_coconut_bandwidth_contract(CoconutBandwidthQueryMsg::GetSpentCredential { blinded_serial_number, - }; - self.client - .query_contract_smart(self.coconut_bandwidth_contract_address(), &request) + }) + .await + } +} + +#[async_trait] +impl CoconutBandwidthQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_coconut_bandwidth_contract( + &self, + query: CoconutBandwidthQueryMsg, + ) -> Result + where + for<'a> T: Deserialize<'a>, + { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + self.query_contract_smart(coconut_bandwidth_contract_address, &query) .await } } + +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: CoconutBandwidthQueryMsg, + ) { + unimplemented!() + } +} 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 92334424c2..9465cc74ab 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 @@ -1,6 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; use crate::nyxd::{CosmWasmClient, NyxdClient}; use async_trait::async_trait; @@ -176,29 +177,32 @@ pub trait DkgQueryClient { } #[async_trait] -impl DkgQueryClient for NyxdClient +impl DkgQueryClient for C where - C: CosmWasmClient + Send + Sync, + C: CosmWasmClient + NymContractsProvider + Send + Sync, { async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.client - .query_contract_smart(self.coconut_dkg_contract_address(), &query) + let dkg_contract_address = &self + .dkg_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?; + self.query_contract_smart(dkg_contract_address, &query) .await } } -#[async_trait] -impl DkgQueryClient for crate::Client -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_dkg_contract(query).await +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: DkgQueryMsg, + ) { + unimplemented!() } } 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 b5b4c1d1ee..6102bcc5f2 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 @@ -1,28 +1,57 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use nym_group_contract_common::msg::QueryMsg; - use async_trait::async_trait; use cw4::MemberResponse; +use nym_group_contract_common::msg::QueryMsg as GroupQueryMsg; +use serde::Deserialize; #[async_trait] pub trait GroupQueryClient { - async fn member(&self, addr: String) -> Result; -} + async fn query_group_contract(&self, query: GroupQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; -#[async_trait] -impl GroupQueryClient for NyxdClient { - async fn member(&self, addr: String) -> Result { - let request = QueryMsg::Member { - addr, - at_height: None, - }; - self.client - .query_contract_smart(self.group_contract_address(), &request) + async fn member( + &self, + addr: String, + at_height: Option, + ) -> Result { + self.query_group_contract(GroupQueryMsg::Member { addr, at_height }) .await } } + +#[async_trait] +impl GroupQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_group_contract(&self, query: GroupQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let group_contract_address = &self + .group_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("group contract"))?; + self.query_contract_smart(group_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: GroupQueryMsg, + ) { + unimplemented!() + } +} 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 0297b90452..f4128f0024 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 @@ -1,9 +1,10 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::define_paged_response; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; -use crate::nyxd::NyxdClient; +use crate::nyxd::CosmWasmClient; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::Nonce; @@ -93,8 +94,8 @@ pub trait MixnetQueryClient { async fn get_all_family_members_paged( &self, - start_after: Option, limit: Option, + start_after: Option, ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after }) .await @@ -435,46 +436,177 @@ pub trait MixnetQueryClient { async fn get_node_family_by_label( &self, - label: &str, + label: String, ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { - label: label.to_string(), - }) - .await + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { label }) + .await } - async fn get_node_family_by_head(&self, head: &str) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { - head: head.to_string(), - }) - .await - } -} - -#[async_trait] -impl MixnetQueryClient for NyxdClient -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart(self.mixnet_contract_address(), &query) + async fn get_node_family_by_head( + &self, + head: String, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { head }) .await } } +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait #[async_trait] -impl MixnetQueryClient for crate::Client +pub trait PagedMixnetClient: MixnetQueryClient + Send + Sync { + // async fn get_all_node_families(&self) -> Result, NyxdError> { + // let mut res = Vec::new(); + // let mut start_after = None; + // + // loop { + // let paged_response = self + // .get_all_node_families_paged(start_after.take(), None) + // .await?; + // res.extend(paged_response.families); + // + // if let Some(start_next_after) = paged_response.start_next_after { + // start_after = Some(start_next_after) + // } else { + // break; + // } + // } + // + // Ok(res) + // } + + // define_paged_response!( + // get_all_node_families, + // Family, + // get_all_node_families_paged, + // families + // ); +} + +#[async_trait] +impl PagedMixnetClient for T where T: MixnetQueryClient {} + +#[async_trait] +impl MixnetQueryClient for C where - C: CosmWasmClient + Sync + Send, + C: CosmWasmClient + NymContractsProvider + Send + Sync, { async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.nyxd.query_mixnet_contract(query).await + let mixnet_contract_address = &self + .mixnet_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?; + self.query_contract_smart(mixnet_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: MixnetQueryMsg, + ) { + todo!() + // match msg { + // MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after } => client + // .get_all_family_members_paged(limit, start_after) + // .await + // .map(|_| ()), + // MixnetQueryMsg::GetAllMembersPaged { limit, start_after } => client + // .get_all_family_members_paged(limit, start_after) + // .await + // .map(|_| ()), + // MixnetQueryMsg::GetFamilyByHead { head } => { + // client.get_node_family_by_head(head).await.map(|_| ()) + // } + // MixnetQueryMsg::GetFamilyByLabel { label } => { + // client.get_node_family_by_label(label).await.map(|_| ()) + // } + // MixnetQueryMsg::GetFamilyMembersByHead { head } => todo!(), + // MixnetQueryMsg::GetFamilyMembersByLabel { label } => todo!(), + // MixnetQueryMsg::GetContractVersion {} => { + // client.get_mixnet_contract_version().await.map(|_| ()) + // } + // MixnetQueryMsg::GetCW2ContractVersion {} => todo!(), + // MixnetQueryMsg::GetRewardingValidatorAddress {} => { + // client.get_rewarding_validator_address().await.map(|_| ()) + // } + // MixnetQueryMsg::GetStateParams {} => todo!(), + // MixnetQueryMsg::GetState {} => client.get_mixnet_contract_state().await.map(|_| ()), + // MixnetQueryMsg::GetRewardingParams {} => {} + // MixnetQueryMsg::GetEpochStatus {} => {} + // MixnetQueryMsg::GetCurrentIntervalDetails {} => {} + // MixnetQueryMsg::GetRewardedSet { limit, start_after } => {} + // MixnetQueryMsg::GetMixNodeBonds { limit, start_after } => {} + // MixnetQueryMsg::GetMixNodesDetailed { limit, start_after } => {} + // MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after } => {} + // MixnetQueryMsg::GetUnbondedMixNodesByOwner { + // owner, + // limit, + // start_after, + // } => {} + // MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { + // identity_key, + // limit, + // start_after, + // } => {} + // MixnetQueryMsg::GetOwnedMixnode { address } => {} + // MixnetQueryMsg::GetMixnodeDetails { mix_id } => {} + // MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id } => {} + // MixnetQueryMsg::GetStakeSaturation { mix_id } => {} + // MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id } => {} + // MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => {} + // MixnetQueryMsg::GetLayerDistribution {} => {} + // MixnetQueryMsg::GetGateways { start_after, limit } => {} + // MixnetQueryMsg::GetGatewayBond { identity } => {} + // MixnetQueryMsg::GetOwnedGateway { address } => {} + // MixnetQueryMsg::GetMixnodeDelegations { + // mix_id, + // start_after, + // limit, + // } => {} + // MixnetQueryMsg::GetDelegatorDelegations { + // delegator, + // start_after, + // limit, + // } => {} + // MixnetQueryMsg::GetDelegationDetails { + // mix_id, + // delegator, + // proxy, + // } => {} + // MixnetQueryMsg::GetAllDelegations { start_after, limit } => {} + // MixnetQueryMsg::GetPendingOperatorReward { address } => {} + // MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id } => {} + // MixnetQueryMsg::GetPendingDelegatorReward { + // address, + // mix_id, + // proxy, + // } => {} + // MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { + // mix_id, + // estimated_performance, + // } => {} + // MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { + // address, + // mix_id, + // proxy, + // estimated_performance, + // } => {} + // MixnetQueryMsg::GetPendingEpochEvents { limit, start_after } => {} + // MixnetQueryMsg::GetPendingIntervalEvents { limit, start_after } => {} + // MixnetQueryMsg::GetPendingEpochEvent { event_id } => {} + // MixnetQueryMsg::GetPendingIntervalEvent { event_id } => {} + // MixnetQueryMsg::GetNumberOfPendingEvents {} => {} + // MixnetQueryMsg::GetSigningNonce { address } => {} + // } + // .expect("ignore error") } } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs index 9f6dac5354..cf75c407af 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs @@ -1,7 +1,11 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -// TODO: expose query-related capabilities to wasm client... +use cosmrs::AccountId; +use nym_network_defaults::NymContracts; +use std::str::FromStr; + +// TODO: all of those could/should be derived via a macro mod coconut_bandwidth_query_client; mod dkg_query_client; @@ -12,20 +16,20 @@ mod name_service_query_client; mod sp_directory_query_client; mod vesting_query_client; -#[cfg(feature = "signing")] -mod coconut_bandwidth_signing_client; -#[cfg(feature = "signing")] -mod dkg_signing_client; -#[cfg(feature = "signing")] -mod mixnet_signing_client; -#[cfg(feature = "signing")] -mod multisig_signing_client; -#[cfg(feature = "signing")] -mod name_service_signing_client; -#[cfg(feature = "signing")] -mod sp_directory_signing_client; -#[cfg(feature = "signing")] -mod vesting_signing_client; +// #[cfg(feature = "signing")] +// mod coconut_bandwidth_signing_client; +// #[cfg(feature = "signing")] +// mod dkg_signing_client; +// #[cfg(feature = "signing")] +// mod mixnet_signing_client; +// #[cfg(feature = "signing")] +// mod multisig_signing_client; +// #[cfg(feature = "signing")] +// mod name_service_signing_client; +// #[cfg(feature = "signing")] +// mod sp_directory_signing_client; +// #[cfg(feature = "signing")] +// mod vesting_signing_client; pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient; pub use dkg_query_client::DkgQueryClient; @@ -36,17 +40,112 @@ pub use name_service_query_client::NameServiceQueryClient; pub use sp_directory_query_client::SpDirectoryQueryClient; pub use vesting_query_client::VestingQueryClient; -#[cfg(feature = "signing")] -pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; -#[cfg(feature = "signing")] -pub use dkg_signing_client::DkgSigningClient; -#[cfg(feature = "signing")] -pub use mixnet_signing_client::MixnetSigningClient; -#[cfg(feature = "signing")] -pub use multisig_signing_client::MultisigSigningClient; -#[cfg(feature = "signing")] -pub use name_service_signing_client::NameServiceSigningClient; -#[cfg(feature = "signing")] -pub use sp_directory_signing_client::SpDirectorySigningClient; -#[cfg(feature = "signing")] -pub use vesting_signing_client::VestingSigningClient; +// #[cfg(feature = "signing")] +// pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; +// #[cfg(feature = "signing")] +// pub use dkg_signing_client::DkgSigningClient; +// #[cfg(feature = "signing")] +// pub use mixnet_signing_client::MixnetSigningClient; +// #[cfg(feature = "signing")] +// pub use multisig_signing_client::MultisigSigningClient; +// #[cfg(feature = "signing")] +// pub use name_service_signing_client::NameServiceSigningClient; +// #[cfg(feature = "signing")] +// pub use sp_directory_signing_client::SpDirectorySigningClient; +// #[cfg(feature = "signing")] +// pub use vesting_signing_client::VestingSigningClient; + +pub trait NymContractsProvider { + // main + fn mixnet_contract_address(&self) -> Option<&AccountId>; + fn vesting_contract_address(&self) -> Option<&AccountId>; + + // coconut-related + fn coconut_bandwidth_contract_address(&self) -> Option<&AccountId>; + fn dkg_contract_address(&self) -> Option<&AccountId>; + fn group_contract_address(&self) -> Option<&AccountId>; + fn multisig_contract_address(&self) -> Option<&AccountId>; + + // SPs + fn name_service_contract_address(&self) -> Option<&AccountId>; + fn service_provider_contract_address(&self) -> Option<&AccountId>; +} + +#[derive(Debug, Clone)] +pub struct TypedNymContracts { + pub mixnet_contract_address: Option, + pub vesting_contract_address: Option, + + pub coconut_bandwidth_contract_address: Option, + pub group_contract_address: Option, + pub multisig_contract_address: Option, + pub coconut_dkg_contract_address: Option, + + pub service_provider_directory_contract_address: Option, + pub name_service_contract_address: Option, +} + +impl TryFrom for TypedNymContracts { + type Error = ::Err; + + fn try_from(value: NymContracts) -> Result { + Ok(TypedNymContracts { + mixnet_contract_address: value + .mixnet_contract_address + .map(|addr| addr.parse()) + .transpose()?, + vesting_contract_address: value + .vesting_contract_address + .map(|addr| addr.parse()) + .transpose()?, + coconut_bandwidth_contract_address: value + .coconut_bandwidth_contract_address + .map(|addr| addr.parse()) + .transpose()?, + group_contract_address: value + .group_contract_address + .map(|addr| addr.parse()) + .transpose()?, + multisig_contract_address: value + .multisig_contract_address + .map(|addr| addr.parse()) + .transpose()?, + coconut_dkg_contract_address: value + .coconut_dkg_contract_address + .map(|addr| addr.parse()) + .transpose()?, + service_provider_directory_contract_address: value + .service_provider_directory_contract_address + .map(|addr| addr.parse()) + .transpose()?, + name_service_contract_address: value + .name_service_contract_address + .map(|addr| addr.parse()) + .transpose()?, + }) + } +} + +// a simple helper macro to define a function to repeatedly call a paged query until a full response is constructed +#[macro_export] +macro_rules! define_paged_response { + ( $f: ident, $r: ty, $fc: ident, $field: ident ) => { + async fn $f(&self) -> Result, NyxdError> { + let mut res = Vec::new(); + let mut start_after = None; + + loop { + let paged_response = self.$fc(start_after.take(), None).await?; + res.extend(paged_response.$field); + + if let Some(start_next_after) = paged_response.start_next_after { + start_after = Some(start_next_after) + } else { + break; + } + } + + Ok(res) + } + }; +} 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 ea461d1080..f25a4f9a64 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 @@ -1,23 +1,93 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use cw3::{ProposalListResponse, ProposalResponse}; -use nym_multisig_contract_common::msg::QueryMsg; - +use crate::nyxd::CosmWasmClient; use async_trait::async_trait; +use cw3::{ + ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterListResponse, + VoterResponse, +}; +use cw_utils::ThresholdResponse; +use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg; +use serde::Deserialize; #[async_trait] pub trait MultisigQueryClient { - async fn get_proposal(&self, proposal_id: u64) -> Result; + async fn query_multisig_contract(&self, query: MultisigQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn query_threshold(&self) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Threshold {}) + .await + } + + async fn query_proposal(&self, proposal_id: u64) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Proposal { proposal_id }) + .await + } async fn list_proposals( &self, start_after: Option, limit: Option, - ) -> Result; + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListProposals { start_after, limit }) + .await + } + + async fn reverse_proposals( + &self, + start_before: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ReverseProposals { + start_before, + limit, + }) + .await + } + + async fn query_vote(&self, proposal_id: u64, voter: String) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Vote { proposal_id, voter }) + .await + } + + async fn list_votes( + &self, + proposal_id: u64, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListVotes { + proposal_id, + start_after, + limit, + }) + .await + } + + async fn query_voter(&self, address: String) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Voter { address }) + .await + } + + async fn list_voters( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListVoters { start_after, limit }) + .await + } + + // technically it's not deprecated, just not implemented, but I need clippy to point it out to me before I make a PR + #[deprecated] + async fn query_config(&self) -> Result<(), NyxdError> { + unimplemented!() + } async fn get_all_proposals(&self) -> Result, NyxdError> { let mut proposals = Vec::new(); @@ -41,22 +111,63 @@ pub trait MultisigQueryClient { } #[async_trait] -impl MultisigQueryClient for NyxdClient { - async fn get_proposal(&self, proposal_id: u64) -> Result { - let request = QueryMsg::Proposal { proposal_id }; - self.client - .query_contract_smart(self.multisig_contract_address(), &request) - .await - } - - async fn list_proposals( - &self, - start_after: Option, - limit: Option, - ) -> Result { - let request = QueryMsg::ListProposals { start_after, limit }; - self.client - .query_contract_smart(self.multisig_contract_address(), &request) +impl MultisigQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_multisig_contract(&self, query: MultisigQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let multisig_contract_address = &self + .multisig_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("multisig contract"))?; + self.query_contract_smart(multisig_contract_address, &query) .await } } + +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + async fn all_query_variants_are_covered( + client: C, + msg: MultisigQueryMsg, + ) { + match msg { + MultisigQueryMsg::Threshold {} => client.query_threshold().await.map(|_| ()), + MultisigQueryMsg::Proposal { proposal_id } => { + client.query_proposal(proposal_id).await.map(|_| ()) + } + MultisigQueryMsg::ListProposals { start_after, limit } => { + client.list_proposals(start_after, limit).await.map(|_| ()) + } + MultisigQueryMsg::ReverseProposals { + start_before, + limit, + } => client + .reverse_proposals(start_before, limit) + .await + .map(|_| ()), + MultisigQueryMsg::Vote { proposal_id, voter } => { + client.query_vote(proposal_id, voter).await.map(|_| ()) + } + MultisigQueryMsg::ListVotes { + proposal_id, + start_after, + limit, + } => client + .list_votes(proposal_id, start_after, limit) + .await + .map(|_| ()), + MultisigQueryMsg::Voter { address } => client.query_voter(address).await.map(|_| ()), + MultisigQueryMsg::ListVoters { start_after, limit } => { + client.list_voters(start_after, limit).await.map(|_| ()) + } + MultisigQueryMsg::Config {} => client.query_config().await.map(|_| ()), + } + .expect("ignore error") + } +} 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 1cb59711ff..4f53fa52dc 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 @@ -1,3 +1,8 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{error::NyxdError, CosmWasmClient}; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; @@ -8,8 +13,6 @@ use nym_name_service_common::{ }; use serde::Deserialize; -use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; - #[async_trait] pub trait NameServiceQueryClient { async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result @@ -81,36 +84,33 @@ pub trait NameServiceQueryClient { .await } } - #[async_trait] -impl NameServiceQueryClient for NyxdClient +impl NameServiceQueryClient for C where - C: CosmWasmClient + Send + Sync, + C: CosmWasmClient + NymContractsProvider + Send + Sync, { async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.client - .query_contract_smart( - self.name_service_contract_address().ok_or( - NyxdError::NoContractAddressAvailable("name service contract".to_string()), - )?, - &query, - ) + let name_service_contract_address = &self + .name_service_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?; + self.query_contract_smart(name_service_contract_address, &query) .await } } -#[async_trait] -impl NameServiceQueryClient for crate::Client -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_name_service_contract(query).await +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: NameQueryMsg, + ) { + unimplemented!() } } 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 0ed99d5c1c..a292cb1121 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 @@ -10,6 +10,7 @@ use nym_service_provider_directory_common::{ }; use serde::Deserialize; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; #[async_trait] @@ -89,36 +90,33 @@ pub trait SpDirectoryQueryClient { } #[async_trait] -impl SpDirectoryQueryClient for NyxdClient +impl SpDirectoryQueryClient for C where - C: CosmWasmClient + Send + Sync, + C: CosmWasmClient + NymContractsProvider + Send + Sync, { async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.client - .query_contract_smart( - self.service_provider_contract_address().ok_or( - NyxdError::NoContractAddressAvailable( - "service provider directory contract".to_string(), - ), - )?, - &query, - ) + let sp_directory_contract_address = + &self.service_provider_contract_address().ok_or_else(|| { + NyxdError::unavailable_contract_address("service provider directory contract") + })?; + self.query_contract_smart(sp_directory_contract_address, &query) .await } } -#[async_trait] -impl SpDirectoryQueryClient for crate::Client -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_service_provider_contract(query).await +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: SpQueryMsg, + ) { + unimplemented!() } } 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 c70f0a78ca..df073b4e45 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 @@ -1,10 +1,10 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::coin::Coin; -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; -use crate::nyxd::NyxdClient; +use crate::nyxd::{CosmWasmClient, NyxdClient}; use async_trait::async_trait; use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp}; use nym_contracts_common::ContractBuildInformation; @@ -334,13 +334,32 @@ pub trait VestingQueryClient { } #[async_trait] -impl VestingQueryClient for NyxdClient { +impl VestingQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.client - .query_contract_smart(self.vesting_contract_address(), &query) + let vesting_contract_address = &self + .vesting_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?; + self.query_contract_smart(vesting_contract_address, &query) .await } } + +#[cfg(test)] +mod tests { + use super::*; + + // it's enough that this compiles + #[deprecated] + async fn all_query_variants_are_covered( + client: C, + msg: VestingQueryMsg, + ) { + unimplemented!() + } +} diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs new file mode 100644 index 0000000000..1fc9b3789d --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod query_client; + +#[cfg(feature = "signing")] +pub mod signing_client; + +pub use query_client::CosmWasmClient; + +#[cfg(feature = "signing")] +pub use signing_client::SigningCosmWasmClient; diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs similarity index 95% rename from common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs index 9ca497e1c8..46073a2671 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs @@ -1,4 +1,4 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::nyxd; @@ -39,25 +39,15 @@ use tendermint_rpc::{ Order, }; +pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); +pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); + #[cfg(feature = "http-client")] #[async_trait] -impl CosmWasmClient for cosmrs::rpc::HttpClient { - fn broadcast_polling_rate(&self) -> Duration { - Duration::from_secs(4) - } - - fn broadcast_timeout(&self) -> Duration { - Duration::from_secs(60) - } -} +impl CosmWasmClient for cosmrs::rpc::HttpClient {} #[async_trait] pub trait CosmWasmClient: TendermintClient { - // this should probably get redesigned, but I'm leaving those like that temporarily to fix - // the underlying issue more quickly - fn broadcast_polling_rate(&self) -> Duration; - fn broadcast_timeout(&self) -> Duration; - // 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? @@ -268,10 +258,20 @@ pub trait CosmWasmClient: TendermintClient { Ok(tendermint_rpc::client::Client::broadcast_tx_commit(self, tx).await?) } - async fn broadcast_tx(&self, tx: T) -> Result + async fn broadcast_tx( + &self, + tx: T, + timeout: impl Into> + Send, + poll_interval: impl Into> + Send, + ) -> Result where T: Into> + Send, { + let timeout = timeout.into().unwrap_or(DEFAULT_BROADCAST_TIMEOUT); + let poll_interval = poll_interval + .into() + .unwrap_or(DEFAULT_BROADCAST_POLLING_RATE); + let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?; if broadcasted.code.is_err() { @@ -292,10 +292,10 @@ pub trait CosmWasmClient: TendermintClient { "Polling for result of including {} in a block...", broadcasted.hash ); - if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() { + if tokio::time::Instant::now().duration_since(start) >= timeout { return Err(NyxdError::BroadcastTimeout { hash: tx_hash, - timeout: self.broadcast_timeout(), + timeout, }); } @@ -303,7 +303,7 @@ pub trait CosmWasmClient: TendermintClient { return Ok(poll_res); } - tokio::time::sleep(self.broadcast_polling_rate()).await; + tokio::time::sleep(poll_interval).await; } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs similarity index 86% rename from common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs index f4d6a41829..a73f987936 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs @@ -1,7 +1,7 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::cosmwasm_client::client_traits::CosmWasmClient; use crate::nyxd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; use crate::nyxd::cosmwasm_client::logs::{self, parse_raw_logs}; use crate::nyxd::cosmwasm_client::types::*; @@ -9,6 +9,7 @@ use crate::nyxd::error::NyxdError; use crate::nyxd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; use crate::nyxd::{Coin, GasAdjustable, GasPrice, TxResponse}; use crate::signing::signer::OfflineSigner; +use crate::signing::tx_signer::TxSigner; use crate::signing::SignerData; use async_trait::async_trait; use cosmrs::abci::GasInfo; @@ -26,21 +27,9 @@ use serde::Serialize; use sha2::Digest; use sha2::Sha256; use std::convert::TryInto; -use std::time::{Duration, SystemTime}; +use std::time::SystemTime; use tendermint_rpc::endpoint::broadcast; -#[cfg(feature = "http-client")] -use crate::signing::tx_signer::TxSigner; - -#[cfg(feature = "http-client")] -use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest}; - -#[cfg(feature = "http-client")] -use cosmrs::rpc::{HttpClient, HttpClientUrl}; - -pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); -pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); - fn empty_fee() -> tx::Fee { tx::Fee { amount: vec![], @@ -65,15 +54,16 @@ fn single_unspecified_signer_auth( } #[async_trait] -pub trait SigningCosmWasmClient: CosmWasmClient { - type Signer: OfflineSigner + Send + Sync; - - fn signer(&self) -> &Self::Signer; - +pub trait SigningCosmWasmClient: CosmWasmClient + TxSigner +where + NyxdError: From<::Error>, +{ + // TODO: would it somehow be possible to get rid of this method and allow for + // blanket implementation for anything that provides CosmWasmClient + TxSigner? fn gas_price(&self) -> &GasPrice; fn signer_public_key(&self, signer_address: &AccountId) -> Option { - let account = self.signer().find_account(signer_address).ok()?; + let account = self.find_account(signer_address).ok()?; Some(account.public_key().into()) } @@ -587,9 +577,9 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER); let gas = gas_estimation.adjust_gas(multiplier); - debug!("Gas estimation: {}", gas_estimation); - debug!("Multiplying the estimation by {}", multiplier); - debug!("Final gas limit used: {}", gas); + debug!("Gas estimation: {gas_estimation}"); + debug!("Multiplying the estimation by {multiplier}"); + debug!("Final gas limit used: {gas}"); let fee = self.gas_price() * gas; Ok::(tx::Fee::from_amount_and_gas(fee, gas)) @@ -687,7 +677,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - self.broadcast_tx(tx_bytes).await + self.broadcast_tx(tx_bytes, None, None).await } async fn sign( @@ -710,161 +700,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { } }; - self.sign_direct(signer_address, messages, fee, memo, signer_data) - } - - fn sign_amino( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result; - - fn sign_direct( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result; -} - -#[cfg(feature = "http-client")] -#[derive(Debug)] -pub struct Client { - // TODO: somehow nicely hide this guy if we decide to use our client in offline mode, - // maybe just convert it into an option? - // or maybe we need another level of indirection. tbd. - rpc_client: HttpClient, - tx_signer: TxSigner, - gas_price: GasPrice, - - broadcast_polling_rate: Duration, - broadcast_timeout: Duration, -} - -#[cfg(feature = "http-client")] -impl Client { - pub fn connect_with_signer( - endpoint: U, - signer: S, - gas_price: GasPrice, - ) -> Result - where - U: TryInto, - { - let rpc_client = HttpClient::new(endpoint)?; - Ok(Client { - rpc_client, - tx_signer: TxSigner::new(signer), - gas_price, - broadcast_polling_rate: DEFAULT_BROADCAST_POLLING_RATE, - broadcast_timeout: DEFAULT_BROADCAST_TIMEOUT, - }) - } - - pub fn offline(signer: S) -> TxSigner - where - S: OfflineSigner, - { - TxSigner::new(signer) - } - - pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> - where - U: TryInto, - { - let new_rpc_client = HttpClient::new(new_endpoint)?; - self.rpc_client = new_rpc_client; - Ok(()) - } - - pub fn into_signer(self) -> S { - self.tx_signer.into_inner_signer() - } - - pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) { - self.broadcast_polling_rate = broadcast_polling_rate - } - - pub fn set_broadcast_timeout(&mut self, broadcast_timeout: Duration) { - self.broadcast_timeout = broadcast_timeout - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl tendermint_rpc::client::Client for Client -where - S: Send + Sync, -{ - async fn perform(&self, request: R) -> Result - where - R: SimpleRequest, - { - self.rpc_client.perform(request).await - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl CosmWasmClient for Client -where - S: Send + Sync, -{ - fn broadcast_polling_rate(&self) -> Duration { - self.broadcast_polling_rate - } - - fn broadcast_timeout(&self) -> Duration { - self.broadcast_timeout - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl SigningCosmWasmClient for Client -where - S: OfflineSigner + Send + Sync, - NyxdError: From, -{ - type Signer = S; - - fn signer(&self) -> &Self::Signer { - self.tx_signer.signer() - } - - fn gas_price(&self) -> &GasPrice { - &self.gas_price - } - - fn sign_amino( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result { - Ok(self - .tx_signer - .sign_amino(signer_address, messages, fee, memo, signer_data)?) - } - - fn sign_direct( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result { - Ok(self - .tx_signer - .sign_direct(signer_address, messages, fee, memo, signer_data)?) + Ok(::sign_direct( + self, + signer_address, + messages, + fee, + memo, + signer_data, + )?) } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs index 0a2d2c7c32..be570d128d 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs @@ -1,37 +1,153 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "http-client")] +use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; use crate::nyxd::error::NyxdError; -#[cfg(feature = "http-client")] -use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl}; -#[cfg(feature = "http-client")] -use std::convert::TryInto; +use crate::nyxd::{Config, GasPrice, TendermintClient}; +use crate::signing::signer::{NoSigner, OfflineSigner}; +use crate::signing::tx_signer::TxSigner; +use crate::signing::AccountData; +use async_trait::async_trait; +use cosmrs::rpc::Error as TendermintRpcError; +use cosmrs::AccountId; +use tendermint_rpc::SimpleRequest; -pub mod client; +pub mod client_traits; mod helpers; pub mod logs; pub mod types; -#[cfg(feature = "signing")] -pub mod signing_client; - -#[cfg(feature = "http-client")] -pub fn connect(endpoint: U) -> Result -where - U: TryInto, -{ - Ok(HttpClient::new(endpoint)?) +#[derive(Debug)] +pub(crate) struct SigningClientOptions { + gas_price: GasPrice, } -#[cfg(all(feature = "signing", feature = "http-client"))] -pub fn connect_with_signer( - endpoint: U, +impl<'a> From<&'a Config> for SigningClientOptions { + fn from(value: &'a Config) -> Self { + SigningClientOptions { + gas_price: value.gas_price.clone(), + } + } +} + +// convenience wrapper around query client to allow for optional signing +#[derive(Debug)] +pub(crate) struct MaybeSigningClient { + client: C, signer: S, - gas_price: crate::nyxd::GasPrice, -) -> Result, NyxdError> -where - U: TryInto, -{ - signing_client::Client::connect_with_signer(endpoint, signer, gas_price) + opts: SigningClientOptions, + derived_addresses: Option>, } + +impl MaybeSigningClient { + pub(crate) fn new(client: C, opts: SigningClientOptions) -> Self { + MaybeSigningClient { + client, + signer: Default::default(), + opts, + derived_addresses: None, + } + } +} + +impl MaybeSigningClient { + pub(crate) fn new_signing( + client: C, + signer: S, + opts: SigningClientOptions, + ) -> Result + where + S: OfflineSigner, + { + let derived_addresses = signer + .get_accounts()? + .into_iter() + .map(|account| account.address) + .collect(); + Ok(MaybeSigningClient { + client, + signer, + opts, + derived_addresses: Some(derived_addresses), + }) + } + + pub(crate) fn derived_addresses(&self) -> &[AccountId] { + // the unwrap is fine here as you can't construct a signing client without setting the addresses + self.derived_addresses.as_ref().unwrap() + } +} + +#[async_trait] +impl TendermintClient for MaybeSigningClient +where + C: TendermintClient + Send + Sync, + S: Send + Sync, +{ + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.client.perform(request).await + } +} + +#[async_trait] +impl CosmWasmClient for MaybeSigningClient +where + C: CosmWasmClient + Send + Sync, + S: Send + Sync, +{ +} + +impl OfflineSigner for MaybeSigningClient +where + C: CosmWasmClient, + S: OfflineSigner, +{ + type Error = S::Error; + + fn get_accounts(&self) -> Result, Self::Error> { + self.signer.get_accounts() + } +} + +impl TxSigner for MaybeSigningClient +where + C: CosmWasmClient, + S: OfflineSigner, +{ +} + +#[async_trait] +impl SigningCosmWasmClient for MaybeSigningClient +where + C: CosmWasmClient + Send + Sync, + S: OfflineSigner + Send + Sync, + NyxdError: From, +{ + fn gas_price(&self) -> &GasPrice { + &self.opts.gas_price + } +} + +// +// #[cfg(feature = "http-client")] +// pub fn connect(endpoint: U) -> Result, NyxdError> +// where +// U: TryInto, +// { +// Ok(HttpClient::new(endpoint)?) +// } +// +// #[cfg(all(feature = "signing", feature = "http-client"))] +// pub fn connect_with_signer( +// endpoint: U, +// signer: S, +// gas_price: crate::nyxd::GasPrice, +// ) -> Result, NyxdError> +// where +// U: TryInto, +// { +// signing_client::Client::connect_with_signer(endpoint, signer, gas_price) +// } diff --git a/common/client-libs/validator-client/src/nyxd/error.rs b/common/client-libs/validator-client/src/nyxd/error.rs index b63be6e75c..7a414a6914 100644 --- a/common/client-libs/validator-client/src/nyxd/error.rs +++ b/common/client-libs/validator-client/src/nyxd/error.rs @@ -224,4 +224,8 @@ impl NyxdError { _ => false, } } + + pub fn unavailable_contract_address>(contract_type: S) -> Self { + NyxdError::NoContractAddressAvailable(contract_type.into()) + } } diff --git a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs index caccceb436..f6c0d83c24 100644 --- a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs @@ -6,6 +6,7 @@ use cosmrs::Coin; use cosmrs::Gas; use cosmwasm_std::{Decimal, Fraction, Uint128}; use nym_config::defaults; +use nym_network_defaults::{ChainDetails, NymNetworkDetails}; use std::ops::Mul; use std::str::FromStr; @@ -73,6 +74,19 @@ impl FromStr for GasPrice { } } +impl<'a> TryFrom<&'a NymNetworkDetails> for GasPrice { + type Error = NyxdError; + + fn try_from(value: &'a NymNetworkDetails) -> Result { + format!( + "{}{}", + value.default_gas_price_amount(), + value.chain_details.mix_denom.base + ) + .parse() + } +} + impl GasPrice { pub fn new_with_default_price(denom: &str) -> Result { format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse() diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index 934aeec8b2..3fb4cc9372 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -6,12 +6,11 @@ use crate::nyxd::error::NyxdError; use log::{debug, trace}; use nym_network_defaults::{ChainDetails, NymNetworkDetails}; use serde::{Deserialize, Serialize}; -use tendermint_rpc::{endpoint::block::Response as BlockResponse, query::Query}; +use tendermint_rpc::{ + endpoint::block::Response as BlockResponse, query::Query, HttpClient, HttpClientUrl, +}; -#[cfg(feature = "http-client")] -use tendermint_rpc::Error as TendermintRpcError; - -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; pub use crate::nyxd::fee::Fee; pub use coin::Coin; pub use cosmrs::bank::MsgSend; @@ -33,10 +32,8 @@ pub use tendermint_rpc::{ }; #[cfg(feature = "http-client")] -pub use cosmrs::rpc::{HttpClient as QueryNyxdClient, HttpClientUrl}; +use tendermint_rpc::Error as TendermintRpcError; -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::nyxd::cosmwasm_client::signing_client; #[cfg(feature = "signing")] use crate::nyxd::cosmwasm_client::types::{ ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, @@ -46,6 +43,7 @@ use crate::nyxd::cosmwasm_client::types::{ use crate::signing::direct_wallet::DirectSecp256k1HdWallet; #[cfg(all(feature = "signing", feature = "http-client"))] use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; #[cfg(feature = "signing")] use cosmrs::cosmwasm; #[cfg(feature = "signing")] @@ -55,14 +53,10 @@ use cosmwasm_std::Addr; #[cfg(feature = "signing")] use std::time::SystemTime; -#[cfg(feature = "signing")] -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; - -#[cfg(all(feature = "signing", feature = "http-client"))] -pub use signing_client::Client as SigningNyxdClient; - -#[cfg(all(feature = "signing", feature = "http-client"))] -pub type DirectSigningNyxdClient = SigningNyxdClient; +use crate::nyxd::contract_traits::{NymContractsProvider, TypedNymContracts}; +use crate::nyxd::cosmwasm_client::MaybeSigningClient; +use crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; +use crate::signing::signer::NoSigner; pub mod coin; pub mod contract_traits; @@ -70,22 +64,24 @@ pub mod cosmwasm_client; pub mod error; pub mod fee; +// temp +#[deprecated] +type QueryNyxdClient = HttpClient; + +// helper types +#[cfg(all(feature = "signing", feature = "http-client"))] +pub type DirectSigningHttpNyxdClient = NyxdClient; +#[cfg(feature = "http-client")] +pub type QueryHttpNyxdClient = NyxdClient; + +// TODO: reqwest based for wasm + #[derive(Debug, Clone)] pub struct Config { pub(crate) chain_details: ChainDetails, - - // I'd love to have used `NymContracts` struct directly here instead, - // however, I'd really prefer to use something more strongly typed (i.e. AccountId vs String) - pub(crate) mixnet_contract_address: Option, - pub(crate) vesting_contract_address: Option, - pub(crate) coconut_bandwidth_contract_address: Option, - pub(crate) group_contract_address: Option, - pub(crate) multisig_contract_address: Option, - pub(crate) coconut_dkg_contract_address: Option, - pub(crate) service_provider_contract_address: Option, - pub(crate) name_service_contract_address: Option, - // TODO: add this in later commits - // pub(crate) gas_price: GasPrice, + pub(crate) contracts: TypedNymContracts, + pub(crate) gas_price: GasPrice, + pub(crate) simulated_gas_multiplier: f32, } impl Config { @@ -114,96 +110,64 @@ impl Config { } pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result { - let prefix = &details.chain_details.bech32_account_prefix; Ok(Config { chain_details: details.chain_details.clone(), - mixnet_contract_address: Self::parse_optional_account( - details.contracts.mixnet_contract_address.as_ref(), - prefix, - )?, - vesting_contract_address: Self::parse_optional_account( - details.contracts.vesting_contract_address.as_ref(), - prefix, - )?, - coconut_bandwidth_contract_address: Self::parse_optional_account( - details - .contracts - .coconut_bandwidth_contract_address - .as_ref(), - prefix, - )?, - group_contract_address: Self::parse_optional_account( - details.contracts.group_contract_address.as_ref(), - prefix, - )?, - multisig_contract_address: Self::parse_optional_account( - details.contracts.multisig_contract_address.as_ref(), - prefix, - )?, - coconut_dkg_contract_address: Self::parse_optional_account( - details.contracts.coconut_dkg_contract_address.as_ref(), - prefix, - )?, - service_provider_contract_address: Self::parse_optional_account( - details - .contracts - .service_provider_directory_contract_address - .as_ref(), - prefix, - )?, - name_service_contract_address: Self::parse_optional_account( - details.contracts.name_service_contract_address.as_ref(), - prefix, - )?, + contracts: TypedNymContracts::try_from(details.contracts.clone())?, + gas_price: details.try_into()?, + simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } } +// so youd have: +// for queries: +// NyxdClient +// NyxdClient +// +// for signing: +// NyxdClient +// NyxdClient + #[derive(Debug)] -pub struct NyxdClient { - client: C, +pub struct NyxdClient { + client: MaybeSigningClient, config: Config, - // TODO: refactor because that field is only really used for signing - #[allow(dead_code)] - client_address: Option>, - simulated_gas_multiplier: f32, } #[cfg(feature = "http-client")] -impl NyxdClient { - pub fn connect(config: Config, endpoint: U) -> Result, NyxdError> +impl NyxdClient { + pub fn connect(config: Config, endpoint: U) -> Result where U: TryInto, { + let client = HttpClient::new(endpoint)?; + Ok(NyxdClient { - client: QueryNyxdClient::new(endpoint)?, + client: MaybeSigningClient::new(client, (&config).into()), config, - client_address: None, - simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } } #[cfg(all(feature = "signing", feature = "http-client"))] -impl NyxdClient> { +impl NyxdClient { // TODO: rename this one pub fn connect_with_mnemonic( config: Config, endpoint: U, mnemonic: bip39::Mnemonic, - gas_price: Option, - ) -> Result>, NyxdError> + ) -> Result where U: TryInto, { let prefix = &config.chain_details.bech32_account_prefix; let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); - Self::connect_with_signer(config, endpoint, wallet, gas_price) + Self::connect_with_signer(config, endpoint, wallet) } } #[cfg(all(feature = "signing", feature = "http-client"))] -impl NyxdClient> +impl NyxdClient where S: OfflineSigner, // I have no idea why S::Error: Into bound wouldn't do the trick @@ -214,319 +178,41 @@ where config: Config, endpoint: U, signer: S, - gas_price: Option, - ) -> Result>, NyxdError> + ) -> Result, NyxdError> where U: TryInto, { - let denom = &config.chain_details.mix_denom.base; - let client_address = signer - .get_accounts()? - .into_iter() - .map(|account| account.address) - .collect(); - let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?); - + let client = HttpClient::new(endpoint)?; Ok(NyxdClient { - client: SigningNyxdClient::connect_with_signer(endpoint, signer, gas_price)?, + client: MaybeSigningClient::new_signing(client, signer, (&config).into())?, config, - client_address: Some(client_address), - simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } - #[cfg(feature = "http-client")] - pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> + // #[cfg(feature = "http-client")] + // pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> + // where + // U: TryInto, + // { + // self.client.change_endpoint(new_endpoint) + // } + + #[deprecated] + pub fn into_signer(self) -> S { + todo!() + // self.client.into_signer() + } +} + +// no trait bounds +impl NyxdClient { + pub(crate) fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> where U: TryInto, { - self.client.change_endpoint(new_endpoint) + todo!() } - pub fn into_signer(self) -> S { - self.client.into_signer() - } -} - -#[cfg(feature = "signing")] -impl NyxdClient -where - C: SigningCosmWasmClient + Sync, -{ - pub fn address(&self) -> &AccountId - where - C: SigningCosmWasmClient, - { - // if this is a signing client (as required by the trait bound), it must have the address set - &self.client_address.as_ref().unwrap()[0] - } - - pub fn cw_address(&self) -> Addr - where - C: SigningCosmWasmClient, - { - // the call to unchecked is fine here as we're converting directly from `AccountId` - // which must have been a valid bech32 address - Addr::unchecked(self.address().as_ref()) - } - - pub async fn account_sequence(&self) -> Result - where - C: SigningCosmWasmClient + Sync, - { - self.client.get_sequence(self.address()).await - } - - pub fn signer(&self) -> &::Signer - where - C: SigningCosmWasmClient, - { - self.client.signer() - } - - pub fn gas_price(&self) -> &GasPrice - where - C: SigningCosmWasmClient, - { - self.client.gas_price() - } - - pub fn wrap_contract_execute_message( - &self, - contract_address: &AccountId, - msg: &M, - funds: Vec, - ) -> Result - where - C: SigningCosmWasmClient, - M: ?Sized + Serialize, - { - Ok(cosmwasm::MsgExecuteContract { - sender: self.address().clone(), - contract: contract_address.clone(), - msg: serde_json::to_vec(msg)?, - funds: funds.into_iter().map(Into::into).collect(), - }) - } - - pub async fn simulate(&self, messages: I) -> Result - where - C: SigningCosmWasmClient + Sync, - I: IntoIterator + Send, - M: Msg, - { - self.client - .simulate( - self.address(), - messages - .into_iter() - .map(|msg| msg.into_any()) - .collect::, _>>() - .map_err(|_| { - NyxdError::SerializationError("custom simulate messages".to_owned()) - })?, - "simulating execution of transactions", - ) - .await - } - - /// Send funds from one address to another - pub async fn send( - &self, - recipient: &AccountId, - amount: Vec, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .send_tokens(self.address(), recipient, amount, fee, memo) - .await - } - - /// Send funds from one address to multiple others - pub async fn send_multiple( - &self, - msgs: Vec<(AccountId, Vec)>, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .send_tokens_multiple(self.address(), msgs, fee, memo) - .await - } - - /// Grant a fee allowance from one address to another - pub async fn grant_allowance( - &self, - grantee: &AccountId, - spend_limit: Vec, - expiration: Option, - allowed_messages: Vec, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .grant_allowance( - self.address(), - grantee, - spend_limit, - expiration, - allowed_messages, - fee, - memo, - ) - .await - } - - /// Revoke a fee allowance from one address to another - pub async fn revoke_allowance( - &self, - grantee: &AccountId, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .revoke_allowance(self.address(), grantee, fee, memo) - .await - } - - pub async fn execute( - &self, - contract_address: &AccountId, - msg: &M, - fee: Option, - memo: impl Into + Send + 'static, - funds: Vec, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .execute(self.address(), contract_address, msg, fee, memo, funds) - .await - } - - pub async fn execute_multiple( - &self, - contract_address: &AccountId, - msgs: I, - fee: Option, - memo: impl Into + Send + 'static, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - I: IntoIterator)> + Send, - M: Serialize, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .execute_multiple(self.address(), contract_address, msgs, fee, memo) - .await - } - - pub async fn upload( - &self, - wasm_code: Vec, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .upload(self.address(), wasm_code, fee, memo) - .await - } - - pub async fn instantiate( - &self, - code_id: ContractCodeId, - msg: &M, - label: String, - memo: impl Into + Send + 'static, - options: Option, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .instantiate(self.address(), code_id, msg, label, fee, memo, options) - .await - } - - pub async fn update_admin( - &self, - contract_address: &AccountId, - new_admin: &AccountId, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .update_admin(self.address(), contract_address, new_admin, fee, memo) - .await - } - - pub async fn clear_admin( - &self, - contract_address: &AccountId, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .clear_admin(self.address(), contract_address, fee, memo) - .await - } - - pub async fn migrate( - &self, - contract_address: &AccountId, - code_id: ContractCodeId, - msg: &M, - memo: impl Into + Send + 'static, - fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - self.client - .migrate(self.address(), contract_address, code_id, fee, msg, memo) - .await - } -} - -impl NyxdClient { pub fn current_config(&self) -> &Config { &self.config } @@ -536,130 +222,78 @@ impl NyxdClient { } pub fn set_mixnet_contract_address(&mut self, address: AccountId) { - self.config.mixnet_contract_address = Some(address); + self.config.contracts.mixnet_contract_address = Some(address); } pub fn set_vesting_contract_address(&mut self, address: AccountId) { - self.config.vesting_contract_address = Some(address); + self.config.contracts.vesting_contract_address = Some(address); } pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { - self.config.coconut_bandwidth_contract_address = Some(address); + self.config.contracts.coconut_bandwidth_contract_address = Some(address); } pub fn set_multisig_contract_address(&mut self, address: AccountId) { - self.config.multisig_contract_address = Some(address); + self.config.contracts.multisig_contract_address = Some(address); } pub fn set_service_provider_contract_address(&mut self, address: AccountId) { - self.config.service_provider_contract_address = Some(address); - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn mixnet_contract_address(&self) -> &AccountId { - self.config.mixnet_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn vesting_contract_address(&self) -> &AccountId { - self.config.vesting_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn coconut_bandwidth_contract_address(&self) -> &AccountId { self.config - .coconut_bandwidth_contract_address - .as_ref() - .unwrap() - } - - pub fn group_contract_address(&self) -> &AccountId { - self.config.group_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn multisig_contract_address(&self) -> &AccountId { - self.config.multisig_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn coconut_dkg_contract_address(&self) -> &AccountId { - self.config.coconut_dkg_contract_address.as_ref().unwrap() - } - - // The service provider directory contract is optional, so we return an Option not a Result - pub fn service_provider_contract_address(&self) -> Option<&AccountId> { - self.config.service_provider_contract_address.as_ref() - } - - // The name service contract is optional, so we return an Option not a Result - pub fn name_service_contract_address(&self) -> Option<&AccountId> { - self.config.name_service_contract_address.as_ref() + .contracts + .service_provider_directory_contract_address = Some(address); } pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.simulated_gas_multiplier = multiplier; + self.config.simulated_gas_multiplier = multiplier; + } +} + +impl NymContractsProvider for NyxdClient { + fn mixnet_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.mixnet_contract_address.as_ref() } - pub async fn query_contract_smart( - &self, - contract: &AccountId, - query_msg: &M, - ) -> Result - where - C: CosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - for<'a> T: Deserialize<'a>, - { - self.client.query_contract_smart(contract, query_msg).await + fn vesting_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.vesting_contract_address.as_ref() } - pub async fn query_contract_raw( - &self, - contract: &AccountId, - query_data: Vec, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.query_contract_raw(contract, query_data).await + fn coconut_bandwidth_contract_address(&self) -> Option<&AccountId> { + self.config + .contracts + .coconut_bandwidth_contract_address + .as_ref() } - pub fn gas_adjustment(&self) -> GasAdjustment { - self.simulated_gas_multiplier + fn dkg_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.coconut_dkg_contract_address.as_ref() } - // ============= - // CHAIN RELATED - // ============= - - // CHAIN QUERIES - - pub async fn get_account_details( - &self, - address: &AccountId, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_account(address).await + fn group_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.group_contract_address.as_ref() } + fn multisig_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.multisig_contract_address.as_ref() + } + + fn name_service_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.name_service_contract_address.as_ref() + } + + fn service_provider_contract_address(&self) -> Option<&AccountId> { + self.config + .contracts + .service_provider_directory_contract_address + .as_ref() + } +} + +// queries +impl NyxdClient +where + C: CosmWasmClient + Send + Sync, + S: Send + Sync, +{ pub async fn get_account_public_key( &self, address: &AccountId, @@ -720,54 +354,260 @@ impl NyxdClient { .await .map(|block| block.block_id.hash) } +} - pub async fn get_validators( +// signing +#[cfg(feature = "signing")] +impl NyxdClient +where + C: SigningCosmWasmClient + Send + Sync, + S: OfflineSigner + Send + Sync, + + // TODO: figure out those trait bounds lol + NyxdError: From<::Error>, + NyxdError: From<::Error>, +{ + pub fn address(&self) -> &AccountId { + &self.client.derived_addresses()[0] + } + + pub fn cw_address(&self) -> Addr { + // the call to unchecked is fine here as we're converting directly from `AccountId` + // which must have been a valid bech32 address + Addr::unchecked(self.address().as_ref()) + } + + pub async fn account_sequence(&self) -> Result { + self.client.get_sequence(self.address()).await + } + + pub fn wrap_contract_execute_message( &self, - height: u64, - paging: Paging, - ) -> Result + contract_address: &AccountId, + msg: &M, + funds: Vec, + ) -> Result where - C: CosmWasmClient + Sync, + M: ?Sized + Serialize, { - Ok(self.client.validators(height as u32, paging).await?) + Ok(cosmwasm::MsgExecuteContract { + sender: self.address().clone(), + contract: contract_address.clone(), + msg: serde_json::to_vec(msg)?, + funds: funds.into_iter().map(Into::into).collect(), + }) } - pub async fn get_balance( + pub async fn simulate(&self, messages: I) -> Result + where + I: IntoIterator + Send, + M: Msg, + { + self.client + .simulate( + self.address(), + messages + .into_iter() + .map(|msg| msg.into_any()) + .collect::, _>>() + .map_err(|_| { + NyxdError::SerializationError("custom simulate messages".to_owned()) + })?, + "simulating execution of transactions", + ) + .await + } + + /// Send funds from one address to another + pub async fn send( &self, - address: &AccountId, - denom: String, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_balance(address, denom).await + recipient: &AccountId, + amount: Vec, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .send_tokens(self.address(), recipient, amount, fee, memo) + .await } - pub async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_all_balances(address).await + /// Send funds from one address to multiple others + pub async fn send_multiple( + &self, + msgs: Vec<(AccountId, Vec)>, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .send_tokens_multiple(self.address(), msgs, fee, memo) + .await } - pub async fn get_tx(&self, id: Hash) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_tx(id).await + /// Grant a fee allowance from one address to another + pub async fn grant_allowance( + &self, + grantee: &AccountId, + spend_limit: Vec, + expiration: Option, + allowed_messages: Vec, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .grant_allowance( + self.address(), + grantee, + spend_limit, + expiration, + allowed_messages, + fee, + memo, + ) + .await } - pub async fn search_tx(&self, query: Query) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.search_tx(query).await + /// Revoke a fee allowance from one address to another + pub async fn revoke_allowance( + &self, + grantee: &AccountId, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .revoke_allowance(self.address(), grantee, fee, memo) + .await } - pub async fn get_total_supply(&self) -> Result, NyxdError> + pub async fn execute( + &self, + contract_address: &AccountId, + msg: &M, + fee: Option, + memo: impl Into + Send + 'static, + funds: Vec, + ) -> Result where - C: CosmWasmClient + Sync, + M: ?Sized + Serialize + Sync, { - self.client.get_total_supply().await + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .execute(self.address(), contract_address, msg, fee, memo, funds) + .await + } + + pub async fn execute_multiple( + &self, + contract_address: &AccountId, + msgs: I, + fee: Option, + memo: impl Into + Send + 'static, + ) -> Result + where + I: IntoIterator)> + Send, + M: Serialize, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .execute_multiple(self.address(), contract_address, msgs, fee, memo) + .await + } + + pub async fn upload( + &self, + wasm_code: Vec, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .upload(self.address(), wasm_code, fee, memo) + .await + } + + pub async fn instantiate( + &self, + code_id: ContractCodeId, + msg: &M, + label: String, + memo: impl Into + Send + 'static, + options: Option, + fee: Option, + ) -> Result + where + M: ?Sized + Serialize + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .instantiate(self.address(), code_id, msg, label, fee, memo, options) + .await + } + + pub async fn update_admin( + &self, + contract_address: &AccountId, + new_admin: &AccountId, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .update_admin(self.address(), contract_address, new_admin, fee, memo) + .await + } + + pub async fn clear_admin( + &self, + contract_address: &AccountId, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .clear_admin(self.address(), contract_address, fee, memo) + .await + } + + pub async fn migrate( + &self, + contract_address: &AccountId, + code_id: ContractCodeId, + msg: &M, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result + where + M: ?Sized + Serialize + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); + self.client + .migrate(self.address(), contract_address, code_id, fee, msg, memo) + .await } } + +#[async_trait] +impl TendermintClient for NyxdClient +where + C: TendermintClient + Send + Sync, +{ + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.client.perform(request).await + } +} + +#[async_trait] +impl CosmWasmClient for NyxdClient where C: CosmWasmClient + Send + Sync {} + +// #[async_trait] +// impl SigningCosmWasmClient for NyxdClient where C: SigningCosmWasmClient + Send + Sync { +// fn gas_price(&self) -> &GasPrice { +// self.client.gas_price() +// } +// } diff --git a/common/client-libs/validator-client/src/signing/direct_wallet.rs b/common/client-libs/validator-client/src/signing/direct_wallet.rs index 177e50a2f2..b26ac39ada 100644 --- a/common/client-libs/validator-client/src/signing/direct_wallet.rs +++ b/common/client-libs/validator-client/src/signing/direct_wallet.rs @@ -35,6 +35,7 @@ pub enum DirectSecp256k1HdWalletError { AccountDerivationError { source: eyre::Report }, } +// TODO: maybe lock this one behind feature flag? #[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)] pub struct DirectSecp256k1HdWallet { /// Base secret diff --git a/common/client-libs/validator-client/src/signing/signer.rs b/common/client-libs/validator-client/src/signing/signer.rs index 074924a0cd..d4fd30d58b 100644 --- a/common/client-libs/validator-client/src/signing/signer.rs +++ b/common/client-libs/validator-client/src/signing/signer.rs @@ -94,3 +94,25 @@ pub trait OfflineSigner { // fn sign_amino_with_account(&self, signer: &AccountData, sign_doc: AminoSignDoc) -> Result; } + +#[derive(Debug, Default, Copy, Clone)] +pub struct NoSigner; + +// #[derive(Debug, Copy, Clone, Error)] +// #[error("no signer is available")] +// struct SignerUnavailable; +// +// // trait bound requirements +// impl From for SignerUnavailable { +// fn from(_: SigningError) -> Self { +// SignerUnavailable +// } +// } +// +// impl OfflineSigner for NoSigner { +// type Error = SignerUnavailable; +// +// fn get_accounts(&self) -> Result, Self::Error> { +// return Err(SignerUnavailable); +// } +// } diff --git a/common/client-libs/validator-client/src/signing/tx_signer.rs b/common/client-libs/validator-client/src/signing/tx_signer.rs index f14f640793..7db9a7617e 100644 --- a/common/client-libs/validator-client/src/signing/tx_signer.rs +++ b/common/client-libs/validator-client/src/signing/tx_signer.rs @@ -5,54 +5,98 @@ use crate::signing::signer::{OfflineSigner, SigningError}; use crate::signing::SignerData; use cosmrs::tx::{SignDoc, SignerInfo}; use cosmrs::{tx, AccountId, Any}; +// +// #[derive(Debug)] +// /// A client that has only one responsibility - sign transactions +// /// and not touch chain. +// pub struct TxSigner { +// signer: S, +// } +// +// impl TxSigner { +// pub fn new(signer: S) -> Self { +// TxSigner { signer } +// } +// +// pub fn signer(&self) -> &S { +// &self.signer +// } +// +// pub fn into_inner_signer(self) -> S { +// self.signer +// } +// +// pub fn sign_amino( +// &self, +// _signer_address: &AccountId, +// _messages: Vec, +// _fee: tx::Fee, +// _memo: impl Into + Send + 'static, +// _signer_data: SignerData, +// ) -> Result +// where +// S: OfflineSigner, +// { +// unimplemented!() +// } +// +// // TODO: change this sucker to use the trait better +// pub fn sign_direct( +// &self, +// signer_address: &AccountId, +// messages: Vec, +// fee: tx::Fee, +// memo: impl Into + Send + 'static, +// signer_data: SignerData, +// ) -> Result +// where +// S: OfflineSigner, +// { +// let account_from_signer = self.signer.find_account(signer_address)?; +// +// // TODO: experiment with this field +// let timeout_height = 0u32; +// +// let tx_body = tx::Body::new(messages, memo, timeout_height); +// let signer_info = +// SignerInfo::single_direct(Some(account_from_signer.public_key), signer_data.sequence); +// let auth_info = signer_info.auth_info(fee); +// +// let sign_doc = SignDoc::new( +// &tx_body, +// &auth_info, +// &signer_data.chain_id, +// signer_data.account_number, +// ) +// .map_err(|source| SigningError::SignDocFailure { source })?; +// +// self.signer +// .sign_direct_with_account(&account_from_signer, sign_doc) +// } +// } -#[derive(Debug)] -/// A client that has only one responsibility - sign transactions -/// and not touch chain. -pub struct TxSigner { - signer: S, -} - -impl TxSigner { - pub fn new(signer: S) -> Self { - TxSigner { signer } - } - - pub fn signer(&self) -> &S { - &self.signer - } - - pub fn into_inner_signer(self) -> S { - self.signer - } - - pub fn sign_amino( +// extension trait for the OfflineSigner to allow to sign transactions +pub trait TxSigner: OfflineSigner { + fn sign_amino( &self, _signer_address: &AccountId, _messages: Vec, _fee: tx::Fee, _memo: impl Into + Send + 'static, _signer_data: SignerData, - ) -> Result - where - S: OfflineSigner, - { + ) -> Result::Error> { unimplemented!() } - // TODO: change this sucker to use the trait better - pub fn sign_direct( + fn sign_direct( &self, signer_address: &AccountId, messages: Vec, fee: tx::Fee, memo: impl Into + Send + 'static, signer_data: SignerData, - ) -> Result - where - S: OfflineSigner, - { - let account_from_signer = self.signer.find_account(signer_address)?; + ) -> Result::Error> { + let account_from_signer = self.find_account(signer_address)?; // TODO: experiment with this field let timeout_height = 0u32; @@ -70,7 +114,6 @@ impl TxSigner { ) .map_err(|source| SigningError::SignDocFailure { source })?; - self.signer - .sign_direct_with_account(&account_from_signer, sign_doc) + self.sign_direct_with_account(&account_from_signer, sign_doc) } } diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 77b1761d1c..2be09b0e70 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -164,6 +164,10 @@ impl NymNetworkDetails { } } + pub fn default_gas_price_amount(&self) -> f64 { + GAS_PRICE_AMOUNT + } + #[must_use] pub fn with_network_name(mut self, network_name: String) -> Self { self.network_name = network_name; diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index ef73c6c55e..d30e02be2f 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -4561,6 +4561,7 @@ dependencies = [ "colored 2.0.4", "cosmrs", "cosmwasm-std", + "cw-utils", "cw3", "cw4", "eyre", diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index bc7f8ae20f..68eff5cacf 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -3415,6 +3415,7 @@ dependencies = [ "colored 2.0.4", "cosmrs", "cosmwasm-std", + "cw-utils", "cw3", "cw4", "eyre",