Skip to main content

nym_bandwidth_controller/
traits.rs

1// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use async_trait::async_trait;
5
6use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
7use nym_credentials::ecash::bandwidth::serialiser::signatures::{
8    AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
9};
10use nym_credentials_interface::TicketType;
11use nym_crypto::asymmetric::ed25519;
12use nym_ecash_time::{Date, OffsetDateTime};
13use nym_validator_client::nym_api::EpochId;
14
15use crate::error::FetcherErrorKind;
16use crate::NymCredential;
17use crate::{error::BandwidthControllerError, PreparedCredential, PreparedCredentialMetadata};
18
19#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
20#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
21pub trait BandwidthTicketProvider: Send + Sync {
22    async fn get_ecash_ticket(
23        &self,
24        ticket_type: TicketType,
25        gateway_id: ed25519::PublicKey,
26        tickets_to_spend: u32,
27        spend_time: OffsetDateTime,
28    ) -> Result<Option<PreparedCredential>, BandwidthControllerError>;
29
30    async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError>;
31
32    async fn attempt_revert_spending(
33        &self,
34        metadata: PreparedCredentialMetadata,
35    ) -> Result<bool, BandwidthControllerError>;
36
37    async fn close(&self);
38}
39
40#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
41#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
42impl<T: BandwidthTicketProvider + ?Sized + Send> BandwidthTicketProvider for Box<T> {
43    async fn get_ecash_ticket(
44        &self,
45        ticket_type: TicketType,
46        gateway_id: ed25519::PublicKey,
47        tickets_to_spend: u32,
48        spend_time: OffsetDateTime,
49    ) -> Result<Option<PreparedCredential>, BandwidthControllerError> {
50        (**self)
51            .get_ecash_ticket(ticket_type, gateway_id, tickets_to_spend, spend_time)
52            .await
53    }
54
55    async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
56        (**self).get_upgrade_mode_token().await
57    }
58
59    async fn attempt_revert_spending(
60        &self,
61        metadata: PreparedCredentialMetadata,
62    ) -> Result<bool, BandwidthControllerError> {
63        (**self).attempt_revert_spending(metadata).await
64    }
65
66    // For compatibility for now. Remove once BC is properly implemented in client repo
67    async fn close(&self) {
68        (**self).close().await;
69    }
70}
71
72/// Boxed, dyn-compatible fetcher error. Deliberately a boxed trait object rather than an associated
73/// type on [`CredentialFetcher`]: an associated type would make the trait dyn-incompatible, and the
74/// controller wraps it in its own [`crate::error::BandwidthControllerError`] anyway.
75pub type CredentialFetcherError = Box<dyn FetcherError>;
76
77/// Error any fetcher implementation may return; the controller wraps it with context.
78pub trait FetcherError: std::error::Error + Send + Sync + 'static {
79    /// Coarse category the controller can branch on without knowing the concrete error type.
80    fn kind(&self) -> FetcherErrorKind;
81}
82
83// so `?` converts any concrete fetcher error into the boxed form
84impl<E: FetcherError> From<E> for CredentialFetcherError {
85    fn from(e: E) -> Self {
86        Box::new(e)
87    }
88}
89
90#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
91#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
92pub trait CredentialFetcher: CredentialPublicDataFetcher + Send + Sync {
93    /// Fetches (or recovers) ticketbooks of the given type. The controller does **not** retry on
94    /// failure - retrying transient errors is the fetcher's responsibility. Recovery can yield
95    /// several ticketbooks of the same type, hence the `Vec`.
96    async fn fetch_ticketbooks(
97        &self,
98        ticketbook_type: TicketType,
99    ) -> Result<Vec<NymCredential>, CredentialFetcherError>;
100
101    /// Persists any in-progress state (e.g. closing storage) before the fetcher is dropped, such
102    /// that it can be resumed later.
103    async fn cleanup(&self);
104
105    /// Wipes the fetcher's long-term data (e.g. removes its storage).
106    async fn reset(self) -> Result<(), CredentialFetcherError>;
107}
108
109#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
110#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
111pub trait CredentialPublicDataFetcher: Send + Sync {
112    async fn fetch_master_verification_key(
113        &self,
114        epoch_id: EpochId,
115    ) -> Result<EpochVerificationKey, CredentialFetcherError>;
116
117    async fn fetch_coin_index_signatures(
118        &self,
119        epoch_id: EpochId,
120    ) -> Result<AggregatedCoinIndicesSignatures, CredentialFetcherError>;
121
122    async fn fetch_expiration_date_signatures(
123        &self,
124        expiration_date: Date,
125        epoch_id: EpochId,
126    ) -> Result<AggregatedExpirationDateSignatures, CredentialFetcherError>;
127}