nym-api caching node self described information

This commit is contained in:
Jędrzej Stuczyński
2023-10-05 17:11:24 +01:00
parent 00cdc5010a
commit baf7178dc8
27 changed files with 388 additions and 46 deletions
+1
View File
@@ -162,6 +162,7 @@ once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = "0.11.18"
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
tap = "1.0.1"
@@ -148,7 +148,7 @@ impl Client<ReqwestRpcClient> {
impl<C> Client<C> {
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client {
nym_api: nym_api_client,
@@ -162,7 +162,7 @@ impl<C, S> Client<C, S> {
where
S: OfflineSigner,
{
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client {
nym_api: nym_api_client,
@@ -242,7 +242,7 @@ pub struct NymApiClient {
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url);
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient { nym_api }
}
+1
View File
@@ -17,3 +17,4 @@ url = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
+49 -14
View File
@@ -6,9 +6,13 @@ use reqwest::{IntoUrl, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::warn;
use url::Url;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
pub type PathSegments<'a> = &'a [&'a str];
pub type Params<'a, K, V> = &'a [(K, V)];
@@ -28,14 +32,20 @@ pub enum HttpClientError<E: Display = String> {
source: url::ParseError,
},
#[error("not found")]
#[error("the requested resource could not be found")]
NotFound,
#[error("request failed with error message: {0}")]
GenericRequestFailure(String),
#[error("failed to resolve request. status code: {status}, additional error message: {error}")]
EndpointFailure { status: u16, error: E },
#[error("the request failed with status '{status}'. no additional error message provided")]
RequestFailure { status: StatusCode },
#[error("the returned response was empty. status: '{status}'")]
EmptyResponse { status: StatusCode },
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
EndpointFailure { status: StatusCode, error: E },
}
/// A simple extendable client wrapper for http request with extra url sanitization.
@@ -46,19 +56,35 @@ pub struct Client {
}
impl Client {
pub fn new(base_url: Url) -> Self {
pub fn new(base_url: Url, timeout: Option<Duration>) -> Self {
Client {
base_url,
reqwest_client: reqwest::Client::new(),
// TODO: we should probably be propagating the error rather than panicking,
// but that'd break bunch of things due to type changes
reqwest_client: reqwest::ClientBuilder::new()
.timeout(timeout.unwrap_or(DEFAULT_TIMEOUT))
.build()
.expect("Client::new()"),
}
}
pub fn new_url<U, E>(url: U) -> Result<Self, HttpClientError<E>>
pub fn new_url<U, E>(url: U, timeout: Option<Duration>) -> Result<Self, HttpClientError<E>>
where
U: IntoUrl,
E: Display,
{
Ok(Self::new(url.into_url()?))
// a naive check: if the provided URL does not start with http(s), add that scheme
let str_url = url.as_str();
if !str_url.starts_with("http") {
let alt = format!("http://{str_url}");
warn!("the provided url ('{str_url}') does not contain scheme information. Changing it to '{alt}' ...");
// TODO: or should we maybe default to https?
Self::new_url(alt, timeout)
} else {
Ok(Self::new(url.into_url()?, timeout))
}
}
pub fn change_base_url(&mut self, new_url: Url) {
@@ -111,7 +137,7 @@ impl Client {
E: Display + DeserializeOwned,
{
let res = self.send_get_request(path, params).await?;
parse_response(res).await
parse_response(res, false).await
}
pub async fn post_json<B, T, K, V, E>(
@@ -128,7 +154,7 @@ impl Client {
E: Display + DeserializeOwned,
{
let res = self.send_post_request(path, params, json_body).await?;
parse_response(res).await
parse_response(res, true).await
}
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
@@ -142,7 +168,7 @@ impl Client {
.get(self.base_url.join(endpoint.as_ref())?)
.send()
.await?;
parse_response(res).await
parse_response(res, false).await
}
pub async fn post_json_endpoint<B, T, S, E>(
@@ -162,7 +188,7 @@ impl Client {
.json(json_body)
.send()
.await?;
parse_response(res).await
parse_response(res, true).await
}
}
@@ -299,22 +325,31 @@ pub fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
url
}
async fn parse_response<T, E>(res: Response) -> Result<T, HttpClientError<E>>
async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
where
T: DeserializeOwned,
E: DeserializeOwned + Display,
{
let status = res.status();
if !allow_empty {
if let Some(0) = res.content_length() {
return Err(HttpClientError::EmptyResponse { status });
}
}
if res.status().is_success() {
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(HttpClientError::NotFound)
} else {
let plaintext = res.text().await?;
let Ok(plaintext) = res.text().await else {
return Err(HttpClientError::RequestFailure { status });
};
if let Ok(request_error) = serde_json::from_str(&plaintext) {
Err(HttpClientError::EndpointFailure {
status: status.as_u16(),
status,
error: request_error,
})
} else {
+1 -1
View File
@@ -12,7 +12,7 @@ cfg-if = { workspace = true }
dotenvy = { workspace = true }
hex-literal = "0.3.3"
once_cell = { workspace = true }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"]}
thiserror = { workspace = true }
url = { workspace = true }
+1 -1
View File
@@ -13,7 +13,7 @@ log = { workspace = true }
pin-project = "1.0"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
reqwest = { workspace = true }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
tap = "1.0.1"
thiserror = { workspace = true }
+1 -1
View File
@@ -24,7 +24,7 @@ reqwest = { workspace = true }
rocket = { version = "0.5.0-rc.2", features = ["json"] }
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
@@ -7,6 +7,6 @@ edition = "2021"
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { version = "1.0", features = ["derive"] }
ts-rs = { workspace = true, optional = true }
+5 -5
View File
@@ -16,7 +16,7 @@ rust-version = "1.56"
[dependencies]
async-trait = { workspace = true }
bs58 = {version = "0.4.0" }
bs58 = { version = "0.4.0" }
bip39 = { workspace = true }
cfg-if = "1.0"
clap = { version = "4.0", features = ["cargo", "derive"] }
@@ -27,7 +27,6 @@ humantime-serde = "1.0"
lazy_static = "1.4.0"
log = { workspace = true }
pin-project = "1.0"
pretty_env_logger = "0.4.0"
rand = "0.8.5"
rand-07 = { package = "rand", version = "0.7.3" } # required for compatibility
reqwest = { workspace = true, features = ["json"] }
@@ -49,7 +48,7 @@ url = { workspace = true }
ts-rs = { workspace = true, optional = true}
anyhow = "1.0"
anyhow = { workspace = true }
getset = "0.1.1"
sqlx = { version = "0.6.2", features = [
@@ -61,7 +60,7 @@ sqlx = { version = "0.6.2", features = [
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
zeroize = { workspace = true }
## ephemera-specific
@@ -106,13 +105,14 @@ nym-api-requests = { path = "nym-api-requests" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-bin-common = { path = "../common/bin-common" }
nym-node-tester-utils = { path = "../common/node-tester-utils" }
nym-node-requests = { path = "../nym-node/nym-node-requests" }
[features]
no-reward = []
generate-ts = ["ts-rs"]
[build-dependencies]
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { version = "0.6.2", features = [
"runtime-tokio-rustls",
"sqlite",
+3 -2
View File
@@ -10,13 +10,14 @@ bs58 = "0.4.0"
cosmrs = { workspace = true }
cosmwasm-std = { workspace = true }
getset = "0.1.1"
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"] }
ts-rs = { workspace = true, optional = true }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-mixnet-contract-common = { path= "../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-node-requests = { path = "../../nym-node/nym-node-requests", default-features = false }
[features]
default = []
generate-ts = ["ts-rs"]
generate-ts = ["ts-rs", "nym-mixnet-contract-common/generate-ts"]
+31
View File
@@ -9,6 +9,7 @@ use nym_mixnet_contract_common::rewarding::RewardEstimate;
use nym_mixnet_contract_common::{
GatewayBond, IdentityKey, Interval, MixId, MixNode, Percent, RewardedSetNodeStatus,
};
use nym_node_requests::api::v1::gateway::models::WebSockets;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
@@ -142,6 +143,10 @@ impl MixNodeBondAnnotated {
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct GatewayBondAnnotated {
pub gateway_bond: GatewayBond,
#[serde(default)]
pub self_described: Option<GatewayDescription>,
// NOTE: the performance field is deprecated in favour of node_performance
pub performance: Performance,
pub node_performance: NodePerformance,
@@ -158,6 +163,11 @@ impl GatewayBondAnnotated {
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct GatewayDescription {
// for now only expose what we need. this struct will evolve in the future (or be incorporated into nym-node properly)
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ComputeRewardEstParam {
pub performance: Option<Performance>,
@@ -344,3 +354,24 @@ pub struct CirculatingSupplyResponse {
pub vesting_tokens: Coin,
pub circulating_supply: Coin,
}
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct NymNodeDescription {
// for now we only care about their ws/wss situation, nothing more
pub mixnet_websockets: WebSockets,
}
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DescribedGateway {
pub bond: GatewayBond,
pub self_described: Option<NymNodeDescription>,
}
impl From<GatewayBond> for DescribedGateway {
fn from(bond: GatewayBond) -> Self {
DescribedGateway {
bond,
self_described: None,
}
}
}
+17
View File
@@ -6,7 +6,9 @@ extern crate rocket;
use crate::epoch_operations::RewardedSetUpdater;
use crate::network::models::NetworkDetails;
use crate::node_describe_cache::DescribedNodes;
use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater;
use crate::support::caching::cache::SharedCache;
use crate::support::cli;
use crate::support::cli::CliArgs;
use crate::support::config::Config;
@@ -34,8 +36,10 @@ mod ephemera;
mod epoch_operations;
pub(crate) mod network;
mod network_monitor;
pub(crate) mod node_describe_cache;
pub(crate) mod node_status_api;
pub(crate) mod nym_contract_cache;
pub(crate) mod nym_nodes;
pub(crate) mod support;
struct ShutdownHandles {
@@ -90,6 +94,18 @@ async fn start_nym_api_tasks(
let node_status_cache_state = rocket.state::<NodeStatusCache>().unwrap();
let circulating_supply_cache_state = rocket.state::<CirculatingSupplyCache>().unwrap();
let maybe_storage = rocket.state::<NymApiStorage>();
let described_nodes_state = rocket.state::<SharedCache<DescribedNodes>>().unwrap();
// start note describe cache refresher
// we should be doing the below, but can't due to our current startup structure
// let refresher = node_describe_cache::new_refresher(&config.topology_cacher);
// let cache = refresher.get_shared_cache();
node_describe_cache::new_refresher_with_initial_value(
&config.topology_cacher,
described_nodes_state.to_owned(),
)
.named("node-self-described-data-refresher")
.start(shutdown.subscribe_named("node-self-described-data-refresher"));
// start all the caches first
let nym_contract_cache_listener = nym_contract_cache::start_refresher(
@@ -98,6 +114,7 @@ async fn start_nym_api_tasks(
nyxd_client.clone(),
&shutdown,
);
node_status_api::start_cache_refresh(
&config.node_status_api,
nym_contract_cache_state,
+158
View File
@@ -0,0 +1,158 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::support::caching::cache::{SharedCache, UninitialisedCache};
use crate::support::caching::refresher::{CacheItemProvider, CacheRefresher};
use crate::support::config;
use crate::support::config::DEFAULT_NODE_DESCRIBE_BATCH_SIZE;
use futures_util::{stream, StreamExt};
use nym_api_requests::models::NymNodeDescription;
use nym_contracts_common::IdentityKey;
use nym_mixnet_contract_common::{Gateway, GatewayBond};
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
use nym_node_requests::api::v1::gateway::models::WebSockets;
use std::collections::HashMap;
use std::ops::Deref;
use thiserror::Error;
// type alias for ease of use
pub type DescribedNodes = HashMap<IdentityKey, NymNodeDescription>;
#[derive(Debug, Error)]
pub enum NodeDescribeCacheError {
#[error("contract cache hasn't been initialised")]
UninitialisedContractCache {
#[from]
source: UninitialisedCache,
},
#[error("gateway {gateway} has provided malformed host information ({host}: {source}")]
MalformedHost {
host: String,
gateway: IdentityKey,
#[source]
source: NymNodeApiClientError,
},
#[error("failed to query gateway '{gateway}': {source}")]
ApiFailure {
gateway: IdentityKey,
#[source]
source: NymNodeApiClientError,
},
}
pub struct NodeDescriptionProvider {
// for now we only care about gateways, nothing more
network_gateways: SharedCache<Vec<GatewayBond>>,
batch_size: usize,
}
impl NodeDescriptionProvider {
pub(crate) fn new() -> NodeDescriptionProvider {
NodeDescriptionProvider {
network_gateways: SharedCache::new(),
batch_size: DEFAULT_NODE_DESCRIBE_BATCH_SIZE,
}
}
#[must_use]
pub(crate) fn with_batch_size(mut self, batch_size: usize) -> Self {
self.batch_size = batch_size;
self
}
}
async fn get_gateway_websocket_support(
gateway: Gateway,
) -> Result<(IdentityKey, WebSockets), NodeDescribeCacheError> {
let client = match nym_node_requests::api::Client::new_url(&gateway.host, None) {
Ok(client) => client,
Err(err) => {
return Err(NodeDescribeCacheError::MalformedHost {
host: gateway.host,
gateway: gateway.identity_key,
source: err,
});
}
};
let websockets =
client
.get_mixnet_websockets()
.await
.map_err(|err| NodeDescribeCacheError::ApiFailure {
gateway: gateway.identity_key.clone(),
source: err,
})?;
Ok((gateway.identity_key, websockets))
}
#[async_trait]
impl CacheItemProvider for NodeDescriptionProvider {
type Item = HashMap<IdentityKey, NymNodeDescription>;
type Error = NodeDescribeCacheError;
async fn try_refresh(&self) -> Result<Self::Item, Self::Error> {
let guard = self.network_gateways.get().await?;
let gateways = &*guard;
// TODO: somehow bypass the 'higher-ranked lifetime error' and remove that redundant clone
let websockets = stream::iter(
gateways
.deref()
.clone()
.into_iter()
.map(|bond| bond.gateway)
.map(get_gateway_websocket_support),
)
.buffer_unordered(self.batch_size)
.filter_map(|res| async move {
match res {
Ok((identity, mixnet_websockets)) => {
Some((identity, NymNodeDescription { mixnet_websockets }))
}
Err(err) => {
// TODO: reduce it to trace/debug before PR
warn!("{err}");
None
}
}
})
.collect::<HashMap<_, _>>()
.await;
Ok(websockets)
}
}
// currently dead code : (
#[allow(dead_code)]
pub(crate) fn new_refresher(
config: &config::TopologyCacher,
) -> CacheRefresher<DescribedNodes, NodeDescribeCacheError> {
CacheRefresher::new(
Box::new(
NodeDescriptionProvider::new().with_batch_size(config.debug.node_describe_batch_size),
),
config.debug.node_describe_caching_interval,
)
}
pub(crate) fn new_refresher_with_initial_value(
config: &config::TopologyCacher,
initial: SharedCache<DescribedNodes>,
) -> CacheRefresher<DescribedNodes, NodeDescribeCacheError> {
CacheRefresher::new_with_initial_value(
Box::new(
NodeDescriptionProvider::new().with_batch_size(config.debug.node_describe_batch_size),
),
config.debug.node_describe_caching_interval,
initial,
)
}
+1
View File
@@ -180,6 +180,7 @@ pub(crate) async fn annotate_gateways_with_details(
annotated.push(GatewayBondAnnotated {
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
gateway_bond,
self_described: None,
performance,
node_performance,
});
+6 -7
View File
@@ -1,6 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use self::cache::refresher::NodeStatusCacheRefresher;
use crate::support::config;
use crate::{
nym_contract_cache::cache::NymContractCache,
support::{self, storage},
};
pub(crate) use cache::NodeStatusCache;
use nym_task::TaskManager;
use okapi::openapi3::OpenApi;
@@ -8,13 +14,6 @@ use rocket::Route;
use rocket_okapi::{openapi_get_routes_spec, settings::OpenApiSettings};
use std::time::Duration;
use crate::support::config;
use crate::{
nym_contract_cache::cache::NymContractCache,
support::{self, storage},
};
use self::cache::refresher::NodeStatusCacheRefresher;
pub(crate) mod cache;
pub(crate) mod helpers;
pub(crate) mod local_guard;
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use okapi::openapi3::OpenApi;
use rocket::Route;
use rocket_okapi::openapi_get_routes_spec;
use rocket_okapi::settings::OpenApiSettings;
pub(crate) mod routes;
/// Merges the routes with http information and returns it to Rocket for serving
pub(crate) fn nym_node_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
settings: routes::get_gateways_described
]
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::node_describe_cache::DescribedNodes;
use crate::nym_contract_cache::cache::NymContractCache;
use crate::support::caching::cache::SharedCache;
use nym_api_requests::models::DescribedGateway;
use rocket::serde::json::Json;
use rocket::State;
use rocket_okapi::openapi;
use std::ops::Deref;
// obviously this should get refactored later on because gateways will go away.
// unless maybe this will be filtering based on which nodes got assigned gateway role? TBD
#[openapi(tag = "Nym Nodes")]
#[get("/gateways/described")]
pub async fn get_gateways_described(
contract_cache: &State<NymContractCache>,
describe_cache: &State<SharedCache<DescribedNodes>>,
) -> Json<Vec<DescribedGateway>> {
let gateways = contract_cache.gateways_filtered().await;
if gateways.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(gateways.into_iter().map(Into::into).collect());
};
// TODO: this is extremely inefficient, but given we don't have many gateways,
// it shouldn't be too much of a problem until we go ahead with directory v3 / the smoosh 2: electric smoosharoo,
// but at that point (I hope) the whole caching situation should get refactored
Json(
gateways
.into_iter()
.map(|bond| DescribedGateway {
self_described: self_descriptions.deref().get(bond.identity()).cloned(),
bond,
})
.collect(),
)
}
+12
View File
@@ -49,6 +49,11 @@ const DEFAULT_PER_NODE_TEST_PACKETS: usize = 3;
const DEFAULT_TOPOLOGY_CACHE_INTERVAL: Duration = Duration::from_secs(30);
const DEFAULT_NODE_STATUS_CACHE_INTERVAL: Duration = Duration::from_secs(120);
const DEFAULT_CIRCULATING_SUPPLY_CACHE_INTERVAL: Duration = Duration::from_secs(3600);
pub(crate) const DEFAULT_NODE_DESCRIBE_CACHE_INTERVAL: Duration = Duration::from_secs(4500);
pub(crate) const DEFAULT_NODE_DESCRIBE_BATCH_SIZE: usize = 50;
const DEFAULT_MONITOR_THRESHOLD: u8 = 60;
const DEFAULT_MIN_MIXNODE_RELIABILITY: u8 = 50;
const DEFAULT_MIN_GATEWAY_RELIABILITY: u8 = 20;
@@ -451,12 +456,19 @@ pub struct TopologyCacher {
pub struct TopologyCacherDebug {
#[serde(with = "humantime_serde")]
pub caching_interval: Duration,
#[serde(with = "humantime_serde")]
pub node_describe_caching_interval: Duration,
pub node_describe_batch_size: usize,
}
impl Default for TopologyCacherDebug {
fn default() -> Self {
TopologyCacherDebug {
caching_interval: DEFAULT_TOPOLOGY_CACHE_INTERVAL,
node_describe_caching_interval: DEFAULT_NODE_DESCRIBE_CACHE_INTERVAL,
node_describe_batch_size: DEFAULT_NODE_DESCRIBE_BATCH_SIZE,
}
}
}
@@ -126,6 +126,7 @@ impl From<ConfigV1_1_21> for Config {
topology_cacher: TopologyCacher {
debug: TopologyCacherDebug {
caching_interval: value.topology_cacher.caching_interval,
..Default::default()
},
},
circulating_supply_cacher: CirculatingSupplyCacher {
+5 -1
View File
@@ -5,11 +5,13 @@ use crate::circulating_supply_api::cache::CirculatingSupplyCache;
use crate::coconut::{self, comm::QueryCommunicationChannel, InternalSignRequest};
use crate::network::models::NetworkDetails;
use crate::network::network_routes;
use crate::node_describe_cache::DescribedNodes;
use crate::node_status_api::{self, NodeStatusCache};
use crate::nym_contract_cache::cache::NymContractCache;
use crate::support::caching::cache::SharedCache;
use crate::support::config::Config;
use crate::support::{nyxd, storage};
use crate::{circulating_supply_api, nym_contract_cache};
use crate::{circulating_supply_api, nym_contract_cache, nym_nodes::nym_node_routes};
use anyhow::Result;
use rocket::http::Method;
use rocket::{Ignite, Rocket};
@@ -39,10 +41,12 @@ pub(crate) async fn setup_rocket(
"" => nym_contract_cache::nym_contract_cache_routes(&openapi_settings),
"/status" => node_status_api::node_status_routes(&openapi_settings, config.network_monitor.enabled),
"/network" => network_routes(&openapi_settings),
"" => nym_node_routes(&openapi_settings),
}
let rocket = rocket
.manage(network_details)
.manage(SharedCache::<DescribedNodes>::new())
.mount("/swagger", make_swagger_ui(&openapi::get_docs()))
.attach(setup_cors()?)
.attach(NymContractCache::stage())
+14
View File
@@ -2973,6 +2973,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tracing",
"url",
]
@@ -3886,6 +3887,7 @@ dependencies = [
"getset",
"nym-coconut-interface",
"nym-mixnet-contract-common",
"nym-node-requests",
"schemars",
"serde",
]
@@ -4322,6 +4324,18 @@ dependencies = [
"url",
]
[[package]]
name = "nym-node-requests"
version = "0.1.0"
dependencies = [
"nym-bin-common",
"nym-crypto",
"schemars",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "nym-nonexhaustive-delayqueue"
version = "0.1.0"
+1
View File
@@ -11,6 +11,7 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
+3 -1
View File
@@ -1,12 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::api::v1::node::models::HostInformation;
use crate::error::Error;
use nym_crypto::asymmetric::identity;
use serde::Serialize;
use std::ops::Deref;
#[cfg(feature = "openapi")]
use crate::api::v1::node::models::HostInformation;
#[cfg(feature = "client")]
pub mod client;
pub mod v1;
@@ -1,15 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Gateway {
pub client_interfaces: ClientInterfaces,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Wireguard {
#[cfg_attr(feature = "openapi", schema(example = 51820, default = 51820))]
@@ -18,7 +19,7 @@ pub struct Wireguard {
pub public_key: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ClientInterfaces {
pub wireguard: Option<Wireguard>,
@@ -27,7 +28,7 @@ pub struct ClientInterfaces {
// pub mixnet_tcp:
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct WebSockets {
#[cfg_attr(feature = "openapi", schema(example = 9000, default = 9000))]
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Mixnode {
// /// Base58 encoded ed25519 EdDSA public key of the mixnode.
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct NetworkRequester {
/// Base58 encoded ed25519 EdDSA public key of the network requester.
@@ -1,13 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "openapi")]
pub use crate::api::SignedHostInformation;
pub use nym_bin_common::build_information::BinaryBuildInformationOwned;
#[derive(Clone, Default, Debug, Copy, Serialize, Deserialize)]
#[derive(Clone, Default, Debug, Copy, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct NodeRoles {
pub mixnode_enabled: bool,
@@ -15,7 +16,7 @@ pub struct NodeRoles {
pub network_requester_enabled: bool,
}
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct HostInformation {
/// Ip address(es) of this host, such as `1.1.1.1`.
@@ -28,7 +29,7 @@ pub struct HostInformation {
pub keys: HostKeys,
}
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct HostKeys {
/// Base58-encoded ed25519 public key of this node. Currently it corresponds to either mixnode's or gateway's identity.