Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eaa52f8781 | |||
| 7468f7584a | |||
| 5f7ff2756c | |||
| d74ae36fd5 | |||
| c1dafcea7d | |||
| 7b8753e529 | |||
| c24814179f | |||
| 7a7a14f585 | |||
| 4e5dfcbfeb | |||
| 60261a0fa6 | |||
| b9d96f337a |
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
use crate::nyxd::{self, NyxdClient};
|
||||
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
use crate::signing::signer::{NoSigner, OfflineSigner};
|
||||
@@ -18,9 +19,11 @@ use nym_api_requests::ecash::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
|
||||
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
||||
};
|
||||
use nym_api_requests::models::described::v1::NymNodeDescriptionV1;
|
||||
use nym_api_requests::models::described::v2::NymNodeDescriptionV2;
|
||||
use nym_api_requests::models::{
|
||||
ApiHealthResponse, GatewayCoreStatusResponse, HistoricalPerformanceResponse,
|
||||
MixnodeCoreStatusResponse, NymNodeDescriptionV1, NymNodeDescriptionV2,
|
||||
MixnodeCoreStatusResponse,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{
|
||||
NodesByAddressesResponse, SemiSkimmedNodesWithMetadata, SkimmedNodeV1, SkimmedNodesWithMetadata,
|
||||
@@ -28,15 +31,13 @@ use nym_api_requests::nym_nodes::{
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_http_api_client::UserAgent;
|
||||
use nym_mixnet_contract_common::EpochRewardedSet;
|
||||
pub use nym_mixnet_contract_common::{
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails,
|
||||
};
|
||||
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;
|
||||
|
||||
|
||||
@@ -15,16 +15,13 @@ use nym_api_requests::ecash::models::{
|
||||
VerifyEcashTicketBody,
|
||||
};
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::network_monitor::{
|
||||
KnownNetworkMonitorResponse, StressTestBatchSubmission,
|
||||
};
|
||||
|
||||
use nym_api_requests::models::node_families::NodeFamily;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponseV1, AnnotationResponseV2, ApiHealthResponse, BinaryBuildInformationOwned,
|
||||
ChainBlocksStatusResponse, ChainStatusResponse, KeyRotationInfoResponse,
|
||||
NodePerformanceResponse, NodeRefreshBody, NymNodeDescriptionV1, NymNodeDescriptionV2,
|
||||
PerformanceHistoryResponse, RewardedSetResponse, SignerInformationResponse,
|
||||
StressTestBatchSubmissionResponse,
|
||||
NodePerformanceResponse, NodeRefreshBody, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
SignerInformationResponse,
|
||||
};
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
use nym_http_api_client::{ApiClient, NO_PARAMS};
|
||||
@@ -34,6 +31,11 @@ use time::format_description::BorrowedFormatItem;
|
||||
use time::Date;
|
||||
use tracing::instrument;
|
||||
|
||||
use nym_api_requests::models::described::v1::NymNodeDescriptionV1;
|
||||
use nym_api_requests::models::described::v2::NymNodeDescriptionV2;
|
||||
use nym_api_requests::models::v3::{
|
||||
KnownNetworkMonitorResponse, StressTestBatchSubmission, StressTestBatchSubmissionResponse,
|
||||
};
|
||||
pub use nym_api_requests::{
|
||||
ecash::{
|
||||
models::SpentCredentialsResponse, BlindSignRequestBody, BlindedSignatureResponse,
|
||||
@@ -52,7 +54,6 @@ pub use nym_api_requests::{
|
||||
},
|
||||
NymNetworkDetailsResponse,
|
||||
};
|
||||
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
|
||||
pub mod error;
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod query;
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
// pub use cosmrs::feegrant::{Q}
|
||||
|
||||
// unfortunately queries are not implemented within cosmrs
|
||||
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{Any, CosmWasmClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
|
||||
use cosmrs::proto::cosmos::feegrant::v1beta1::{
|
||||
QueryAllowancesRequest as ProtoQueryAllowancesRequest,
|
||||
QueryAllowancesResponse as ProtoQueryAllowancesResponse,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct QueryAllowancesRequest {
|
||||
pub grantee: AccountId,
|
||||
|
||||
pub pagination: Option<cosmrs::query::PageRequest>,
|
||||
}
|
||||
|
||||
impl TryFrom<cosmrs::proto::cosmos::feegrant::v1beta1::QueryAllowancesRequest>
|
||||
for QueryAllowancesRequest
|
||||
{
|
||||
type Error = cosmrs::ErrorReport;
|
||||
|
||||
fn try_from(
|
||||
value: cosmrs::proto::cosmos::feegrant::v1beta1::QueryAllowancesRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(QueryAllowancesRequest {
|
||||
grantee: value.grantee.parse()?,
|
||||
pagination: value.pagination.map(Into::into),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryAllowancesRequest>
|
||||
for cosmrs::proto::cosmos::feegrant::v1beta1::QueryAllowancesRequest
|
||||
{
|
||||
fn from(value: QueryAllowancesRequest) -> Self {
|
||||
Self {
|
||||
grantee: value.grantee.to_string(),
|
||||
pagination: value.pagination.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct QueryAllowancesResponse {
|
||||
pub allowances: Vec<Grant>,
|
||||
pub pagination: Option<cosmrs::query::PageResponse>,
|
||||
}
|
||||
|
||||
impl TryFrom<cosmrs::proto::cosmos::feegrant::v1beta1::QueryAllowancesResponse>
|
||||
for QueryAllowancesResponse
|
||||
{
|
||||
type Error = cosmrs::ErrorReport;
|
||||
|
||||
fn try_from(
|
||||
value: cosmrs::proto::cosmos::feegrant::v1beta1::QueryAllowancesResponse,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(QueryAllowancesResponse {
|
||||
allowances: value
|
||||
.allowances
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<_, _>>()?,
|
||||
pagination: value.pagination.map(Into::into),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryAllowancesResponse>
|
||||
for cosmrs::proto::cosmos::feegrant::v1beta1::QueryAllowancesResponse
|
||||
{
|
||||
fn from(value: QueryAllowancesResponse) -> Self {
|
||||
Self {
|
||||
allowances: value.allowances.into_iter().map(Into::into).collect(),
|
||||
pagination: value.pagination.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Grant is stored in the KVStore to record a grant with full context
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Grant {
|
||||
/// granter is the address of the user granting an allowance of their funds.
|
||||
pub granter: AccountId,
|
||||
|
||||
/// grantee is the address of the user being granted an allowance of another user's funds.
|
||||
pub grantee: AccountId,
|
||||
|
||||
/// allowance can be any of basic, periodic, allowed fee allowance.
|
||||
pub allowance: Option<Any>,
|
||||
}
|
||||
|
||||
impl TryFrom<cosmrs::proto::cosmos::feegrant::v1beta1::Grant> for Grant {
|
||||
type Error = cosmrs::ErrorReport;
|
||||
|
||||
fn try_from(
|
||||
value: cosmrs::proto::cosmos::feegrant::v1beta1::Grant,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Grant {
|
||||
granter: value.granter.parse()?,
|
||||
grantee: value.grantee.parse()?,
|
||||
allowance: value.allowance,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Grant> for cosmrs::proto::cosmos::feegrant::v1beta1::Grant {
|
||||
fn from(value: Grant) -> Self {
|
||||
Self {
|
||||
granter: value.granter.to_string(),
|
||||
grantee: value.grantee.to_string(),
|
||||
allowance: value.allowance,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change trait restriction from `CosmWasmClient` to `TendermintRpcClient`
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait FeegrantQueryClient: CosmWasmClient {
|
||||
async fn allowances(
|
||||
&self,
|
||||
grantee: AccountId,
|
||||
pagination: Option<cosmrs::query::PageRequest>,
|
||||
) -> Result<QueryAllowancesResponse, NyxdError> {
|
||||
let path = Some("/cosmos.feegrant.v1beta1.Query/Allowances".to_owned());
|
||||
|
||||
let req = QueryAllowancesRequest {
|
||||
grantee,
|
||||
pagination,
|
||||
};
|
||||
|
||||
let res = self
|
||||
.make_abci_query::<ProtoQueryAllowancesRequest, ProtoQueryAllowancesResponse>(
|
||||
path,
|
||||
req.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(res.try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<T> FeegrantQueryClient for T where T: CosmWasmClient {}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod feegrant;
|
||||
pub mod slashing;
|
||||
pub mod staking;
|
||||
|
||||
|
||||
@@ -102,6 +102,16 @@ pub struct Config {
|
||||
pub(crate) simulated_gas_multiplier: f32,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn chain_details(&self) -> &ChainDetails {
|
||||
&self.chain_details
|
||||
}
|
||||
|
||||
pub fn contracts(&self) -> &TypedNymContracts {
|
||||
&self.contracts
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result<Self, NyxdError> {
|
||||
Ok(Config {
|
||||
@@ -315,6 +325,10 @@ impl<C, S> NyxdClient<C, S> {
|
||||
pub fn get_nym_contracts(&self) -> TypedNymContracts {
|
||||
self.config.contracts.clone()
|
||||
}
|
||||
|
||||
pub fn get_chain_details(&self) -> ChainDetails {
|
||||
self.config.chain_details.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, S> NymContractsProvider for NyxdClient<C, S> {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_api_requests::models::DeclaredRolesV1;
|
||||
use nym_api_requests::models::described::type_translation::DeclaredRolesV1;
|
||||
use nym_api_requests::nym_nodes::SkimmedNodeV1;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
pub use nym_mixnet_contract_common::LegacyMixLayer;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
|
||||
use nym_sphinx_types::Node as SphinxNode;
|
||||
@@ -12,8 +13,6 @@ use std::fmt::Debug;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use thiserror::Error;
|
||||
|
||||
pub use nym_mixnet_contract_common::LegacyMixLayer;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RoutingNodeError {
|
||||
#[error("node {node_id} ('{identity}') has not provided any valid ip addresses")]
|
||||
|
||||
@@ -7,9 +7,9 @@ use crate::measurements::{Config, PacketListener, PacketSender};
|
||||
use crate::models::VerlocNodeResult;
|
||||
use futures::StreamExt;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use nym_api_requests::models::described::v1::NymNodeDescriptionV1;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_validator_client::models::NymNodeDescriptionV1;
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
|
||||
@@ -9,11 +9,7 @@ use utoipa::ToSchema;
|
||||
pub mod type_translation;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
|
||||
// don't break existing imports
|
||||
pub use type_translation::*;
|
||||
pub use v1::*;
|
||||
pub use v2::*;
|
||||
pub mod v3;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
pub struct NoiseDetails {
|
||||
|
||||
@@ -77,7 +77,7 @@ pub struct AnnouncePortsV1 {
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Serialize, Deserialize, schemars::JsonSchema, ToSchema, PartialEq,
|
||||
)]
|
||||
pub struct AuxiliaryDetailsV1 {
|
||||
pub struct NymNodeAuxiliaryDetailsV1 {
|
||||
/// Optional ISO 3166 alpha-2 two-letter country code of the node's **physical** location
|
||||
#[schema(example = "PL", value_type = Option<String>)]
|
||||
#[schemars(with = "Option<String>")]
|
||||
@@ -395,9 +395,11 @@ impl From<nym_node_requests::api::v1::node::models::AnnouncePorts> for AnnounceP
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1> for AuxiliaryDetailsV1 {
|
||||
impl From<nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1>
|
||||
for NymNodeAuxiliaryDetailsV1
|
||||
{
|
||||
fn from(value: nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1) -> Self {
|
||||
AuxiliaryDetailsV1 {
|
||||
NymNodeAuxiliaryDetailsV1 {
|
||||
location: value.location,
|
||||
announce_ports: value.announce_ports.into(),
|
||||
accepted_operator_terms_and_conditions: value.accepted_operator_terms_and_conditions,
|
||||
@@ -405,6 +407,33 @@ impl From<nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1> for Auxi
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1>
|
||||
for super::v3::NymNodeAuxiliaryDetailsV3
|
||||
{
|
||||
fn from(value: nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1) -> Self {
|
||||
super::v3::NymNodeAuxiliaryDetailsV3 {
|
||||
location: value.location,
|
||||
// v1 wire predates the chain-address field
|
||||
address: None,
|
||||
announce_ports: value.announce_ports.into(),
|
||||
accepted_operator_terms_and_conditions: value.accepted_operator_terms_and_conditions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_node_requests::api::v2::node::models::AuxiliaryDetailsV2>
|
||||
for super::v3::NymNodeAuxiliaryDetailsV3
|
||||
{
|
||||
fn from(value: nym_node_requests::api::v2::node::models::AuxiliaryDetailsV2) -> Self {
|
||||
super::v3::NymNodeAuxiliaryDetailsV3 {
|
||||
location: value.location,
|
||||
address: Some(value.address),
|
||||
announce_ports: value.announce_ports.into(),
|
||||
accepted_operator_terms_and_conditions: value.accepted_operator_terms_and_conditions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_node_requests::api::v1::node::models::NodeRoles> for DeclaredRolesV1 {
|
||||
fn from(value: nym_node_requests::api::v1::node::models::NodeRoles) -> Self {
|
||||
DeclaredRolesV1 {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::{
|
||||
AuthenticatorDetailsV1, AuxiliaryDetailsV1, BinaryBuildInformationOwned, DeclaredRolesV1,
|
||||
HostInformationV1, IpPacketRouterDetailsV1, NetworkRequesterDetailsV1,
|
||||
OffsetDateTimeJsonSchemaWrapper, WebSocketsV1, WireguardDetailsV1,
|
||||
use crate::models::described::type_translation::{
|
||||
AuthenticatorDetailsV1, DeclaredRolesV1, HostInformationV1, IpPacketRouterDetailsV1,
|
||||
NetworkRequesterDetailsV1, NymNodeAuxiliaryDetailsV1, WebSocketsV1, WireguardDetailsV1,
|
||||
};
|
||||
use crate::models::{BinaryBuildInformationOwned, OffsetDateTimeJsonSchemaWrapper};
|
||||
use crate::nym_nodes::{BasicEntryInformation, NodeRole, SemiSkimmedNodeV1, SkimmedNodeV1};
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
@@ -160,7 +160,7 @@ pub struct NymNodeDataV1 {
|
||||
pub declared_role: DeclaredRolesV1,
|
||||
|
||||
#[serde(default)]
|
||||
pub auxiliary_details: AuxiliaryDetailsV1,
|
||||
pub auxiliary_details: NymNodeAuxiliaryDetailsV1,
|
||||
|
||||
// TODO: do we really care about ALL build info or just the version?
|
||||
pub build_information: BinaryBuildInformationOwned,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::{
|
||||
AuthenticatorDetailsV1, AuxiliaryDetailsV1, BinaryBuildInformationOwned, DeclaredRolesV1,
|
||||
DescribedNodeTypeV1, HostInformationV1, HostKeysV1, IpPacketRouterDetailsV1,
|
||||
LewesProtocolDetailsV1, NetworkRequesterDetailsV1, NymNodeDataV1, NymNodeDescriptionV1,
|
||||
OffsetDateTimeJsonSchemaWrapper, SphinxKeyV1, WebSocketsV1, WireguardDetailsV1,
|
||||
};
|
||||
use crate::nym_nodes::{
|
||||
BasicEntryInformation, NodeRole, SemiSkimmedNodeV1, SemiSkimmedNodeV3, SkimmedNodeV1,
|
||||
use crate::models::described::type_translation::{
|
||||
AnnouncePortsV1, AuthenticatorDetailsV1, DeclaredRolesV1, HostInformationV1, HostKeysV1,
|
||||
IpPacketRouterDetailsV1, LewesProtocolDetailsV1, NetworkRequesterDetailsV1,
|
||||
NymNodeAuxiliaryDetailsV1, SphinxKeyV1, WebSocketsV1, WireguardDetailsV1,
|
||||
};
|
||||
use crate::models::described::v1::{DescribedNodeTypeV1, NymNodeDataV1, NymNodeDescriptionV1};
|
||||
use crate::models::{BinaryBuildInformationOwned, OffsetDateTimeJsonSchemaWrapper};
|
||||
use crate::nym_nodes::{BasicEntryInformation, NodeRole, SkimmedNodeV1};
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
@@ -22,7 +21,8 @@ use utoipa::ToSchema;
|
||||
// no changes for the following types
|
||||
pub type HostInformationV2 = HostInformationV1;
|
||||
pub type DeclaredRolesV2 = DeclaredRolesV1;
|
||||
pub type AuxiliaryDetailsV2 = AuxiliaryDetailsV1;
|
||||
pub type AnnouncePortsV2 = AnnouncePortsV1;
|
||||
pub type NymNodeAuxiliaryDetailsV2 = NymNodeAuxiliaryDetailsV1;
|
||||
pub type NetworkRequesterDetailsV2 = NetworkRequesterDetailsV1;
|
||||
pub type IpPacketRouterDetailsV2 = IpPacketRouterDetailsV1;
|
||||
pub type AuthenticatorDetailsV2 = AuthenticatorDetailsV1;
|
||||
@@ -117,44 +117,6 @@ impl NymNodeDescriptionV2 {
|
||||
performance,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_semi_skimmed_node(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
role: NodeRole,
|
||||
performance: Performance,
|
||||
) -> SemiSkimmedNodeV1 {
|
||||
let skimmed_node = self.to_skimmed_node(current_rotation_id, role, performance);
|
||||
|
||||
SemiSkimmedNodeV1 {
|
||||
basic: skimmed_node,
|
||||
x25519_noise_versioned_key: self
|
||||
.description
|
||||
.host_information
|
||||
.keys
|
||||
.x25519_versioned_noise,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_semi_skimmed_node_v3(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
role: NodeRole,
|
||||
performance: Performance,
|
||||
) -> SemiSkimmedNodeV3 {
|
||||
let skimmed_node = self.to_skimmed_node(current_rotation_id, role, performance);
|
||||
|
||||
SemiSkimmedNodeV3 {
|
||||
basic: skimmed_node,
|
||||
noise_key: self
|
||||
.description
|
||||
.host_information
|
||||
.keys
|
||||
.x25519_versioned_noise,
|
||||
build_version: self.description.build_information.build_version.clone(),
|
||||
lp: self.description.lewes_protocol.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to whoever is thinking of modifying this struct.
|
||||
@@ -172,7 +134,7 @@ pub struct NymNodeDataV2 {
|
||||
pub declared_role: DeclaredRolesV2,
|
||||
|
||||
#[serde(default)]
|
||||
pub auxiliary_details: AuxiliaryDetailsV2,
|
||||
pub auxiliary_details: NymNodeAuxiliaryDetailsV2,
|
||||
|
||||
// TODO: do we really care about ALL build info or just the version?
|
||||
pub build_information: BinaryBuildInformationOwned,
|
||||
@@ -266,117 +228,3 @@ impl From<NymNodeDescriptionV1> for NymNodeDescriptionV2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "mock-fixtures"))]
|
||||
pub fn mock_nym_node_description(seed: u64) -> NymNodeDescriptionV2 {
|
||||
use nym_node_requests::api::v1::lewes_protocol::models::{LPHashFunction, LPKEM};
|
||||
use nym_test_utils::helpers::{u64_seeded_rng, RngCore};
|
||||
|
||||
let mut rng = u64_seeded_rng(seed);
|
||||
|
||||
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
|
||||
// just reuse the same x25519 key for everything - this is just a data mock
|
||||
let x25519 = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let mut dummy_kems = std::collections::BTreeMap::new();
|
||||
for kem in [LPKEM::McEliece, LPKEM::McEliece] {
|
||||
let mut kem_digests = std::collections::BTreeMap::new();
|
||||
for (i, sf) in [
|
||||
LPHashFunction::Blake3,
|
||||
LPHashFunction::Shake128,
|
||||
LPHashFunction::Shake256,
|
||||
LPHashFunction::Sha256,
|
||||
]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
kem_digests.insert(*sf, hex::encode([((seed + i as u64) % 256) as u8; 32]));
|
||||
}
|
||||
dummy_kems.insert(kem, kem_digests);
|
||||
}
|
||||
|
||||
// make sure the serialisation stays the same and signature is still valid
|
||||
let dummy_lp = nym_node_requests::api::v1::lewes_protocol::models::LewesProtocol {
|
||||
enabled: false,
|
||||
control_port: 123,
|
||||
data_port: 345,
|
||||
x25519: (*x25519.public_key()).into(),
|
||||
kem_keys: dummy_kems,
|
||||
};
|
||||
let dummy_signed_lp =
|
||||
nym_node_requests::api::SignedLewesProtocol::new(dummy_lp, ed25519.private_key()).unwrap();
|
||||
|
||||
NymNodeDescriptionV2 {
|
||||
node_id: rng.next_u32(),
|
||||
contract_node_type: DescribedNodeTypeV1::NymNode,
|
||||
description: NymNodeDataV2 {
|
||||
last_polled: time::OffsetDateTime::from_unix_timestamp(1767225600)
|
||||
.unwrap()
|
||||
.into(),
|
||||
host_information: HostInformationV2 {
|
||||
ip_address: vec![
|
||||
std::net::IpAddr::V4(std::net::Ipv4Addr::new(1, 2, 3, (seed % 255) as u8)),
|
||||
],
|
||||
hostname: Some(format!("my-awesome-node-{seed}.com")),
|
||||
keys: HostKeysV2 {
|
||||
ed25519: *ed25519.public_key(),
|
||||
x25519: *x25519.public_key(),
|
||||
current_x25519_sphinx_key: SphinxKeyV2 {
|
||||
rotation_id: 123,
|
||||
public_key: *x25519.public_key(),
|
||||
},
|
||||
pre_announced_x25519_sphinx_key: None,
|
||||
x25519_versioned_noise: Some(VersionedNoiseKeyV2 {
|
||||
supported_version: nym_noise_keys::NoiseVersion::V1,
|
||||
x25519_pubkey: *x25519.public_key(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
declared_role: DeclaredRolesV2 {
|
||||
mixnode: false,
|
||||
entry: true,
|
||||
exit_nr: true,
|
||||
exit_ipr: true,
|
||||
},
|
||||
auxiliary_details: AuxiliaryDetailsV2 {
|
||||
location: Some(celes::Country::switzerland()),
|
||||
announce_ports: Default::default(),
|
||||
accepted_operator_terms_and_conditions: true,
|
||||
},
|
||||
build_information: BinaryBuildInformationOwned {
|
||||
binary_name: "dummy-node".to_string(),
|
||||
build_timestamp: "2021-02-23T20:14:46.558472672+00:00".to_string(),
|
||||
build_version: "0.1.0-9-g46f83e1".to_string(),
|
||||
commit_sha: "46f83e112520533338245862d366f6a02cef07d4".to_string(),
|
||||
commit_timestamp: "2021-02-23T08:08:02-05:00".to_string(),
|
||||
commit_branch: "master".to_string(),
|
||||
rustc_version: "1.52.0-nightly".to_string(),
|
||||
rustc_channel: "nightly".to_string(),
|
||||
cargo_profile: "release".to_string(),
|
||||
cargo_triple: "wasm32-unknown-unknown".to_string(),
|
||||
},
|
||||
network_requester: Some(NetworkRequesterDetailsV2 {
|
||||
address: "FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve".to_string(),
|
||||
uses_exit_policy: true,
|
||||
}),
|
||||
ip_packet_router: Some(IpPacketRouterDetailsV2 {
|
||||
address: "FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve".to_string(),
|
||||
}),
|
||||
authenticator: Some(AuthenticatorDetailsV2 {
|
||||
address: "FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve".to_string(),
|
||||
}),
|
||||
wireguard: Some(WireguardDetailsV2 {
|
||||
port: 123,
|
||||
tunnel_port: 234,
|
||||
metadata_port: 456,
|
||||
public_key: x25519.public_key().to_base58_string(),
|
||||
}),
|
||||
lewes_protocol: Some(dummy_signed_lp.into()),
|
||||
mixnet_websockets: WebSocketsV2 {
|
||||
ws_port: 9000,
|
||||
wss_port: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::models::described::type_translation::LewesProtocolDetailsV1;
|
||||
use crate::models::described::v1::NymNodeDescriptionV1;
|
||||
use crate::models::described::v2::{
|
||||
AnnouncePortsV2, AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2,
|
||||
HostInformationV2, HostKeysV2, IpPacketRouterDetailsV2, NetworkRequesterDetailsV2,
|
||||
NymNodeAuxiliaryDetailsV2, NymNodeDataV2, NymNodeDescriptionV2, SphinxKeyV2,
|
||||
VersionedNoiseKeyV2, WebSocketsV2, WireguardDetailsV2,
|
||||
};
|
||||
use crate::models::{BinaryBuildInformationOwned, OffsetDateTimeJsonSchemaWrapper};
|
||||
use crate::nym_nodes::{
|
||||
BasicEntryInformation, NodeRole, SemiSkimmedNodeV1, SemiSkimmedNodeV3, SkimmedNodeV1,
|
||||
};
|
||||
use celes::Country;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
// no changes for the following types
|
||||
pub type AnnouncePortsV3 = AnnouncePortsV2;
|
||||
pub type HostInformationV3 = HostInformationV2;
|
||||
pub type DeclaredRolesV3 = DeclaredRolesV2;
|
||||
pub type NetworkRequesterDetailsV3 = NetworkRequesterDetailsV2;
|
||||
pub type IpPacketRouterDetailsV3 = IpPacketRouterDetailsV2;
|
||||
pub type AuthenticatorDetailsV3 = AuthenticatorDetailsV2;
|
||||
pub type WireguardDetailsV3 = WireguardDetailsV2;
|
||||
pub type WebSocketsV3 = WebSocketsV2;
|
||||
pub type DescribedNodeTypeV3 = DescribedNodeTypeV2;
|
||||
pub type HostKeysV3 = HostKeysV2;
|
||||
pub type SphinxKeyV3 = SphinxKeyV2;
|
||||
pub type VersionedNoiseKeyV3 = VersionedNoiseKeyV2;
|
||||
pub type LewesProtocolDetailsV3 = LewesProtocolDetailsV1;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
pub struct NymNodeDescriptionV3 {
|
||||
#[schema(value_type = u32)]
|
||||
pub node_id: NodeId,
|
||||
pub contract_node_type: DescribedNodeTypeV3,
|
||||
pub description: NymNodeDataV3,
|
||||
}
|
||||
|
||||
impl NymNodeDescriptionV3 {
|
||||
pub fn version(&self) -> &str {
|
||||
&self.description.build_information.build_version
|
||||
}
|
||||
|
||||
pub fn entry_information(&self) -> BasicEntryInformation {
|
||||
BasicEntryInformation {
|
||||
hostname: self.description.host_information.hostname.clone(),
|
||||
ws_port: self.description.mixnet_websockets.ws_port,
|
||||
wss_port: self.description.mixnet_websockets.wss_port,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ed25519_identity_key(&self) -> ed25519::PublicKey {
|
||||
self.description.host_information.keys.ed25519
|
||||
}
|
||||
|
||||
pub fn current_sphinx_key(&self, current_rotation_id: u32) -> x25519::PublicKey {
|
||||
let keys = &self.description.host_information.keys;
|
||||
|
||||
if keys.current_x25519_sphinx_key.rotation_id == u32::MAX {
|
||||
// legacy case (i.e. node doesn't support rotation)
|
||||
return keys.current_x25519_sphinx_key.public_key;
|
||||
}
|
||||
|
||||
if current_rotation_id == keys.current_x25519_sphinx_key.rotation_id {
|
||||
// it's the 'current' key
|
||||
return keys.current_x25519_sphinx_key.public_key;
|
||||
}
|
||||
|
||||
if let Some(pre_announced) = &keys.pre_announced_x25519_sphinx_key {
|
||||
if pre_announced.rotation_id == current_rotation_id {
|
||||
return pre_announced.public_key;
|
||||
}
|
||||
}
|
||||
|
||||
warn!(
|
||||
"unexpected key rotation {current_rotation_id} for node {}",
|
||||
self.node_id
|
||||
);
|
||||
// this should never be reached, but just in case, return the fallback option
|
||||
keys.current_x25519_sphinx_key.public_key
|
||||
}
|
||||
|
||||
pub fn to_skimmed_node(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
role: NodeRole,
|
||||
performance: Performance,
|
||||
) -> SkimmedNodeV1 {
|
||||
let keys = &self.description.host_information.keys;
|
||||
let entry = if self.description.declared_role.entry {
|
||||
Some(self.entry_information())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
SkimmedNodeV1 {
|
||||
node_id: self.node_id,
|
||||
ed25519_identity_pubkey: keys.ed25519,
|
||||
ip_addresses: self.description.host_information.ip_address.clone(),
|
||||
mix_port: self.description.mix_port(),
|
||||
x25519_sphinx_pubkey: self.current_sphinx_key(current_rotation_id),
|
||||
// we can't use the declared roles, we have to take whatever was provided in the contract.
|
||||
// why? say this node COULD operate as an exit, but it might be the case the contract decided
|
||||
// to assign it an ENTRY role only. we have to use that one instead.
|
||||
role,
|
||||
supported_roles: self.description.declared_role,
|
||||
entry,
|
||||
performance,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_semi_skimmed_node(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
role: NodeRole,
|
||||
performance: Performance,
|
||||
) -> SemiSkimmedNodeV1 {
|
||||
let skimmed_node = self.to_skimmed_node(current_rotation_id, role, performance);
|
||||
|
||||
SemiSkimmedNodeV1 {
|
||||
basic: skimmed_node,
|
||||
x25519_noise_versioned_key: self
|
||||
.description
|
||||
.host_information
|
||||
.keys
|
||||
.x25519_versioned_noise,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_semi_skimmed_node_v3(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
role: NodeRole,
|
||||
performance: Performance,
|
||||
) -> SemiSkimmedNodeV3 {
|
||||
let skimmed_node = self.to_skimmed_node(current_rotation_id, role, performance);
|
||||
|
||||
SemiSkimmedNodeV3 {
|
||||
basic: skimmed_node,
|
||||
noise_key: self
|
||||
.description
|
||||
.host_information
|
||||
.keys
|
||||
.x25519_versioned_noise,
|
||||
build_version: self.description.build_information.build_version.clone(),
|
||||
lp: self.description.lewes_protocol.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NymNodeDescriptionV3> for NymNodeDescriptionV2 {
|
||||
fn from(value: NymNodeDescriptionV3) -> Self {
|
||||
NymNodeDescriptionV2 {
|
||||
node_id: value.node_id,
|
||||
contract_node_type: value.contract_node_type,
|
||||
description: value.description.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NymNodeDescriptionV3> for NymNodeDescriptionV1 {
|
||||
fn from(value: NymNodeDescriptionV3) -> Self {
|
||||
NymNodeDescriptionV1 {
|
||||
node_id: value.node_id,
|
||||
contract_node_type: value.contract_node_type,
|
||||
description: NymNodeDataV2::from(value.description).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
pub struct NymNodeDataV3 {
|
||||
#[serde(default)]
|
||||
pub last_polled: OffsetDateTimeJsonSchemaWrapper,
|
||||
|
||||
pub host_information: HostInformationV3,
|
||||
|
||||
#[serde(default)]
|
||||
pub declared_role: DeclaredRolesV3,
|
||||
|
||||
#[serde(default)]
|
||||
pub auxiliary_details: NymNodeAuxiliaryDetailsV3,
|
||||
|
||||
// TODO: do we really care about ALL build info or just the version?
|
||||
pub build_information: BinaryBuildInformationOwned,
|
||||
|
||||
#[serde(default)]
|
||||
pub network_requester: Option<NetworkRequesterDetailsV3>,
|
||||
|
||||
#[serde(default)]
|
||||
pub ip_packet_router: Option<IpPacketRouterDetailsV3>,
|
||||
|
||||
#[serde(default)]
|
||||
pub authenticator: Option<AuthenticatorDetailsV3>,
|
||||
|
||||
#[serde(default)]
|
||||
pub wireguard: Option<WireguardDetailsV3>,
|
||||
|
||||
// for now we only care about their ws/wss situation, nothing more
|
||||
pub mixnet_websockets: WebSocketsV3,
|
||||
|
||||
#[serde(default)]
|
||||
pub lewes_protocol: Option<LewesProtocolDetailsV3>,
|
||||
}
|
||||
|
||||
impl NymNodeDataV3 {
|
||||
pub fn mix_port(&self) -> u16 {
|
||||
self.auxiliary_details
|
||||
.announce_ports
|
||||
.mix_port
|
||||
.unwrap_or(DEFAULT_MIX_LISTENING_PORT)
|
||||
}
|
||||
|
||||
pub fn verloc_port(&self) -> u16 {
|
||||
self.auxiliary_details
|
||||
.announce_ports
|
||||
.verloc_port
|
||||
.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NymNodeDataV3> for NymNodeDataV2 {
|
||||
fn from(data: NymNodeDataV3) -> Self {
|
||||
NymNodeDataV2 {
|
||||
last_polled: data.last_polled,
|
||||
host_information: data.host_information,
|
||||
declared_role: data.declared_role,
|
||||
auxiliary_details: data.auxiliary_details.into(),
|
||||
build_information: data.build_information,
|
||||
network_requester: data.network_requester,
|
||||
ip_packet_router: data.ip_packet_router,
|
||||
authenticator: data.authenticator,
|
||||
wireguard: data.wireguard,
|
||||
mixnet_websockets: data.mixnet_websockets,
|
||||
lewes_protocol: data.lewes_protocol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Default, Serialize, Deserialize, schemars::JsonSchema, ToSchema, PartialEq,
|
||||
)]
|
||||
pub struct NymNodeAuxiliaryDetailsV3 {
|
||||
/// Optional ISO 3166 alpha-2 two-letter country code of the node's **physical** location
|
||||
#[schema(example = "PL", value_type = Option<String>)]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(length(equal = 2))]
|
||||
pub location: Option<Country>,
|
||||
|
||||
/// On-chain address of this node
|
||||
#[serde(default)]
|
||||
pub address: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub announce_ports: AnnouncePortsV3,
|
||||
|
||||
/// Specifies whether this node operator has agreed to the terms and conditions
|
||||
/// as defined at <https://nymtech.net/terms-and-conditions/operators/v1.0.0>
|
||||
// make sure to include the default deserialisation as this field hasn't existed when the struct was first created
|
||||
#[serde(default)]
|
||||
pub accepted_operator_terms_and_conditions: bool,
|
||||
}
|
||||
|
||||
impl From<NymNodeAuxiliaryDetailsV3> for NymNodeAuxiliaryDetailsV2 {
|
||||
fn from(value: NymNodeAuxiliaryDetailsV3) -> Self {
|
||||
NymNodeAuxiliaryDetailsV2 {
|
||||
location: value.location,
|
||||
announce_ports: value.announce_ports,
|
||||
accepted_operator_terms_and_conditions: value.accepted_operator_terms_and_conditions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "mock-fixtures"))]
|
||||
pub fn mock_nym_node_description(seed: u64) -> NymNodeDescriptionV3 {
|
||||
use nym_node_requests::api::v1::lewes_protocol::models::{LPHashFunction, LPKEM};
|
||||
use nym_test_utils::helpers::{u64_seeded_rng, RngCore};
|
||||
|
||||
let mut rng = u64_seeded_rng(seed);
|
||||
|
||||
let ed25519 = nym_crypto::asymmetric::ed25519::KeyPair::new(&mut rng);
|
||||
|
||||
// just reuse the same x25519 key for everything - this is just a data mock
|
||||
let x25519 = nym_crypto::asymmetric::x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let mut dummy_kems = std::collections::BTreeMap::new();
|
||||
for kem in [LPKEM::McEliece, LPKEM::McEliece] {
|
||||
let mut kem_digests = std::collections::BTreeMap::new();
|
||||
for (i, sf) in [
|
||||
LPHashFunction::Blake3,
|
||||
LPHashFunction::Shake128,
|
||||
LPHashFunction::Shake256,
|
||||
LPHashFunction::Sha256,
|
||||
]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
kem_digests.insert(*sf, hex::encode([((seed + i as u64) % 256) as u8; 32]));
|
||||
}
|
||||
dummy_kems.insert(kem, kem_digests);
|
||||
}
|
||||
|
||||
// make sure the serialisation stays the same and signature is still valid
|
||||
let dummy_lp = nym_node_requests::api::v1::lewes_protocol::models::LewesProtocol {
|
||||
enabled: false,
|
||||
control_port: 123,
|
||||
data_port: 345,
|
||||
x25519: (*x25519.public_key()).into(),
|
||||
kem_keys: dummy_kems,
|
||||
};
|
||||
let dummy_signed_lp =
|
||||
nym_node_requests::api::SignedLewesProtocol::new(dummy_lp, ed25519.private_key()).unwrap();
|
||||
|
||||
NymNodeDescriptionV3 {
|
||||
node_id: rng.next_u32(),
|
||||
contract_node_type: DescribedNodeTypeV3::NymNode,
|
||||
description: NymNodeDataV3 {
|
||||
last_polled: time::OffsetDateTime::from_unix_timestamp(1767225600)
|
||||
.unwrap()
|
||||
.into(),
|
||||
host_information: HostInformationV3 {
|
||||
ip_address: vec![
|
||||
std::net::IpAddr::V4(std::net::Ipv4Addr::new(1, 2, 3, (seed % 255) as u8)),
|
||||
],
|
||||
hostname: Some(format!("my-awesome-node-{seed}.com")),
|
||||
keys: HostKeysV3 {
|
||||
ed25519: *ed25519.public_key(),
|
||||
x25519: *x25519.public_key(),
|
||||
current_x25519_sphinx_key: SphinxKeyV3 {
|
||||
rotation_id: 123,
|
||||
public_key: *x25519.public_key(),
|
||||
},
|
||||
pre_announced_x25519_sphinx_key: None,
|
||||
x25519_versioned_noise: Some(VersionedNoiseKeyV3 {
|
||||
supported_version: nym_noise_keys::NoiseVersion::V1,
|
||||
x25519_pubkey: *x25519.public_key(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
declared_role: DeclaredRolesV3 {
|
||||
mixnode: false,
|
||||
entry: true,
|
||||
exit_nr: true,
|
||||
exit_ipr: true,
|
||||
},
|
||||
auxiliary_details: NymNodeAuxiliaryDetailsV3 {
|
||||
location: Some(celes::Country::switzerland()),
|
||||
address: Some("n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf".to_string()),
|
||||
announce_ports: Default::default(),
|
||||
accepted_operator_terms_and_conditions: true,
|
||||
},
|
||||
build_information: BinaryBuildInformationOwned {
|
||||
binary_name: "dummy-node".to_string(),
|
||||
build_timestamp: "2021-02-23T20:14:46.558472672+00:00".to_string(),
|
||||
build_version: "0.1.0-9-g46f83e1".to_string(),
|
||||
commit_sha: "46f83e112520533338245862d366f6a02cef07d4".to_string(),
|
||||
commit_timestamp: "2021-02-23T08:08:02-05:00".to_string(),
|
||||
commit_branch: "master".to_string(),
|
||||
rustc_version: "1.52.0-nightly".to_string(),
|
||||
rustc_channel: "nightly".to_string(),
|
||||
cargo_profile: "release".to_string(),
|
||||
cargo_triple: "wasm32-unknown-unknown".to_string(),
|
||||
},
|
||||
network_requester: Some(NetworkRequesterDetailsV3 {
|
||||
address: "FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve".to_string(),
|
||||
uses_exit_policy: true,
|
||||
}),
|
||||
ip_packet_router: Some(IpPacketRouterDetailsV3 {
|
||||
address: "FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve".to_string(),
|
||||
}),
|
||||
authenticator: Some(AuthenticatorDetailsV3 {
|
||||
address: "FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve".to_string(),
|
||||
}),
|
||||
wireguard: Some(WireguardDetailsV3 {
|
||||
port: 123,
|
||||
tunnel_port: 234,
|
||||
metadata_port: 456,
|
||||
public_key: x25519.public_key().to_base58_string(),
|
||||
}),
|
||||
lewes_protocol: Some(dummy_signed_lp.into()),
|
||||
mixnet_websockets: WebSocketsV3 {
|
||||
ws_port: 9000,
|
||||
wss_port: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ pub mod utility;
|
||||
// don't break existing imports
|
||||
pub use api_status::*;
|
||||
pub use circulating_supply::*;
|
||||
pub use described::*;
|
||||
pub use legacy::*;
|
||||
pub use mixnet::*;
|
||||
pub use network::*;
|
||||
|
||||
@@ -75,8 +75,6 @@ pub struct GatewayCoreStatusResponse {
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
pub use v3::*;
|
||||
|
||||
/// Request/response types for the v3 network-monitor flow, in which an orchestrator submits
|
||||
/// stress testing results to nym-api via signed batches.
|
||||
pub mod v3 {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::helpers::PlaceholderJsonSchemaImpl;
|
||||
use crate::models::DisplayRole;
|
||||
use crate::models::{CoinSchema, DisplayRole};
|
||||
use crate::pagination::PaginatedResponse;
|
||||
use cosmwasm_std::Decimal;
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
use nym_contracts_common::{IdentityKey, NaiveFloat};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
|
||||
@@ -419,7 +419,26 @@ pub struct NodeAnnotationV1 {
|
||||
pub detailed_performance: DetailedNodePerformanceV1,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/ChainInteractionCapabilitiesDetailed.ts"
|
||||
)
|
||||
)]
|
||||
pub struct ChainInteractionCapabilitiesDetailed {
|
||||
#[schema(value_type = CoinSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "Coin"))]
|
||||
pub on_chain_balance: Coin,
|
||||
|
||||
// later to be expanded with information on whether the grant would cover
|
||||
// cosmwasm executemsg, but for now we assume any feegrant is sufficient
|
||||
pub is_feegrant_grantee: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
@@ -431,6 +450,8 @@ pub struct NodeAnnotationV1 {
|
||||
pub struct NodeAnnotationV2 {
|
||||
pub current_role: Option<DisplayRole>,
|
||||
|
||||
pub chain_interaction_capabilities: Option<ChainInteractionCapabilitiesDetailed>,
|
||||
|
||||
pub detailed_performance: DetailedNodePerformanceV2,
|
||||
}
|
||||
|
||||
@@ -449,7 +470,7 @@ impl From<NodeAnnotationV2> for NodeAnnotationV1 {
|
||||
detailed_performance: DetailedNodePerformanceV1 {
|
||||
performance_score: value.detailed_performance.performance_score,
|
||||
routing_score: value.detailed_performance.routing_score,
|
||||
config_score: value.detailed_performance.config_score,
|
||||
config_score: value.detailed_performance.config_score.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -470,14 +491,14 @@ pub struct DetailedNodePerformanceV1 {
|
||||
pub performance_score: f64,
|
||||
|
||||
pub routing_score: RoutingScore,
|
||||
pub config_score: ConfigScore,
|
||||
pub config_score: ConfigScoreV1,
|
||||
}
|
||||
|
||||
impl DetailedNodePerformanceV1 {
|
||||
pub fn new(
|
||||
performance_score: f64,
|
||||
routing_score: RoutingScore,
|
||||
config_score: ConfigScore,
|
||||
config_score: ConfigScoreV1,
|
||||
) -> DetailedNodePerformanceV1 {
|
||||
Self {
|
||||
performance_score,
|
||||
@@ -508,7 +529,7 @@ pub struct DetailedNodePerformanceV2 {
|
||||
pub performance_score: f64,
|
||||
|
||||
pub routing_score: RoutingScore,
|
||||
pub config_score: ConfigScore,
|
||||
pub config_score: ConfigScoreV2,
|
||||
pub stress_testing_score: StressTestingScore,
|
||||
}
|
||||
|
||||
@@ -516,7 +537,7 @@ impl DetailedNodePerformanceV2 {
|
||||
pub fn new(
|
||||
performance_score: f64,
|
||||
routing_score: RoutingScore,
|
||||
config_score: ConfigScore,
|
||||
config_score: ConfigScoreV2,
|
||||
stress_testing_score: StressTestingScore,
|
||||
) -> DetailedNodePerformanceV2 {
|
||||
Self {
|
||||
@@ -588,10 +609,13 @@ impl StressTestingScore {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/ConfigScore.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/ConfigScoreV2.ts"
|
||||
)
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct ConfigScore {
|
||||
pub struct ConfigScoreV2 {
|
||||
/// Total score after taking all the criteria into consideration
|
||||
pub score: f64,
|
||||
|
||||
@@ -599,45 +623,111 @@ pub struct ConfigScore {
|
||||
pub self_described_api_available: bool,
|
||||
pub accepted_terms_and_conditions: bool,
|
||||
pub runs_nym_node_binary: bool,
|
||||
|
||||
/// Describes the node is capable of sending chain/contract transactions
|
||||
pub chain_interaction_capabilities: ChainInteractionCapabilities,
|
||||
}
|
||||
|
||||
impl ConfigScore {
|
||||
impl ConfigScoreV2 {
|
||||
pub fn new(
|
||||
score: f64,
|
||||
versions_behind: u32,
|
||||
accepted_terms_and_conditions: bool,
|
||||
runs_nym_node_binary: bool,
|
||||
) -> ConfigScore {
|
||||
chain_interaction_capabilities: ChainInteractionCapabilities,
|
||||
) -> ConfigScoreV2 {
|
||||
Self {
|
||||
score,
|
||||
versions_behind: Some(versions_behind),
|
||||
self_described_api_available: true,
|
||||
accepted_terms_and_conditions,
|
||||
runs_nym_node_binary,
|
||||
chain_interaction_capabilities,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bad_semver() -> ConfigScore {
|
||||
ConfigScore {
|
||||
pub fn bad_semver() -> ConfigScoreV2 {
|
||||
ConfigScoreV2 {
|
||||
score: 0.0,
|
||||
versions_behind: None,
|
||||
self_described_api_available: true,
|
||||
accepted_terms_and_conditions: false,
|
||||
runs_nym_node_binary: false,
|
||||
chain_interaction_capabilities: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unavailable() -> ConfigScore {
|
||||
ConfigScore {
|
||||
pub fn unavailable() -> ConfigScoreV2 {
|
||||
ConfigScoreV2 {
|
||||
score: 0.0,
|
||||
versions_behind: None,
|
||||
self_described_api_available: false,
|
||||
accepted_terms_and_conditions: false,
|
||||
runs_nym_node_binary: false,
|
||||
chain_interaction_capabilities: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/ChainInteractionCapabilities.ts"
|
||||
)
|
||||
)]
|
||||
pub struct ChainInteractionCapabilities {
|
||||
pub has_sufficient_tokens: bool,
|
||||
pub is_fee_grant_grantee: bool,
|
||||
}
|
||||
|
||||
impl ChainInteractionCapabilities {
|
||||
pub fn new(has_sufficient_tokens: bool, is_fee_grant_grantee: bool) -> Self {
|
||||
Self {
|
||||
has_sufficient_tokens,
|
||||
is_fee_grant_grantee,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_send_transactions(&self) -> bool {
|
||||
self.has_sufficient_tokens || self.is_fee_grant_grantee
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigScoreV2> for ConfigScoreV1 {
|
||||
fn from(score_v2: ConfigScoreV2) -> ConfigScoreV1 {
|
||||
ConfigScoreV1 {
|
||||
score: score_v2.score,
|
||||
versions_behind: score_v2.versions_behind,
|
||||
self_described_api_available: score_v2.self_described_api_available,
|
||||
accepted_terms_and_conditions: score_v2.accepted_terms_and_conditions,
|
||||
runs_nym_node_binary: score_v2.runs_nym_node_binary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/ConfigScoreV1.ts"
|
||||
)
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct ConfigScoreV1 {
|
||||
/// Total score after taking all the criteria into consideration
|
||||
pub score: f64,
|
||||
|
||||
pub versions_behind: Option<u32>,
|
||||
pub self_described_api_available: bool,
|
||||
pub accepted_terms_and_conditions: bool,
|
||||
pub runs_nym_node_binary: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
@@ -653,7 +743,7 @@ pub struct AnnotationResponseV1 {
|
||||
pub annotation: Option<NodeAnnotationV1>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::{
|
||||
DeclaredRolesV1, LewesProtocolDetailsV1, NymNodeDataV1, OffsetDateTimeJsonSchemaWrapper,
|
||||
};
|
||||
use crate::models::described::type_translation::{DeclaredRolesV1, LewesProtocolDetailsV1};
|
||||
use crate::models::described::v1::NymNodeDataV1;
|
||||
use crate::models::OffsetDateTimeJsonSchemaWrapper;
|
||||
use crate::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
|
||||
use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
|
||||
|
||||
@@ -54,9 +54,9 @@ impl<T> SignedMessage<T> {
|
||||
// make sure only our types can implement this trait (to ensure infallible serialisation)
|
||||
pub(crate) mod sealed {
|
||||
use crate::ecash::models::*;
|
||||
use crate::models::network_monitor::v3::StressTestBatchSubmissionContent;
|
||||
use crate::models::{
|
||||
v3, ChainBlocksStatusResponseBody, DetailedSignersStatusResponseBody,
|
||||
SignersStatusResponseBody,
|
||||
ChainBlocksStatusResponseBody, DetailedSignersStatusResponseBody, SignersStatusResponseBody,
|
||||
};
|
||||
|
||||
pub trait Sealed {}
|
||||
@@ -75,5 +75,5 @@ pub(crate) mod sealed {
|
||||
impl Sealed for DetailedSignersStatusResponseBody {}
|
||||
|
||||
// v3 stress testing
|
||||
impl Sealed for v3::StressTestBatchSubmissionContent {}
|
||||
impl Sealed for StressTestBatchSubmissionContent {}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::node_describe_cache::cache::DescribedNodes;
|
||||
use crate::node_describe_cache::NodeDescriptionTopologyExt;
|
||||
use crate::node_status_api::NodeStatusCache;
|
||||
use crate::support::caching::cache::SharedCache;
|
||||
use nym_api_requests::models::{NodeAnnotationV2, NymNodeDescriptionV2};
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_api_requests::models::NodeAnnotationV2;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId};
|
||||
use nym_node_tester_utils::node::{NodeType, TestableNode};
|
||||
@@ -205,7 +206,7 @@ impl PacketPreparer {
|
||||
rng: &mut R,
|
||||
current_rotation_id: u32,
|
||||
node_statuses: &HashMap<NodeId, NodeAnnotationV2>,
|
||||
mixing_nym_nodes: impl Iterator<Item = &'a NymNodeDescriptionV2> + 'a,
|
||||
mixing_nym_nodes: impl Iterator<Item = &'a NymNodeDescriptionV3> + 'a,
|
||||
) -> HashMap<LegacyMixLayer, Vec<(RoutingNode, f64)>> {
|
||||
let mut layered_mixes = HashMap::new();
|
||||
|
||||
@@ -245,7 +246,7 @@ impl PacketPreparer {
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
node_statuses: &HashMap<NodeId, NodeAnnotationV2>,
|
||||
gateway_capable_nym_nodes: impl Iterator<Item = &'a NymNodeDescriptionV2> + 'a,
|
||||
gateway_capable_nym_nodes: impl Iterator<Item = &'a NymNodeDescriptionV3> + 'a,
|
||||
) -> Vec<(RoutingNode, f64)> {
|
||||
let mut gateways = Vec::new();
|
||||
|
||||
@@ -400,7 +401,7 @@ impl PacketPreparer {
|
||||
fn nym_node_to_routing_node(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
description: &NymNodeDescriptionV2,
|
||||
description: &NymNodeDescriptionV3,
|
||||
) -> Option<RoutingNode> {
|
||||
description.try_to_topology_node(current_rotation_id).ok()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::{DescribedNodeTypeV2, NymNodeDataV2, NymNodeDescriptionV2};
|
||||
use nym_api_requests::models::described::v3::{
|
||||
DescribedNodeTypeV3, NymNodeDataV3, NymNodeDescriptionV3,
|
||||
};
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -9,54 +11,54 @@ use std::net::IpAddr;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DescribedNodes {
|
||||
pub(crate) nodes: HashMap<NodeId, NymNodeDescriptionV2>,
|
||||
pub(crate) nodes: HashMap<NodeId, NymNodeDescriptionV3>,
|
||||
pub(crate) addresses_cache: HashMap<IpAddr, NodeId>,
|
||||
}
|
||||
|
||||
impl DescribedNodes {
|
||||
pub fn force_update(&mut self, node: NymNodeDescriptionV2) {
|
||||
pub fn force_update(&mut self, node: NymNodeDescriptionV3) {
|
||||
for ip in &node.description.host_information.ip_address {
|
||||
self.addresses_cache.insert(*ip, node.node_id);
|
||||
}
|
||||
self.nodes.insert(node.node_id, node);
|
||||
}
|
||||
|
||||
pub fn get_description(&self, node_id: &NodeId) -> Option<&NymNodeDataV2> {
|
||||
pub fn get_description(&self, node_id: &NodeId) -> Option<&NymNodeDataV3> {
|
||||
self.nodes.get(node_id).map(|n| &n.description)
|
||||
}
|
||||
|
||||
pub fn get_node(&self, node_id: &NodeId) -> Option<&NymNodeDescriptionV2> {
|
||||
pub fn get_node(&self, node_id: &NodeId) -> Option<&NymNodeDescriptionV3> {
|
||||
self.nodes.get(node_id)
|
||||
}
|
||||
|
||||
pub fn all_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV2> {
|
||||
pub fn all_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV3> {
|
||||
self.nodes.values()
|
||||
}
|
||||
|
||||
pub fn all_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV2> {
|
||||
pub fn all_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV3> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV2::NymNode)
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV3::NymNode)
|
||||
}
|
||||
|
||||
pub fn mixing_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV2> {
|
||||
pub fn mixing_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV3> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV2::NymNode)
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV3::NymNode)
|
||||
.filter(|n| n.description.declared_role.mixnode)
|
||||
}
|
||||
|
||||
pub fn entry_capable_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV2> {
|
||||
pub fn entry_capable_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV3> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV2::NymNode)
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV3::NymNode)
|
||||
.filter(|n| n.description.declared_role.entry)
|
||||
}
|
||||
|
||||
pub fn exit_capable_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV2> {
|
||||
pub fn exit_capable_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescriptionV3> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV2::NymNode)
|
||||
.filter(|n| n.contract_node_type == DescribedNodeTypeV3::NymNode)
|
||||
.filter(|n| n.description.declared_role.can_operate_exit_gateway())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::support::caching::cache::UninitialisedCache;
|
||||
use nym_api_requests::models::{NymNodeDescriptionV1, NymNodeDescriptionV2};
|
||||
use nym_api_requests::models::described::v1::NymNodeDescriptionV1;
|
||||
use nym_api_requests::models::described::v2::NymNodeDescriptionV2;
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_node_requests::api::client::NymNodeApiClientError;
|
||||
use nym_topology::node::RoutingNodeError;
|
||||
@@ -72,3 +74,15 @@ impl NodeDescriptionTopologyExt for NymNodeDescriptionV2 {
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDescriptionTopologyExt for NymNodeDescriptionV3 {
|
||||
fn try_to_topology_node(
|
||||
&self,
|
||||
current_rotation_id: u32,
|
||||
) -> Result<RoutingNode, RoutingNodeError> {
|
||||
// for the purposes of routing, performance is completely ignored,
|
||||
// so add dummy value and piggyback on existing conversion
|
||||
(&self.to_skimmed_node(current_rotation_id, Default::default(), Default::default()))
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
use crate::node_describe_cache::NodeDescribeCacheError;
|
||||
use futures::future::{maybe_done, MaybeDone};
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use nym_api_requests::models::{
|
||||
AuthenticatorDetailsV2, AuxiliaryDetailsV2, DeclaredRolesV2, HostInformationV2,
|
||||
IpPacketRouterDetailsV2, LewesProtocolDetailsV1, NetworkRequesterDetailsV2, NymNodeDataV2,
|
||||
WebSocketsV2, WireguardDetailsV2,
|
||||
use nym_api_requests::models::described::v3::NymNodeAuxiliaryDetailsV3;
|
||||
use nym_api_requests::models::described::v3::{
|
||||
AuthenticatorDetailsV3, DeclaredRolesV3, HostInformationV3, IpPacketRouterDetailsV3,
|
||||
LewesProtocolDetailsV3, NetworkRequesterDetailsV3, NymNodeDataV3, WebSocketsV3,
|
||||
WireguardDetailsV3,
|
||||
};
|
||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||
use nym_config::defaults::mainnet;
|
||||
@@ -23,20 +24,37 @@ use tracing::debug;
|
||||
|
||||
async fn network_requester_future(
|
||||
client: &Client,
|
||||
) -> Result<Option<NetworkRequesterDetailsV2>, NymNodeApiClientError> {
|
||||
) -> Result<Option<NetworkRequesterDetailsV3>, NymNodeApiClientError> {
|
||||
let Ok(nr) = client.get_network_requester().await else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
client.get_exit_policy().await.map(|exit_policy| {
|
||||
let uses_nym_exit_policy = exit_policy.upstream_source == mainnet::EXIT_POLICY_URL;
|
||||
Some(NetworkRequesterDetailsV2 {
|
||||
Some(NetworkRequesterDetailsV3 {
|
||||
address: nr.address,
|
||||
uses_exit_policy: exit_policy.enabled && uses_nym_exit_policy,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// try v2 first; nodes that haven't been upgraded yet won't expose it, so fall back to v1
|
||||
// (the v1 path yields no chain address).
|
||||
async fn auxiliary_details_future(client: &Client, node_id: NodeId) -> NymNodeAuxiliaryDetailsV3 {
|
||||
if let Ok(v2) = client.get_auxiliary_details_v2().await {
|
||||
return v2.into();
|
||||
}
|
||||
|
||||
client
|
||||
.get_auxiliary_details()
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
debug!("could not obtain auxiliary details of node {node_id}: {err} is it running an old version?")
|
||||
})
|
||||
.map(Into::into)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn query_for_described_data(
|
||||
client: &Client,
|
||||
node_id: NodeId,
|
||||
@@ -50,13 +68,7 @@ pub(crate) async fn query_for_described_data(
|
||||
NodeDescribedInfoMegaFuture::new(
|
||||
client.get_build_information().map_err(map_query_err),
|
||||
client.get_roles().ok_into().map_err(map_query_err),
|
||||
client.get_auxiliary_details()
|
||||
.inspect_err(|err| {
|
||||
// old nym-nodes will not have this field, so use the default instead
|
||||
debug!("could not obtain auxiliary details of node {node_id}: {err} is it running an old version?")
|
||||
})
|
||||
.ok_into()
|
||||
.unwrap_or_else(|_| AuxiliaryDetailsV2::default()),
|
||||
auxiliary_details_future(client, node_id),
|
||||
client.get_mixnet_websockets().ok_into().map_err(map_query_err),
|
||||
network_requester_future(client).map_err(map_query_err),
|
||||
// `ok_into` ultimately calls `IpPacketRouter::into` to transform it into `IpPacketRouterDetails`
|
||||
@@ -113,14 +125,14 @@ impl<F1, F2, F3, F4, F5, F6, F7, F8, F9> Future
|
||||
for NodeDescribedInfoMegaFuture<F1, F2, F3, F4, F5, F6, F7, F8, F9>
|
||||
where
|
||||
F1: Future<Output = Result<BinaryBuildInformationOwned, NodeDescribeCacheError>>,
|
||||
F2: Future<Output = Result<DeclaredRolesV2, NodeDescribeCacheError>>,
|
||||
F3: Future<Output = AuxiliaryDetailsV2>,
|
||||
F4: Future<Output = Result<WebSocketsV2, NodeDescribeCacheError>>,
|
||||
F5: Future<Output = Result<Option<NetworkRequesterDetailsV2>, NodeDescribeCacheError>>,
|
||||
F6: Future<Output = Option<IpPacketRouterDetailsV2>>,
|
||||
F7: Future<Output = Option<AuthenticatorDetailsV2>>,
|
||||
F8: Future<Output = Option<WireguardDetailsV2>>,
|
||||
F9: Future<Output = Option<LewesProtocolDetailsV1>>,
|
||||
F2: Future<Output = Result<DeclaredRolesV3, NodeDescribeCacheError>>,
|
||||
F3: Future<Output = NymNodeAuxiliaryDetailsV3>,
|
||||
F4: Future<Output = Result<WebSocketsV3, NodeDescribeCacheError>>,
|
||||
F5: Future<Output = Result<Option<NetworkRequesterDetailsV3>, NodeDescribeCacheError>>,
|
||||
F6: Future<Output = Option<IpPacketRouterDetailsV3>>,
|
||||
F7: Future<Output = Option<AuthenticatorDetailsV3>>,
|
||||
F8: Future<Output = Option<WireguardDetailsV3>>,
|
||||
F9: Future<Output = Option<LewesProtocolDetailsV3>>,
|
||||
{
|
||||
type Output = Result<UnwrappedResolvedNodeDescribedInfo, NodeDescribeCacheError>;
|
||||
|
||||
@@ -203,15 +215,15 @@ where
|
||||
|
||||
struct ResolvedNodeDescribedInfo {
|
||||
build_info: Result<BinaryBuildInformationOwned, NodeDescribeCacheError>,
|
||||
roles: Result<DeclaredRolesV2, NodeDescribeCacheError>,
|
||||
roles: Result<DeclaredRolesV3, NodeDescribeCacheError>,
|
||||
// TODO: in the future make it return a Result as well.
|
||||
auxiliary_details: AuxiliaryDetailsV2,
|
||||
websockets: Result<WebSocketsV2, NodeDescribeCacheError>,
|
||||
network_requester: Result<Option<NetworkRequesterDetailsV2>, NodeDescribeCacheError>,
|
||||
ipr: Option<IpPacketRouterDetailsV2>,
|
||||
authenticator: Option<AuthenticatorDetailsV2>,
|
||||
wireguard: Option<WireguardDetailsV2>,
|
||||
lewes_protocol: Option<LewesProtocolDetailsV1>,
|
||||
auxiliary_details: NymNodeAuxiliaryDetailsV3,
|
||||
websockets: Result<WebSocketsV3, NodeDescribeCacheError>,
|
||||
network_requester: Result<Option<NetworkRequesterDetailsV3>, NodeDescribeCacheError>,
|
||||
ipr: Option<IpPacketRouterDetailsV3>,
|
||||
authenticator: Option<AuthenticatorDetailsV3>,
|
||||
wireguard: Option<WireguardDetailsV3>,
|
||||
lewes_protocol: Option<LewesProtocolDetailsV3>,
|
||||
}
|
||||
|
||||
impl ResolvedNodeDescribedInfo {
|
||||
@@ -233,22 +245,22 @@ impl ResolvedNodeDescribedInfo {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UnwrappedResolvedNodeDescribedInfo {
|
||||
pub(crate) build_info: BinaryBuildInformationOwned,
|
||||
pub(crate) roles: DeclaredRolesV2,
|
||||
pub(crate) auxiliary_details: AuxiliaryDetailsV2,
|
||||
pub(crate) websockets: WebSocketsV2,
|
||||
pub(crate) network_requester: Option<NetworkRequesterDetailsV2>,
|
||||
pub(crate) ipr: Option<IpPacketRouterDetailsV2>,
|
||||
pub(crate) authenticator: Option<AuthenticatorDetailsV2>,
|
||||
pub(crate) wireguard: Option<WireguardDetailsV2>,
|
||||
pub(crate) lewes_protocol: Option<LewesProtocolDetailsV1>,
|
||||
pub(crate) roles: DeclaredRolesV3,
|
||||
pub(crate) auxiliary_details: NymNodeAuxiliaryDetailsV3,
|
||||
pub(crate) websockets: WebSocketsV3,
|
||||
pub(crate) network_requester: Option<NetworkRequesterDetailsV3>,
|
||||
pub(crate) ipr: Option<IpPacketRouterDetailsV3>,
|
||||
pub(crate) authenticator: Option<AuthenticatorDetailsV3>,
|
||||
pub(crate) wireguard: Option<WireguardDetailsV3>,
|
||||
pub(crate) lewes_protocol: Option<LewesProtocolDetailsV3>,
|
||||
}
|
||||
|
||||
impl UnwrappedResolvedNodeDescribedInfo {
|
||||
pub(crate) fn into_node_description(
|
||||
self,
|
||||
host_info: impl Into<HostInformationV2>,
|
||||
) -> NymNodeDataV2 {
|
||||
NymNodeDataV2 {
|
||||
host_info: impl Into<HostInformationV3>,
|
||||
) -> NymNodeDataV3 {
|
||||
NymNodeDataV3 {
|
||||
host_information: host_info.into(),
|
||||
last_polled: OffsetDateTime::now_utc().into(),
|
||||
build_information: self.build_info,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::node_describe_cache::query_helpers::query_for_described_data;
|
||||
use crate::node_describe_cache::NodeDescribeCacheError;
|
||||
use nym_api_requests::models::{DescribedNodeTypeV2, NymNodeDescriptionV2};
|
||||
use nym_api_requests::models::described::v3::{DescribedNodeTypeV3, NymNodeDescriptionV3};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_mixnet_contract_common::{NodeId, NymNodeDetails};
|
||||
@@ -15,7 +15,7 @@ pub(crate) struct RefreshData {
|
||||
host: String,
|
||||
node_id: NodeId,
|
||||
expected_identity: ed25519::PublicKey,
|
||||
node_type: DescribedNodeTypeV2,
|
||||
node_type: DescribedNodeTypeV3,
|
||||
|
||||
port: Option<u16>,
|
||||
}
|
||||
@@ -27,7 +27,7 @@ impl<'a> TryFrom<&'a NymNodeDetails> for RefreshData {
|
||||
Ok(RefreshData::new(
|
||||
&node.bond_information.node.host,
|
||||
node.bond_information.identity().parse()?,
|
||||
DescribedNodeTypeV2::NymNode,
|
||||
DescribedNodeTypeV3::NymNode,
|
||||
node.node_id(),
|
||||
node.bond_information.node.custom_http_port,
|
||||
))
|
||||
@@ -38,7 +38,7 @@ impl RefreshData {
|
||||
pub fn new(
|
||||
host: impl Into<String>,
|
||||
expected_identity: ed25519::PublicKey,
|
||||
node_type: DescribedNodeTypeV2,
|
||||
node_type: DescribedNodeTypeV3,
|
||||
node_id: NodeId,
|
||||
port: Option<u16>,
|
||||
) -> Self {
|
||||
@@ -55,7 +55,7 @@ impl RefreshData {
|
||||
self.node_id
|
||||
}
|
||||
|
||||
pub(crate) async fn try_refresh(self, allow_all_ips: bool) -> Option<NymNodeDescriptionV2> {
|
||||
pub(crate) async fn try_refresh(self, allow_all_ips: bool) -> Option<NymNodeDescriptionV3> {
|
||||
match try_get_description(self, allow_all_ips).await {
|
||||
Ok(description) => Some(description),
|
||||
Err(err) => {
|
||||
@@ -69,7 +69,7 @@ impl RefreshData {
|
||||
async fn try_get_description(
|
||||
data: RefreshData,
|
||||
allow_all_ips: bool,
|
||||
) -> Result<NymNodeDescriptionV2, NodeDescribeCacheError> {
|
||||
) -> Result<NymNodeDescriptionV3, NodeDescribeCacheError> {
|
||||
let client = NymNodeApiClientRetriever::new(bin_info!())
|
||||
.with_expected_identity(Some(data.expected_identity.to_base58_string()))
|
||||
.with_verify_host_information()
|
||||
@@ -101,7 +101,7 @@ async fn try_get_description(
|
||||
let node_info = query_for_described_data(&client.client, data.node_id).await?;
|
||||
let description = node_info.into_node_description(host_info.data);
|
||||
|
||||
Ok(NymNodeDescriptionV2 {
|
||||
Ok(NymNodeDescriptionV3 {
|
||||
node_id: data.node_id,
|
||||
contract_node_type: data.node_type,
|
||||
description,
|
||||
|
||||
+38
-6
@@ -2,7 +2,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::mixnet_contract_cache::cache::data::ConfigScoreData;
|
||||
use nym_api_requests::models::{ConfigScore, NymNodeDescriptionV2};
|
||||
use cosmwasm_std::Coin;
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_api_requests::models::{
|
||||
ChainInteractionCapabilities, ChainInteractionCapabilitiesDetailed, ConfigScoreV2,
|
||||
};
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
use nym_mixnet_contract_common::VersionScoreFormulaParams;
|
||||
|
||||
@@ -17,17 +21,36 @@ fn versions_behind_factor_to_config_score(
|
||||
penalty.powf((versions_behind as f64).powf(scaling))
|
||||
}
|
||||
|
||||
fn has_sufficient_tokens(
|
||||
minimum_balance: &Coin,
|
||||
capabilities: &Option<ChainInteractionCapabilitiesDetailed>,
|
||||
) -> bool {
|
||||
let Some(capabilities) = capabilities else {
|
||||
return false;
|
||||
};
|
||||
let chain_balance = &capabilities.on_chain_balance;
|
||||
|
||||
// this should never happen because we have queried for this specific balance,
|
||||
// but some defensive coding never hurt
|
||||
if chain_balance.denom != minimum_balance.denom {
|
||||
return false;
|
||||
}
|
||||
chain_balance.amount >= minimum_balance.amount
|
||||
}
|
||||
|
||||
pub(crate) fn calculate_config_score(
|
||||
minimum_balance: &Coin,
|
||||
config_score_data: &ConfigScoreData,
|
||||
described_data: Option<&NymNodeDescriptionV2>,
|
||||
) -> ConfigScore {
|
||||
described_data: Option<&NymNodeDescriptionV3>,
|
||||
chain_capabilities: &Option<ChainInteractionCapabilitiesDetailed>,
|
||||
) -> ConfigScoreV2 {
|
||||
let Some(described) = described_data else {
|
||||
return ConfigScore::unavailable();
|
||||
return ConfigScoreV2::unavailable();
|
||||
};
|
||||
|
||||
let node_version = &described.description.build_information.build_version;
|
||||
let Ok(reported_semver) = node_version.parse::<semver::Version>() else {
|
||||
return ConfigScore::bad_semver();
|
||||
return ConfigScoreV2::bad_semver();
|
||||
};
|
||||
let versions_behind = config_score_data
|
||||
.config_score_params
|
||||
@@ -54,10 +77,19 @@ pub(crate) fn calculate_config_score(
|
||||
)
|
||||
};
|
||||
|
||||
ConfigScore::new(
|
||||
let chain_interaction = ChainInteractionCapabilities {
|
||||
has_sufficient_tokens: has_sufficient_tokens(minimum_balance, chain_capabilities),
|
||||
is_fee_grant_grantee: chain_capabilities
|
||||
.as_ref()
|
||||
.map(|c| c.is_feegrant_grantee)
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
ConfigScoreV2::new(
|
||||
version_score,
|
||||
versions_behind,
|
||||
accepted_terms_and_conditions,
|
||||
runs_nym_node,
|
||||
chain_interaction,
|
||||
)
|
||||
}
|
||||
|
||||
+281
-13
@@ -11,46 +11,133 @@ use crate::node_status_api::cache::config_score::calculate_config_score;
|
||||
use crate::support::caching::cache::SharedCache;
|
||||
use crate::support::caching::refresher::RefreshRequester;
|
||||
use crate::support::caching::CacheNotificationWatcher;
|
||||
use crate::support::nyxd::Client;
|
||||
use crate::{
|
||||
mixnet_contract_cache::cache::MixnetContractCache,
|
||||
node_status_api::cache::NodeStatusCacheError, support::caching::CacheNotification,
|
||||
};
|
||||
use ::time::OffsetDateTime;
|
||||
use nym_api_requests::models::{DetailedNodePerformanceV2, NodeAnnotationV2, NymNodeDescriptionV2};
|
||||
use cosmwasm_std::{coin, Coin};
|
||||
use futures::StreamExt;
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_api_requests::models::{
|
||||
ChainInteractionCapabilitiesDetailed, DetailedNodePerformanceV2, NodeAnnotationV2,
|
||||
};
|
||||
use nym_mixnet_contract_common::{NodeId, NymNodeDetails};
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_topology::CachedEpochRewardedSet;
|
||||
use std::collections::HashMap;
|
||||
use nym_validator_client::nyxd::module_traits::feegrant::query::FeegrantQueryClient;
|
||||
use nym_validator_client::nyxd::{AccountId, CosmWasmClient};
|
||||
use nym_validator_client::QueryHttpRpcNyxdClient;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use tokio::time;
|
||||
use tokio::time::Instant;
|
||||
use tracing::{error, info, trace, warn};
|
||||
|
||||
pub(crate) struct NodeStatusCacheConfig {
|
||||
pub(crate) minimum_on_chain_balance: Coin,
|
||||
pub(crate) chain_capabilities_retrieval_concurrency: usize,
|
||||
|
||||
/// How long a node's cached chain capabilities (balance + feegrant) stay valid before being
|
||||
/// re-queried. Evaluated per node, so lookups are spread out over time rather than refreshed
|
||||
/// in a single burst.
|
||||
pub(crate) chain_capabilities_refresh_interval: Duration,
|
||||
|
||||
pub(crate) fallback_caching_interval: Duration,
|
||||
|
||||
/// Specify whether external stress testing data should be used for calculating node performance
|
||||
/// score used for rewarding and active set selection
|
||||
/// note: this can only be enabled if use_performance_contract_data is set to false!
|
||||
pub use_stress_testing_data: bool,
|
||||
pub(crate) use_stress_testing_data: bool,
|
||||
|
||||
/// If `use_stress_testing_data` is set to true, this specifies the minimum % of nodes,
|
||||
/// that must have their stress data available in the `stress_testing_data_period`,
|
||||
/// in order to include that metric in performance calculation.
|
||||
/// This is done to protect against Network Monitor failures and not receiving any data.
|
||||
pub minimum_available_stress_testing_results: f32,
|
||||
pub(crate) minimum_available_stress_testing_results: f32,
|
||||
|
||||
/// If use_stress_testing_data is enabled, specifies the weight of the stress testing score in the overall performance score.
|
||||
pub stress_testing_score_weight: f64,
|
||||
pub(crate) stress_testing_score_weight: f64,
|
||||
}
|
||||
|
||||
/// A successfully-retrieved chain-capability lookup for a single node, tagged with the instant it
|
||||
/// was fetched so its freshness can be evaluated against a TTL.
|
||||
struct CachedChainCapabilities {
|
||||
capabilities: ChainInteractionCapabilitiesDetailed,
|
||||
fetched_at: Instant,
|
||||
}
|
||||
|
||||
/// In-memory cache of successful chain-capability lookups, keyed by node id.
|
||||
///
|
||||
/// Only successful lookups are stored. Nodes that don't advertise a usable on-chain address, and
|
||||
/// nodes whose query failed, are intentionally absent: they're cheaply re-derived from the
|
||||
/// described data on every refresh and retried as needed, rather than being pinned to a stale
|
||||
/// `false` for a whole TTL window. Entries for nodes that unbond or later drop their address are
|
||||
/// evicted on the next refresh, so a stale value can't outlive the address it was derived from.
|
||||
/// This map is purely in-memory and never persisted, so a restart simply triggers a one-off full
|
||||
/// re-query on the first refresh.
|
||||
#[derive(Default)]
|
||||
struct ChainCapabilitiesCache {
|
||||
entries: HashMap<NodeId, CachedChainCapabilities>,
|
||||
}
|
||||
|
||||
impl ChainCapabilitiesCache {
|
||||
/// Whether the node should be (re)queried: true if we have no cached value, or the cached value
|
||||
/// is older than `ttl`.
|
||||
fn needs_refresh(&self, node_id: NodeId, ttl: Duration) -> bool {
|
||||
match self.entries.get(&node_id) {
|
||||
None => true,
|
||||
Some(entry) => entry.fetched_at.elapsed() > ttl,
|
||||
}
|
||||
}
|
||||
|
||||
/// Last known capabilities for a node, regardless of age. `None` if it has never been
|
||||
/// successfully queried (e.g. it advertises no address, or every query so far has failed).
|
||||
fn get(&self, node_id: NodeId) -> Option<ChainInteractionCapabilitiesDetailed> {
|
||||
self.entries
|
||||
.get(&node_id)
|
||||
.map(|entry| entry.capabilities.clone())
|
||||
}
|
||||
|
||||
fn record(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
capabilities: ChainInteractionCapabilitiesDetailed,
|
||||
fetched_at: Instant,
|
||||
) {
|
||||
self.entries.insert(
|
||||
node_id,
|
||||
CachedChainCapabilities {
|
||||
capabilities,
|
||||
fetched_at,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Drops every entry whose node id is not in `keep`. Used to evict nodes that have unbonded or
|
||||
/// no longer advertise a usable on-chain address, so their stale capabilities stop being served.
|
||||
fn retain_only(&mut self, keep: &HashSet<NodeId>) {
|
||||
self.entries.retain(|node_id, _| keep.contains(node_id));
|
||||
}
|
||||
}
|
||||
|
||||
// Long running task responsible for keeping the node status cache up-to-date.
|
||||
pub struct NodeStatusCacheRefresher {
|
||||
config: NodeStatusCacheConfig,
|
||||
|
||||
/// Successful chain-capability lookups (balance + feegrant) cached per node, each with its own
|
||||
/// TTL so they're re-queried independently rather than all at once.
|
||||
chain_capabilities: ChainCapabilitiesCache,
|
||||
|
||||
// Main stored data
|
||||
cache: NodeStatusCache,
|
||||
|
||||
/// Query client for retrieving blockchain data
|
||||
query_client: QueryHttpRpcNyxdClient,
|
||||
|
||||
// Sources for when refreshing data
|
||||
mixnet_contract_cache: MixnetContractCache,
|
||||
described_cache: SharedCache<DescribedNodes>,
|
||||
@@ -75,9 +162,10 @@ pub struct NodeStatusCacheRefresher {
|
||||
|
||||
impl NodeStatusCacheRefresher {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
pub(crate) async fn new(
|
||||
cache: NodeStatusCache,
|
||||
config: NodeStatusCacheConfig,
|
||||
chain_client: &Client,
|
||||
contract_cache: MixnetContractCache,
|
||||
described_cache: SharedCache<DescribedNodes>,
|
||||
contract_cache_listener: CacheNotificationWatcher,
|
||||
@@ -85,9 +173,14 @@ impl NodeStatusCacheRefresher {
|
||||
performance_provider: Box<dyn NodePerformanceProvider + Send + Sync>,
|
||||
on_disk_file: PathBuf,
|
||||
) -> Self {
|
||||
// due to the number of queries required, create an explicit query instance
|
||||
// of our nyxd client to avoid potentially blocking tasks requiring signing access
|
||||
let query_client = chain_client.query_client().await;
|
||||
|
||||
Self {
|
||||
cache,
|
||||
config,
|
||||
chain_capabilities: ChainCapabilitiesCache::default(),
|
||||
mixnet_contract_cache: contract_cache,
|
||||
described_cache,
|
||||
mixnet_contract_cache_listener: contract_cache_listener,
|
||||
@@ -95,6 +188,7 @@ impl NodeStatusCacheRefresher {
|
||||
refresh_requester: Default::default(),
|
||||
on_disk_file,
|
||||
performance_provider,
|
||||
query_client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +276,7 @@ impl NodeStatusCacheRefresher {
|
||||
}
|
||||
|
||||
async fn maybe_refresh(
|
||||
&self,
|
||||
&mut self,
|
||||
fallback_interval: &mut time::Interval,
|
||||
last_updated: &mut OffsetDateTime,
|
||||
) {
|
||||
@@ -202,7 +296,69 @@ impl NodeStatusCacheRefresher {
|
||||
fallback_interval.reset();
|
||||
}
|
||||
|
||||
pub(crate) async fn produce_node_annotations(
|
||||
/// Refreshes cached chain capabilities (balance + feegrant) for described nodes that need it:
|
||||
/// those with no cached value or whose value is older than the configured TTL. Nodes that
|
||||
/// don't advertise a usable on-chain address are skipped entirely - there's nothing to query,
|
||||
/// so we neither store nor retry them, and any value cached from a previously-advertised address
|
||||
/// is evicted. Only successful lookups are recorded; a failed query leaves any previous value
|
||||
/// untouched and is retried on the next refresh.
|
||||
// SAFETY: unwrap is fine as if the mutex got poisoned we'd be experiencing some UB anyway
|
||||
#[allow(clippy::unwrap_used)]
|
||||
async fn refresh_chain_capabilities(&mut self, nodes: &DescribedNodes) {
|
||||
// resolve the current usable on-chain account for every described node (parsing once so the
|
||||
// result is shared by both the eviction and the query paths below).
|
||||
let addressed = usable_chain_accounts(nodes);
|
||||
|
||||
// evict cached entries for any node not in this set: those that have unbonded (the describe
|
||||
// cache only ever holds bonded nym-nodes) and those that no longer advertise a usable
|
||||
// address, so a stale value can't keep flowing into scoring after its address is gone.
|
||||
let usable_ids = addressed.iter().map(|(id, _)| *id).collect::<HashSet<_>>();
|
||||
self.chain_capabilities.retain_only(&usable_ids);
|
||||
|
||||
let ttl = self.config.chain_capabilities_refresh_interval;
|
||||
let denom = self.config.minimum_on_chain_balance.denom.clone();
|
||||
|
||||
// of the addressed nodes, query only those due a refresh (no cached value or past TTL).
|
||||
// materialised into a Vec so the immutable borrow on `self.chain_capabilities` ends before
|
||||
// the async queries (and before we record the results back into it).
|
||||
let to_query = addressed
|
||||
.into_iter()
|
||||
.filter(|(node_id, _)| self.chain_capabilities.needs_refresh(*node_id, ttl))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if to_query.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// note: we use `for_each_concurrent` rather than `stream::iter(..).buffer_unordered(..)`.
|
||||
// The latter yields a `Stream` whose `Send` bound gets over-generalised once chained into
|
||||
// `collect()`, tripping "implementation of `Send` is not general enough" (rust-lang/rust#102211)
|
||||
let concurrency = self.config.chain_capabilities_retrieval_concurrency.max(1);
|
||||
|
||||
// std Mutex is fine because we don't hold it across await points
|
||||
let fresh = std::sync::Mutex::new(Vec::new());
|
||||
futures::stream::iter(to_query)
|
||||
.for_each_concurrent(concurrency, |(node_id, account_id)| {
|
||||
let denom = denom.clone();
|
||||
let query_client = &self.query_client;
|
||||
let fresh = &fresh;
|
||||
async move {
|
||||
if let Some(caps) =
|
||||
retrieve_chain_capabilities(query_client, node_id, account_id, denom).await
|
||||
{
|
||||
fresh.lock().unwrap().push((node_id, caps));
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let now = Instant::now();
|
||||
for (node_id, caps) in fresh.into_inner().unwrap() {
|
||||
self.chain_capabilities.record(node_id, caps, now);
|
||||
}
|
||||
}
|
||||
|
||||
async fn produce_node_annotations(
|
||||
&self,
|
||||
config_score_data: &ConfigScoreData,
|
||||
routing_scores: &NodesRoutingScores,
|
||||
@@ -216,6 +372,7 @@ impl NodeStatusCacheRefresher {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
let minimum_balance = &self.config.minimum_on_chain_balance;
|
||||
let use_stress_testing_scores = self.config.use_stress_testing_data;
|
||||
let threshold = self.config.minimum_available_stress_testing_results;
|
||||
|
||||
@@ -248,8 +405,15 @@ impl NodeStatusCacheRefresher {
|
||||
let node_id = nym_node.node_id();
|
||||
let described = described_nodes.get_node(&node_id);
|
||||
let routing_score = routing_scores.get_or_log(node_id);
|
||||
let config_score = calculate_config_score(config_score_data, described);
|
||||
let stress_testing_score = stress_testing_scores.get_or_log(node_id);
|
||||
let node_chain_cap = self.chain_capabilities.get(node_id);
|
||||
|
||||
let config_score = calculate_config_score(
|
||||
minimum_balance,
|
||||
config_score_data,
|
||||
described,
|
||||
&node_chain_cap,
|
||||
);
|
||||
|
||||
// a node only takes the stress-testing component if it is actually stress-tested (i.e.
|
||||
// it is a mixnode); gateways have no stress data and must not be penalised for it.
|
||||
@@ -266,6 +430,7 @@ impl NodeStatusCacheRefresher {
|
||||
node_id,
|
||||
NodeAnnotationV2 {
|
||||
current_role: rewarded_set.role(node_id).map(|r| r.into()),
|
||||
chain_interaction_capabilities: node_chain_cap,
|
||||
detailed_performance: DetailedNodePerformanceV2::new(
|
||||
performance,
|
||||
routing_score,
|
||||
@@ -281,7 +446,7 @@ impl NodeStatusCacheRefresher {
|
||||
|
||||
/// Refreshes the node status cache by fetching the latest data from the contract cache
|
||||
#[allow(deprecated)]
|
||||
async fn refresh(&self) -> Result<(), NodeStatusCacheError> {
|
||||
async fn refresh(&mut self) -> Result<(), NodeStatusCacheError> {
|
||||
info!("Updating node status cache");
|
||||
|
||||
// Fetch contract cache data to work with
|
||||
@@ -290,7 +455,10 @@ impl NodeStatusCacheRefresher {
|
||||
let nym_nodes = self.mixnet_contract_cache.nym_nodes().await;
|
||||
let config_score_data = self.mixnet_contract_cache.maybe_config_score_data().await?;
|
||||
|
||||
let Ok(described) = self.described_cache.get().await else {
|
||||
// clone the cache handle (cheap Arc clone) so the read guard borrows the local rather than
|
||||
// `self`, leaving us free to take `&mut self` for the chain-capability refresh below.
|
||||
let described_cache = self.described_cache.clone();
|
||||
let Ok(described) = described_cache.get().await else {
|
||||
return Err(NodeStatusCacheError::UnavailableDescribedCache);
|
||||
};
|
||||
|
||||
@@ -313,6 +481,10 @@ impl NodeStatusCacheRefresher {
|
||||
)
|
||||
.await?;
|
||||
|
||||
// refresh chain capabilities (balance + feegrant) for nodes that are due (new, previously
|
||||
// failed, or past their TTL), querying only the delta rather than the whole network.
|
||||
self.refresh_chain_capabilities(&described).await;
|
||||
|
||||
// Create annotated data
|
||||
let node_annotations = self
|
||||
.produce_node_annotations(
|
||||
@@ -340,6 +512,59 @@ impl NodeStatusCacheRefresher {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the current usable on-chain account for each described node, returning `(node_id,
|
||||
/// account_id)` pairs. Nodes that advertise no address (e.g. running an old version) or an
|
||||
/// unparseable one are excluded - there's nothing to query for them.
|
||||
fn usable_chain_accounts(nodes: &DescribedNodes) -> Vec<(NodeId, AccountId)> {
|
||||
nodes
|
||||
.nodes
|
||||
.values()
|
||||
.filter_map(|n| {
|
||||
let addr = n.description.auxiliary_details.address.as_ref()?;
|
||||
AccountId::from_str(addr)
|
||||
.inspect_err(|_| {
|
||||
warn!("node {} has provided an invalid account address", n.node_id)
|
||||
})
|
||||
.ok()
|
||||
.map(|account_id| (n.node_id, account_id))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn retrieve_chain_capabilities(
|
||||
query_client: &QueryHttpRpcNyxdClient,
|
||||
node_id: NodeId,
|
||||
account_id: AccountId,
|
||||
balance_denom: String,
|
||||
) -> Option<ChainInteractionCapabilitiesDetailed> {
|
||||
let on_chain_balance = match query_client
|
||||
.get_balance(&account_id, balance_denom.clone())
|
||||
.await
|
||||
{
|
||||
Ok(balance) => balance.map(Into::into).unwrap_or(coin(0, balance_denom)),
|
||||
Err(err) => {
|
||||
warn!(node_id, %err, "failed to retrieve node balance");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let is_feegrant_grantee = match query_client.allowances(account_id, None).await {
|
||||
// currently this is a very coarse check. the grant might be expired, it might not allow for
|
||||
// cosmwasm executemsg, but that's a good enough first iteration
|
||||
Ok(allowances) => !allowances.allowances.is_empty(),
|
||||
Err(err) => {
|
||||
warn!(node_id, %err, "failed to retrieve node feegrant allowances");
|
||||
// if there was a network blip, at least preserve the balance information
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
Some(ChainInteractionCapabilitiesDetailed {
|
||||
on_chain_balance,
|
||||
is_feegrant_grantee,
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether `node` is currently in scope for stress testing, and therefore expected to have a
|
||||
/// stress-test sample. This is the single source of truth for stress-test scope and must stay in
|
||||
/// sync with the orchestrator's test-target selection (`NodeType::from_roles`, which keys off the
|
||||
@@ -354,7 +579,7 @@ impl NodeStatusCacheRefresher {
|
||||
/// Today only mixnodes are stress-tested; when gateway stress testing lands, widen this predicate
|
||||
/// (e.g. to also accept `entry`/`exit` capable nodes) and nothing else in the scoring path needs
|
||||
/// to change.
|
||||
fn stress_test_eligible(described: Option<&NymNodeDescriptionV2>) -> bool {
|
||||
fn stress_test_eligible(described: Option<&NymNodeDescriptionV3>) -> bool {
|
||||
described
|
||||
.map(|n| n.description.declared_role.mixnode)
|
||||
.unwrap_or(false)
|
||||
@@ -392,7 +617,7 @@ fn node_performance(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_api_requests::models::mock_nym_node_description;
|
||||
use nym_api_requests::models::described::v3::mock_nym_node_description;
|
||||
|
||||
#[test]
|
||||
fn ineligible_nodes_are_not_penalised_for_missing_stress_data() {
|
||||
@@ -439,4 +664,47 @@ mod tests {
|
||||
// a node with no self-described data is out of scope (the orchestrator can't classify it)
|
||||
assert!(!stress_test_eligible(None));
|
||||
}
|
||||
|
||||
fn described_nodes(nodes: impl IntoIterator<Item = NymNodeDescriptionV3>) -> DescribedNodes {
|
||||
DescribedNodes {
|
||||
nodes: nodes.into_iter().map(|n| (n.node_id, n)).collect(),
|
||||
addresses_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cached_capabilities_are_evicted_when_a_node_loses_its_usable_address() {
|
||||
let (with_addr, without_addr, unbonded) = (1, 2, 3);
|
||||
|
||||
let mut keeps_address = mock_nym_node_description(0);
|
||||
keeps_address.node_id = with_addr;
|
||||
|
||||
// still bonded/described, but its self-described address is gone (e.g. downgraded to a
|
||||
// version that only exposes v1 auxiliary details)
|
||||
let mut drops_address = mock_nym_node_description(1);
|
||||
drops_address.node_id = without_addr;
|
||||
drops_address.description.auxiliary_details.address = None;
|
||||
|
||||
let described = described_nodes([keeps_address, drops_address]);
|
||||
|
||||
let mut cache = ChainCapabilitiesCache::default();
|
||||
let caps = ChainInteractionCapabilitiesDetailed {
|
||||
on_chain_balance: coin(1_000000, "unym"),
|
||||
is_feegrant_grantee: true,
|
||||
};
|
||||
let now = Instant::now();
|
||||
cache.record(with_addr, caps.clone(), now);
|
||||
cache.record(without_addr, caps.clone(), now);
|
||||
cache.record(unbonded, caps, now); // no longer in the describe cache at all
|
||||
|
||||
let usable_ids = usable_chain_accounts(&described)
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<HashSet<_>>();
|
||||
cache.retain_only(&usable_ids);
|
||||
|
||||
assert!(cache.get(with_addr).is_some()); // still advertises a usable address -> kept
|
||||
assert!(cache.get(without_addr).is_none()); // address dropped -> evicted
|
||||
assert!(cache.get(unbonded).is_none()); // no longer described -> evicted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ use crate::node_status_api::cache::refresher::NodeStatusCacheConfig;
|
||||
use crate::support::caching::cache::SharedCache;
|
||||
use crate::support::caching::refresher::RefreshRequester;
|
||||
use crate::support::config;
|
||||
use crate::support::nyxd::Client;
|
||||
use crate::{
|
||||
mixnet_contract_cache::cache::MixnetContractCache,
|
||||
support::{self},
|
||||
};
|
||||
pub(crate) use cache::NodeStatusCache;
|
||||
use cosmwasm_std::Coin;
|
||||
use nym_task::ShutdownManager;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
@@ -34,8 +36,9 @@ pub(crate) const ONE_DAY: Duration = Duration::from_secs(86400);
|
||||
/// It is primarily refreshed in-sync with the contract cache and described, however provide a fallback
|
||||
/// caching interval that is twice the nym contract cache
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn start_cache_refresh(
|
||||
pub(crate) async fn start_cache_refresh(
|
||||
config: &config::Config,
|
||||
chain_client: &Client,
|
||||
nym_contract_cache_state: &MixnetContractCache,
|
||||
described_cache: &SharedCache<DescribedNodes>,
|
||||
node_status_cache_state: &NodeStatusCache,
|
||||
@@ -45,7 +48,23 @@ pub(crate) fn start_cache_refresh(
|
||||
on_disk_file: PathBuf,
|
||||
shutdown_manager: &ShutdownManager,
|
||||
) -> RefreshRequester {
|
||||
let denom = chain_client.chain_details().await.mix_denom.base;
|
||||
let minimum_on_chain_balance = Coin::new(
|
||||
config.node_status_api.debug.minimum_on_chain_balance_amount,
|
||||
denom,
|
||||
);
|
||||
|
||||
let config = NodeStatusCacheConfig {
|
||||
minimum_on_chain_balance,
|
||||
chain_capabilities_retrieval_concurrency: config
|
||||
.node_status_api
|
||||
.debug
|
||||
.chain_capabilities_retrieval_concurrency,
|
||||
chain_capabilities_refresh_interval: config
|
||||
.node_status_api
|
||||
.debug
|
||||
.chain_capabilities_refresh_interval,
|
||||
|
||||
fallback_caching_interval: config.node_status_api.debug.caching_interval,
|
||||
use_stress_testing_data: config.performance_provider.debug.use_stress_testing_data,
|
||||
minimum_available_stress_testing_results: config
|
||||
@@ -61,13 +80,15 @@ pub(crate) fn start_cache_refresh(
|
||||
let mut nym_api_cache_refresher = NodeStatusCacheRefresher::new(
|
||||
node_status_cache_state.to_owned(),
|
||||
config,
|
||||
chain_client,
|
||||
nym_contract_cache_state.to_owned(),
|
||||
described_cache.clone(),
|
||||
nym_contract_cache_listener,
|
||||
described_cache_cache_listener,
|
||||
performance_provider,
|
||||
on_disk_file,
|
||||
);
|
||||
)
|
||||
.await;
|
||||
let refresh_requester = nym_api_cache_refresher.refresh_requester();
|
||||
let shutdown_listener = shutdown_manager.clone_shutdown_token();
|
||||
tokio::spawn(async move { nym_api_cache_refresher.run(shutdown_listener).await });
|
||||
|
||||
@@ -7,10 +7,12 @@ use crate::support::http::state::AppState;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::routing::{get, post};
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::models::described::v1::NymNodeDescriptionV1;
|
||||
use nym_api_requests::models::described::NoiseDetails;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponseV1, NodeDatePerformanceResponse, NodePerformanceResponse, NodeRefreshBody,
|
||||
NoiseDetails, NymNodeDescriptionV1, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
StakeSaturationResponse, UptimeHistoryResponse,
|
||||
PerformanceHistoryResponse, RewardedSetResponse, StakeSaturationResponse,
|
||||
UptimeHistoryResponse,
|
||||
};
|
||||
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
|
||||
@@ -7,9 +7,10 @@ use crate::support::http::state::AppState;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use nym_api_requests::models::{AnnotationResponseV2, NymNodeDescriptionV2};
|
||||
use nym_api_requests::models::described::v2::NymNodeDescriptionV2;
|
||||
use nym_api_requests::models::AnnotationResponseV2;
|
||||
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use nym_http_api_common::{FormattedResponse, OutputParamsV2};
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
pub(crate) fn routes() -> Router<AppState> {
|
||||
@@ -42,7 +43,11 @@ async fn get_described_nodes(
|
||||
let output = pagination.output.unwrap_or_default();
|
||||
|
||||
let cache = state.described_nodes_cache.get().await?;
|
||||
let descriptions = cache.all_nodes().cloned().collect::<Vec<_>>();
|
||||
// convert description to V2
|
||||
let descriptions = cache
|
||||
.all_nodes()
|
||||
.map(|d| d.clone().into())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(output.to_response(PaginatedResponse {
|
||||
pagination: Pagination {
|
||||
@@ -63,14 +68,13 @@ async fn get_described_nodes(
|
||||
(status = 200, content(
|
||||
(AnnotationResponseV2 = "application/json"),
|
||||
(AnnotationResponseV2 = "application/yaml"),
|
||||
(AnnotationResponseV2 = "application/bincode")
|
||||
))
|
||||
),
|
||||
params(NodeIdParam, OutputParams),
|
||||
params(NodeIdParam, OutputParamsV2),
|
||||
)]
|
||||
async fn get_node_annotation(
|
||||
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
||||
Query(output): Query<OutputParams>,
|
||||
Query(output): Query<OutputParamsV2>,
|
||||
State(state): State<AppState>,
|
||||
) -> AxumResult<FormattedResponse<AnnotationResponseV2>> {
|
||||
let annotations = state.node_status_cache().node_annotations().await?;
|
||||
|
||||
@@ -7,8 +7,9 @@ use crate::support::storage::models::NymNodeStressTestingResult;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::routing::{get, post};
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::models::network_monitor::KnownNetworkMonitorResponse;
|
||||
use nym_api_requests::models::{StressTestBatchSubmission, StressTestBatchSubmissionResponse};
|
||||
use nym_api_requests::models::v3::{
|
||||
KnownNetworkMonitorResponse, StressTestBatchSubmission, StressTestBatchSubmissionResponse,
|
||||
};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -388,7 +388,7 @@ impl<T> DeserialisedCache<T> {
|
||||
};
|
||||
|
||||
serialiser.deserialize_from(file).map_err(|err| {
|
||||
error!("failed to deserialised persistent cache file at {path:?}: {err}");
|
||||
error!("failed to deserialise persistent cache file at {path:?}: {err}");
|
||||
std::io::Error::other(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -322,6 +322,7 @@ async fn start_nym_api_tasks(mut config: Config) -> anyhow::Result<ShutdownManag
|
||||
|
||||
let node_status_cache_refresh_requester = node_status_api::start_cache_refresh(
|
||||
&config,
|
||||
&nyxd_client,
|
||||
&mixnet_contract_cache_state,
|
||||
&described_nodes_cache,
|
||||
&node_status_cache_state,
|
||||
@@ -330,7 +331,8 @@ async fn start_nym_api_tasks(mut config: Config) -> anyhow::Result<ShutdownManag
|
||||
describe_cache_watcher,
|
||||
annotations_path,
|
||||
&shutdown_manager,
|
||||
);
|
||||
)
|
||||
.await;
|
||||
|
||||
node_families_cache_refresher.start(shutdown_manager.clone_shutdown_token());
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ const DEFAULT_MINIMUM_TEST_ROUTES: usize = 1;
|
||||
const DEFAULT_ROUTE_TEST_PACKETS: usize = 1000;
|
||||
const DEFAULT_PER_NODE_TEST_PACKETS: usize = 3;
|
||||
|
||||
const DEFAULT_NODE_STATUS_CACHE_REFRESH_INTERVAL: Duration = Duration::from_secs(305);
|
||||
const DEFAULT_MIXNET_CACHE_REFRESH_INTERVAL: Duration = Duration::from_secs(150);
|
||||
const DEFAULT_NODE_FAMILIES_CACHE_REFRESH_INTERVAL: Duration = Duration::from_secs(600);
|
||||
|
||||
@@ -722,12 +721,31 @@ pub struct NodeStatusAPIDebug {
|
||||
// port: u16,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub caching_interval: Duration,
|
||||
|
||||
// base amount (in unym)
|
||||
pub minimum_on_chain_balance_amount: u128,
|
||||
|
||||
pub chain_capabilities_retrieval_concurrency: usize,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub chain_capabilities_refresh_interval: Duration,
|
||||
}
|
||||
|
||||
impl NodeStatusAPIDebug {
|
||||
const DEFAULT_NODE_STATUS_CACHE_REFRESH_INTERVAL: Duration = Duration::from_secs(305);
|
||||
const DEFAULT_CHAIN_CAPABILITIES_RETRIEVAL_CONCURRENCY: usize = 8;
|
||||
const DEFAULT_CHAIN_CAPABILITIES_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60); // once a day is more than enough
|
||||
const DEFAULT_MINIMUM_ON_CHAIN_BALANCE: u128 = 1_000000; // 1 nym is enough for all tx fees for quite some time
|
||||
}
|
||||
|
||||
impl Default for NodeStatusAPIDebug {
|
||||
fn default() -> Self {
|
||||
NodeStatusAPIDebug {
|
||||
caching_interval: DEFAULT_NODE_STATUS_CACHE_REFRESH_INTERVAL,
|
||||
caching_interval: Self::DEFAULT_NODE_STATUS_CACHE_REFRESH_INTERVAL,
|
||||
minimum_on_chain_balance_amount: Self::DEFAULT_MINIMUM_ON_CHAIN_BALANCE,
|
||||
chain_capabilities_retrieval_concurrency:
|
||||
Self::DEFAULT_CHAIN_CAPABILITIES_RETRIEVAL_CONCURRENCY,
|
||||
chain_capabilities_refresh_interval: Self::DEFAULT_CHAIN_CAPABILITIES_REFRESH_INTERVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use nym_coconut_dkg_common::{
|
||||
types::{EncodedBTEPublicKeyWithProof, Epoch},
|
||||
verification_key::{ContractVKShare, VerificationKeyShare},
|
||||
};
|
||||
use nym_config::defaults::NymNetworkDetails;
|
||||
use nym_config::defaults::{ChainDetails, NymNetworkDetails};
|
||||
use nym_dkg::Threshold;
|
||||
use nym_ecash_contract_common::blacklist::BlacklistedAccountResponse;
|
||||
use nym_ecash_contract_common::deposit::{DepositId, DepositResponse};
|
||||
@@ -137,6 +137,10 @@ impl Client {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn query_client(&self) -> QueryHttpRpcNyxdClient {
|
||||
nyxd_query!(self, clone_query_client())
|
||||
}
|
||||
|
||||
pub(crate) async fn read(&self) -> RwLockReadGuard<'_, ClientInner> {
|
||||
self.inner.read().await
|
||||
}
|
||||
@@ -176,6 +180,10 @@ impl Client {
|
||||
nyxd_query!(self, get_nym_contracts())
|
||||
}
|
||||
|
||||
pub(crate) async fn chain_details(&self) -> ChainDetails {
|
||||
nyxd_query!(self, get_chain_details())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_ecash_contract_address(&self) -> Result<AccountId, EcashError> {
|
||||
nyxd_query!(
|
||||
self,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::{StressTestResult, StressTestingScore, TestNode};
|
||||
use nym_api_requests::models::v3::StressTestResult;
|
||||
use nym_api_requests::models::{StressTestingScore, TestNode};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use sqlx::FromRow;
|
||||
|
||||
@@ -6,8 +6,9 @@ use crate::support::http::state::AppState;
|
||||
use crate::unstable_routes::helpers::refreshed_at;
|
||||
use crate::unstable_routes::v2::nym_nodes::helpers::NodesParamsWithRole;
|
||||
use axum::extract::{Query, State};
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_api_requests::models::{
|
||||
NodeAnnotationV1, NodeAnnotationV2, NymNodeDescriptionV2, OffsetDateTimeJsonSchemaWrapper,
|
||||
NodeAnnotationV1, NodeAnnotationV2, OffsetDateTimeJsonSchemaWrapper,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{NodeRole, PaginatedCachedNodesResponseV2, SemiSkimmedNodeV1};
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
@@ -29,7 +30,7 @@ fn build_nym_nodes_response<'a, NI>(
|
||||
active_only: bool,
|
||||
) -> Vec<SemiSkimmedNodeV1>
|
||||
where
|
||||
NI: Iterator<Item = &'a NymNodeDescriptionV2> + 'a,
|
||||
NI: Iterator<Item = &'a NymNodeDescriptionV3> + 'a,
|
||||
{
|
||||
let mut nodes = Vec::new();
|
||||
for nym_node in nym_nodes_subset {
|
||||
@@ -46,7 +47,7 @@ where
|
||||
// but in that case just use 0 performance
|
||||
let annotation: NodeAnnotationV1 = annotations
|
||||
.get(&node_id)
|
||||
.copied()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.into();
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ use crate::unstable_routes::helpers::refreshed_at;
|
||||
use crate::unstable_routes::v2::nym_nodes::helpers::NodesParams;
|
||||
use crate::unstable_routes::v2::nym_nodes::skimmed::PaginatedSkimmedNodes;
|
||||
use axum::extract::{Query, State};
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_api_requests::models::{
|
||||
NodeAnnotationV1, NodeAnnotationV2, NymNodeDescriptionV2, OffsetDateTimeJsonSchemaWrapper,
|
||||
NodeAnnotationV1, NodeAnnotationV2, OffsetDateTimeJsonSchemaWrapper,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{NodeRole, PaginatedCachedNodesResponseV2, SkimmedNodeV1};
|
||||
use nym_http_api_common::Output;
|
||||
@@ -25,7 +26,7 @@ fn build_nym_nodes_response<'a, NI>(
|
||||
active_only: bool,
|
||||
) -> Vec<SkimmedNodeV1>
|
||||
where
|
||||
NI: Iterator<Item = &'a NymNodeDescriptionV2> + 'a,
|
||||
NI: Iterator<Item = &'a NymNodeDescriptionV3> + 'a,
|
||||
{
|
||||
let mut nodes = Vec::new();
|
||||
for nym_node in nym_nodes_subset {
|
||||
@@ -42,7 +43,7 @@ where
|
||||
// but in that case just use 0 performance
|
||||
let annotation: NodeAnnotationV1 = annotations
|
||||
.get(&node_id)
|
||||
.copied()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.into();
|
||||
|
||||
@@ -95,7 +96,7 @@ pub(crate) async fn build_skimmed_nodes_response<'a, NI>(
|
||||
) -> PaginatedSkimmedNodes
|
||||
where
|
||||
// iterator returning relevant subset of nym-nodes (like mixing nym-nodes, entries, etc.)
|
||||
NI: Iterator<Item = &'a NymNodeDescriptionV2> + 'a,
|
||||
NI: Iterator<Item = &'a NymNodeDescriptionV3> + 'a,
|
||||
{
|
||||
// TODO: implement it
|
||||
let _ = query_params.per_page;
|
||||
|
||||
@@ -5,8 +5,9 @@ use crate::node_status_api::models::AxumResult;
|
||||
use crate::support::http::state::AppState;
|
||||
use crate::unstable_routes::helpers::refreshed_at;
|
||||
use axum::extract::{Query, State};
|
||||
use nym_api_requests::models::described::v3::NymNodeDescriptionV3;
|
||||
use nym_api_requests::models::{
|
||||
NodeAnnotationV1, NodeAnnotationV2, NymNodeDescriptionV2, OffsetDateTimeJsonSchemaWrapper,
|
||||
NodeAnnotationV1, NodeAnnotationV2, OffsetDateTimeJsonSchemaWrapper,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{NodeRole, PaginatedCachedNodesResponseV2, SemiSkimmedNodeV3};
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
@@ -21,7 +22,7 @@ pub type PaginatedSemiSkimmedNodes =
|
||||
|
||||
fn build_response<'a>(
|
||||
rewarded_set: &CachedEpochRewardedSet,
|
||||
nym_nodes: impl Iterator<Item = &'a NymNodeDescriptionV2>,
|
||||
nym_nodes: impl Iterator<Item = &'a NymNodeDescriptionV3>,
|
||||
annotations: &HashMap<NodeId, NodeAnnotationV2>,
|
||||
current_key_rotation: u32,
|
||||
) -> Vec<SemiSkimmedNodeV3> {
|
||||
@@ -35,7 +36,7 @@ fn build_response<'a>(
|
||||
// but in that case just use 0 performance
|
||||
let annotation: NodeAnnotationV1 = annotations
|
||||
.get(&node_id)
|
||||
.copied()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.into();
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use nym_api_requests::models::{
|
||||
use nym_api_requests::models::OffsetDateTimeJsonSchemaWrapper;
|
||||
use nym_api_requests::models::described::v2::{
|
||||
AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2, HostInformationV2,
|
||||
IpPacketRouterDetailsV2, NetworkRequesterDetailsV2, NymNodeDataV2,
|
||||
OffsetDateTimeJsonSchemaWrapper, WebSocketsV2, WireguardDetailsV2,
|
||||
IpPacketRouterDetailsV2, NetworkRequesterDetailsV2, NymNodeDataV2, NymNodeDescriptionV2,
|
||||
WebSocketsV2, WireguardDetailsV2,
|
||||
};
|
||||
use nym_authenticator_requests::AuthenticatorVersion;
|
||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||
@@ -20,7 +21,6 @@ use nym_node_requests::api::v1::node::models::AuxiliaryDetailsV1 as NodeAuxiliar
|
||||
use nym_sdk::mixnet::NodeIdentity;
|
||||
use nym_sdk::mixnet::Recipient;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::NymNodeDescriptionV2;
|
||||
use rand::prelude::IteratorRandom;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
+1
-1
@@ -4,10 +4,10 @@
|
||||
use crate::orchestrator::config::Config;
|
||||
use crate::storage::NetworkMonitorStorage;
|
||||
use anyhow::Context;
|
||||
use nym_api_requests::models::v3::{StressTestBatchSubmissionContent, StressTestResult};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_node_requests::api::Client;
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_validator_client::models::{StressTestBatchSubmissionContent, StressTestResult};
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use nym_validator_client::signable::SignableMessageBody;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use anyhow::Context;
|
||||
use nym_api_requests::models::network_monitor::StressTestResult;
|
||||
use nym_api_requests::models::v3::StressTestResult;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_network_monitor_orchestrator_requests::models::{
|
||||
self as api, LatencyDistribution, NymNodeData, TestRunData, TestRunInProgressData,
|
||||
|
||||
@@ -333,15 +333,17 @@ pub(crate) const GATEWAYS_HISTORICAL_COUNT: &str = "gateways.historical.count";
|
||||
use crate::node_scraper::models::BridgeInformation;
|
||||
use gateway::GatewaySummary;
|
||||
use mixnode::MixnodeSummary;
|
||||
use nym_api_requests::models::described::type_translation::{
|
||||
LewesProtocolDetailsV1, SphinxKeyV1, WebSocketsV1,
|
||||
};
|
||||
use nym_api_requests::models::described::v2::{
|
||||
AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2, HostInformationV2, HostKeysV2,
|
||||
IpPacketRouterDetailsV2, NetworkRequesterDetailsV2, NymNodeAuxiliaryDetailsV2, NymNodeDataV2,
|
||||
NymNodeDescriptionV2, WireguardDetailsV2,
|
||||
};
|
||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::models::{
|
||||
AuthenticatorDetailsV2, AuxiliaryDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2,
|
||||
HostInformationV2, HostKeysV2, IpPacketRouterDetailsV2, LewesProtocolDetailsV1,
|
||||
NetworkRequesterDetailsV2, NymNodeDataV2, NymNodeDescriptionV2,
|
||||
OffsetDateTimeJsonSchemaWrapper, SphinxKeyV1, VersionedNoiseKeyV1, WebSocketsV1,
|
||||
WireguardDetailsV2,
|
||||
};
|
||||
use nym_validator_client::models::{OffsetDateTimeJsonSchemaWrapper, VersionedNoiseKeyV1};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
|
||||
pub(crate) struct NetworkSummary {
|
||||
@@ -743,7 +745,7 @@ pub struct NymNodeDataDeHelper {
|
||||
pub declared_role: DeclaredRolesV2,
|
||||
|
||||
#[serde(default)]
|
||||
pub auxiliary_details: AuxiliaryDetailsV2,
|
||||
pub auxiliary_details: NymNodeAuxiliaryDetailsV2,
|
||||
|
||||
// TODO: do we really care about ALL build info or just the version?
|
||||
pub build_information: BinaryBuildInformationOwned,
|
||||
|
||||
@@ -10,9 +10,9 @@ use crate::{
|
||||
node_scraper::helpers::NodeDescriptionResponse,
|
||||
};
|
||||
use futures_util::TryStreamExt;
|
||||
use nym_api_requests::models::described::v2::NymNodeDescriptionV2;
|
||||
use nym_node_requests::api::v1::node::models::NodeDescription;
|
||||
use nym_validator_client::client::{NodeId, NymNodeDetails};
|
||||
use nym_validator_client::models::NymNodeDescriptionV2;
|
||||
use std::collections::HashMap;
|
||||
use tracing::{error, instrument, warn};
|
||||
|
||||
|
||||
@@ -225,12 +225,14 @@ mod db_tests {
|
||||
node_role: serde_json::json!(nym_validator_client::nym_nodes::NodeRole::Mixnode {
|
||||
layer: 1
|
||||
}),
|
||||
supported_roles: serde_json::json!(nym_validator_client::models::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}),
|
||||
supported_roles: serde_json::json!(
|
||||
nym_api_requests::models::described::type_translation::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}
|
||||
),
|
||||
entry: None,
|
||||
performance: "1.0".to_string(),
|
||||
self_described: None,
|
||||
@@ -278,7 +280,7 @@ fn test_nym_node_insert_record_new() {
|
||||
mix_port: 1789,
|
||||
x25519_sphinx_pubkey: x25519_pk,
|
||||
role: nym_validator_client::nym_nodes::NodeRole::Mixnode { layer: 1 },
|
||||
supported_roles: nym_validator_client::models::DeclaredRolesV1 {
|
||||
supported_roles: nym_api_requests::models::described::type_translation::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
@@ -305,12 +307,14 @@ fn test_nym_node_insert_record_new() {
|
||||
);
|
||||
assert_eq!(
|
||||
record.supported_roles,
|
||||
serde_json::json!(nym_validator_client::models::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
})
|
||||
serde_json::json!(
|
||||
nym_api_requests::models::described::type_translation::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(record.performance, "1");
|
||||
assert!(record.entry.is_none());
|
||||
@@ -330,7 +334,7 @@ fn test_nym_node_insert_record_with_entry() {
|
||||
mix_port: 1789,
|
||||
x25519_sphinx_pubkey: x25519_pk,
|
||||
role: nym_validator_client::nym_nodes::NodeRole::EntryGateway,
|
||||
supported_roles: nym_validator_client::models::DeclaredRolesV1 {
|
||||
supported_roles: nym_api_requests::models::described::type_translation::DeclaredRolesV1 {
|
||||
entry: true,
|
||||
mixnode: false,
|
||||
exit_nr: true,
|
||||
@@ -524,12 +528,14 @@ fn test_nym_node_dto_with_invalid_keys() {
|
||||
node_role: serde_json::json!(nym_validator_client::nym_nodes::NodeRole::Mixnode {
|
||||
layer: 1
|
||||
}),
|
||||
supported_roles: serde_json::json!(nym_validator_client::models::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}),
|
||||
supported_roles: serde_json::json!(
|
||||
nym_api_requests::models::described::type_translation::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}
|
||||
),
|
||||
entry: None,
|
||||
performance: "1.0".to_string(),
|
||||
self_described: None,
|
||||
@@ -562,12 +568,14 @@ fn test_nym_node_dto_with_invalid_performance() {
|
||||
node_role: serde_json::json!(nym_validator_client::nym_nodes::NodeRole::Mixnode {
|
||||
layer: 1
|
||||
}),
|
||||
supported_roles: serde_json::json!(nym_validator_client::models::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}),
|
||||
supported_roles: serde_json::json!(
|
||||
nym_api_requests::models::described::type_translation::DeclaredRolesV1 {
|
||||
entry: false,
|
||||
mixnode: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
}
|
||||
),
|
||||
entry: None,
|
||||
performance: "invalid_percent".to_string(),
|
||||
self_described: None,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::db::models::NymNodeDataDeHelper;
|
||||
use crate::monitor::geodata;
|
||||
use crate::node_scraper::models::BridgeInformation;
|
||||
use crate::{
|
||||
http::models::gw_probe::{
|
||||
DvpnGwProbe, DvpnProbeOutcome, LastProbeResult, ScoreValue, calc_gateway_visual_score,
|
||||
@@ -9,15 +12,19 @@ use crate::{
|
||||
monitor::ExplorerPrettyBond,
|
||||
};
|
||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||
use nym_api_requests::models::described::type_translation::{
|
||||
AuthenticatorDetailsV1, IpPacketRouterDetailsV1,
|
||||
LewesProtocolDetailsDataV1 as LewesProtocolDetailsDataV1Validator,
|
||||
LewesProtocolDetailsV1 as LewesProtocolDetailsV1Validator,
|
||||
};
|
||||
use nym_api_requests::models::described::v1::DescribedNodeTypeV1;
|
||||
use nym_api_requests::models::described::v2::NymNodeDataV2;
|
||||
use nym_mixnet_contract_common::{CoinSchema, NodeRewarding};
|
||||
use nym_node_requests::api::v1::node::models::NodeDescription;
|
||||
pub(crate) use nym_node_status_client::models::TestrunAssignment;
|
||||
use nym_validator_client::{
|
||||
client::NodeId,
|
||||
models::{
|
||||
AuthenticatorDetailsV1, BinaryBuildInformationOwned, IpPacketRouterDetailsV1,
|
||||
LewesProtocolDetailsDataV1 as LewesProtocolDetailsDataV1Validator,
|
||||
LewesProtocolDetailsV1 as LewesProtocolDetailsV1Validator,
|
||||
},
|
||||
models::BinaryBuildInformationOwned,
|
||||
nym_api::SkimmedNodeV1,
|
||||
nym_nodes::{BasicEntryInformation, NodeRole},
|
||||
};
|
||||
@@ -26,12 +33,6 @@ use strum_macros::EnumString;
|
||||
use tracing::{error, instrument};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::db::models::NymNodeDataDeHelper;
|
||||
use crate::node_scraper::models::BridgeInformation;
|
||||
|
||||
use crate::monitor::geodata;
|
||||
pub(crate) use nym_node_status_client::models::TestrunAssignment;
|
||||
|
||||
pub(crate) mod gw_probe;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
@@ -774,11 +775,11 @@ pub(crate) struct ExtendedNymNode {
|
||||
pub(crate) original_pledge: u128,
|
||||
pub(crate) bonding_address: Option<String>,
|
||||
pub(crate) bonded: bool,
|
||||
pub(crate) node_type: nym_validator_client::models::DescribedNodeTypeV1,
|
||||
pub(crate) node_type: DescribedNodeTypeV1,
|
||||
pub(crate) ip_address: String,
|
||||
pub(crate) accepted_tnc: bool,
|
||||
pub(crate) self_description: nym_validator_client::models::NymNodeDataV2,
|
||||
pub(crate) rewarding_details: Option<nym_mixnet_contract_common::NodeRewarding>,
|
||||
pub(crate) self_description: NymNodeDataV2,
|
||||
pub(crate) rewarding_details: Option<NodeRewarding>,
|
||||
pub(crate) description: NodeDescription,
|
||||
pub(crate) geoip: Option<NodeGeoData>,
|
||||
pub family_data: Option<NodeFamilyInformation>,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use crate::db::{DbPool, models::GatewaySessionsRecord, queries};
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_node_requests::api::{client::NymNodeApiClientExt, v1::metrics::models::SessionStats};
|
||||
use nym_validator_client::{
|
||||
client::{NodeId, NymNodeDetails},
|
||||
models::{DescribedNodeTypeV1, NymNodeDescriptionV1},
|
||||
};
|
||||
use nym_validator_client::client::{NodeId, NymNodeDetails};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use nym_api_requests::models::described::v1::{DescribedNodeTypeV1, NymNodeDescriptionV1};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_node_requests::try_get_valid_nym_node_api_client;
|
||||
use nym_statistics_common::types::SessionType;
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::utils::{LogError, NumericalCheckedCast};
|
||||
use moka::future::Cache;
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_validator_client::client::{NodeId, NymApiClientExt, NymNodeDetails};
|
||||
use nym_validator_client::models::NymNodeDescriptionV2;
|
||||
use nym_validator_client::{
|
||||
QueryHttpRpcNyxdClient,
|
||||
nym_nodes::{NodeRole, SkimmedNodeV1},
|
||||
@@ -23,6 +22,7 @@ use tracing::instrument;
|
||||
|
||||
pub(crate) use geodata::{ExplorerPrettyBond, IpInfoClient, Location};
|
||||
pub(crate) use node_delegations::DelegationsCache;
|
||||
use nym_api_requests::models::described::v2::NymNodeDescriptionV2;
|
||||
|
||||
pub(crate) mod geodata;
|
||||
mod node_delegations;
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::api::v1::node::models::{
|
||||
AuxiliaryDetailsV1, NodeDescription, NodeRoles, SignedHostInformation,
|
||||
};
|
||||
use crate::api::v1::node_load::models::NodeLoad;
|
||||
use crate::api::v2::node::models::AuxiliaryDetailsV2;
|
||||
use crate::routes;
|
||||
use async_trait::async_trait;
|
||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||
@@ -60,6 +61,11 @@ pub trait NymNodeApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_auxiliary_details_v2(&self) -> Result<AuxiliaryDetailsV2, NymNodeApiClientError> {
|
||||
self.get_json_from(routes::api::v2::auxiliary_absolute())
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO: implement calls for other endpoints; for now I only care about the wss
|
||||
async fn get_mixnet_websockets(&self) -> Result<WebSockets, NymNodeApiClientError> {
|
||||
self.get_json_from(
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use anyhow::Result;
|
||||
use nym_task::ShutdownToken;
|
||||
|
||||
use celes::Country;
|
||||
use nym_validator_client::models::NymNodeDescriptionV1;
|
||||
use nym_http_api_client::Client;
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::described::v1::NymNodeDescriptionV1;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
use std::{net::IpAddr, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::interval;
|
||||
use url::Url;
|
||||
|
||||
use nym_http_api_client::Client;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use tracing::{error, info, trace, warn};
|
||||
use url::Url;
|
||||
|
||||
const NETWORK_CACHE_TTL: Duration = Duration::from_secs(600);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use nym_api_requests::models::{LPHashFunction, LPKEM};
|
||||
use nym_api_requests::nym_nodes::SkimmedNodeV1;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_http_api_client::UserAgent;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use nym_api_requests::models::described::type_translation::DeclaredRolesV1;
|
||||
use nym_api_requests::models::described::v1::DescribedNodeTypeV1;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponseV1, AnnotationResponseV2, DeclaredRolesV1, DescribedNodeTypeV1,
|
||||
GatewayCoreStatusResponse, HistoricalPerformanceResponse, HistoricalUptimeResponse,
|
||||
MixnodeCoreStatusResponse, MixnodeStatus, MixnodeStatusResponse, NodeAnnotationV1,
|
||||
NodeAnnotationV2, NodeDatePerformanceResponse, NodePerformanceResponse,
|
||||
PerformanceHistoryResponse, StakeSaturationResponse, UptimeHistoryResponse,
|
||||
AnnotationResponseV1, AnnotationResponseV2, GatewayCoreStatusResponse,
|
||||
HistoricalPerformanceResponse, HistoricalUptimeResponse, MixnodeCoreStatusResponse,
|
||||
MixnodeStatus, MixnodeStatusResponse, NodeAnnotationV1, NodeAnnotationV2,
|
||||
NodeDatePerformanceResponse, NodePerformanceResponse, PerformanceHistoryResponse,
|
||||
StakeSaturationResponse, UptimeHistoryResponse,
|
||||
};
|
||||
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_mixnet_contract_common::nym_node::{NodeConfigUpdate, Role};
|
||||
|
||||
Reference in New Issue
Block a user