diff --git a/clients/credential/src/client.rs b/clients/credential/src/client.rs index ef0fc6d9fd..1c3286f7c5 100644 --- a/clients/credential/src/client.rs +++ b/clients/credential/src/client.rs @@ -1,14 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use bip39::Mnemonic; -use std::str::FromStr; -use url::Url; - use crate::error::Result; use crate::{MNEMONIC, NYMD_URL}; - +use bip39::Mnemonic; use network_defaults::{DEFAULT_NETWORK, MIX_DENOM, VOUCHER_INFO}; +use std::str::FromStr; +use url::Url; +use validator_client::nymd; use validator_client::nymd::traits::CoconutBandwidthSigningClient; use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient}; @@ -20,9 +19,10 @@ impl Client { pub fn new() -> Self { let nymd_url = Url::from_str(NYMD_URL).unwrap(); let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap(); + let config = nymd::Config::try_from_nym_network_details(&DEFAULT_NETWORK.details()) + .expect("failed to construct valid validator client config with the provided network"); let nymd_client = - NymdClient::connect_with_mnemonic(DEFAULT_NETWORK, nymd_url.as_ref(), mnemonic, None) - .unwrap(); + NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap(); Client { nymd_client } } diff --git a/clients/webassembly/js-example/package-lock.json b/clients/webassembly/js-example/package-lock.json index 3309d8e944..9b7ff01b7d 100644 --- a/clients/webassembly/js-example/package-lock.json +++ b/clients/webassembly/js-example/package-lock.json @@ -24,7 +24,8 @@ }, "../pkg": { "name": "@nymproject/nym-client-wasm", - "version": "0.0.1" + "version": "1.0.1", + "license": "Apache-2.0" }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 85a3524f77..07e8a08a8e 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -9,20 +9,17 @@ use validator_api_requests::coconut::{ ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse, VerifyCredentialBody, VerifyCredentialResponse, }; - use validator_api_requests::models::{ CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, }; + #[cfg(feature = "nymd-client")] use validator_api_requests::models::{MixNodeBondAnnotated, UptimeResponse}; -#[cfg(feature = "nymd-client")] -use network_defaults::DEFAULT_NETWORK; - #[cfg(feature = "nymd-client")] use crate::nymd::{ - error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient, + self, error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient, }; #[cfg(feature = "nymd-client")] @@ -31,19 +28,20 @@ use mixnet_contract_common::{ MixnetContractVersion, MixnodeRewardingStatusResponse, RewardedSetNodeStatus, RewardedSetUpdateDetails, }; +use network_defaults::all::Network; +#[cfg(feature = "nymd-client")] +use network_defaults::NymNetworkDetails; #[cfg(feature = "nymd-client")] use std::collections::{HashMap, HashSet}; #[cfg(feature = "nymd-client")] #[must_use] -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Config { - network: network_defaults::all::Network, api_url: Url, nymd_url: Url, - mixnet_contract_address: cosmrs::AccountId, - vesting_contract_address: cosmrs::AccountId, - bandwidth_claim_contract_address: cosmrs::AccountId, + + nymd_config: nymd::Config, mixnode_page_limit: Option, gateway_page_limit: Option, @@ -53,42 +51,44 @@ pub struct Config { #[cfg(feature = "nymd-client")] impl Config { - pub fn new(network: network_defaults::all::Network, nymd_url: Url, api_url: Url) -> Self { - Config { - network, - nymd_url, - mixnet_contract_address: DEFAULT_NETWORK - .mixnet_contract_address() + pub fn try_from_nym_network_details( + details: &NymNetworkDetails, + ) -> Result { + let mut api_url = details + .endpoints + .iter() + .filter_map(|d| d.api_url.as_ref()) + .map(|url| Url::parse(url)) + .collect::, _>>()?; + + if api_url.is_empty() { + return Err(ValidatorClientError::NoAPIUrlAvailable); + } + + Ok(Config { + api_url: api_url.pop().unwrap(), + nymd_url: details.endpoints[0] + .nymd_url .parse() - .expect("Error parsing mixnet contract address"), - vesting_contract_address: DEFAULT_NETWORK - .vesting_contract_address() - .parse() - .expect("Error parsing vesting contract address"), - bandwidth_claim_contract_address: DEFAULT_NETWORK - .bandwidth_claim_contract_address() - .parse() - .expect("Error parsing bandwidth claim contract address"), - api_url, + .map_err(ValidatorClientError::MalformedUrlProvided)?, + nymd_config: nymd::Config::try_from_nym_network_details(details)?, mixnode_page_limit: None, gateway_page_limit: None, mixnode_delegations_page_limit: None, rewarded_set_page_limit: None, - } + }) } - pub fn with_mixnode_contract_address(mut self, address: cosmrs::AccountId) -> Self { - self.mixnet_contract_address = address; + // TODO: this method shouldn't really exist as all information should be included immediately + // via `from_nym_network_details`, but it's here for, you guessed it, legacy compatibility + pub fn with_urls(mut self, nymd_url: Url, api_url: Url) -> Self { + self.nymd_url = nymd_url; + self.api_url = api_url; self } - pub fn with_vesting_contract_address(mut self, address: cosmrs::AccountId) -> Self { - self.vesting_contract_address = address; - self - } - - pub fn with_bandwidth_claim_contract_address(mut self, address: cosmrs::AccountId) -> Self { - self.bandwidth_claim_contract_address = address; + pub fn with_nymd_url(mut self, nymd_url: Url) -> Self { + self.nymd_url = nymd_url; self } @@ -115,10 +115,11 @@ impl Config { #[cfg(feature = "nymd-client")] pub struct Client { - pub network: network_defaults::all::Network, - mixnet_contract_address: cosmrs::AccountId, - vesting_contract_address: cosmrs::AccountId, - bandwidth_claim_contract_address: cosmrs::AccountId, + // compatibility : ( + pub network: Network, + + // TODO: we really shouldn't be storing a mnemonic here, but removing it would be + // non-trivial amount of work and it's out of scope of the current branch mnemonic: Option, mixnode_page_limit: Option, @@ -135,29 +136,26 @@ pub struct Client { impl Client { pub fn new_signing( config: Config, + // we need to provide network argument due to compatibility with other components (wallet...) + // that rely on its existence... + network: Network, mnemonic: bip39::Mnemonic, ) -> Result, ValidatorClientError> { let validator_api_client = validator_api::Client::new(config.api_url.clone()); let nymd_client = NymdClient::connect_with_mnemonic( - config.network, + config.nymd_config.clone(), config.nymd_url.as_str(), mnemonic.clone(), None, - )? - .with_mixnet_contract_address(config.mixnet_contract_address.clone()) - .with_vesting_contract_address(config.vesting_contract_address.clone()) - .with_bandwidth_claim_contract_address(config.bandwidth_claim_contract_address.clone()); + )?; Ok(Client { - network: config.network, - mixnet_contract_address: config.mixnet_contract_address, - vesting_contract_address: config.vesting_contract_address, - bandwidth_claim_contract_address: config.bandwidth_claim_contract_address, + network, mnemonic: Some(mnemonic), mixnode_page_limit: config.mixnode_page_limit, gateway_page_limit: config.gateway_page_limit, mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, - rewarded_set_page_limit: None, + rewarded_set_page_limit: config.rewarded_set_page_limit, validator_api: validator_api_client, nymd: nymd_client, }) @@ -165,14 +163,11 @@ impl Client { pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { self.nymd = NymdClient::connect_with_mnemonic( - self.network, + self.nymd.current_config().clone(), new_endpoint.as_ref(), self.mnemonic.clone().unwrap(), None, - )? - .with_mixnet_contract_address(self.mixnet_contract_address.clone()) - .with_vesting_contract_address(self.vesting_contract_address.clone()) - .with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone()); + )?; Ok(()) } @@ -183,17 +178,18 @@ impl Client { #[cfg(feature = "nymd-client")] impl Client { - pub fn new_query(config: Config) -> Result, ValidatorClientError> { + pub fn new_query( + config: Config, + // we need to provide network argument due to compatibility with other components (wallet...) + // that rely on its existence... + network: Network, + ) -> Result, ValidatorClientError> { let validator_api_client = validator_api::Client::new(config.api_url.clone()); - let nymd_client = NymdClient::connect(config.nymd_url.as_str())? - .with_mixnet_contract_address(config.mixnet_contract_address.clone()) - .with_vesting_contract_address(config.vesting_contract_address.clone()); + let nymd_client = + NymdClient::connect(config.nymd_config.clone(), config.nymd_url.as_str())?; Ok(Client { - network: config.network, - mixnet_contract_address: config.mixnet_contract_address, - vesting_contract_address: config.vesting_contract_address, - bandwidth_claim_contract_address: config.bandwidth_claim_contract_address, + network, mnemonic: None, mixnode_page_limit: config.mixnode_page_limit, gateway_page_limit: config.gateway_page_limit, @@ -205,10 +201,7 @@ impl Client { } pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { - self.nymd = NymdClient::connect(new_endpoint.as_ref())? - .with_mixnet_contract_address(self.mixnet_contract_address.clone()) - .with_vesting_contract_address(self.vesting_contract_address.clone()) - .with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone()); + self.nymd = NymdClient::connect(self.nymd.current_config().clone(), new_endpoint.as_ref())?; Ok(()) } } @@ -222,11 +215,12 @@ impl Client { // use case: somebody initialised client without a contract in order to upload and initialise one // and now they want to actually use it without making new client pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) { - self.mixnet_contract_address = mixnet_contract_address + self.nymd + .set_mixnet_contract_address(mixnet_contract_address) } pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId { - self.mixnet_contract_address.clone() + self.nymd.mixnet_contract_address().clone() } pub async fn get_cached_mixnodes(&self) -> Result, ValidatorClientError> { diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index 9e41f91989..33e698bb2c 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,5 +1,5 @@ use crate::nymd::error::NymdError; -use crate::nymd::{NymdClient, QueryNymdClient}; +use crate::nymd::{Config as ClientConfig, NymdClient, QueryNymdClient}; use crate::ApiClient; use network_defaults::all::Network; @@ -54,10 +54,20 @@ fn setup_connection_tests( .get(&network) .expect("No configured contract address") .clone(); - NymdClient::::connect(url.as_str()) - .map(|client| client.with_mixnet_contract_address(address)) - .map(move |client| ClientForConnectionTest::Nymd(network, url, Box::new(client))) - .ok() + let config = ClientConfig::try_from_nym_network_details(&network.details()) + .expect("failed to create valid nymd client config"); + + if let Ok(mut client) = NymdClient::::connect(config, url.as_str()) { + // possibly redundant, but lets just leave it here + client.set_mixnet_contract_address(address); + Some(ClientForConnectionTest::Nymd( + network, + url, + Box::new(client), + )) + } else { + None + } }); let api_connection_test_clients = api_urls.map(|(network, url)| { @@ -76,7 +86,7 @@ fn extract_and_collect_results_into_map( .filter(|c| &c.url_type() == url_type) .map(|c| { let (network, url, result) = c.result(); - (*network, (url.clone(), *result)) + (network.clone(), (url.clone(), *result)) }) .into_group_map() } diff --git a/common/client-libs/validator-client/src/error.rs b/common/client-libs/validator-client/src/error.rs index 692605bc44..e832b12618 100644 --- a/common/client-libs/validator-client/src/error.rs +++ b/common/client-libs/validator-client/src/error.rs @@ -18,4 +18,7 @@ pub enum ValidatorClientError { #[cfg(feature = "nymd-client")] #[error("There was an issue with the Nymd client - {0}")] NymdError(#[from] crate::nymd::error::NymdError), + + #[error("No validator API url has been provided")] + NoAPIUrlAvailable, } diff --git a/common/client-libs/validator-client/src/nymd/error.rs b/common/client-libs/validator-client/src/nymd/error.rs index 8ccd41c09e..0b6dcfe22b 100644 --- a/common/client-libs/validator-client/src/nymd/error.rs +++ b/common/client-libs/validator-client/src/nymd/error.rs @@ -130,6 +130,9 @@ pub enum NymdError { #[error("Coconut interface error: {0}")] CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError), + + #[error("Account had an unexpected bech32 prefix. Expected: {expected}, got: {got}")] + UnexpectedBech32Prefix { got: String, expected: String }, } impl NymdError { diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index ca512d044d..5fb967c963 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -24,7 +24,6 @@ use mixnet_contract_common::{ PagedMixDelegationsResponse, PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails, }; -use network_defaults::DEFAULT_NETWORK; use serde::Serialize; use std::convert::TryInto; use vesting_contract_common::ExecuteMsg as VestingExecuteMsg; @@ -50,6 +49,7 @@ pub use cosmrs::Coin as CosmosCoin; pub use cosmrs::{bip32, AccountId, Decimal, Denom}; pub use cosmwasm_std::Coin as CosmWasmCoin; pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment}; +use network_defaults::{ChainDetails, NymNetworkDetails}; pub use signing_client::Client as SigningNymdClient; pub use traits::{VestingQueryClient, VestingSigningClient}; @@ -60,67 +60,92 @@ pub mod fee; pub mod traits; pub mod wallet; +#[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) bandwidth_claim_contract_address: Option, + pub(crate) coconut_bandwidth_contract_address: Option, + pub(crate) multisig_contract_address: Option, + // TODO: add this in later commits + // pub(crate) gas_price: GasPrice, +} + +impl Config { + fn parse_optional_account( + raw: Option<&String>, + expected_prefix: &str, + ) -> Result, NymdError> { + if let Some(address) = raw { + let parsed: AccountId = address + .parse() + .map_err(|_| NymdError::MalformedAccountAddress(address.clone()))?; + if parsed.prefix() != expected_prefix { + Err(NymdError::UnexpectedBech32Prefix { + got: parsed.prefix().into(), + expected: expected_prefix.into(), + }) + } else { + Ok(Some(parsed)) + } + } else { + Ok(None) + } + } + + pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result { + let prefix = &details.chain_details.bech32_account_prefix; + Ok(Config { + chain_details: details.chain_details.clone(), + mixnet_contract_address: Self::parse_optional_account( + details.contracts.mixnet_contract_address.as_ref(), + prefix, + )?, + vesting_contract_address: Self::parse_optional_account( + details.contracts.vesting_contract_address.as_ref(), + prefix, + )?, + bandwidth_claim_contract_address: Self::parse_optional_account( + details.contracts.bandwidth_claim_contract_address.as_ref(), + prefix, + )?, + coconut_bandwidth_contract_address: Self::parse_optional_account( + details + .contracts + .coconut_bandwidth_contract_address + .as_ref(), + prefix, + )?, + multisig_contract_address: Self::parse_optional_account( + details.contracts.multisig_contract_address.as_ref(), + prefix, + )?, + }) + } +} + #[derive(Debug)] pub struct NymdClient { client: C, - mixnet_contract_address: AccountId, - vesting_contract_address: AccountId, - bandwidth_claim_contract_address: AccountId, - coconut_bandwidth_contract_address: AccountId, - multisig_contract_address: AccountId, + config: Config, client_address: Option>, simulated_gas_multiplier: f32, } -impl NymdClient { - pub fn with_mixnet_contract_address(mut self, address: AccountId) -> Self { - self.mixnet_contract_address = address; - self - } - pub fn with_vesting_contract_address(mut self, address: AccountId) -> Self { - self.vesting_contract_address = address; - self - } - pub fn with_bandwidth_claim_contract_address(mut self, address: AccountId) -> Self { - self.bandwidth_claim_contract_address = address; - self - } - pub fn with_coconut_bandwidth_contract_address(mut self, address: AccountId) -> Self { - self.coconut_bandwidth_contract_address = address; - self - } - pub fn with_multisig_contract_address(mut self, address: AccountId) -> Self { - self.multisig_contract_address = address; - self - } -} - impl NymdClient { - pub fn connect(endpoint: U) -> Result, NymdError> + pub fn connect(config: Config, endpoint: U) -> Result, NymdError> where U: TryInto, { Ok(NymdClient { client: QueryNymdClient::new(endpoint)?, + config, client_address: None, simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, - mixnet_contract_address: DEFAULT_NETWORK - .mixnet_contract_address() - .parse() - .expect("Error parsing mixnet contract address"), - vesting_contract_address: DEFAULT_NETWORK - .vesting_contract_address() - .parse() - .expect("Error parsing vesting contract address"), - bandwidth_claim_contract_address: DEFAULT_NETWORK - .bandwidth_claim_contract_address() - .parse() - .expect("Error parsing bandwidth claim contract address"), - coconut_bandwidth_contract_address: DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .parse() - .unwrap(), - multisig_contract_address: DEFAULT_NETWORK.multisig_contract_address().parse().unwrap(), }) } } @@ -128,6 +153,7 @@ impl NymdClient { impl NymdClient { // maybe the wallet could be made into a generic, but for now, let's just have this one implementation pub fn connect_with_signer( + config: Config, network: config::defaults::all::Network, endpoint: U, signer: DirectSecp256k1HdWallet, @@ -136,40 +162,24 @@ impl NymdClient { where U: TryInto, { - let denom = network.mix_denom().base; + let denom = network.base_mix_denom(); let client_address = signer .try_derive_accounts()? .into_iter() .map(|account| account.address) .collect(); - let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?); + let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(&denom)?); Ok(NymdClient { client: SigningNymdClient::connect_with_signer(endpoint, signer, gas_price)?, + config, client_address: Some(client_address), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, - mixnet_contract_address: DEFAULT_NETWORK - .mixnet_contract_address() - .parse() - .expect("Error parsing mixnet contract address"), - vesting_contract_address: DEFAULT_NETWORK - .vesting_contract_address() - .parse() - .expect("Error parsing vesting contract address"), - bandwidth_claim_contract_address: DEFAULT_NETWORK - .bandwidth_claim_contract_address() - .parse() - .expect("Error parsing bandwidth claim contract address"), - coconut_bandwidth_contract_address: DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .parse() - .unwrap(), - multisig_contract_address: DEFAULT_NETWORK.multisig_contract_address().parse().unwrap(), }) } pub fn connect_with_mnemonic( - network: config::defaults::all::Network, + config: Config, endpoint: U, mnemonic: bip39::Mnemonic, gas_price: Option, @@ -177,8 +187,8 @@ impl NymdClient { where U: TryInto, { - let prefix = network.bech32_prefix(); - let denom = network.mix_denom().base; + let prefix = &config.chain_details.bech32_account_prefix; + let denom = &config.chain_details.mix_denom.base; let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic)?; let client_address = wallet .try_derive_accounts()? @@ -189,48 +199,82 @@ impl NymdClient { Ok(NymdClient { client: SigningNymdClient::connect_with_signer(endpoint, wallet, gas_price)?, + config, client_address: Some(client_address), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, - mixnet_contract_address: network - .mixnet_contract_address() - .parse() - .expect("Error parsing mixnet contract address"), - vesting_contract_address: network - .vesting_contract_address() - .parse() - .expect("Error parsing vesting contract address"), - bandwidth_claim_contract_address: network - .bandwidth_claim_contract_address() - .parse() - .expect("Error parsing bandwidth claim contract address"), - coconut_bandwidth_contract_address: network - .coconut_bandwidth_contract_address() - .parse() - .unwrap(), - multisig_contract_address: network.multisig_contract_address().parse().unwrap(), }) } } impl NymdClient { + pub fn current_config(&self) -> &Config { + &self.config + } + + pub fn set_mixnet_contract_address(&mut self, address: AccountId) { + self.config.mixnet_contract_address = Some(address); + } + + pub fn set_vesting_contract_address(&mut self, address: AccountId) { + self.config.vesting_contract_address = Some(address); + } + + pub fn set_bandwidth_claim_contract_address(&mut self, address: AccountId) { + self.config.bandwidth_claim_contract_address = Some(address); + } + + pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { + self.config.coconut_bandwidth_contract_address = Some(address); + } + + pub fn set_multisig_contract_address(&mut self, address: AccountId) { + self.config.multisig_contract_address = Some(address); + } + + // TODO: this should get changed into Result<&AccountId, NymdError> (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.mixnet_contract_address + self.config.mixnet_contract_address.as_ref().unwrap() } + // TODO: this should get changed into Result<&AccountId, NymdError> (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.vesting_contract_address + self.config.vesting_contract_address.as_ref().unwrap() } + // TODO: this should get changed into Result<&AccountId, NymdError> (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 bandwidth_claim_contract_address(&self) -> &AccountId { - &self.bandwidth_claim_contract_address + self.config + .bandwidth_claim_contract_address + .as_ref() + .unwrap() } + // TODO: this should get changed into Result<&AccountId, NymdError> (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.coconut_bandwidth_contract_address + self.config + .coconut_bandwidth_contract_address + .as_ref() + .unwrap() } + // TODO: this should get changed into Result<&AccountId, NymdError> (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.multisig_contract_address + self.config.multisig_contract_address.as_ref().unwrap() } pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { diff --git a/common/client-libs/validator-client/src/nymd/wallet/mod.rs b/common/client-libs/validator-client/src/nymd/wallet/mod.rs index 96a613b43f..0ea6b081c2 100644 --- a/common/client-libs/validator-client/src/nymd/wallet/mod.rs +++ b/common/client-libs/validator-client/src/nymd/wallet/mod.rs @@ -214,7 +214,7 @@ mod tests { ]; for prefix in prefixes { - let addrs = match prefix { + let addrs = match prefix.as_ref() { "nymt" => vec![ "nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94", "nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv", @@ -229,7 +229,7 @@ mod tests { }; for (idx, mnemonic) in mnemonics.iter().enumerate() { let wallet = - DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap()) + DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap()) .unwrap(); assert_eq!( wallet.try_derive_accounts().unwrap()[0].address, diff --git a/common/network-defaults/src/all.rs b/common/network-defaults/src/all.rs index f5903e6f3b..73997106b0 100644 --- a/common/network-defaults/src/all.rs +++ b/common/network-defaults/src/all.rs @@ -1,14 +1,12 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::{ + DefaultNetworkDetails, DenomDetailsOwned, NymNetworkDetails, ValidatorDetails, + MAINNET_DEFAULTS, QA_DEFAULTS, SANDBOX_DEFAULTS, +}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt, str::FromStr}; - -use crate::{ - DefaultNetworkDetails, DenomDetails, DenomDetailsOwned, ValidatorDetails, MAINNET_DEFAULTS, - QA_DEFAULTS, SANDBOX_DEFAULTS, -}; - use thiserror::Error; #[derive(Error, Debug)] @@ -17,64 +15,95 @@ pub enum NetworkDefaultsError { MalformedNetworkProvided(String), } -#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +// the reason for allowing it is that this is just a temporary solution +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum Network { QA, SANDBOX, MAINNET, + CUSTOM { details: NymNetworkDetails }, } impl Network { - fn details(&self) -> &DefaultNetworkDetails { + pub fn new_custom(details: NymNetworkDetails) -> Self { + Network::CUSTOM { details } + } + + pub fn details(&self) -> NymNetworkDetails { match self { - Self::QA => &QA_DEFAULTS, - Self::SANDBOX => &SANDBOX_DEFAULTS, - Self::MAINNET => &MAINNET_DEFAULTS, + Self::QA => (&*QA_DEFAULTS).into(), + Self::SANDBOX => (&*SANDBOX_DEFAULTS).into(), + Self::MAINNET => (&*MAINNET_DEFAULTS).into(), + // I dislike the clone here, but for compatibility reasons we cannot define other networks with `NymNetworkDetails` directly yet + Self::CUSTOM { details } => details.clone(), } } - pub fn bech32_prefix(&self) -> &str { - self.details().bech32_prefix + pub fn bech32_prefix(&self) -> String { + self.details().chain_details.bech32_account_prefix } - pub fn mix_denom(&self) -> &DenomDetails { - &self.details().mix_denom + pub fn mix_denom(&self) -> DenomDetailsOwned { + self.details().chain_details.mix_denom } - pub fn stake_denom(&self) -> &DenomDetails { - &self.details().stake_denom + pub fn stake_denom(&self) -> DenomDetailsOwned { + self.details().chain_details.stake_denom } - pub fn mixnet_contract_address(&self) -> &str { - self.details().mixnet_contract_address + pub fn base_mix_denom(&self) -> String { + self.details().chain_details.mix_denom.base } - pub fn vesting_contract_address(&self) -> &str { - self.details().vesting_contract_address + pub fn base_stake_denom(&self) -> String { + self.details().chain_details.stake_denom.base } - pub fn bandwidth_claim_contract_address(&self) -> &str { - self.details().bandwidth_claim_contract_address + pub fn mixnet_contract_address(&self) -> Option { + self.details().contracts.mixnet_contract_address } - pub fn coconut_bandwidth_contract_address(&self) -> &str { - self.details().coconut_bandwidth_contract_address + pub fn vesting_contract_address(&self) -> Option { + self.details().contracts.vesting_contract_address } - pub fn multisig_contract_address(&self) -> &str { - self.details().multisig_contract_address + pub fn bandwidth_claim_contract_address(&self) -> Option { + self.details().contracts.bandwidth_claim_contract_address } + pub fn coconut_bandwidth_contract_address(&self) -> Option { + self.details().contracts.coconut_bandwidth_contract_address + } + + pub fn multisig_contract_address(&self) -> Option { + self.details().contracts.multisig_contract_address + } + + pub fn validators(&self) -> Vec { + self.details().endpoints + } + + // only used in mixnet contract tests, but I don't want to be messing with that code now pub fn rewarding_validator_address(&self) -> &str { - self.details().rewarding_validator_address + match self { + Network::QA => crate::qa::REWARDING_VALIDATOR_ADDRESS, + Network::SANDBOX => crate::sandbox::REWARDING_VALIDATOR_ADDRESS, + Network::MAINNET => crate::mainnet::REWARDING_VALIDATOR_ADDRESS, + Network::CUSTOM { .. } => { + panic!("rewarding validator address is unavailable for a custom network") + } + } } + // this should be handled differently, but I don't want to break compatibility pub fn statistics_service_url(&self) -> &str { - self.details().statistics_service_url - } - - pub fn validators(&self) -> impl Iterator { - self.details().validators.iter() + match self { + Network::MAINNET => crate::mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS, + _ => { + panic!("statistics service url is only available for mainnet!") + } + } } } @@ -99,6 +128,7 @@ impl fmt::Display for Network { Network::QA => f.write_str("QA"), Network::SANDBOX => f.write_str("Sandbox"), Network::MAINNET => f.write_str("Mainnet"), + Network::CUSTOM { .. } => f.write_str("Custom"), } } } @@ -130,6 +160,31 @@ impl From<&DefaultNetworkDetails> for NetworkDetails { } } +// this also has to exist for compatibility reasons since I don't want to be touching the wallet now +impl From for NetworkDetails { + fn from(details: NymNetworkDetails) -> Self { + NetworkDetails { + bech32_prefix: details.chain_details.bech32_account_prefix, + mix_denom: details.chain_details.mix_denom, + stake_denom: details.chain_details.stake_denom, + mixnet_contract_address: details + .contracts + .mixnet_contract_address + .unwrap_or_default(), + vesting_contract_address: details + .contracts + .vesting_contract_address + .unwrap_or_default(), + bandwidth_claim_contract_address: details + .contracts + .bandwidth_claim_contract_address + .unwrap_or_default(), + statistics_service_url: "".to_string(), + validators: details.endpoints, + } + } +} + impl NetworkDetails { pub fn base_mix_denom(&self) -> &str { &self.mix_denom.base @@ -146,7 +201,10 @@ impl SupportedNetworks { SupportedNetworks { networks: support .into_iter() - .map(|n| (n, n.details().into())) + .map(|n| { + let details = n.details().into(); + (n, details) + }) .collect(), } } diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index ea8ff56899..5487899696 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -17,7 +17,7 @@ pub mod sandbox; cfg_if::cfg_if! { if #[cfg(network = "mainnet")] { pub const DEFAULT_NETWORK: all::Network = all::Network::MAINNET; - pub const MIX_DENOM: DenomDetails = mainnet::MIX_DENOM; + pub const MIX_DENOM: DenomDetails = mainnet::MIX_DENOM; pub const STAKE_DENOM: DenomDetails = mainnet::STAKE_DENOM; pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS; @@ -25,7 +25,7 @@ cfg_if::cfg_if! { } else if #[cfg(network = "qa")] { pub const DEFAULT_NETWORK: all::Network = all::Network::QA; - pub const MIX_DENOM: DenomDetails = qa::MIX_DENOM; + pub const MIX_DENOM: DenomDetails = qa::MIX_DENOM; pub const STAKE_DENOM: DenomDetails = qa::STAKE_DENOM; pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS; @@ -41,10 +41,157 @@ cfg_if::cfg_if! { } } +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ChainDetails { + pub bech32_account_prefix: String, + pub mix_denom: DenomDetailsOwned, + pub stake_denom: DenomDetailsOwned, +} + +// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms +impl Default for ChainDetails { + fn default() -> Self { + ChainDetails { + bech32_account_prefix: mainnet::BECH32_PREFIX.into(), + mix_denom: mainnet::MIX_DENOM.into(), + stake_denom: mainnet::STAKE_DENOM.into(), + } + } +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct NymContracts { + pub mixnet_contract_address: Option, + pub vesting_contract_address: Option, + pub bandwidth_claim_contract_address: Option, + pub coconut_bandwidth_contract_address: Option, + pub multisig_contract_address: Option, +} + +// I wanted to use the simpler `NetworkDetails` name, but there's a clash +// with `NetworkDetails` defined in all.rs... +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct NymNetworkDetails { + pub chain_details: ChainDetails, + pub endpoints: Vec, + pub contracts: NymContracts, +} + +impl NymNetworkDetails { + pub fn new() -> Self { + NymNetworkDetails::default() + } + + pub fn new_qa() -> Self { + (&*QA_DEFAULTS).into() + } + + pub fn new_sandbox() -> Self { + (&*SANDBOX_DEFAULTS).into() + } + + pub fn new_mainnet() -> Self { + (&*MAINNET_DEFAULTS).into() + } + + pub fn current_default() -> Self { + // backwards compatibility reasons + DEFAULT_NETWORK.details() + } + + pub fn with_bech32_account_prefix>(mut self, prefix: S) -> Self { + self.chain_details.bech32_account_prefix = prefix.into(); + self + } + + pub fn with_mix_denom(mut self, mix_denom: DenomDetailsOwned) -> Self { + self.chain_details.mix_denom = mix_denom; + self + } + + pub fn with_stake_denom(mut self, stake_denom: DenomDetailsOwned) -> Self { + self.chain_details.stake_denom = stake_denom; + self + } + + pub fn with_base_mix_denom>(mut self, base_mix_denom: S) -> Self { + self.chain_details.mix_denom = DenomDetailsOwned::base_only(base_mix_denom.into()); + self + } + + pub fn with_base_stake_denom>(mut self, base_stake_denom: S) -> Self { + self.chain_details.stake_denom = DenomDetailsOwned::base_only(base_stake_denom.into()); + self + } + + pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { + self.endpoints.push(endpoint); + self + } + + pub fn with_mixnet_contract>(mut self, contract: Option) -> Self { + self.contracts.mixnet_contract_address = contract.map(Into::into); + self + } + + pub fn with_vesting_contract>(mut self, contract: Option) -> Self { + self.contracts.vesting_contract_address = contract.map(Into::into); + self + } + + pub fn with_bandwidth_claim_contract>(mut self, contract: Option) -> Self { + self.contracts.bandwidth_claim_contract_address = contract.map(Into::into); + self + } + + pub fn with_coconut_bandwidth_contract>(mut self, contract: Option) -> Self { + self.contracts.coconut_bandwidth_contract_address = contract.map(Into::into); + self + } + + pub fn with_multisig_contract>(mut self, contract: Option) -> Self { + self.contracts.multisig_contract_address = contract.map(Into::into); + self + } +} + +// This conversion only exists for convenience reasons until +// we can completely phase out `DefaultNetworkDetails` +impl<'a> From<&'a DefaultNetworkDetails> for NymNetworkDetails { + fn from(details: &'a DefaultNetworkDetails) -> Self { + fn parse_optional_str(raw: &str) -> Option { + if raw.is_empty() { + None + } else { + Some(raw.into()) + } + } + + NymNetworkDetails { + chain_details: ChainDetails { + bech32_account_prefix: details.bech32_prefix.into(), + mix_denom: details.mix_denom.into(), + stake_denom: details.stake_denom.into(), + }, + endpoints: details.validators.clone(), + contracts: NymContracts { + mixnet_contract_address: parse_optional_str(details.mixnet_contract_address), + vesting_contract_address: parse_optional_str(details.vesting_contract_address), + bandwidth_claim_contract_address: parse_optional_str( + details.bandwidth_claim_contract_address, + ), + coconut_bandwidth_contract_address: parse_optional_str( + details.coconut_bandwidth_contract_address, + ), + multisig_contract_address: parse_optional_str(details.multisig_contract_address), + }, + } + } +} + // Since these are lazily constructed, we can afford to switch some of them to stronger types in the // future. If we do this, and also get rid of the references we could potentially unify with // `NetworkDetails`. -#[derive(Debug)] pub struct DefaultNetworkDetails { bech32_prefix: &'static str, mix_denom: DenomDetails, @@ -54,6 +201,7 @@ pub struct DefaultNetworkDetails { bandwidth_claim_contract_address: &'static str, coconut_bandwidth_contract_address: &'static str, multisig_contract_address: &'static str, + #[allow(dead_code)] rewarding_validator_address: &'static str, statistics_service_url: &'static str, validators: Vec, @@ -119,7 +267,7 @@ impl DenomDetails { } } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct DenomDetailsOwned { pub base: String, pub display: String, @@ -137,27 +285,38 @@ impl From for DenomDetailsOwned { } } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +impl DenomDetailsOwned { + pub fn base_only(base: String) -> Self { + DenomDetailsOwned { + base: base.clone(), + display: base, + display_exponent: 0, + } + } +} + +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ValidatorDetails { // it is assumed those values are always valid since they're being provided in our defaults file pub nymd_url: String, // Right now api_url is optional as we are not running the api reliably on all validators // however, later on it should be a mandatory field pub api_url: Option, + // TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting } impl ValidatorDetails { - pub fn new(nymd_url: &str, api_url: Option<&str>) -> Self { + pub fn new>(nymd_url: S, api_url: Option) -> Self { ValidatorDetails { - nymd_url: nymd_url.to_string(), - api_url: api_url.map(ToString::to_string), + nymd_url: nymd_url.into(), + api_url: api_url.map(Into::into), } } - pub fn new_with_name(nymd_url: &str, api_url: Option<&str>) -> Self { + pub fn new_nymd_only>(nymd_url: S) -> Self { ValidatorDetails { - nymd_url: nymd_url.to_string(), - api_url: api_url.map(ToString::to_string), + nymd_url: nymd_url.into(), + api_url: None, } } @@ -184,6 +343,7 @@ pub fn default_statistics_service_url() -> Url { pub fn default_nymd_endpoints() -> Vec { DEFAULT_NETWORK .validators() + .iter() .map(ValidatorDetails::nymd_url) .collect() } @@ -191,6 +351,7 @@ pub fn default_nymd_endpoints() -> Vec { pub fn default_api_endpoints() -> Vec { DEFAULT_NETWORK .validators() + .iter() .filter_map(ValidatorDetails::api_url) .collect() } diff --git a/common/types/src/error.rs b/common/types/src/error.rs index d1903d643f..1bc8323e4a 100644 --- a/common/types/src/error.rs +++ b/common/types/src/error.rs @@ -51,6 +51,8 @@ pub enum TypesError { #[from] source: cosmwasm_std::DecimalRangeExceeded, }, + #[error("No validator API URL configured")] + NoValidatorApiUrlConfigured, #[error("{0} is not a valid amount string")] InvalidAmount(String), #[error("{0} is not a valid denomination string")] @@ -78,6 +80,7 @@ impl From for TypesError { ValidatorClientError::ValidatorAPIError { source } => source.into(), ValidatorClientError::MalformedUrlProvided(e) => e.into(), ValidatorClientError::NymdError(e) => e.into(), + ValidatorClientError::NoAPIUrlAvailable => TypesError::NoValidatorApiUrlConfigured, } } } diff --git a/contracts/coconut-bandwidth/src/support/tests.rs b/contracts/coconut-bandwidth/src/support/tests.rs index d4ee26149f..99bfba148d 100644 --- a/contracts/coconut-bandwidth/src/support/tests.rs +++ b/contracts/coconut-bandwidth/src/support/tests.rs @@ -22,7 +22,7 @@ pub mod helpers { let env = mock_env(); let info = mock_info("creator", &[]); instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); - return deps; + deps } pub fn mock_app(init_funds: &[Coin]) -> App { diff --git a/explorer-api/src/client.rs b/explorer-api/src/client.rs index b7c628b256..1f15891c16 100644 --- a/explorer-api/src/client.rs +++ b/explorer-api/src/client.rs @@ -26,9 +26,13 @@ pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient { let nymd_url = default_nymd_endpoints()[0].clone(); let api_url = default_api_endpoints()[0].clone(); - let client_config = validator_client::Config::new(DEFAULT_NETWORK, nymd_url, api_url); + let client_config = + validator_client::Config::try_from_nym_network_details(&DEFAULT_NETWORK.details()) + .expect("failed to construct valid validator client config with the provided network") + .with_urls(nymd_url, api_url); ThreadsafeValidatorClient(Arc::new( - validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"), + validator_client::Client::new_query(client_config, DEFAULT_NETWORK) + .expect("Failed to connect to nymd!"), )) } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/eth_events.rs b/gateway/src/node/client_handling/websocket/connection_handler/eth_events.rs index 033918f16f..ea5b721434 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/eth_events.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/eth_events.rs @@ -1,14 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; -use web3::contract::tokens::Detokenize; -use web3::contract::{Contract, Error}; -use web3::ethabi::Token; -use web3::transports::Http; -use web3::types::{BlockNumber, FilterBuilder, H256}; -use web3::Web3; - use crate::node::client_handling::websocket::connection_handler::authenticated::RequestHandlingError; use bandwidth_claim_contract::msg::ExecuteMsg; use bandwidth_claim_contract::payment::LinkPaymentData; @@ -16,7 +8,14 @@ use credentials::token::bandwidth::TokenCredential; use crypto::asymmetric::identity::{PublicKey, Signature, SIGNATURE_LENGTH}; use gateway_client::bandwidth::eth_contract; use network_defaults::{ETH_EVENT_NAME, ETH_MIN_BLOCK_DEPTH}; +use std::str::FromStr; use validator_client::nymd::{AccountId, NymdClient, SigningNymdClient}; +use web3::contract::tokens::Detokenize; +use web3::contract::{Contract, Error}; +use web3::ethabi::Token; +use web3::transports::Http; +use web3::types::{BlockNumber, FilterBuilder, H256}; +use web3::Web3; pub(crate) struct ERC20Bridge { // This is needed because web3's Contract doesn't sufficiently expose it's eth interface diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index 1de793046f..d539dafb1a 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -27,6 +27,7 @@ use crate::node::client_handling::websocket::connection_handler::coconut::Coconu use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge; #[cfg(feature = "coconut")] use credentials::obtain_aggregate_verification_key; +use validator_client::nymd; use self::storage::PersistentStorage; @@ -248,8 +249,11 @@ where .choose(&mut thread_rng()) .expect("The list of validators is empty"); + let client_config = nymd::Config::try_from_nym_network_details(&DEFAULT_NETWORK.details()) + .expect("failed to construct valid validator client config with the provided network"); + validator_client::nymd::NymdClient::connect_with_mnemonic( - DEFAULT_NETWORK, + client_config, validator_nymd.as_ref(), self.config.get_cosmos_mnemonic(), None, diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 183f44e0cf..bc6c9e45f1 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -5508,6 +5508,10 @@ dependencies = [ name = "validator-api-requests" version = "0.1.0" dependencies = [ + "bs58", + "coconut-interface", + "cosmrs", + "getset", "mixnet-contract-common", "schemars", "serde", diff --git a/nym-wallet/nym-wallet-types/src/network.rs b/nym-wallet/nym-wallet-types/src/network.rs index 172f900d2b..5a41cd7b95 100644 --- a/nym-wallet/nym-wallet-types/src/network.rs +++ b/nym-wallet/nym-wallet-types/src/network.rs @@ -65,6 +65,7 @@ impl From for Network { ConfigNetwork::QA => Network::QA, ConfigNetwork::SANDBOX => Network::SANDBOX, ConfigNetwork::MAINNET => Network::MAINNET, + ConfigNetwork::CUSTOM { .. } => panic!("custom network is not supported"), } } } diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 8161f964cc..265563947d 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -123,6 +123,9 @@ impl From for BackendError { ValidatorClientError::ValidatorAPIError { source } => source.into(), ValidatorClientError::MalformedUrlProvided(e) => e.into(), ValidatorClientError::NymdError(e) => e.into(), + ValidatorClientError::NoAPIUrlAvailable => { + TypesError::NoValidatorApiUrlConfigured.into() + } } } } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index d29f80adfe..07e52cbdb9 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -157,7 +157,7 @@ async fn _connect_with_mnemonic( let default_network: WalletNetwork = config::defaults::DEFAULT_NETWORK.into(); let client_for_default_network = clients .iter() - .find(|client| WalletNetwork::from(client.network) == default_network); + .find(|client| WalletNetwork::from(client.network.clone()) == default_network); let account_for_default_network = match client_for_default_network { Some(client) => Ok(Account::new( client.nymd.mixnet_contract_address().to_string(), @@ -175,7 +175,7 @@ async fn _connect_with_mnemonic( w_state.logout(); } for client in clients { - let network: WalletNetwork = client.network.into(); + let network: WalletNetwork = client.network.clone().into(); let mut w_state = state.write().await; w_state.add_client(network, client); } @@ -252,15 +252,21 @@ fn create_clients( log::info!("Connecting to: nymd_url: {nymd_url} for {network}"); log::info!("Connecting to: api_url: {api_url} for {network}"); - let mut client = validator_client::Client::new_signing( - validator_client::Config::new(network.into(), nymd_url, api_url) - .with_mixnode_contract_address(config.get_mixnet_contract_address(network)) - .with_vesting_contract_address(config.get_vesting_contract_address(network)) - .with_bandwidth_claim_contract_address( - config.get_bandwidth_claim_contract_address(network), - ), - mnemonic.clone(), - )?; + let network_details = Network::from(network) + .details() + .with_mixnet_contract(Some(config.get_mixnet_contract_address(network).as_ref())) + .with_vesting_contract(Some(config.get_vesting_contract_address(network).as_ref())) + .with_bandwidth_claim_contract(Some( + config + .get_bandwidth_claim_contract_address(network) + .as_ref(), + )); + + let config = validator_client::Config::try_from_nym_network_details(&network_details)? + .with_urls(nymd_url, api_url); + + let mut client = + validator_client::Client::new_signing(config, network.into(), mnemonic.clone())?; client.set_nymd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); clients.push(client); } @@ -435,7 +441,7 @@ pub async fn add_account_for_password( let address = { let state = state.read().await; let network: Network = state.current_network().into(); - derive_address(mnemonic, network.bech32_prefix())?.to_string() + derive_address(mnemonic, &network.bech32_prefix())?.to_string() }; // Re-read all the acccounts from the wallet to reset the state, rather than updating it @@ -477,7 +483,7 @@ async fn set_state_with_all_accounts( let config_network: Network = network.into(); ( network, - derive_address(mnemonic.clone(), config_network.bech32_prefix()).unwrap(), + derive_address(mnemonic.clone(), &config_network.bech32_prefix()).unwrap(), ) }) .collect(); diff --git a/service-providers/network-requester/src/statistics/collector.rs b/service-providers/network-requester/src/statistics/collector.rs index 2b2485b14d..6df70c3e39 100644 --- a/service-providers/network-requester/src/statistics/collector.rs +++ b/service-providers/network-requester/src/statistics/collector.rs @@ -71,6 +71,9 @@ impl OptionalStatsProviderConfig { network_defaults::all::Network::MAINNET => self.mainnet.clone(), network_defaults::all::Network::SANDBOX => self.sandbox.clone(), network_defaults::all::Network::QA => self.qa.clone(), + network_defaults::all::Network::CUSTOM { .. } => { + panic!("custom network is not supported") + } }; entry_config.map(|e| e.stats_client_address) } diff --git a/validator-api/src/config/mod.rs b/validator-api/src/config/mod.rs index af1f03e271..9a98dd2158 100644 --- a/validator-api/src/config/mod.rs +++ b/validator-api/src/config/mod.rs @@ -108,7 +108,9 @@ impl Default for Base { local_validator: DEFAULT_LOCAL_VALIDATOR .parse() .expect("default local validator is malformed!"), - mixnet_contract_address: DEFAULT_NETWORK.mixnet_contract_address().to_string(), + mixnet_contract_address: DEFAULT_NETWORK + .mixnet_contract_address() + .expect("mixnet contract address is unavailable"), mnemonic: "exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day".to_string(), } } diff --git a/validator-api/src/nymd_client.rs b/validator-api/src/nymd_client.rs index aaa52d3a60..21532f90be 100644 --- a/validator-api/src/nymd_client.rs +++ b/validator-api/src/nymd_client.rs @@ -53,16 +53,16 @@ impl Client { .unwrap(); let nymd_url = config.get_nymd_validator_url(); let network = DEFAULT_NETWORK; + let details = network + .details() + .with_mixnet_contract(Some(config.get_mixnet_contract_address())); - let mixnet_contract = config - .get_mixnet_contract_address() - .parse() - .expect("the mixnet contract address is invalid!"); + let client_config = validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct valid validator client config with the provided network") + .with_urls(nymd_url, api_url); - let client_config = validator_client::Config::new(network, nymd_url, api_url) - .with_mixnode_contract_address(mixnet_contract); - let inner = - validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"); + let inner = validator_client::Client::new_query(client_config, network) + .expect("Failed to connect to nymd!"); Client(Arc::new(RwLock::new(inner))) } @@ -76,20 +76,22 @@ impl Client { .parse() .unwrap(); let nymd_url = config.get_nymd_validator_url(); - let network = DEFAULT_NETWORK; - let mixnet_contract = config - .get_mixnet_contract_address() - .parse() - .expect("the mixnet contract address is invalid!"); + let network = DEFAULT_NETWORK; + let details = network + .details() + .with_mixnet_contract(Some(config.get_mixnet_contract_address())); + + let client_config = validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct valid validator client config with the provided network") + .with_urls(nymd_url, api_url); + let mnemonic = config .get_mnemonic() .parse() .expect("the mnemonic is invalid!"); - let client_config = validator_client::Config::new(network, nymd_url, api_url) - .with_mixnode_contract_address(mixnet_contract); - let inner = validator_client::Client::new_signing(client_config, mnemonic) + let inner = validator_client::Client::new_signing(client_config, network, mnemonic) .expect("Failed to connect to nymd!"); Client(Arc::new(RwLock::new(inner)))