Files
nym/explorer-api/src/nym_nodes/models.rs
T
Jędrzej Stuczyński 317f7fffa9 added hacky routes to return nymnodes alongside legacy nodes (#5051)
* added hacky routes to return nymnodes alongside legacy nodes

* fixed mixing role

* Update client (#5054)

* removed hacky mixnodes endpoint for its not used

* construct explorer-api client with timeout

---------

Co-authored-by: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com>
2024-10-29 08:35:07 +00:00

155 lines
5.4 KiB
Rust

// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::location::{LocationCache, LocationCacheItem};
use crate::nym_nodes::location::NymNodeLocationCache;
use crate::nym_nodes::CACHE_ENTRY_TTL;
use nym_explorer_api_requests::{Location, PrettyDetailedGatewayBond};
use nym_mixnet_contract_common::{Gateway, NodeId, NymNodeDetails};
use nym_validator_client::models::NymNodeDescription;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use tokio::sync::{RwLock, RwLockReadGuard};
pub(crate) struct NymNodesCache {
pub(crate) valid_until: SystemTime,
pub(crate) bonded_nym_nodes: HashMap<NodeId, NymNodeDetails>,
pub(crate) described_nodes: HashMap<NodeId, NymNodeDescription>,
}
impl NymNodesCache {
fn new() -> Self {
NymNodesCache {
valid_until: SystemTime::now() - Duration::from_secs(60), // in the past
bonded_nym_nodes: Default::default(),
described_nodes: Default::default(),
}
}
// fn is_valid(&self) -> bool {
// self.valid_until >= SystemTime::now()
// }
}
#[derive(Clone)]
pub(crate) struct ThreadSafeNymNodesCache {
nymnodes: Arc<RwLock<NymNodesCache>>,
locations: Arc<RwLock<LocationCache<NodeId>>>,
}
impl ThreadSafeNymNodesCache {
pub(crate) fn new() -> Self {
ThreadSafeNymNodesCache {
nymnodes: Arc::new(RwLock::new(NymNodesCache::new())),
locations: Arc::new(RwLock::new(NymNodeLocationCache::new())),
}
}
pub(crate) fn new_with_location_cache(locations: NymNodeLocationCache) -> Self {
ThreadSafeNymNodesCache {
nymnodes: Arc::new(RwLock::new(NymNodesCache::new())),
locations: Arc::new(RwLock::new(locations)),
}
}
pub(crate) async fn is_location_valid(&self, node_id: NodeId) -> bool {
self.locations
.read()
.await
.get(&node_id)
.map_or(false, |cache_item| {
cache_item.valid_until > SystemTime::now()
})
}
pub(crate) async fn get_bonded_nymnodes(
&self,
) -> RwLockReadGuard<HashMap<NodeId, NymNodeDetails>> {
let guard = self.nymnodes.read().await;
RwLockReadGuard::map(guard, |n| &n.bonded_nym_nodes)
}
pub(crate) async fn get_locations(&self) -> NymNodeLocationCache {
self.locations.read().await.clone()
}
pub(crate) async fn set_location(&self, node_id: NodeId, location: Option<Location>) {
// cache the location for this mix node so that it can be used when the mix node list is refreshed
self.locations
.write()
.await
.insert(node_id, LocationCacheItem::new_from_location(location));
}
pub(crate) async fn update_cache(
&self,
all_bonds: Vec<NymNodeDetails>,
descriptions: Vec<NymNodeDescription>,
) {
let mut guard = self.nymnodes.write().await;
guard.bonded_nym_nodes = all_bonds
.into_iter()
.map(|details| (details.node_id(), details))
.collect();
guard.described_nodes = descriptions
.into_iter()
.map(|description| (description.node_id, description))
.collect();
guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL;
}
pub(crate) async fn pretty_gateways(&self) -> Vec<PrettyDetailedGatewayBond> {
let nodes_guard = self.nymnodes.read().await;
let location_guard = self.locations.read().await;
let mut pretty_gateways = vec![];
for (node_id, native_nymnode) in &nodes_guard.bonded_nym_nodes {
let Some(description) = nodes_guard.described_nodes.get(node_id) else {
continue;
};
if description.description.declared_role.entry {
let location = location_guard.get(node_id);
let bond = &native_nymnode.bond_information;
pretty_gateways.push(PrettyDetailedGatewayBond {
pledge_amount: bond.original_pledge.clone(),
owner: bond.owner.clone(),
block_height: bond.bonding_height,
gateway: Gateway {
host: bond.node.host.clone(),
mix_port: description.description.mix_port(),
clients_port: description.description.mixnet_websockets.ws_port,
location: description
.description
.auxiliary_details
.location
.as_ref()
.map(|l| l.to_string())
.unwrap_or_default(),
sphinx_key: description
.description
.host_information
.keys
.x25519
.to_base58_string(),
identity_key: bond.node.identity_key.clone(),
version: description
.description
.build_information
.build_version
.clone(),
},
proxy: None,
location: location.and_then(|l| l.location.clone()),
})
}
}
pretty_gateways
}
}