Pain/polyfill deprecated endpoints (#5131)

* polyfilled contract cache endpoints

* polyfilled legacy described endpoints
This commit is contained in:
Jędrzej Stuczyński
2024-11-14 15:32:20 +00:00
committed by GitHub
parent 46a33b5ef6
commit 6809f7302e
4 changed files with 295 additions and 48 deletions
+141 -17
View File
@@ -6,10 +6,12 @@ use crate::node_status_api::helpers::{
_get_rewarded_set_legacy_mixnodes_detailed,
};
use crate::support::http::state::AppState;
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
use axum::extract::State;
use axum::{Json, Router};
use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer;
use nym_api_requests::models::MixNodeBondAnnotated;
use nym_mixnet_contract_common::reward_params::Performance;
use nym_mixnet_contract_common::{reward_params::RewardingParams, GatewayBond, Interval, NodeId};
use std::collections::HashSet;
@@ -58,11 +60,47 @@ pub(crate) fn nym_contract_cache_routes() -> Router<AppState> {
)]
#[deprecated]
async fn get_mixnodes(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
state
.nym_contract_cache()
.legacy_mixnodes_filtered()
.await
.into()
let mut out = state.nym_contract_cache().legacy_mixnodes_filtered().await;
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
return Json(out);
};
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
return Json(out);
};
let Ok(annotations) = state.node_annotations().await else {
return Json(out);
};
// safety: valid percentage value
#[allow(clippy::unwrap_used)]
let p50 = Performance::from_percentage_value(50).unwrap();
for nym_node in &**migrated_nymnodes {
// if we can't get it self-described data, ignore it
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
continue;
};
// if the node hasn't declared it can be a mixnode, ignore it
if !description.declared_role.mixnode {
continue;
}
// if we don't have annotation for this node, ignore it
let Some(annotation) = annotations.get(&nym_node.node_id()) else {
continue;
};
// equivalent of legacy mixnode being blacklisted
if annotation.last_24h_performance < p50 {
continue;
}
let node = to_legacy_mixnode(nym_node, description);
out.push(node);
}
Json(out)
}
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
@@ -97,15 +135,54 @@ async fn get_mixnodes_detailed(State(state): State<AppState>) -> Json<Vec<MixNod
)]
#[deprecated]
async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
Json(
state
.nym_contract_cache()
.legacy_gateways_filtered()
.await
.into_iter()
.map(Into::into)
.collect(),
)
// legacy
let mut out: Vec<GatewayBond> = state
.nym_contract_cache()
.legacy_gateways_filtered()
.await
.into_iter()
.map(Into::into)
.collect();
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
return Json(out);
};
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
return Json(out);
};
let Ok(annotations) = state.node_annotations().await else {
return Json(out);
};
// safety: valid percentage value
#[allow(clippy::unwrap_used)]
let p50 = Performance::from_percentage_value(50).unwrap();
for nym_node in &**migrated_nymnodes {
// if we can't get it self-described data, ignore it
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
continue;
};
// if the node hasn't declared it can be a gateway, ignore it
if !description.declared_role.entry {
continue;
}
// if we don't have annotation for this node, ignore it
let Some(annotation) = annotations.get(&nym_node.node_id()) else {
continue;
};
// equivalent of legacy gateway being blacklisted
if annotation.last_24h_performance < p50 {
continue;
}
let node = to_legacy_gateway(nym_node, description);
out.push(node);
}
Json(out)
}
#[utoipa::path(
@@ -166,12 +243,59 @@ async fn get_rewarded_set_detailed(
)]
#[deprecated]
async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
state
let mut out = state
.nym_contract_cache()
.legacy_v1_active_set_mixnodes()
.await
.clone()
.into()
.clone();
let Some(rewarded_set) = state.nym_contract_cache().rewarded_set().await else {
return Json(out);
};
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
return Json(out);
};
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
return Json(out);
};
let Ok(annotations) = state.node_annotations().await else {
return Json(out);
};
// safety: valid percentage value
#[allow(clippy::unwrap_used)]
let p50 = Performance::from_percentage_value(50).unwrap();
for nym_node in &**migrated_nymnodes {
// if we can't get it self-described data, ignore it
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
continue;
};
// if the node hasn't declared it can be a mixnode, ignore it
if !description.declared_role.mixnode {
continue;
}
// if we don't have annotation for this node, ignore it
let Some(annotation) = annotations.get(&nym_node.node_id()) else {
continue;
};
// equivalent of legacy mixnode being blacklisted
if annotation.last_24h_performance < p50 {
continue;
}
// if the node is not in the active set, ignore it
if !rewarded_set.is_active_mixnode(&nym_node.node_id()) {
continue;
}
let node = to_legacy_mixnode(nym_node, description);
out.push(node);
}
Json(out)
}
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
+67 -31
View File
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::http::state::AppState;
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
use axum::extract::State;
use axum::{Json, Router};
use nym_api_requests::legacy::LegacyMixNodeBondWithLayer;
use nym_api_requests::models::{LegacyDescribedGateway, LegacyDescribedMixNode};
// we want to mark the routes as deprecated in swagger, but still expose them
@@ -34,25 +36,44 @@ async fn get_gateways_described(
) -> Json<Vec<LegacyDescribedGateway>> {
let contract_cache = state.nym_contract_cache();
let describe_cache = state.described_nodes_cache();
let gateways = contract_cache.legacy_gateways_filtered().await;
if gateways.is_empty() {
return Json(Vec::new());
}
// legacy
let legacy = contract_cache.legacy_gateways_filtered().await;
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy gateways
let Ok(self_descriptions) = describe_cache.get().await else {
return Json(gateways.into_iter().map(Into::into).collect());
let Ok(describe_cache) = describe_cache.get().await else {
return Json(legacy.into_iter().map(Into::into).collect());
};
Json(
gateways
.into_iter()
.map(|bond| LegacyDescribedGateway {
self_described: self_descriptions.get_description(&bond.node_id).cloned(),
bond: bond.bond,
})
.collect(),
)
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
let mut out = Vec::new();
for legacy_bond in legacy {
out.push(LegacyDescribedGateway {
self_described: describe_cache
.get_description(&legacy_bond.node_id)
.cloned(),
bond: legacy_bond.bond,
})
}
for nym_node in migrated_nymnodes {
// we ALWAYS need description to set legacy fields
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
continue;
};
// if the node hasn't declared it can be a gateway, ignore it
if !description.declared_role.entry {
continue;
}
out.push(LegacyDescribedGateway {
bond: to_legacy_gateway(&nym_node, description),
self_described: Some(description.clone()),
})
}
Json(out)
}
#[utoipa::path(
@@ -70,28 +91,43 @@ async fn get_mixnodes_described(
let contract_cache = state.nym_contract_cache();
let describe_cache = state.described_nodes_cache();
let mixnodes = contract_cache
let legacy: Vec<LegacyMixNodeBondWithLayer> = contract_cache
.legacy_mixnodes_filtered()
.await
.into_iter()
.map(|m| m.bond_information)
.collect::<Vec<_>>();
if mixnodes.is_empty() {
return Json(Vec::new());
}
// if the self describe cache is unavailable, well, don't attach describe data
let Ok(self_descriptions) = describe_cache.get().await else {
return Json(mixnodes.into_iter().map(Into::into).collect());
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy mixnodes
let Ok(describe_cache) = describe_cache.get().await else {
return Json(legacy.into_iter().map(Into::into).collect());
};
Json(
mixnodes
.into_iter()
.map(|bond| LegacyDescribedMixNode {
self_described: self_descriptions.get_description(&bond.mix_id).cloned(),
bond,
})
.collect(),
)
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
let mut out = Vec::new();
for legacy_bond in legacy {
out.push(LegacyDescribedMixNode {
self_described: describe_cache.get_description(&legacy_bond.mix_id).cloned(),
bond: legacy_bond,
})
}
for nym_node in migrated_nymnodes {
// we ALWAYS need description to set legacy fields
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
continue;
};
// if the node hasn't declared it can be a gateway, ignore it
if !description.declared_role.mixnode {
continue;
}
out.push(LegacyDescribedMixNode {
bond: to_legacy_mixnode(&nym_node, description).bond_information,
self_described: Some(description.clone()),
})
}
Json(out)
}
+85
View File
@@ -0,0 +1,85 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_api_requests::legacy::{LegacyMixNodeBondWithLayer, LegacyMixNodeDetailsWithLayer};
use nym_api_requests::models::NymNodeData;
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
use nym_crypto::aes::cipher::crypto_common::rand_core::OsRng;
use nym_mixnet_contract_common::mixnode::LegacyPendingMixNodeChanges;
use nym_mixnet_contract_common::{
Gateway, GatewayBond, LegacyMixLayer, MixNode, MixNodeBond, NymNodeDetails,
};
use rand::prelude::SliceRandom;
pub(crate) fn to_legacy_mixnode(
nym_node: &NymNodeDetails,
description: &NymNodeData,
) -> LegacyMixNodeDetailsWithLayer {
let layer_choices = [
LegacyMixLayer::One,
LegacyMixLayer::Two,
LegacyMixLayer::Three,
];
let mut rng = OsRng;
// slap a random layer on it because legacy clients don't understand a concept of layerless mixnodes
// SAFETY: the slice is not empty so the unwrap is fine
#[allow(clippy::unwrap_used)]
let layer = layer_choices.choose(&mut rng).copied().unwrap();
LegacyMixNodeDetailsWithLayer {
bond_information: LegacyMixNodeBondWithLayer {
bond: MixNodeBond {
mix_id: nym_node.node_id(),
owner: nym_node.bond_information.owner.clone(),
original_pledge: nym_node.bond_information.original_pledge.clone(),
mix_node: MixNode {
host: nym_node.bond_information.node.host.clone(),
mix_port: description.mix_port(),
verloc_port: description.verloc_port(),
http_api_port: nym_node
.bond_information
.node
.custom_http_port
.unwrap_or(DEFAULT_NYM_NODE_HTTP_PORT),
sphinx_key: description.host_information.keys.x25519.to_base58_string(),
identity_key: nym_node.bond_information.node.identity_key.clone(),
version: description.build_information.build_version.clone(),
},
proxy: None,
bonding_height: nym_node.bond_information.bonding_height,
is_unbonding: nym_node.bond_information.is_unbonding,
},
layer,
},
rewarding_details: nym_node.rewarding_details.clone(),
pending_changes: LegacyPendingMixNodeChanges {
pledge_change: nym_node.pending_changes.pledge_change,
},
}
}
pub(crate) fn to_legacy_gateway(
nym_node: &NymNodeDetails,
description: &NymNodeData,
) -> GatewayBond {
GatewayBond {
pledge_amount: nym_node.bond_information.original_pledge.clone(),
owner: nym_node.bond_information.owner.clone(),
block_height: nym_node.bond_information.bonding_height,
gateway: Gateway {
host: nym_node.bond_information.node.host.clone(),
mix_port: description.mix_port(),
clients_port: description.mixnet_websockets.ws_port,
location: description
.auxiliary_details
.location
.map(|c| c.to_string())
.unwrap_or_default(),
sphinx_key: description.host_information.keys.x25519.to_base58_string(),
identity_key: nym_node.bond_information.node.identity_key.clone(),
version: description.build_information.build_version.clone(),
},
proxy: None,
}
}
+2
View File
@@ -7,3 +7,5 @@ pub(crate) mod config;
pub(crate) mod http;
pub(crate) mod nyxd;
pub(crate) mod storage;
pub(crate) mod legacy_helpers;