1use nym_crypto::asymmetric::ed25519::{PrivateKey, PublicKey, Signature};
2use nym_mixnet_contract_common::NodeId;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::{collections::HashSet, sync::LazyLock, time::SystemTime};
6use utoipa::ToSchema;
7
8static NETWORK_MONITORS: LazyLock<HashSet<String>> = LazyLock::new(|| {
9 let mut nm = HashSet::new();
10 nm.insert("5VsPyLbsBCq9PAMWmjKkToteVAKNabNqex6QwDf5fWzt".to_string());
11 nm
12});
13
14#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, ToSchema)]
15pub struct NodeResult {
16 #[schema(value_type = u32)]
17 pub node_id: NodeId,
18 pub identity: String,
19 pub reliability: u8,
20}
21
22impl NodeResult {
23 pub fn new(node_id: NodeId, identity: String, reliability: u8) -> Self {
24 NodeResult {
25 node_id,
26 identity,
27 reliability,
28 }
29 }
30}
31
32#[derive(Serialize, Deserialize, JsonSchema)]
33#[serde(untagged)]
34pub enum MonitorResults {
35 Mixnode(Vec<NodeResult>),
36 Gateway(Vec<NodeResult>),
37}
38
39#[derive(Serialize, Deserialize, JsonSchema, ToSchema)]
40pub struct MonitorMessage {
41 results: Vec<NodeResult>,
42 signature: String,
43 signer: String,
44 timestamp: i64,
45}
46
47impl MonitorMessage {
48 fn message_to_sign(results: &[NodeResult], timestamp: i64) -> Vec<u8> {
49 let mut msg = serde_json::to_vec(results).unwrap_or_default();
50 msg.extend_from_slice(×tamp.to_le_bytes());
51 msg
52 }
53
54 pub fn timely(&self) -> bool {
55 let now = SystemTime::now()
56 .duration_since(SystemTime::UNIX_EPOCH)
57 .expect("Time went backwards")
58 .as_secs() as i64;
59
60 now - self.timestamp < 5
61 }
62
63 pub fn new(results: Vec<NodeResult>, private_key: &PrivateKey) -> Self {
64 let timestamp = SystemTime::now()
65 .duration_since(SystemTime::UNIX_EPOCH)
66 .expect("Time went backwards")
67 .as_secs() as i64;
68
69 let msg = Self::message_to_sign(&results, timestamp);
70 let signature = private_key.sign(&msg);
71 let public_key = private_key.public_key();
72
73 MonitorMessage {
74 results,
75 signature: signature.to_base58_string(),
76 signer: public_key.to_base58_string(),
77 timestamp,
78 }
79 }
80
81 pub fn is_in_allowed(&self) -> bool {
82 NETWORK_MONITORS.contains(&self.signer)
83 }
84
85 pub fn results(&self) -> &[NodeResult] {
86 &self.results
87 }
88
89 pub fn verify(&self) -> bool {
90 let msg = Self::message_to_sign(&self.results, self.timestamp);
91
92 let signature = match Signature::from_base58_string(&self.signature) {
93 Ok(sig) => sig,
94 Err(_) => return false,
95 };
96
97 PublicKey::from_base58_string(&self.signer)
98 .map(|pk| pk.verify(msg, &signature).is_ok())
99 .unwrap_or(false)
100 }
101}