From 8f2cb95ffaceb1314481189e64923d5745ec6bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 6 Oct 2023 09:55:27 +0100 Subject: [PATCH] caching self reported host info --- nym-api/nym-api-requests/src/models.rs | 3 ++ nym-api/src/node_describe_cache/mod.rs | 37 +++++++++++++++---- nym-node/nym-node-requests/src/api/client.rs | 6 +++ nym-node/nym-node-requests/src/api/mod.rs | 22 ++++++++--- .../src/api/v1/node/models.rs | 1 - 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index 0ee2160623..a9c8e0d7d9 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -10,6 +10,7 @@ use nym_mixnet_contract_common::{ GatewayBond, IdentityKey, Interval, MixId, MixNode, Percent, RewardedSetNodeStatus, }; use nym_node_requests::api::v1::gateway::models::WebSockets; +use nym_node_requests::api::v1::node::models::HostInformation; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; @@ -357,6 +358,8 @@ pub struct CirculatingSupplyResponse { #[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct NymNodeDescription { + pub host_information: HostInformation, + // for now we only care about their ws/wss situation, nothing more pub mixnet_websockets: WebSockets, } diff --git a/nym-api/src/node_describe_cache/mod.rs b/nym-api/src/node_describe_cache/mod.rs index 6b3a4f57ea..f50aabfcf1 100644 --- a/nym-api/src/node_describe_cache/mod.rs +++ b/nym-api/src/node_describe_cache/mod.rs @@ -11,7 +11,6 @@ use nym_api_requests::models::NymNodeDescription; use nym_contracts_common::IdentityKey; use nym_mixnet_contract_common::Gateway; use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt}; -use nym_node_requests::api::v1::gateway::models::WebSockets; use std::collections::HashMap; use thiserror::Error; @@ -43,6 +42,10 @@ pub enum NodeDescribeCacheError { #[source] source: NymNodeApiClientError, }, + + // TODO: perhaps include more details here like whether key/signature/payload was malformed + #[error("could not verify signed host information for gateway '{gateway}'")] + MissignedHostInformation { gateway: IdentityKey }, } pub struct NodeDescriptionProvider { @@ -68,9 +71,9 @@ impl NodeDescriptionProvider { } } -async fn get_gateway_websocket_support( +async fn get_gateway_description( gateway: Gateway, -) -> Result<(IdentityKey, WebSockets), NodeDescribeCacheError> { +) -> Result<(IdentityKey, NymNodeDescription), NodeDescribeCacheError> { let client = match nym_node_requests::api::Client::new_url(&gateway.host, None) { Ok(client) => client, Err(err) => { @@ -82,6 +85,21 @@ async fn get_gateway_websocket_support( } }; + let host_info = + client + .get_host_information() + .await + .map_err(|err| NodeDescribeCacheError::ApiFailure { + gateway: gateway.identity_key.clone(), + source: err, + })?; + + if !host_info.verify_host_information() { + return Err(NodeDescribeCacheError::MissignedHostInformation { + gateway: gateway.identity_key, + }); + } + let websockets = client .get_mixnet_websockets() @@ -91,7 +109,12 @@ async fn get_gateway_websocket_support( source: err, })?; - Ok((gateway.identity_key, websockets)) + let description = NymNodeDescription { + host_information: host_info.data, + mixnet_websockets: websockets, + }; + + Ok((gateway.identity_key, description)) } #[async_trait] @@ -120,14 +143,12 @@ impl CacheItemProvider for NodeDescriptionProvider { // .clone() .into_iter() .map(|bond| bond.gateway) - .map(get_gateway_websocket_support), + .map(get_gateway_description), ) .buffer_unordered(self.batch_size) .filter_map(|res| async move { match res { - Ok((identity, mixnet_websockets)) => { - Some((identity, NymNodeDescription { mixnet_websockets })) - } + Ok((identity, description)) => Some((identity, description)), Err(err) => { // TODO: reduce it to trace/debug before PR warn!("{err}"); diff --git a/nym-node/nym-node-requests/src/api/client.rs b/nym-node/nym-node-requests/src/api/client.rs index 4b05bc9c67..26c43cd6d9 100644 --- a/nym-node/nym-node-requests/src/api/client.rs +++ b/nym-node/nym-node-requests/src/api/client.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::api::v1::gateway::models::WebSockets; +use crate::api::v1::node::models::SignedHostInformation; use crate::routes; use async_trait::async_trait; use http_api_client::{ApiClient, HttpClientError}; @@ -13,6 +14,11 @@ pub type NymNodeApiClientError = HttpClientError; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait NymNodeApiClientExt: ApiClient { + async fn get_host_information(&self) -> Result { + self.get_json_from(routes::api::v1::host_info_absolute()) + .await + } + // TODO: implement calls for other endpoints; for now I only care about the wss async fn get_mixnet_websockets(&self) -> Result { self.get_json_from( diff --git a/nym-node/nym-node-requests/src/api/mod.rs b/nym-node/nym-node-requests/src/api/mod.rs index 6c7811a9e2..22cf67ce10 100644 --- a/nym-node/nym-node-requests/src/api/mod.rs +++ b/nym-node/nym-node-requests/src/api/mod.rs @@ -1,14 +1,12 @@ // Copyright 2023 - Nym Technologies SA // 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 serde::{Deserialize, Serialize}; use std::ops::Deref; -#[cfg(feature = "openapi")] -use crate::api::v1::node::models::HostInformation; - #[cfg(feature = "client")] pub mod client; pub mod v1; @@ -16,7 +14,11 @@ pub mod v1; #[cfg(feature = "client")] pub use client::Client; -#[derive(Debug, Clone, Serialize)] +// create the type alias manually if openapi is not enabled +#[cfg(not(feature = "openapi"))] +pub type SignedHostInformation = SignedData; + +#[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[cfg_attr(feature = "openapi", aliases(SignedHostInformation = SignedData))] pub struct SignedData { @@ -50,6 +52,16 @@ impl SignedData { } } +impl SignedHostInformation { + pub fn verify_host_information(&self) -> bool { + let Ok(pub_key) = identity::PublicKey::from_base58_string(&self.keys.ed25519) else { + return false; + }; + + self.verify(&pub_key) + } +} + impl Deref for SignedData { type Target = T; diff --git a/nym-node/nym-node-requests/src/api/v1/node/models.rs b/nym-node/nym-node-requests/src/api/v1/node/models.rs index 2be2625c34..9e2fb3902b 100644 --- a/nym-node/nym-node-requests/src/api/v1/node/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/node/models.rs @@ -4,7 +4,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -#[cfg(feature = "openapi")] pub use crate::api::SignedHostInformation; pub use nym_bin_common::build_information::BinaryBuildInformationOwned;