use crate::models::{
AuthenticatorDetailsV1, AuxiliaryDetailsV1, BinaryBuildInformationOwned, DeclaredRolesV1,
HostInformationV1, IpPacketRouterDetailsV1, NetworkRequesterDetailsV1,
OffsetDateTimeJsonSchemaWrapper, WebSocketsV1, WireguardDetailsV1,
};
use crate::nym_nodes::{BasicEntryInformation, NodeRole, SemiSkimmedNodeV1, SkimmedNodeV1};
use nym_crypto::asymmetric::{ed25519, x25519};
use nym_mixnet_contract_common::reward_params::Performance;
use nym_mixnet_contract_common::NodeId;
use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT};
use serde::{Deserialize, Serialize};
use tracing::warn;
use utoipa::ToSchema;
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
pub struct NymNodeDescriptionV1 {
#[schema(value_type = u32)]
pub node_id: NodeId,
pub contract_node_type: DescribedNodeTypeV1,
pub description: NymNodeDataV1,
}
impl NymNodeDescriptionV1 {
pub fn version(&self) -> &str {
&self.description.build_information.build_version
}
pub fn entry_information(&self) -> BasicEntryInformation {
BasicEntryInformation {
hostname: self.description.host_information.hostname.clone(),
ws_port: self.description.mixnet_websockets.ws_port,
wss_port: self.description.mixnet_websockets.wss_port,
}
}
pub fn ed25519_identity_key(&self) -> ed25519::PublicKey {
self.description.host_information.keys.ed25519
}
pub fn current_sphinx_key(&self, current_rotation_id: u32) -> x25519::PublicKey {
let keys = &self.description.host_information.keys;
if keys.current_x25519_sphinx_key.rotation_id == u32::MAX {
return keys.current_x25519_sphinx_key.public_key;
}
if current_rotation_id == keys.current_x25519_sphinx_key.rotation_id {
return keys.current_x25519_sphinx_key.public_key;
}
if let Some(pre_announced) = &keys.pre_announced_x25519_sphinx_key {
if pre_announced.rotation_id == current_rotation_id {
return pre_announced.public_key;
}
}
warn!(
"unexpected key rotation {current_rotation_id} for node {}",
self.node_id
);
keys.current_x25519_sphinx_key.public_key
}
pub fn to_skimmed_node(
&self,
current_rotation_id: u32,
role: NodeRole,
performance: Performance,
) -> SkimmedNodeV1 {
let keys = &self.description.host_information.keys;
let entry = if self.description.declared_role.entry {
Some(self.entry_information())
} else {
None
};
SkimmedNodeV1 {
node_id: self.node_id,
ed25519_identity_pubkey: keys.ed25519,
ip_addresses: self.description.host_information.ip_address.clone(),
mix_port: self.description.mix_port(),
x25519_sphinx_pubkey: self.current_sphinx_key(current_rotation_id),
role,
supported_roles: self.description.declared_role,
entry,
performance,
}
}
pub fn to_semi_skimmed_node(
&self,
current_rotation_id: u32,
role: NodeRole,
performance: Performance,
) -> SemiSkimmedNodeV1 {
let skimmed_node = self.to_skimmed_node(current_rotation_id, role, performance);
SemiSkimmedNodeV1 {
basic: skimmed_node,
x25519_noise_versioned_key: self
.description
.host_information
.keys
.x25519_versioned_noise,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(
export,
export_to = "ts-packages/types/src/types/rust/DescribedNodeType.ts"
)
)]
pub enum DescribedNodeTypeV1 {
LegacyMixnode,
LegacyGateway,
NymNode,
}
impl DescribedNodeTypeV1 {
pub fn is_nym_node(&self) -> bool {
matches!(self, DescribedNodeTypeV1::NymNode)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
pub struct NymNodeDataV1 {
#[serde(default)]
pub last_polled: OffsetDateTimeJsonSchemaWrapper,
pub host_information: HostInformationV1,
#[serde(default)]
pub declared_role: DeclaredRolesV1,
#[serde(default)]
pub auxiliary_details: AuxiliaryDetailsV1,
pub build_information: BinaryBuildInformationOwned,
#[serde(default)]
pub network_requester: Option<NetworkRequesterDetailsV1>,
#[serde(default)]
pub ip_packet_router: Option<IpPacketRouterDetailsV1>,
#[serde(default)]
pub authenticator: Option<AuthenticatorDetailsV1>,
#[serde(default)]
pub wireguard: Option<WireguardDetailsV1>,
pub mixnet_websockets: WebSocketsV1,
}
impl NymNodeDataV1 {
pub fn mix_port(&self) -> u16 {
self.auxiliary_details
.announce_ports
.mix_port
.unwrap_or(DEFAULT_MIX_LISTENING_PORT)
}
pub fn verloc_port(&self) -> u16 {
self.auxiliary_details
.announce_ports
.verloc_port
.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT)
}
}