Pain/polyfill deprecated endpoints (#5131)
* polyfilled contract cache endpoints * polyfilled legacy described endpoints
This commit is contained in:
committed by
GitHub
parent
46a33b5ef6
commit
6809f7302e
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user