From 1de3317e759239bdad125fca41f6c275cce7cefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Wed, 12 Apr 2023 10:47:56 +0200 Subject: [PATCH] Add sp directory contract traits and methods to nyxd client --- Cargo.lock | 1 + .../client-libs/validator-client/Cargo.toml | 5 + .../query_service_provider_directory.rs | 43 +++++++ .../validator-client/src/nyxd/mod.rs | 16 +++ .../validator-client/src/nyxd/traits/mod.rs | 5 + .../nyxd/traits/sp_directory_query_client.rs | 98 ++++++++++++++++ .../traits/sp_directory_signing_client.rs | 107 ++++++++++++++++++ .../service-provider-directory/src/msg.rs | 18 +++ common/network-defaults/src/lib.rs | 17 +++ common/network-defaults/src/mainnet.rs | 1 + common/network-defaults/src/var_names.rs | 2 + envs/qa-qwerty.env | 1 + nym-wallet/Cargo.lock | 9 ++ nym-wallet/nym-wallet-types/src/network.rs | 10 ++ 14 files changed, 333 insertions(+) create mode 100644 common/client-libs/validator-client/examples/query_service_provider_directory.rs create mode 100644 common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs create mode 100644 common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs diff --git a/Cargo.lock b/Cargo.lock index 818a296b22..52d5429eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4216,6 +4216,7 @@ dependencies = [ "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-network-defaults", + "nym-service-provider-directory-common", "nym-vesting-contract", "nym-vesting-contract-common", "prost 0.10.4", diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 0c7b2004ad..f6327630db 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -18,6 +18,7 @@ nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-c nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" } nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" } nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" } +nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" } nym-vesting-contract = { path = "../../../contracts/vesting" } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } @@ -63,6 +64,10 @@ name = "offline_signing" # (traits would need to be moved around and refactored themselves) required-features = ["nyxd-client"] +[[example]] +name = "query_service_provider_directory" +required-features = ["nyxd-client"] + [features] nyxd-client = [ "async-trait", diff --git a/common/client-libs/validator-client/examples/query_service_provider_directory.rs b/common/client-libs/validator-client/examples/query_service_provider_directory.rs new file mode 100644 index 0000000000..12369b2696 --- /dev/null +++ b/common/client-libs/validator-client/examples/query_service_provider_directory.rs @@ -0,0 +1,43 @@ +use std::str::FromStr; + +use cosmrs::AccountId; +use nym_network_defaults::{setup_env, NymNetworkDetails}; +use nym_service_provider_directory_common::NymAddress; +use nym_validator_client::nyxd::traits::SpDirectoryQueryClient; + +#[tokio::main] +async fn main() { + setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap())); + let network_details = NymNetworkDetails::new_from_env(); + let config = + nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap(); + let client = nym_validator_client::Client::new_query(config).unwrap(); + + let config = client.nyxd.get_service_config().await.unwrap(); + println!("config: {config:?}"); + + let services_paged = client.nyxd.get_services_paged(None, None).await.unwrap(); + println!("services (paged): {services_paged:#?}"); + + let services = client.nyxd.get_all_services().await.unwrap(); + println!("services: {services:#?}"); + + let announcer = AccountId::from_str("n1hmf957kc7arcd39rl7xq8l0a4zyg7kxnv7su87").unwrap(); + let services_by_announcer = client + .nyxd + .get_services_by_announcer(announcer) + .await + .unwrap(); + println!("services (by announcer): {services_by_announcer:#?}"); + + let nym_address = NymAddress::new("foo.bar@gateway"); + let services_by_nym_address = client + .nyxd + .get_services_by_nym_address(nym_address) + .await + .unwrap(); + assert_eq!(services_by_announcer, services_by_nym_address); + + let service_info = client.nyxd.get_service_info(1).await; + println!("service info: {service_info:#?}"); +} diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index a37354f462..35e2de5068 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -67,6 +67,7 @@ pub struct Config { 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, // TODO: add this in later commits // pub(crate) gas_price: GasPrice, } @@ -131,6 +132,10 @@ impl Config { 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, + )?, }) } } @@ -246,6 +251,10 @@ impl NyxdClient { self.config.multisig_contract_address = Some(address); } + pub fn set_service_provider_contract_address(&mut self, address: AccountId) { + self.config.service_provider_contract_address = Some(address); + } + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, @@ -304,6 +313,13 @@ impl NyxdClient { self.config.coconut_dkg_contract_address.as_ref().unwrap() } + pub fn service_provider_contract_address(&self) -> &AccountId { + self.config + .service_provider_contract_address + .as_ref() + .expect("no service provider contract address set") + } + pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { self.simulated_gas_multiplier = multiplier; } diff --git a/common/client-libs/validator-client/src/nyxd/traits/mod.rs b/common/client-libs/validator-client/src/nyxd/traits/mod.rs index de075ff7f8..1439d66de7 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mod.rs @@ -16,15 +16,20 @@ mod mixnet_signing_client; mod multisig_signing_client; mod vesting_signing_client; +mod sp_directory_query_client; +mod sp_directory_signing_client; + pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient; pub use dkg_query_client::DkgQueryClient; pub use group_query_client::GroupQueryClient; pub use mixnet_query_client::MixnetQueryClient; pub use multisig_query_client::MultisigQueryClient; pub use vesting_query_client::VestingQueryClient; +pub use sp_directory_query_client::SpDirectoryQueryClient; pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; pub use dkg_signing_client::DkgSigningClient; pub use mixnet_signing_client::MixnetSigningClient; pub use multisig_signing_client::MultisigSigningClient; pub use vesting_signing_client::VestingSigningClient; +pub use sp_directory_signing_client::SpDirectorySigningClient; diff --git a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs new file mode 100644 index 0000000000..3391a815cd --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs @@ -0,0 +1,98 @@ +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::ContractBuildInformation; +use nym_service_provider_directory_common::{ + msg::QueryMsg as SpQuery, + response::{ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse}, + NymAddress, ServiceId, ServiceInfo, +}; +use serde::Deserialize; + +use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; + +#[async_trait] +pub trait SpDirectoryQueryClient { + async fn query_service_provider_contract(&self, query: SpQuery) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_service_config(&self) -> Result { + self.query_service_provider_contract(SpQuery::Config {}) + .await + } + + async fn get_service_info( + &self, + service_id: ServiceId, + ) -> Result { + self.query_service_provider_contract(SpQuery::ServiceId { service_id }) + .await + } + + async fn get_services_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_service_provider_contract(SpQuery::All { limit, start_after }) + .await + } + + async fn get_services_by_announcer( + &self, + announcer: AccountId, + ) -> Result { + self.query_service_provider_contract(SpQuery::ByAnnouncer { + announcer: announcer.to_string(), + }) + .await + } + + async fn get_services_by_nym_address( + &self, + nym_address: NymAddress, + ) -> Result { + self.query_service_provider_contract(SpQuery::ByNymAddress { nym_address }) + .await + } + + async fn get_sp_contract_version(&self) -> Result { + self.query_service_provider_contract(SpQuery::GetContractVersion {}) + .await + } + + async fn get_all_services(&self) -> Result, NyxdError> { + let mut services = Vec::new(); + let mut start_after = None; + + loop { + let mut paged_response = self.get_services_paged(start_after.take(), None).await?; + + let last_id = paged_response.services.last().map(|serv| serv.service_id); + services.append(&mut paged_response.services); + + if let Some(start_after_res) = last_id { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(services) + } +} + +#[async_trait] +impl SpDirectoryQueryClient for NyxdClient +where + C: CosmWasmClient + Send + Sync, +{ + async fn query_service_provider_contract(&self, query: SpQuery) -> Result + where + for<'a> T: Deserialize<'a>, + { + self.client + .query_contract_smart(self.service_provider_contract_address(), &query) + .await + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs new file mode 100644 index 0000000000..4e76cf913e --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs @@ -0,0 +1,107 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use nym_service_provider_directory_common::{ + msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceId, ServiceType, +}; + +use crate::nyxd::{ + coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient, + SigningCosmWasmClient, +}; + +#[async_trait] +pub trait SpDirectorySigningClient { + async fn exectute_service_provider_directory_contract( + &self, + fee: Option, + msg: SpExecuteMsg, + funds: Vec, + ) -> Result; + + async fn announce_service_provider( + &self, + nym_address: NymAddress, + service_type: ServiceType, + deposit: Coin, + fee: Option, + ) -> Result { + self.exectute_service_provider_directory_contract( + fee, + SpExecuteMsg::Announce { + nym_address, + service_type, + }, + vec![deposit], + ) + .await + } + + async fn delete_service_provider_by_id( + &self, + service_id: ServiceId, + fee: Option, + ) -> Result { + self.exectute_service_provider_directory_contract( + fee, + SpExecuteMsg::DeleteId { service_id }, + vec![], + ) + .await + } + + async fn delete_service_provider_by_nym_address( + &self, + nym_address: NymAddress, + fee: Option, + ) -> Result { + self.exectute_service_provider_directory_contract( + fee, + SpExecuteMsg::DeleteNymAddress { nym_address }, + vec![], + ) + .await + } + + async fn update_deposit_required( + &self, + deposit_required: Coin, + fee: Option, + ) -> Result { + self.exectute_service_provider_directory_contract( + fee, + SpExecuteMsg::UpdateDepositRequired { + deposit_required: deposit_required.into(), + }, + vec![], + ) + .await + } +} + +#[async_trait] +impl SpDirectorySigningClient for NyxdClient +where + C: SigningCosmWasmClient + Sync + Send, +{ + async fn exectute_service_provider_directory_contract( + &self, + fee: Option, + msg: SpExecuteMsg, + funds: Vec, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let memo = msg.default_memo(); + self.client + .execute( + self.address(), + self.service_provider_contract_address(), + &msg, + fee, + memo, + funds, + ) + .await + } +} diff --git a/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs b/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs index 8276cdddb2..ba746c3b10 100644 --- a/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs +++ b/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs @@ -40,6 +40,24 @@ impl ExecuteMsg { pub fn delete_id(service_id: ServiceId) -> Self { ExecuteMsg::DeleteId { service_id } } + + pub fn default_memo(&self) -> String { + match self { + ExecuteMsg::Announce { + nym_address, + service_type, + } => format!("announcing {nym_address} as type {service_type}"), + ExecuteMsg::DeleteId { service_id } => { + format!("deleting service with service id {service_id}") + } + ExecuteMsg::DeleteNymAddress { nym_address } => { + format!("deleting service with nym address {nym_address}") + } + ExecuteMsg::UpdateDepositRequired { deposit_required } => { + format!("updating the deposit required to {deposit_required}") + } + } + } } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 58c4b8d250..02b10ae69c 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -28,6 +28,7 @@ pub struct NymContracts { pub group_contract_address: Option, pub multisig_contract_address: Option, pub coconut_dkg_contract_address: Option, + pub service_provider_directory_contract_address: Option, } // I wanted to use the simpler `NetworkDetails` name, but there's a clash @@ -117,6 +118,10 @@ impl NymNetworkDetails { .with_coconut_dkg_contract(Some( var(var_names::COCONUT_DKG_CONTRACT_ADDRESS).expect("coconut dkg contract not set"), )) + .with_service_provider_directory_contract(Some( + var(var_names::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS) + .expect("service provider contract not set"), + )) } pub fn new_mainnet() -> Self { @@ -146,6 +151,9 @@ impl NymNetworkDetails { coconut_dkg_contract_address: parse_optional_str( mainnet::COCONUT_DKG_CONTRACT_ADDRESS, ), + service_provider_directory_contract_address: parse_optional_str( + mainnet::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS, + ), }, } } @@ -227,6 +235,15 @@ impl NymNetworkDetails { self.contracts.coconut_dkg_contract_address = contract.map(Into::into); self } + + #[must_use] + pub fn with_service_provider_directory_contract>( + mut self, + contract: Option, + ) -> Self { + self.contracts.service_provider_directory_contract_address = contract.map(Into::into); + self + } } #[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)] diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs index 3e37633b3f..8977aaea4a 100644 --- a/common/network-defaults/src/mainnet.rs +++ b/common/network-defaults/src/mainnet.rs @@ -20,6 +20,7 @@ pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; +pub(crate) const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = hex_literal::hex!("0000000000000000000000000000000000000000"); pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs index 0fd5506ddc..70f2ed79af 100644 --- a/common/network-defaults/src/var_names.rs +++ b/common/network-defaults/src/var_names.rs @@ -19,6 +19,8 @@ pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS"; pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS"; pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS"; pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_ADDRESS"; +pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = + "SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS"; pub const NYXD: &str = "NYXD"; pub const NYM_API: &str = "NYM_API"; diff --git a/envs/qa-qwerty.env b/envs/qa-qwerty.env index 474bf8d073..40cafc524b 100644 --- a/envs/qa-qwerty.env +++ b/envs/qa-qwerty.env @@ -17,6 +17,7 @@ GROUP_CONTRACT_ADDRESS=n1fqquzw4mk0pkamgr2ywt2v7h2j9nuyjjn4gvpy8zlpp6xn0uyuzqfm2 MULTISIG_CONTRACT_ADDRESS=n1gaq3666chd5348apj8cka8t2mckv7azp9espyr7wgpxyuzur5d0sazpysy COCONUT_DKG_CONTRACT_ADDRESS=n18yadscxw8v35dds7ksv3j0svmjh3h6e7tmxpadk96mvgz27zygkshuf4vs REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy +SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ryt076cufyddallg5x0gz3qjz0pd3wg0m4cwkg9njhmlnp6u88qq6nczgj STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090" NYXD="https://qwerty-validator.qa.nymte.ch/" NYM_API="https://qwerty-validator-api.qa.nymte.ch/api" diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 71911c45b4..87c3b91e31 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -2966,6 +2966,14 @@ dependencies = [ "pem", ] +[[package]] +name = "nym-service-provider-directory-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", + "serde", +] + [[package]] name = "nym-sphinx-types" version = "0.2.0" @@ -3025,6 +3033,7 @@ dependencies = [ "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-network-defaults", + "nym-service-provider-directory-common", "nym-vesting-contract", "nym-vesting-contract-common", "prost", diff --git a/nym-wallet/nym-wallet-types/src/network.rs b/nym-wallet/nym-wallet-types/src/network.rs index 8a443243e7..57a5e234bb 100644 --- a/nym-wallet/nym-wallet-types/src/network.rs +++ b/nym-wallet/nym-wallet-types/src/network.rs @@ -114,6 +114,8 @@ mod sandbox { "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; + pub(crate) const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = + "n1ryt076cufyddallg5x0gz3qjz0pd3wg0m4cwkg9njhmlnp6u88qq6nczgj"; pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = hex_literal::hex!("8e0DcFF7F3085235C32E845f3667aEB3f1e83133"); pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = @@ -149,6 +151,9 @@ mod sandbox { group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS), + service_provider_directory_contract_address: parse_optional_str( + SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS, + ), }, } } @@ -176,6 +181,8 @@ mod qa { pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs"; + pub(crate) const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = + "n1ryt076cufyddallg5x0gz3qjz0pd3wg0m4cwkg9njhmlnp6u88qq6nczgj"; pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = hex_literal::hex!("0000000000000000000000000000000000000000"); pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = @@ -210,6 +217,9 @@ mod qa { group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS), + service_provider_directory_contract_address: parse_optional_str( + SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS, + ), }, } }