46c67440bb
* Squashing the mix stress testing branch (#6575) reduced chain watcher per block log severity update network monitors contract semver to 1.0.0 fix build issues fix mixnet client dropping initial packet on egress reconnection adjusted logs for network monitor agent changed default testing interval to 2h refresh NM contract information explicit return type for batch submission for mixnet listener task to get scheduled before beginning connectivity test make sure to always use canonical ip for network monitor noise keys feat: NMv3: make agents decide egress port (#6746) add config v12->v13 config migration for nym nodes fix formatting in wallet types simplified client config creation remove other swagger redirect removed swagger redirect on /swagger/ route log version info on startup add workflows, contract address, and dockerfile bugfix: use correct endpoints when setting up orchestrator (#6733) clippy adjust DEFAULT_MIN_STRESS_TESTED_NODES ratio expose route with new performance metrics fixes and additional docs use stress testing scores stub for usage of stress testing scores stub traits added new fields to nym-api config controlling usage of stress test data guard against duplicate packets prevent usage of chain_authorisation_check_max_attempts with value of 0 make sure duplicate results cant be inserted into the db submit test results from orchestrator on an interval docs and fixes nym-api side of handling result submission stubs for submitting results NM orchestrator verifying nym-api result submission permissions NM orchestrator to update announced key on startup allow NM orchestrator to announce its identity key to the contract stubs within nym-api for accepting NMv3 results added additional metrics docs bugfixes + making sure to only assign mixnode testruns fixed node refresher to only retrieve mixnodes and add additional metrics topology metrics defined basic prometheus metrics authorised endpoint for returning prometheus data create initial stub for prometheus metrics post rebasing fixes adjusted routes missing implementation for storage getters a lot of new stubs and db accessors stubs for results endpoints update utoipa tags for agent rountes shared auth between metrics and results moved stale results eviction into the interval.tick branch refactor and comments create background process to evict stale data include sphinx packet delay as part of the stats fix mock construction add median to the calculated latency distribution remove unused imports cleanup performing testrun and submitting the results assigning testruns to requesting agents basic stub for http server for the NMv3 orchestrator chore: rename existing 'NetworkMonitorAgent' to 'NodeStressTester' make sure to use canonical ips within the noise config fixed contract tests cargo fmt additional comments and unit tests contract and nym-node support of NM agents being run on the same host basic unit tests refactoring make agents retrieve mix port assignment from the orchestrator provide sensible defaults to CLI arguments stub the initial structure for the agent chore: remove redundant import missed tick behaviour removed redundant mutex removed redundant try_get_client reuse existing constant for default nymnode port add node refresher for periodic scraping of bonded nym-node details - NodeRefresher periodically queries the mixnet contract for all bonded nodes and probes each node's HTTP API for host information, sphinx keys, noise keys, and key rotation IDs - Extract NymNodeApiClientRetriever into nym-node-requests with port probing, identity verification, and host information signature checking - Add clone_query_client on NyxdClient so the refresher can hold its own query client without locking the signing client - Batch upsert for nym_node rows (single transaction instead of per-row) - Reuse the new helpers in nym-api's node_describe_cache ensure assignment of testrun begins an IMMEDIATE tx construction of the orchestrator struct initial set of cli args make sure to not assign testable nodes too often very initial database structure and cli fixed construction of RoutableNetworkMonitors remove redundant constructor for NoiseNode forbid 0-nonsense config values add type safety for test route construction moved lioness and arrayref to workspace deps fixed dockerfile build always use canonical addresses in RoutableNetworkMonitors fixed old contract formatting issues removed redundant into() call network monitor agent fixes additional logs config unit tests more docs standalone stress testing invocation further refactoring and changes refactor testing loop and return valid test result upon completion initial sending/receiving test loop generating reusable sphinx headers additional structure for receiving ingress packets initial scaffolding for NMv3 agent added validation of x25519 noise key removed unstable call to 'is_multiple_of' remove calls to from_octets as they're unavailable in pre 1.91 additional docs/comments propagating noise information about NM for mixnet routing pass full socket address of the agent into the contract storage feat: store noise keys alongside ip addresses within the contract removed redundant comment ensure NM packets can only go to NM PR review comments added additional docs allow NM to replay packets + fix replay prometheus metrics propagate information about nm agent to connection handler updated nym-node config migration feat: introduced nym-node websocket subscription for keeping updated list of NM agents allow admin to also revoke monitor agents remove agents upon orchestrator removal fixed schema generation and regenerated the contract schema removed rustc restriction on contracts-common added client methods for interacting with the contract added unit tests for contract methods implemented logic of the network monitors contract create initial structure for network monitors contract start mix stress testing topic branch * make nym-node default to the new blockstream rpc/ws node cluster * reduced mixnet-client log severity * set network monitors contract address for mainnet
698 lines
23 KiB
Rust
698 lines
23 KiB
Rust
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::nyxd::{self, NyxdClient};
|
|
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
|
use crate::signing::signer::{NoSigner, OfflineSigner};
|
|
use crate::{
|
|
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ValidatorClientError,
|
|
};
|
|
use nym_api_requests::ecash::models::{
|
|
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
|
BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse,
|
|
IssuedTicketbooksChallengeCommitmentRequest, IssuedTicketbooksChallengeCommitmentResponse,
|
|
IssuedTicketbooksDataRequest, IssuedTicketbooksDataResponse, IssuedTicketbooksForCountResponse,
|
|
IssuedTicketbooksForResponse, VerifyEcashTicketBody,
|
|
};
|
|
use nym_api_requests::ecash::{
|
|
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
|
|
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
|
};
|
|
use nym_api_requests::models::{
|
|
ApiHealthResponse, GatewayCoreStatusResponse, HistoricalPerformanceResponse,
|
|
MixnodeCoreStatusResponse, NymNodeDescriptionV1, NymNodeDescriptionV2,
|
|
};
|
|
use nym_api_requests::nym_nodes::{
|
|
NodesByAddressesResponse, SemiSkimmedNodesWithMetadata, SkimmedNodeV1, SkimmedNodesWithMetadata,
|
|
};
|
|
use nym_coconut_dkg_common::types::EpochId;
|
|
use nym_http_api_client::UserAgent;
|
|
use nym_mixnet_contract_common::EpochRewardedSet;
|
|
use nym_network_defaults::NymNetworkDetails;
|
|
use std::net::IpAddr;
|
|
use time::Date;
|
|
use url::Url;
|
|
|
|
pub use crate::nym_api::NymApiClientExt;
|
|
pub use nym_mixnet_contract_common::{
|
|
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails,
|
|
};
|
|
// re-export the type to not break existing imports
|
|
pub use crate::coconut::EcashApiClient;
|
|
|
|
#[cfg(feature = "http-client")]
|
|
use crate::rpc::http_client;
|
|
#[cfg(feature = "http-client")]
|
|
use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient};
|
|
|
|
// a simple helper macro to define to repeatedly call a paged query until a full response is constructed
|
|
macro_rules! collect_paged_skimmed_v2 {
|
|
( $self:ident, $f: ident ) => {{
|
|
// unroll first loop iteration in order to obtain the metadata
|
|
let mut page = 0;
|
|
let res = $self
|
|
.nym_api
|
|
.$f(false, Some(page), None, $self.use_bincode)
|
|
.await?;
|
|
let mut nodes = res.nodes.data;
|
|
let metadata = res.metadata;
|
|
|
|
if res.nodes.pagination.total == nodes.len() {
|
|
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
|
|
}
|
|
|
|
page += 1;
|
|
|
|
loop {
|
|
let mut res = $self
|
|
.nym_api
|
|
.$f(false, Some(page), None, $self.use_bincode)
|
|
.await?;
|
|
|
|
if !metadata.consistency_check(&res.metadata) {
|
|
return Err(ValidatorClientError::InconsistentPagedMetadata);
|
|
}
|
|
|
|
nodes.append(&mut res.nodes.data);
|
|
if nodes.len() < res.nodes.pagination.total {
|
|
page += 1
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
|
|
}};
|
|
}
|
|
|
|
#[must_use]
|
|
#[derive(Debug, Clone)]
|
|
pub struct Config {
|
|
api_url: Url,
|
|
nyxd_url: Url,
|
|
|
|
// TODO: until refactored, this is a dead field under some features
|
|
nyxd_config: nyxd::Config,
|
|
}
|
|
|
|
impl TryFrom<NymNetworkDetails> for Config {
|
|
type Error = ValidatorClientError;
|
|
|
|
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
|
|
Config::try_from_nym_network_details(&value)
|
|
}
|
|
}
|
|
|
|
impl Config {
|
|
pub fn new(nyxd_url: Url, api_url: Url, nyxd_config: nyxd::Config) -> Self {
|
|
Config {
|
|
api_url,
|
|
nyxd_url,
|
|
nyxd_config,
|
|
}
|
|
}
|
|
|
|
pub fn try_from_nym_network_details(
|
|
details: &NymNetworkDetails,
|
|
) -> Result<Self, ValidatorClientError> {
|
|
let mut api_url = details
|
|
.endpoints
|
|
.iter()
|
|
.filter_map(|d| d.api_url.as_ref())
|
|
.map(|url| Url::parse(url))
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
if let Some(nym_api_urls) = details.nym_api_urls.as_ref() {
|
|
api_url.extend(
|
|
nym_api_urls
|
|
.iter()
|
|
.map(|url| url.url.parse())
|
|
.collect::<Result<Vec<_>, _>>()?,
|
|
);
|
|
}
|
|
|
|
if api_url.is_empty() {
|
|
return Err(ValidatorClientError::NoAPIUrlAvailable);
|
|
}
|
|
|
|
Ok(Config {
|
|
api_url: api_url.pop().unwrap(),
|
|
nyxd_url: details.endpoints[0]
|
|
.nyxd_url
|
|
.parse()
|
|
.map_err(ValidatorClientError::MalformedUrlProvided)?,
|
|
nyxd_config: nyxd::Config::try_from_nym_network_details(details)?,
|
|
})
|
|
}
|
|
|
|
// 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, nyxd_url: Url, api_url: Url) -> Self {
|
|
self.nyxd_url = nyxd_url;
|
|
self.api_url = api_url;
|
|
self
|
|
}
|
|
|
|
pub fn with_nyxd_url(mut self, nyxd_url: Url) -> Self {
|
|
self.nyxd_url = nyxd_url;
|
|
self
|
|
}
|
|
|
|
pub fn with_simulated_gas_multiplier(mut self, gas_multiplier: f32) -> Self {
|
|
self.nyxd_config.simulated_gas_multiplier = gas_multiplier;
|
|
self
|
|
}
|
|
}
|
|
|
|
pub struct Client<C, S = NoSigner> {
|
|
// ideally they would have been read-only, but unfortunately rust doesn't have such features
|
|
// #[deprecated(note = "please use `nym_api_client` instead")]
|
|
pub nym_api: nym_http_api_client::Client,
|
|
// pub nym_api_client: NymApiClient,
|
|
pub nyxd: NyxdClient<C, S>,
|
|
}
|
|
|
|
#[cfg(feature = "http-client")]
|
|
impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
|
|
pub fn new_signing(
|
|
config: Config,
|
|
mnemonic: bip39::Mnemonic,
|
|
) -> Result<DirectSigningHttpRpcValidatorClient, ValidatorClientError> {
|
|
let rpc_client = http_client(config.nyxd_url.as_str())?;
|
|
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
|
|
let wallet = DirectSecp256k1HdWallet::checked_from_mnemonic(prefix, mnemonic)?;
|
|
|
|
Ok(Self::new_signing_with_rpc_client(
|
|
config, rpc_client, wallet,
|
|
))
|
|
}
|
|
|
|
pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
|
self.nyxd.change_endpoint(new_endpoint.as_ref())?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[allow(deprecated)]
|
|
impl Client<crate::ReqwestRpcClient, DirectSecp256k1HdWallet> {
|
|
pub fn new_reqwest_signing(
|
|
config: Config,
|
|
mnemonic: bip39::Mnemonic,
|
|
) -> DirectSigningReqwestRpcValidatorClient {
|
|
let rpc_client = crate::ReqwestRpcClient::new(config.nyxd_url.clone());
|
|
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
|
|
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
|
|
|
Self::new_signing_with_rpc_client(config, rpc_client, wallet)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "http-client")]
|
|
impl Client<HttpRpcClient> {
|
|
pub fn new_query(config: Config) -> Result<QueryHttpRpcValidatorClient, ValidatorClientError> {
|
|
let rpc_client = http_client(config.nyxd_url.as_str())?;
|
|
Ok(Self::new_with_rpc_client(config, rpc_client))
|
|
}
|
|
|
|
pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
|
self.nyxd = NyxdClient::connect(self.nyxd.current_config().clone(), new_endpoint.as_ref())?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[allow(deprecated)]
|
|
impl Client<crate::ReqwestRpcClient> {
|
|
pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient {
|
|
let rpc_client = crate::ReqwestRpcClient::new(config.nyxd_url.clone());
|
|
Self::new_with_rpc_client(config, rpc_client)
|
|
}
|
|
}
|
|
|
|
impl<C> Client<C> {
|
|
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
|
|
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None);
|
|
|
|
Client {
|
|
nym_api: nym_api_client,
|
|
nyxd: NyxdClient::new(config.nyxd_config, rpc_client),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<C, S> Client<C, S> {
|
|
pub fn new_signing_with_rpc_client(config: Config, rpc_client: C, signer: S) -> Self
|
|
where
|
|
S: OfflineSigner,
|
|
{
|
|
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None);
|
|
|
|
Client {
|
|
nym_api: nym_api_client,
|
|
nyxd: NyxdClient::new_signing(config.nyxd_config, rpc_client, signer),
|
|
}
|
|
}
|
|
}
|
|
|
|
// validator-api wrappers
|
|
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
|
|
#[allow(deprecated)]
|
|
impl<C, S> Client<C, S> {
|
|
pub fn api_url(&self) -> &Url {
|
|
self.nym_api.current_url().as_ref()
|
|
}
|
|
|
|
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
|
self.nym_api.change_base_urls(vec![new_endpoint.into()])
|
|
}
|
|
|
|
pub async fn get_full_node_performance_history(
|
|
&self,
|
|
node_id: NodeId,
|
|
) -> Result<Vec<HistoricalPerformanceResponse>, ValidatorClientError> {
|
|
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
|
let mut page = 0;
|
|
let mut history = Vec::new();
|
|
|
|
loop {
|
|
let mut res = self
|
|
.nym_api
|
|
.get_node_performance_history(node_id, Some(page), None)
|
|
.await?;
|
|
|
|
history.append(&mut res.history.data);
|
|
if history.len() < res.history.pagination.total {
|
|
page += 1
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(history)
|
|
}
|
|
|
|
#[deprecated(note = "use get_all_cached_described_nodes_v2 instead")]
|
|
pub async fn get_all_cached_described_nodes(
|
|
&self,
|
|
) -> Result<Vec<NymNodeDescriptionV1>, ValidatorClientError> {
|
|
Ok(self.nym_api.get_all_described_nodes().await?)
|
|
}
|
|
|
|
pub async fn get_all_cached_described_nodes_v2(
|
|
&self,
|
|
) -> Result<Vec<NymNodeDescriptionV2>, ValidatorClientError> {
|
|
Ok(self.nym_api.get_all_described_nodes_v2().await?)
|
|
}
|
|
|
|
pub async fn get_all_cached_bonded_nym_nodes(
|
|
&self,
|
|
) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
|
self.nym_api.get_all_bonded_nym_nodes().await
|
|
}
|
|
|
|
pub async fn blind_sign(
|
|
&self,
|
|
request_body: &BlindSignRequestBody,
|
|
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.blind_sign(request_body).await?)
|
|
}
|
|
}
|
|
|
|
/// DEPRECATED: Use nym_http_api_client::Client with from_network() or with_bincode() instead
|
|
#[deprecated(
|
|
since = "1.2.0",
|
|
note = "Use nym_http_api_client::Client::from_network() or ClientBuilder::with_bincode() instead"
|
|
)]
|
|
#[derive(Clone)]
|
|
pub struct NymApiClient {
|
|
pub use_bincode: bool,
|
|
pub nym_api: nym_http_api_client::Client,
|
|
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
|
|
// we could re-implement the communication with the REST API on port 1317
|
|
}
|
|
|
|
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
|
|
#[allow(deprecated)]
|
|
impl NymApiClient {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
|
|
let nym_api = nym_http_api_client::Client::new(api_url, Some(timeout));
|
|
|
|
NymApiClient {
|
|
use_bincode: true,
|
|
nym_api,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn with_bincode(mut self, use_bincode: bool) -> Self {
|
|
self.use_bincode = use_bincode;
|
|
self
|
|
}
|
|
|
|
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
|
|
let nym_api = nym_http_api_client::Client::builder(api_url)
|
|
.expect("invalid api url")
|
|
.with_user_agent(user_agent.into())
|
|
.build()
|
|
.expect("failed to build nym api client");
|
|
|
|
NymApiClient {
|
|
use_bincode: false,
|
|
nym_api,
|
|
}
|
|
}
|
|
|
|
pub fn api_url(&self) -> &Url {
|
|
self.nym_api.current_url().as_ref()
|
|
}
|
|
|
|
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
|
self.nym_api.change_base_urls(vec![new_endpoint.into()]);
|
|
}
|
|
|
|
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")]
|
|
pub async fn get_basic_mixnodes(&self) -> Result<Vec<SkimmedNodeV1>, ValidatorClientError> {
|
|
Ok(self.nym_api.get_basic_mixnodes().await?.nodes)
|
|
}
|
|
|
|
#[deprecated(note = "use get_all_basic_entry_assigned_nodes instead")]
|
|
pub async fn get_basic_gateways(&self) -> Result<Vec<SkimmedNodeV1>, ValidatorClientError> {
|
|
Ok(self.nym_api.get_basic_gateways().await?.nodes)
|
|
}
|
|
|
|
pub async fn get_current_rewarded_set(&self) -> Result<EpochRewardedSet, ValidatorClientError> {
|
|
Ok(self.nym_api.get_rewarded_set().await?.into())
|
|
}
|
|
|
|
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
|
/// this includes legacy gateways and nym-nodes
|
|
#[deprecated(note = "use get_all_basic_entry_assigned_nodes_with_metadata instead")]
|
|
pub async fn get_all_basic_entry_assigned_nodes(
|
|
&self,
|
|
) -> Result<Vec<SkimmedNodeV1>, ValidatorClientError> {
|
|
self.get_all_basic_entry_assigned_nodes_with_metadata()
|
|
.await
|
|
.map(|res| res.nodes)
|
|
}
|
|
|
|
pub async fn get_all_basic_entry_assigned_nodes_with_metadata(
|
|
&self,
|
|
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
|
|
collect_paged_skimmed_v2!(self, get_basic_entry_assigned_nodes_v2)
|
|
}
|
|
|
|
/// retrieve basic information for nodes that got assigned 'mixing' node in this epoch
|
|
/// this includes legacy mixnodes and nym-nodes
|
|
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes_with_metadata instead")]
|
|
pub async fn get_all_basic_active_mixing_assigned_nodes(
|
|
&self,
|
|
) -> Result<Vec<SkimmedNodeV1>, ValidatorClientError> {
|
|
self.get_all_basic_active_mixing_assigned_nodes_with_metadata()
|
|
.await
|
|
.map(|res| res.nodes)
|
|
}
|
|
|
|
pub async fn get_all_basic_active_mixing_assigned_nodes_with_metadata(
|
|
&self,
|
|
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
|
|
collect_paged_skimmed_v2!(self, get_basic_active_mixing_assigned_nodes_v2)
|
|
}
|
|
|
|
/// retrieve basic information for nodes are capable of operating as a mixnode
|
|
/// this includes legacy mixnodes and nym-nodes
|
|
#[deprecated(note = "use get_all_basic_mixing_capable_nodes_with_metadata instead")]
|
|
pub async fn get_all_basic_mixing_capable_nodes(
|
|
&self,
|
|
) -> Result<Vec<SkimmedNodeV1>, ValidatorClientError> {
|
|
self.get_all_basic_mixing_capable_nodes_with_metadata()
|
|
.await
|
|
.map(|res| res.nodes)
|
|
}
|
|
|
|
pub async fn get_all_basic_mixing_capable_nodes_with_metadata(
|
|
&self,
|
|
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
|
|
collect_paged_skimmed_v2!(self, get_basic_mixing_capable_nodes_v2)
|
|
}
|
|
|
|
/// retrieve basic information for all bonded nodes on the network
|
|
#[deprecated(note = "use get_all_basic_nodes_with_metadata instead")]
|
|
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNodeV1>, ValidatorClientError> {
|
|
self.get_all_basic_nodes_with_metadata()
|
|
.await
|
|
.map(|res| res.nodes)
|
|
}
|
|
|
|
pub async fn get_all_basic_nodes_with_metadata(
|
|
&self,
|
|
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
|
|
collect_paged_skimmed_v2!(self, get_basic_nodes_v2)
|
|
}
|
|
|
|
/// retrieve expanded information for all bonded nodes on the network
|
|
pub async fn get_all_expanded_nodes(
|
|
&self,
|
|
) -> Result<SemiSkimmedNodesWithMetadata, ValidatorClientError> {
|
|
// Unroll the first iteration to get the metadata
|
|
let mut page = 0;
|
|
|
|
let res = self
|
|
.nym_api
|
|
.get_expanded_nodes(false, Some(page), None)
|
|
.await?;
|
|
let mut nodes = res.nodes.data;
|
|
let metadata = res.metadata;
|
|
|
|
if res.nodes.pagination.total == nodes.len() {
|
|
return Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata));
|
|
}
|
|
|
|
page += 1;
|
|
|
|
loop {
|
|
let mut res = self
|
|
.nym_api
|
|
.get_expanded_nodes(false, Some(page), None)
|
|
.await?;
|
|
|
|
nodes.append(&mut res.nodes.data);
|
|
if nodes.len() < res.nodes.pagination.total {
|
|
page += 1
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata))
|
|
}
|
|
|
|
pub async fn health(&self) -> Result<ApiHealthResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.health().await?)
|
|
}
|
|
|
|
#[deprecated(note = "use .get_all_described_nodes_v2 instead")]
|
|
pub async fn get_all_described_nodes(
|
|
&self,
|
|
) -> Result<Vec<NymNodeDescriptionV1>, ValidatorClientError> {
|
|
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
|
let mut page = 0;
|
|
let mut descriptions = Vec::new();
|
|
|
|
loop {
|
|
let mut res = self.nym_api.get_nodes_described(Some(page), None).await?;
|
|
|
|
descriptions.append(&mut res.data);
|
|
if descriptions.len() < res.pagination.total {
|
|
page += 1
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(descriptions)
|
|
}
|
|
|
|
pub async fn get_all_described_nodes_v2(
|
|
&self,
|
|
) -> Result<Vec<NymNodeDescriptionV2>, ValidatorClientError> {
|
|
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
|
let mut page = 0;
|
|
let mut descriptions = Vec::new();
|
|
|
|
loop {
|
|
let mut res = self
|
|
.nym_api
|
|
.get_nodes_described_v2(Some(page), None)
|
|
.await?;
|
|
|
|
descriptions.append(&mut res.data);
|
|
if descriptions.len() < res.pagination.total {
|
|
page += 1
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(descriptions)
|
|
}
|
|
|
|
pub async fn get_all_bonded_nym_nodes(
|
|
&self,
|
|
) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
|
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
|
let mut page = 0;
|
|
let mut bonds = Vec::new();
|
|
|
|
loop {
|
|
let mut res = self.nym_api.get_nym_nodes(Some(page), None).await?;
|
|
|
|
bonds.append(&mut res.data);
|
|
if bonds.len() < res.pagination.total {
|
|
page += 1
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(bonds)
|
|
}
|
|
|
|
#[deprecated]
|
|
pub async fn get_gateway_core_status_count(
|
|
&self,
|
|
identity: IdentityKeyRef<'_>,
|
|
since: Option<i64>,
|
|
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.get_gateway_core_status_count(identity, since)
|
|
.await?)
|
|
}
|
|
|
|
#[deprecated]
|
|
pub async fn get_mixnode_core_status_count(
|
|
&self,
|
|
mix_id: NodeId,
|
|
since: Option<i64>,
|
|
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.get_mixnode_core_status_count(mix_id, since)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn blind_sign(
|
|
&self,
|
|
request_body: &BlindSignRequestBody,
|
|
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.blind_sign(request_body).await?)
|
|
}
|
|
|
|
pub async fn verify_ecash_ticket(
|
|
&self,
|
|
request_body: &VerifyEcashTicketBody,
|
|
) -> Result<EcashTicketVerificationResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.verify_ecash_ticket(request_body).await?)
|
|
}
|
|
|
|
pub async fn batch_redeem_ecash_tickets(
|
|
&self,
|
|
request_body: &BatchRedeemTicketsBody,
|
|
) -> Result<EcashBatchTicketRedemptionResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.batch_redeem_ecash_tickets(request_body)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn partial_expiration_date_signatures(
|
|
&self,
|
|
expiration_date: Option<Date>,
|
|
epoch_id: Option<EpochId>,
|
|
) -> Result<PartialExpirationDateSignatureResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.partial_expiration_date_signatures(expiration_date, epoch_id)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn partial_coin_indices_signatures(
|
|
&self,
|
|
epoch_id: Option<EpochId>,
|
|
) -> Result<PartialCoinIndicesSignatureResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.partial_coin_indices_signatures(epoch_id)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn global_expiration_date_signatures(
|
|
&self,
|
|
expiration_date: Option<Date>,
|
|
epoch_id: Option<EpochId>,
|
|
) -> Result<AggregatedExpirationDateSignatureResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.global_expiration_date_signatures(expiration_date, epoch_id)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn global_coin_indices_signatures(
|
|
&self,
|
|
epoch_id: Option<EpochId>,
|
|
) -> Result<AggregatedCoinIndicesSignatureResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.global_coin_indices_signatures(epoch_id)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn master_verification_key(
|
|
&self,
|
|
epoch_id: Option<EpochId>,
|
|
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.master_verification_key(epoch_id).await?)
|
|
}
|
|
|
|
pub async fn issued_ticketbooks_for(
|
|
&self,
|
|
expiration_date: Date,
|
|
) -> Result<IssuedTicketbooksForResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.issued_ticketbooks_for(expiration_date).await?)
|
|
}
|
|
|
|
pub async fn issued_ticketbooks_for_count(
|
|
&self,
|
|
expiration_date: Date,
|
|
) -> Result<IssuedTicketbooksForCountResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.issued_ticketbooks_for_count(expiration_date)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn issued_ticketbooks_challenge_commitment(
|
|
&self,
|
|
request: &IssuedTicketbooksChallengeCommitmentRequest,
|
|
) -> Result<IssuedTicketbooksChallengeCommitmentResponse, ValidatorClientError> {
|
|
Ok(self
|
|
.nym_api
|
|
.issued_ticketbooks_challenge_commitment(request)
|
|
.await?)
|
|
}
|
|
|
|
pub async fn issued_ticketbooks_data(
|
|
&self,
|
|
request: &IssuedTicketbooksDataRequest,
|
|
) -> Result<IssuedTicketbooksDataResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.issued_ticketbooks_data(request).await?)
|
|
}
|
|
|
|
pub async fn nodes_by_addresses(
|
|
&self,
|
|
addresses: Vec<IpAddr>,
|
|
) -> Result<NodesByAddressesResponse, ValidatorClientError> {
|
|
Ok(self.nym_api.nodes_by_addresses(addresses).await?)
|
|
}
|
|
}
|