Skip to main content

nym_bandwidth_controller/requests/
sender.rs

1// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use async_trait::async_trait;
5use nym_credentials_interface::TicketType;
6use nym_crypto::asymmetric::ed25519;
7use nym_ecash_time::OffsetDateTime;
8use tokio::sync::mpsc::UnboundedSender;
9use tracing::instrument;
10
11use crate::{
12    error::BandwidthControllerError,
13    requests::{BandwidthControllerRequest, ReturnSender},
14    BandwidthTicketProvider, EcashTicketRequest, PreparedCredential, PreparedCredentialMetadata,
15};
16
17#[derive(Clone)]
18pub struct BandwidthControllerRequestSender {
19    command_tx: UnboundedSender<BandwidthControllerRequest>,
20}
21
22// Basic set of commands that can be sent to the bandwidth controller
23
24impl BandwidthControllerRequestSender {
25    pub fn new(command_tx: UnboundedSender<BandwidthControllerRequest>) -> Self {
26        Self { command_tx }
27    }
28
29    #[instrument(skip(self), level = "debug")]
30    pub async fn get_ecash_ticket(
31        &self,
32        ticket_type: TicketType,
33        gateway_id: ed25519::PublicKey,
34        tickets_to_spend: u32,
35        spend_time: OffsetDateTime,
36    ) -> Result<Option<PreparedCredential>, BandwidthControllerError> {
37        let (tx, rx) = ReturnSender::new();
38        self.command_tx
39            .send(BandwidthControllerRequest::EcashTicket(
40                tx,
41                EcashTicketRequest {
42                    ticket_type,
43                    gateway_id,
44                    tickets_to_spend,
45                    spend_time,
46                },
47            ))
48            .map_err(|_| BandwidthControllerError::ChannelClosed)?;
49        rx.await
50            .map_err(|_| BandwidthControllerError::ChannelClosed)?
51    }
52
53    #[instrument(skip(self), level = "debug")]
54    pub async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
55        let (tx, rx) = ReturnSender::new();
56        self.command_tx
57            .send(BandwidthControllerRequest::UpgradeModeToken(tx))
58            .map_err(|_| BandwidthControllerError::ChannelClosed)?;
59        rx.await
60            .map_err(|_| BandwidthControllerError::ChannelClosed)?
61    }
62
63    #[instrument(skip(self), level = "debug")]
64    pub async fn attempt_revert_spending(
65        &self,
66        metadata: PreparedCredentialMetadata,
67    ) -> Result<bool, BandwidthControllerError> {
68        let (tx, rx) = ReturnSender::new();
69        self.command_tx
70            .send(BandwidthControllerRequest::AttemptRevertSpending(
71                tx, metadata,
72            ))
73            .map_err(|_| BandwidthControllerError::ChannelClosed)?;
74        rx.await
75            .map_err(|_| BandwidthControllerError::ChannelClosed)?
76    }
77}
78
79#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
80#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
81impl BandwidthTicketProvider for BandwidthControllerRequestSender {
82    async fn get_ecash_ticket(
83        &self,
84        ticket_type: TicketType,
85        gateway_id: ed25519::PublicKey,
86        tickets_to_spend: u32,
87        spend_time: OffsetDateTime,
88    ) -> Result<Option<PreparedCredential>, BandwidthControllerError> {
89        self.get_ecash_ticket(ticket_type, gateway_id, tickets_to_spend, spend_time)
90            .await
91    }
92
93    async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
94        self.get_upgrade_mode_token().await
95    }
96
97    async fn attempt_revert_spending(
98        &self,
99        metadata: PreparedCredentialMetadata,
100    ) -> Result<bool, BandwidthControllerError> {
101        self.attempt_revert_spending(metadata).await
102    }
103
104    // No-op, the controller will close when stopped
105    async fn close(&self) {}
106}