Skip to main content

nym_api_requests/models/
node_status.rs

1// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::helpers::PlaceholderJsonSchemaImpl;
5use crate::models::{CoinSchema, DisplayRole};
6use crate::pagination::PaginatedResponse;
7use cosmwasm_std::{Coin, Decimal};
8use nym_contracts_common::{IdentityKey, NaiveFloat};
9use nym_crypto::asymmetric::ed25519;
10use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
11use nym_mixnet_contract_common::reward_params::Performance;
12use nym_mixnet_contract_common::NodeId;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use std::time::Duration;
16use time::{Date, OffsetDateTime};
17use utoipa::ToSchema;
18
19pub use config_score::*;
20
21pub type StakeSaturation = Decimal;
22
23#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
24#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
25#[cfg_attr(
26    feature = "generate-ts",
27    ts(
28        export,
29        export_to = "ts-packages/types/src/types/rust/StakeSaturationResponse.ts"
30    )
31)]
32pub struct StakeSaturationResponse {
33    #[cfg_attr(feature = "generate-ts", ts(type = "string"))]
34    #[schema(value_type = String)]
35    pub saturation: StakeSaturation,
36
37    #[cfg_attr(feature = "generate-ts", ts(type = "string"))]
38    #[schema(value_type = String)]
39    pub uncapped_saturation: StakeSaturation,
40    pub as_at: i64,
41}
42
43pub mod config_score {
44    use nym_contracts_common::NaiveFloat;
45    use serde::{Deserialize, Serialize};
46    use std::cmp::Ordering;
47    use utoipa::ToSchema;
48
49    #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
50    pub struct ConfigScoreDataResponse {
51        pub parameters: ConfigScoreParams,
52        pub version_history: Vec<HistoricalNymNodeVersionEntry>,
53    }
54
55    #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema, PartialEq)]
56    pub struct HistoricalNymNodeVersionEntry {
57        /// The unique, ordered, id of this particular entry
58        pub id: u32,
59
60        /// Data associated with this particular version
61        pub version_information: HistoricalNymNodeVersion,
62    }
63
64    impl PartialOrd for HistoricalNymNodeVersionEntry {
65        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
66            // we only care about id for the purposes of ordering as they should have unique data
67            self.id.partial_cmp(&other.id)
68        }
69    }
70
71    impl From<nym_mixnet_contract_common::HistoricalNymNodeVersionEntry>
72        for HistoricalNymNodeVersionEntry
73    {
74        fn from(value: nym_mixnet_contract_common::HistoricalNymNodeVersionEntry) -> Self {
75            HistoricalNymNodeVersionEntry {
76                id: value.id,
77                version_information: value.version_information.into(),
78            }
79        }
80    }
81
82    #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema, PartialEq)]
83    pub struct HistoricalNymNodeVersion {
84        /// Version of the nym node that is going to be used for determining the version score of a node.
85        /// note: value stored here is pre-validated `semver::Version`
86        pub semver: String,
87
88        /// Block height of when this version has been added to the contract
89        pub introduced_at_height: u64,
90        // for now ignore that field. it will give nothing useful to the users
91        //     pub difference_since_genesis: TotalVersionDifference,
92    }
93
94    impl From<nym_mixnet_contract_common::HistoricalNymNodeVersion> for HistoricalNymNodeVersion {
95        fn from(value: nym_mixnet_contract_common::HistoricalNymNodeVersion) -> Self {
96            HistoricalNymNodeVersion {
97                semver: value.semver,
98                introduced_at_height: value.introduced_at_height,
99            }
100        }
101    }
102
103    #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
104    pub struct ConfigScoreParams {
105        /// Defines weights for calculating numbers of versions behind the current release.
106        pub version_weights: OutdatedVersionWeights,
107
108        /// Defines the parameters of the formula for calculating the version score
109        pub version_score_formula_params: VersionScoreFormulaParams,
110    }
111
112    /// Defines weights for calculating numbers of versions behind the current release.
113    #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
114    pub struct OutdatedVersionWeights {
115        pub major: u32,
116        pub minor: u32,
117        pub patch: u32,
118        pub prerelease: u32,
119    }
120
121    /// Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling)
122    /// define the relevant parameters
123    #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
124    pub struct VersionScoreFormulaParams {
125        pub penalty: f64,
126        pub penalty_scaling: f64,
127    }
128
129    impl From<nym_mixnet_contract_common::ConfigScoreParams> for ConfigScoreParams {
130        fn from(value: nym_mixnet_contract_common::ConfigScoreParams) -> Self {
131            ConfigScoreParams {
132                version_weights: value.version_weights.into(),
133                version_score_formula_params: value.version_score_formula_params.into(),
134            }
135        }
136    }
137
138    impl From<nym_mixnet_contract_common::OutdatedVersionWeights> for OutdatedVersionWeights {
139        fn from(value: nym_mixnet_contract_common::OutdatedVersionWeights) -> Self {
140            OutdatedVersionWeights {
141                major: value.major,
142                minor: value.minor,
143                patch: value.patch,
144                prerelease: value.prerelease,
145            }
146        }
147    }
148
149    impl From<nym_mixnet_contract_common::VersionScoreFormulaParams> for VersionScoreFormulaParams {
150        fn from(value: nym_mixnet_contract_common::VersionScoreFormulaParams) -> Self {
151            VersionScoreFormulaParams {
152                penalty: value.penalty.naive_to_f64(),
153                penalty_scaling: value.penalty_scaling.naive_to_f64(),
154            }
155        }
156    }
157}
158
159#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
160pub struct NodeRefreshBody {
161    #[serde(with = "bs58_ed25519_pubkey")]
162    #[schemars(with = "String")]
163    #[schema(value_type = String)]
164    pub node_identity: ed25519::PublicKey,
165
166    // a poor man's nonce
167    pub request_timestamp: i64,
168
169    #[schemars(with = "PlaceholderJsonSchemaImpl")]
170    #[schema(value_type = String)]
171    pub signature: ed25519::Signature,
172}
173
174impl NodeRefreshBody {
175    pub fn plaintext(node_identity: ed25519::PublicKey, request_timestamp: i64) -> Vec<u8> {
176        node_identity
177            .to_bytes()
178            .into_iter()
179            .chain(request_timestamp.to_be_bytes())
180            .chain(b"describe-cache-refresh-request".iter().copied())
181            .collect()
182    }
183
184    pub fn new(private_key: &ed25519::PrivateKey) -> Self {
185        let node_identity = private_key.public_key();
186        let request_timestamp = OffsetDateTime::now_utc().unix_timestamp();
187        let signature = private_key.sign(Self::plaintext(node_identity, request_timestamp));
188        NodeRefreshBody {
189            node_identity,
190            request_timestamp,
191            signature,
192        }
193    }
194
195    pub fn verify_signature(&self) -> bool {
196        self.node_identity
197            .verify(
198                Self::plaintext(self.node_identity, self.request_timestamp),
199                &self.signature,
200            )
201            .is_ok()
202    }
203
204    pub fn is_stale(&self) -> bool {
205        let Ok(encoded) = OffsetDateTime::from_unix_timestamp(self.request_timestamp) else {
206            return true;
207        };
208        let now = OffsetDateTime::now_utc();
209
210        if encoded > now {
211            return true;
212        }
213
214        if (encoded + Duration::from_secs(30)) < now {
215            return true;
216        }
217
218        false
219    }
220}
221
222#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
223pub struct UptimeResponse {
224    #[schema(value_type = u32)]
225    pub mix_id: NodeId,
226    // The same as node_performance.last_24h. Legacy
227    pub avg_uptime: u8,
228    pub node_performance: NodePerformance,
229}
230
231#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
232pub struct GatewayUptimeResponse {
233    pub identity: String,
234    // The same as node_performance.last_24h. Legacy
235    pub avg_uptime: u8,
236    pub node_performance: NodePerformance,
237}
238
239type Uptime = u8;
240
241#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
242pub struct MixnodeStatusReportResponse {
243    pub mix_id: NodeId,
244    pub identity: IdentityKey,
245    pub owner: String,
246    #[schema(value_type = u8)]
247    pub most_recent: Uptime,
248    #[schema(value_type = u8)]
249    pub last_hour: Uptime,
250    #[schema(value_type = u8)]
251    pub last_day: Uptime,
252}
253
254#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
255pub struct GatewayStatusReportResponse {
256    pub identity: String,
257    pub owner: String,
258    #[schema(value_type = u8)]
259    pub most_recent: Uptime,
260    #[schema(value_type = u8)]
261    pub last_hour: Uptime,
262    #[schema(value_type = u8)]
263    pub last_day: Uptime,
264}
265
266#[derive(Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
267#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
268#[cfg_attr(
269    feature = "generate-ts",
270    ts(
271        export,
272        export_to = "ts-packages/types/src/types/rust/PerformanceHistoryResponse.ts"
273    )
274)]
275pub struct PerformanceHistoryResponse {
276    #[schema(value_type = u32)]
277    pub node_id: NodeId,
278    pub history: PaginatedResponse<HistoricalPerformanceResponse>,
279}
280
281#[derive(Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
282#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
283#[cfg_attr(
284    feature = "generate-ts",
285    ts(
286        export,
287        export_to = "ts-packages/types/src/types/rust/UptimeHistoryResponse.ts"
288    )
289)]
290pub struct UptimeHistoryResponse {
291    #[schema(value_type = u32)]
292    pub node_id: NodeId,
293    pub history: PaginatedResponse<HistoricalUptimeResponse>,
294}
295
296#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
297#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
298#[cfg_attr(
299    feature = "generate-ts",
300    ts(
301        export,
302        export_to = "ts-packages/types/src/types/rust/HistoricalUptimeResponse.ts"
303    )
304)]
305pub struct HistoricalUptimeResponse {
306    #[schema(value_type = String, example = "1970-01-01")]
307    #[schemars(with = "String")]
308    #[cfg_attr(feature = "generate-ts", ts(type = "string"))]
309    pub date: Date,
310
311    pub uptime: Uptime,
312}
313
314#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
315#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
316#[cfg_attr(
317    feature = "generate-ts",
318    ts(
319        export,
320        export_to = "ts-packages/types/src/types/rust/HistoricalPerformanceResponse.ts"
321    )
322)]
323pub struct HistoricalPerformanceResponse {
324    #[schema(value_type = String, example = "1970-01-01")]
325    #[schemars(with = "String")]
326    #[cfg_attr(feature = "generate-ts", ts(type = "string"))]
327    pub date: Date,
328
329    pub performance: f64,
330}
331
332#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
333pub struct OldHistoricalUptimeResponse {
334    pub date: String,
335    #[schema(value_type = u8)]
336    pub uptime: Uptime,
337}
338
339#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
340pub struct MixnodeUptimeHistoryResponse {
341    pub mix_id: NodeId,
342    pub identity: String,
343    pub history: Vec<OldHistoricalUptimeResponse>,
344}
345
346#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
347pub struct GatewayUptimeHistoryResponse {
348    pub identity: String,
349    pub history: Vec<OldHistoricalUptimeResponse>,
350}
351
352#[derive(
353    Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, ToSchema, Default,
354)]
355#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
356#[cfg_attr(
357    feature = "generate-ts",
358    ts(
359        export,
360        export_to = "ts-packages/types/src/types/rust/MixnodeStatus.ts"
361    )
362)]
363#[serde(rename_all = "snake_case")]
364pub enum MixnodeStatus {
365    Active,   // in both the active set and the rewarded set
366    Standby,  // only in the rewarded set
367    Inactive, // in neither the rewarded set nor the active set, but is bonded
368    #[default]
369    NotFound, // doesn't even exist in the bonded set
370}
371impl MixnodeStatus {
372    pub fn is_active(&self) -> bool {
373        *self == MixnodeStatus::Active
374    }
375}
376
377#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
378#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
379#[cfg_attr(
380    feature = "generate-ts",
381    ts(
382        export,
383        export_to = "ts-packages/types/src/types/rust/MixnodeStatusResponse.ts"
384    )
385)]
386pub struct MixnodeStatusResponse {
387    pub status: MixnodeStatus,
388}
389
390#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
391pub struct NodePerformance {
392    #[schema(value_type = String)]
393    pub most_recent: Performance,
394    #[schema(value_type = String)]
395    pub last_hour: Performance,
396    #[schema(value_type = String)]
397    pub last_24h: Performance,
398}
399
400// imo for now there's no point in exposing more than that,
401// nym-api shouldn't be calculating apy or stake saturation for you.
402// it should just return its own metrics (performance) and then you can do with it as you wish
403#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
404#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
405#[cfg_attr(
406    feature = "generate-ts",
407    ts(
408        export,
409        export_to = "ts-packages/types/src/types/rust/NodeAnnotationV1.ts"
410    )
411)]
412pub struct NodeAnnotationV1 {
413    #[cfg_attr(feature = "generate-ts", ts(type = "string"))]
414    // legacy
415    #[schema(value_type = String)]
416    pub last_24h_performance: Performance,
417    pub current_role: Option<DisplayRole>,
418
419    pub detailed_performance: DetailedNodePerformanceV1,
420}
421
422#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
423#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
424#[cfg_attr(
425    feature = "generate-ts",
426    ts(
427        export,
428        export_to = "ts-packages/types/src/types/rust/ChainInteractionCapabilitiesDetailed.ts"
429    )
430)]
431pub struct ChainInteractionCapabilitiesDetailed {
432    #[schema(value_type = CoinSchema)]
433    #[cfg_attr(feature = "generate-ts", ts(type = "Coin"))]
434    pub on_chain_balance: Coin,
435
436    // later to be expanded with information on whether the grant would cover
437    // cosmwasm executemsg, but for now we assume any feegrant is sufficient
438    pub is_feegrant_grantee: bool,
439}
440
441#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
442#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
443#[cfg_attr(
444    feature = "generate-ts",
445    ts(
446        export,
447        export_to = "ts-packages/types/src/types/rust/NodeAnnotationV2.ts"
448    )
449)]
450pub struct NodeAnnotationV2 {
451    pub current_role: Option<DisplayRole>,
452
453    pub chain_interaction_capabilities: Option<ChainInteractionCapabilitiesDetailed>,
454
455    pub detailed_performance: DetailedNodePerformanceV2,
456}
457
458impl From<NodeAnnotationV2> for NodeAnnotationV1 {
459    fn from(value: NodeAnnotationV2) -> Self {
460        // map it from 0-1 range into 0-100
461        let scaled_performance =
462            value.detailed_performance.performance_score.clamp(0.0, 1.0) * 100.;
463        #[allow(clippy::unwrap_used)]
464        let legacy_performance =
465            Performance::from_percentage_value(scaled_performance as u64).unwrap();
466
467        NodeAnnotationV1 {
468            last_24h_performance: legacy_performance,
469            current_role: value.current_role,
470            detailed_performance: DetailedNodePerformanceV1 {
471                performance_score: value.detailed_performance.performance_score,
472                routing_score: value.detailed_performance.routing_score,
473                config_score: value.detailed_performance.config_score.into(),
474            },
475        }
476    }
477}
478
479#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
480#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
481#[cfg_attr(
482    feature = "generate-ts",
483    ts(
484        export,
485        export_to = "ts-packages/types/src/types/rust/DetailedNodePerformanceV1.ts"
486    )
487)]
488#[non_exhaustive]
489pub struct DetailedNodePerformanceV1 {
490    /// routing_score * config_score
491    pub performance_score: f64,
492
493    pub routing_score: RoutingScore,
494    pub config_score: ConfigScoreV1,
495}
496
497impl DetailedNodePerformanceV1 {
498    pub fn new(
499        performance_score: f64,
500        routing_score: RoutingScore,
501        config_score: ConfigScoreV1,
502    ) -> DetailedNodePerformanceV1 {
503        Self {
504            performance_score,
505            routing_score,
506            config_score,
507        }
508    }
509
510    pub fn to_rewarding_performance(&self) -> Performance {
511        Performance::naive_try_from_f64(self.performance_score).unwrap_or_default()
512    }
513}
514
515#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
516#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
517#[cfg_attr(
518    feature = "generate-ts",
519    ts(
520        export,
521        export_to = "ts-packages/types/src/types/rust/DetailedNodePerformanceV2.ts"
522    )
523)]
524#[non_exhaustive]
525pub struct DetailedNodePerformanceV2 {
526    /// The node's overall performance.
527    ///
528    /// A weighted mean, not a product: each enabled component contributes its own weight and
529    /// `routing_score * config_score` takes whatever weight is left over. With every component
530    /// disabled it is exactly `routing_score * config_score`.
531    pub performance_score: f64,
532
533    pub routing_score: RoutingScore,
534    pub config_score: ConfigScoreV2,
535    pub stress_testing_score: StressTestingScore,
536
537    /// The node's aggregated liveness score. Always reported, whether or not it is currently
538    /// folded into `performance_score` - it is served so that the divergence between it and
539    /// `routing_score` can be measured while liveness carries zero weight.
540    pub liveness_score: LivenessScore,
541}
542
543impl DetailedNodePerformanceV2 {
544    pub fn new(
545        performance_score: f64,
546        routing_score: RoutingScore,
547        config_score: ConfigScoreV2,
548        stress_testing_score: StressTestingScore,
549        liveness_score: LivenessScore,
550    ) -> DetailedNodePerformanceV2 {
551        Self {
552            performance_score,
553            routing_score,
554            config_score,
555            stress_testing_score,
556            liveness_score,
557        }
558    }
559
560    pub fn to_rewarding_performance(&self) -> Performance {
561        Performance::naive_try_from_f64(self.performance_score).unwrap_or_default()
562    }
563}
564
565#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
566#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
567#[cfg_attr(
568    feature = "generate-ts",
569    ts(export, export_to = "ts-packages/types/src/types/rust/RoutingScore.ts")
570)]
571#[non_exhaustive]
572pub struct RoutingScore {
573    /// Total score after taking all the criteria into consideration
574    pub score: f64,
575}
576
577impl RoutingScore {
578    pub fn new(score: f64) -> RoutingScore {
579        Self { score }
580    }
581
582    pub const fn zero() -> RoutingScore {
583        RoutingScore { score: 0.0 }
584    }
585
586    pub fn legacy_performance(&self) -> Performance {
587        Performance::naive_try_from_f64(self.score).unwrap_or_default()
588    }
589}
590
591#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
592#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
593#[cfg_attr(
594    feature = "generate-ts",
595    ts(
596        export,
597        export_to = "ts-packages/types/src/types/rust/StressTestingScore.ts"
598    )
599)]
600pub struct StressTestingScore {
601    pub score: f64,
602    /// Distinguishes a genuine zero score (node was tested and scored 0) from
603    /// "node was unreachable" (no successful sample was collected). Consumers may use
604    /// this to decide whether to penalise the node or treat the score as missing.
605    pub was_reachable: bool,
606}
607
608impl StressTestingScore {
609    pub fn unreachable() -> Self {
610        StressTestingScore {
611            score: 0.0,
612            was_reachable: false,
613        }
614    }
615}
616
617/// A node's aggregated liveness score: the average of the minimal-hop delivery ratios measured
618/// against it over the configured window.
619///
620/// Same shape as [`StressTestingScore`] and deliberately a distinct type, because the two are
621/// separate components with separate weights: liveness ships at weight ZERO while its divergence
622/// from the v1 routing score is measured, so a consumer that confused the two would silently
623/// weight an inert score.
624#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
625#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
626#[cfg_attr(
627    feature = "generate-ts",
628    ts(
629        export,
630        export_to = "ts-packages/types/src/types/rust/LivenessScore.ts"
631    )
632)]
633pub struct LivenessScore {
634    pub score: f64,
635
636    /// Distinguishes a genuine zero score (the node was probed and delivered nothing) from
637    /// "no sample was collected". A liveness zero is expected during rollout for reasons unrelated
638    /// to forwarding - a node that has not ingested its agents' authorisations, or a gateway not
639    /// yet carrying the monitor-session behaviour - so consumers need to tell the two apart.
640    pub was_reachable: bool,
641}
642
643impl LivenessScore {
644    pub fn unreachable() -> Self {
645        LivenessScore {
646            score: 0.0,
647            was_reachable: false,
648        }
649    }
650}
651
652#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
653#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
654#[cfg_attr(
655    feature = "generate-ts",
656    ts(
657        export,
658        export_to = "ts-packages/types/src/types/rust/ConfigScoreV2.ts"
659    )
660)]
661#[non_exhaustive]
662pub struct ConfigScoreV2 {
663    /// Total score after taking all the criteria into consideration
664    pub score: f64,
665
666    pub versions_behind: Option<u32>,
667    pub self_described_api_available: bool,
668    pub accepted_terms_and_conditions: bool,
669    pub runs_nym_node_binary: bool,
670
671    /// Describes the node is capable of sending chain/contract transactions
672    pub chain_interaction_capabilities: ChainInteractionCapabilities,
673}
674
675impl ConfigScoreV2 {
676    pub fn new(
677        score: f64,
678        versions_behind: Option<u32>,
679        accepted_terms_and_conditions: bool,
680        runs_nym_node_binary: bool,
681        chain_interaction_capabilities: ChainInteractionCapabilities,
682    ) -> ConfigScoreV2 {
683        Self {
684            score,
685            versions_behind,
686            self_described_api_available: true,
687            accepted_terms_and_conditions,
688            runs_nym_node_binary,
689            chain_interaction_capabilities,
690        }
691    }
692
693    pub fn unavailable() -> ConfigScoreV2 {
694        ConfigScoreV2 {
695            score: 0.0,
696            versions_behind: None,
697            self_described_api_available: false,
698            accepted_terms_and_conditions: false,
699            runs_nym_node_binary: false,
700            chain_interaction_capabilities: Default::default(),
701        }
702    }
703}
704
705impl From<nym_config_score::ConfigScoreBreakdown> for ConfigScoreV2 {
706    fn from(breakdown: nym_config_score::ConfigScoreBreakdown) -> Self {
707        ConfigScoreV2 {
708            score: breakdown.score,
709            versions_behind: breakdown.versions_behind,
710            self_described_api_available: true,
711            accepted_terms_and_conditions: breakdown.accepted_terms,
712            // every bonded node runs nym-node; the field stays for wire/V1 compat
713            runs_nym_node_binary: true,
714            chain_interaction_capabilities: ChainInteractionCapabilities {
715                has_sufficient_tokens: breakdown.has_sufficient_tokens,
716                is_fee_grant_grantee: breakdown.is_feegrant_grantee,
717            },
718        }
719    }
720}
721
722#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
723#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
724#[cfg_attr(
725    feature = "generate-ts",
726    ts(
727        export,
728        export_to = "ts-packages/types/src/types/rust/ChainInteractionCapabilities.ts"
729    )
730)]
731pub struct ChainInteractionCapabilities {
732    pub has_sufficient_tokens: bool,
733    pub is_fee_grant_grantee: bool,
734}
735
736impl ChainInteractionCapabilities {
737    pub fn new(has_sufficient_tokens: bool, is_fee_grant_grantee: bool) -> Self {
738        Self {
739            has_sufficient_tokens,
740            is_fee_grant_grantee,
741        }
742    }
743
744    pub fn can_send_transactions(&self) -> bool {
745        self.has_sufficient_tokens || self.is_fee_grant_grantee
746    }
747}
748
749impl From<ConfigScoreV2> for ConfigScoreV1 {
750    fn from(score_v2: ConfigScoreV2) -> ConfigScoreV1 {
751        ConfigScoreV1 {
752            score: score_v2.score,
753            versions_behind: score_v2.versions_behind,
754            self_described_api_available: score_v2.self_described_api_available,
755            accepted_terms_and_conditions: score_v2.accepted_terms_and_conditions,
756            runs_nym_node_binary: score_v2.runs_nym_node_binary,
757        }
758    }
759}
760
761#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, ToSchema)]
762#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
763#[cfg_attr(
764    feature = "generate-ts",
765    ts(
766        export,
767        export_to = "ts-packages/types/src/types/rust/ConfigScoreV1.ts"
768    )
769)]
770#[non_exhaustive]
771pub struct ConfigScoreV1 {
772    /// Total score after taking all the criteria into consideration
773    pub score: f64,
774
775    pub versions_behind: Option<u32>,
776    pub self_described_api_available: bool,
777    pub accepted_terms_and_conditions: bool,
778    pub runs_nym_node_binary: bool,
779}
780
781#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
782#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
783#[cfg_attr(
784    feature = "generate-ts",
785    ts(
786        export,
787        export_to = "ts-packages/types/src/types/rust/AnnotationResponseV1.ts"
788    )
789)]
790pub struct AnnotationResponseV1 {
791    #[schema(value_type = u32)]
792    pub node_id: NodeId,
793    pub annotation: Option<NodeAnnotationV1>,
794}
795
796#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
797#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
798#[cfg_attr(
799    feature = "generate-ts",
800    ts(
801        export,
802        export_to = "ts-packages/types/src/types/rust/AnnotationResponseV2.ts"
803    )
804)]
805pub struct AnnotationResponseV2 {
806    #[schema(value_type = u32)]
807    pub node_id: NodeId,
808    pub annotation: Option<NodeAnnotationV2>,
809}
810
811#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
812#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
813#[cfg_attr(
814    feature = "generate-ts",
815    ts(
816        export,
817        export_to = "ts-packages/types/src/types/rust/NodePerformanceResponse.ts"
818    )
819)]
820pub struct NodePerformanceResponse {
821    #[schema(value_type = u32)]
822    pub node_id: NodeId,
823    pub performance: Option<f64>,
824}
825
826#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
827#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
828#[cfg_attr(
829    feature = "generate-ts",
830    ts(
831        export,
832        export_to = "ts-packages/types/src/types/rust/NodeDatePerformanceResponse.ts"
833    )
834)]
835pub struct NodeDatePerformanceResponse {
836    #[schema(value_type = u32)]
837    pub node_id: NodeId,
838    #[schema(value_type = String, example = "1970-01-01")]
839    #[schemars(with = "String")]
840    #[cfg_attr(feature = "generate-ts", ts(type = "string"))]
841    pub date: Date,
842    pub performance: Option<f64>,
843}