Skip to content

Commit

Permalink
exposing node role in nym node annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
jstuczyn committed Sep 23, 2024
1 parent 3e70cf2 commit e710105
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 78 deletions.
12 changes: 11 additions & 1 deletion common/client-libs/validator-client/src/nym_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use nym_api_requests::ecash::models::{
};
use nym_api_requests::ecash::VerificationKeyResponse;
use nym_api_requests::legacy::LegacyGatewayBondWithId;
use nym_api_requests::models::{LegacyDescribedMixNode, NodePerformanceResponse};
use nym_api_requests::models::{
AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse,
};
pub use nym_api_requests::{
ecash::{
models::{
Expand Down Expand Up @@ -435,6 +437,14 @@ pub trait NymApiClientExt: ApiClient {
.await
}

async fn get_node_annotation(
&self,
node_id: NodeId,
) -> Result<AnnotationResponse, NymAPIError> {
self.get_json_from(format!("/v1/nym-nodes/annotation/{node_id}"))
.await
}

async fn get_mixnode_avg_uptime(&self, mix_id: NodeId) -> Result<UptimeResponse, NymAPIError> {
self.get_json(
&[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_api_requests::models::StakeSaturationResponse;
use nym_contracts_common::signing::Nonce;
use nym_mixnet_contract_common::gateway::{PreassignedGatewayIdsResponse, PreassignedId};
use nym_mixnet_contract_common::nym_node::{
EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeOwnershipResponse,
NodeRewardingDetailsResponse, PagedNymNodeBondsResponse, PagedNymNodeDetailsResponse,
PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse, UnbondedNodeResponse,
UnbondedNymNode,
PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse, StakeSaturationResponse,
UnbondedNodeResponse, UnbondedNymNode,
};
use nym_mixnet_contract_common::reward_params::WorkFactor;
use nym_mixnet_contract_common::{
Expand Down
56 changes: 0 additions & 56 deletions common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,62 +64,6 @@ impl RewardedSet {
pub fn rewarded_set_size(&self) -> usize {
self.active_set_size() + self.standby.len()
}

pub fn try_get_mix_layer(&self, node_id: &NodeId) -> Option<u8> {
if self.layer1.contains(node_id) {
Some(1)
} else if self.layer2.contains(node_id) {
Some(2)
} else if self.layer3.contains(node_id) {
Some(3)
} else {
None
}
}

pub fn is_entry(&self, node_id: &NodeId) -> bool {
self.entry_gateways.contains(node_id)
}

pub fn is_exit(&self, node_id: &NodeId) -> bool {
self.exit_gateways.contains(node_id)
}

pub fn is_active_mixnode(&self, node_id: &NodeId) -> bool {
self.layer1.contains(node_id)
|| self.layer2.contains(node_id)
|| self.layer3.contains(node_id)
}

pub fn gateways(&self) -> Vec<NodeId> {
let mut gateways = Vec::with_capacity(self.entry_gateways.len() + self.exit_gateways.len());
for entry in &self.entry_gateways {
gateways.push(*entry)
}
for exit in &self.exit_gateways {
gateways.push(*exit)
}
gateways
}

pub fn active_mixnodes(&self) -> Vec<NodeId> {
let mut mixnodes =
Vec::with_capacity(self.layer1.len() + self.layer2.len() + self.layer3.len());
for mix in &self.layer1 {
mixnodes.push(*mix)
}
for mix in &self.layer2 {
mixnodes.push(*mix)
}
for mix in &self.layer3 {
mixnodes.push(*mix)
}
mixnodes
}

pub fn is_standby(&self, node_id: &NodeId) -> bool {
self.standby.contains(node_id)
}
}

#[cw_serde]
Expand Down
2 changes: 2 additions & 0 deletions nym-api/nym-api-requests/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::legacy::{
use crate::nym_nodes::{BasicEntryInformation, NodeRole, SkimmedNode};
use crate::pagination::PaginatedResponse;
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
use nym_mixnet_contract_common::nym_node::Role;
use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams};
use nym_mixnet_contract_common::rewarding::RewardEstimate;
use nym_mixnet_contract_common::{IdentityKey, Interval, MixNode, NodeId, Percent};
Expand Down Expand Up @@ -125,6 +126,7 @@ pub struct NodePerformance {
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct NodeAnnotation {
pub last_24h_performance: Performance,
pub current_role: Option<Role>,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
Expand Down
1 change: 1 addition & 0 deletions nym-api/src/epoch_operations/rewarding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl EpochAdvancer {
.rewarded_set_owned()
.await
.into_inner()
.into()
}
};

Expand Down
6 changes: 3 additions & 3 deletions nym-api/src/network_monitor/monitor/preparer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::network_monitor::monitor::sender::GatewayPackets;
use crate::network_monitor::test_route::TestRoute;
use crate::node_describe_cache::{DescribedNodes, NodeDescriptionTopologyExt};
use crate::nym_contract_cache::cache::NymContractCache;
use crate::nym_contract_cache::cache::{CachedRewardedSet, NymContractCache};
use crate::support::caching::cache::SharedCache;
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeBondWithLayer};
use nym_api_requests::models::NymNodeDescription;
Expand Down Expand Up @@ -275,7 +275,7 @@ impl PacketPreparer {
&self,
rng: &mut R,
blacklist: &mut HashSet<NodeId>,
rewarded_set: &RewardedSet,
rewarded_set: &CachedRewardedSet,
legacy_mixnodes: Vec<LegacyMixNodeBondWithLayer>,
mixing_nym_nodes: impl Iterator<Item = &'a NymNodeDescription> + 'a,
) -> HashMap<LegacyMixLayer, Vec<mix::LegacyNode>> {
Expand Down Expand Up @@ -477,7 +477,7 @@ impl PacketPreparer {
fn nym_node_to_legacy_mix<R: Rng>(
&self,
rng: &mut R,
rewarded_set: &RewardedSet,
rewarded_set: &CachedRewardedSet,
mixing_nym_node: &NymNodeDescription,
) -> Option<mix::LegacyNode> {
let maybe_explicit_layer = rewarded_set
Expand Down
11 changes: 8 additions & 3 deletions nym-api/src/node_status_api/cache/node_sets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@

use crate::node_status_api::helpers::RewardedSetStatus;
use crate::node_status_api::reward_estimate::{compute_apy_from_reward, compute_reward_estimate};
use crate::nym_contract_cache::cache::CachedRewardedSet;
use crate::support::storage::NymApiStorage;
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeDetailsWithLayer};
use nym_api_requests::models::{
GatewayBondAnnotated, MixNodeBondAnnotated, NodeAnnotation, NodePerformance,
};
use nym_mixnet_contract_common::{reward_params::Performance, Interval, NodeId, RewardedSet};
use nym_mixnet_contract_common::{reward_params::Performance, Interval, NodeId};
use nym_mixnet_contract_common::{NymNodeDetails, RewardingParams};
use nym_topology::NetworkAddress;
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -46,7 +47,7 @@ pub(super) async fn get_gateway_performance_from_storage(
}

// TODO: this might have to be moved to a different file if other places also rely on this functionality
fn get_rewarded_set_status(rewarded_set: &RewardedSet, node_id: NodeId) -> RewardedSetStatus {
fn get_rewarded_set_status(rewarded_set: &CachedRewardedSet, node_id: NodeId) -> RewardedSetStatus {
if rewarded_set.is_standby(&node_id) {
RewardedSetStatus::Standby
} else if rewarded_set.is_active_mixnode(&node_id) {
Expand All @@ -61,7 +62,7 @@ pub(super) async fn annotate_legacy_mixnodes_nodes_with_details(
mixnodes: Vec<LegacyMixNodeDetailsWithLayer>,
interval_reward_params: RewardingParams,
current_interval: Interval,
rewarded_set: &RewardedSet,
rewarded_set: &CachedRewardedSet,
blacklist: &HashSet<NodeId>,
) -> HashMap<NodeId, MixNodeBondAnnotated> {
let mut annotated = HashMap::new();
Expand Down Expand Up @@ -188,6 +189,7 @@ pub(crate) async fn produce_node_annotations(
legacy_mixnodes: &[LegacyMixNodeDetailsWithLayer],
legacy_gateways: &[LegacyGatewayBondWithId],
nym_nodes: &[NymNodeDetails],
rewarded_set: &CachedRewardedSet,
current_interval: Interval,
) -> HashMap<NodeId, NodeAnnotation> {
let mut annotations = HashMap::new();
Expand All @@ -207,6 +209,7 @@ pub(crate) async fn produce_node_annotations(
legacy_mix.mix_id(),
NodeAnnotation {
last_24h_performance: perf,
current_role: rewarded_set.role(legacy_mix.mix_id()),
},
);
}
Expand All @@ -226,6 +229,7 @@ pub(crate) async fn produce_node_annotations(
legacy_gateway.node_id,
NodeAnnotation {
last_24h_performance: perf,
current_role: rewarded_set.role(legacy_gateway.node_id),
},
);
}
Expand All @@ -245,6 +249,7 @@ pub(crate) async fn produce_node_annotations(
nym_node.node_id(),
NodeAnnotation {
last_24h_performance: perf,
current_role: rewarded_set.role(nym_node.node_id()),
},
);
}
Expand Down
1 change: 1 addition & 0 deletions nym-api/src/node_status_api/cache/refresher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ impl NodeStatusCacheRefresher {
&mixnode_details,
&gateway_bonds,
&nym_nodes,
&rewarded_set,
current_interval,
)
.await;
Expand Down
123 changes: 122 additions & 1 deletion nym-api/src/nym_contract_cache/cache/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,136 @@
use crate::support::caching::Cache;
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeDetailsWithLayer};
use nym_contracts_common::ContractBuildInformation;
use nym_mixnet_contract_common::nym_node::Role;
use nym_mixnet_contract_common::{Interval, NodeId, NymNodeDetails, RewardedSet, RewardingParams};
use nym_validator_client::nyxd::AccountId;
use std::collections::{HashMap, HashSet};

#[derive(Default, Clone)]
pub(crate) struct CachedRewardedSet {
pub(crate) entry_gateways: HashSet<NodeId>,

pub(crate) exit_gateways: HashSet<NodeId>,

pub(crate) layer1: HashSet<NodeId>,

pub(crate) layer2: HashSet<NodeId>,

pub(crate) layer3: HashSet<NodeId>,

pub(crate) standby: HashSet<NodeId>,
}

impl From<RewardedSet> for CachedRewardedSet {
fn from(value: RewardedSet) -> Self {
CachedRewardedSet {
entry_gateways: value.entry_gateways.into_iter().collect(),
exit_gateways: value.exit_gateways.into_iter().collect(),
layer1: value.layer1.into_iter().collect(),
layer2: value.layer2.into_iter().collect(),
layer3: value.layer3.into_iter().collect(),
standby: value.standby.into_iter().collect(),
}
}
}

impl From<CachedRewardedSet> for RewardedSet {
fn from(value: CachedRewardedSet) -> Self {
RewardedSet {
entry_gateways: value.entry_gateways.into_iter().collect(),
exit_gateways: value.exit_gateways.into_iter().collect(),
layer1: value.layer1.into_iter().collect(),
layer2: value.layer2.into_iter().collect(),
layer3: value.layer3.into_iter().collect(),
standby: value.standby.into_iter().collect(),
}
}
}

impl CachedRewardedSet {
pub(crate) fn role(&self, node_id: NodeId) -> Option<Role> {
if self.entry_gateways.contains(&node_id) {
Some(Role::EntryGateway)
} else if self.exit_gateways.contains(&node_id) {
Some(Role::ExitGateway)
} else if self.layer1.contains(&node_id) {
Some(Role::Layer1)
} else if self.layer2.contains(&node_id) {
Some(Role::Layer2)
} else if self.layer3.contains(&node_id) {
Some(Role::Layer3)
} else if self.standby.contains(&node_id) {
Some(Role::Standby)
} else {
None
}
}

pub fn active_set_size(&self) -> usize {
self.entry_gateways.len()
+ self.exit_gateways.len()
+ self.layer1.len()
+ self.layer2.len()
+ self.layer3.len()
}

pub fn rewarded_set_size(&self) -> usize {
self.active_set_size() + self.standby.len()
}

pub fn try_get_mix_layer(&self, node_id: &NodeId) -> Option<u8> {
if self.layer1.contains(node_id) {
Some(1)
} else if self.layer2.contains(node_id) {
Some(2)
} else if self.layer3.contains(node_id) {
Some(3)
} else {
None
}
}

pub fn is_standby(&self, node_id: &NodeId) -> bool {
self.standby.contains(node_id)
}

pub fn is_entry(&self, node_id: &NodeId) -> bool {
self.entry_gateways.contains(node_id)
}

pub fn is_exit(&self, node_id: &NodeId) -> bool {
self.exit_gateways.contains(node_id)
}

pub fn is_active_mixnode(&self, node_id: &NodeId) -> bool {
self.layer1.contains(node_id)
|| self.layer2.contains(node_id)
|| self.layer3.contains(node_id)
}

pub(crate) fn gateways(&self) -> HashSet<NodeId> {
let mut gateways =
HashSet::with_capacity(self.entry_gateways.len() + self.exit_gateways.len());
gateways.extend(&self.entry_gateways);
gateways.extend(&self.exit_gateways);
gateways
}

pub(crate) fn active_mixnodes(&self) -> HashSet<NodeId> {
let mut mixnodes =
HashSet::with_capacity(self.layer1.len() + self.layer2.len() + self.layer3.len());
mixnodes.extend(&self.layer1);
mixnodes.extend(&self.layer2);
mixnodes.extend(&self.layer3);
mixnodes
}
}

pub(crate) struct ValidatorCacheData {
pub(crate) legacy_mixnodes: Cache<Vec<LegacyMixNodeDetailsWithLayer>>,
pub(crate) legacy_gateways: Cache<Vec<LegacyGatewayBondWithId>>,
pub(crate) nym_nodes: Cache<Vec<NymNodeDetails>>,
pub(crate) rewarded_set: Cache<RewardedSet>,
pub(crate) rewarded_set: Cache<CachedRewardedSet>,

// this purposely does not deal with nym-nodes as they don't have a concept of a blacklist.
// instead clients are meant to be filtering out them themselves based on the provided scores.
Expand Down
6 changes: 4 additions & 2 deletions nym-api/src/nym_contract_cache/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use tracing::{debug, error};
mod data;
pub(crate) mod refresher;

pub(crate) use self::data::CachedRewardedSet;

const CACHE_TIMEOUT_MS: u64 = 100;

#[derive(Clone)]
Expand Down Expand Up @@ -259,11 +261,11 @@ impl NymContractCache {
todo!()
}

pub async fn rewarded_set(&self) -> Option<RwLockReadGuard<Cache<RewardedSet>>> {
pub async fn rewarded_set(&self) -> Option<RwLockReadGuard<Cache<CachedRewardedSet>>> {
self.get(|cache| &cache.rewarded_set).await
}

pub async fn rewarded_set_owned(&self) -> Cache<RewardedSet> {
pub async fn rewarded_set_owned(&self) -> Cache<CachedRewardedSet> {
self.get_owned(|cache| cache.rewarded_set.clone_cache())
.await
.unwrap_or_default()
Expand Down
Loading

0 comments on commit e710105

Please sign in to comment.