// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::error::BandwidthControllerError; use nym_credential_storage::error::StorageError; use nym_credential_storage::storage::Storage; use nym_validator_client::coconut::all_coconut_api_clients; use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::str::FromStr; use { nym_coconut_interface::Base58, nym_credentials::coconut::{ bandwidth::prepare_for_spending, utils::obtain_aggregate_verification_key, }, }; pub mod acquire; pub mod error; pub struct BandwidthController { storage: St, client: C, } impl BandwidthController { pub fn new(storage: St, client: C) -> Self { BandwidthController { storage, client } } pub fn storage(&self) -> &St { &self.storage } pub async fn prepare_coconut_credential( &self, ) -> Result<(nym_coconut_interface::Credential, i64), BandwidthControllerError> where C: DkgQueryClient + Sync + Send, ::StorageError: Send + Sync + 'static, { let bandwidth_credential = self .storage .get_next_coconut_credential() .await .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?; let voucher_value = u64::from_str(&bandwidth_credential.voucher_value) .map_err(|_| StorageError::InconsistentData)?; let voucher_info = bandwidth_credential.voucher_info.clone(); let serial_number = nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?; let binding_number = nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?; let signature = nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?; let epoch_id = u64::from_str(&bandwidth_credential.epoch_id) .map_err(|_| StorageError::InconsistentData)?; let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?; let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?; // the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff) Ok(( prepare_for_spending( voucher_value, voucher_info, serial_number, binding_number, epoch_id, &signature, &verification_key, )?, bandwidth_credential.id, )) } pub async fn consume_credential(&self, id: i64) -> Result<(), BandwidthControllerError> where ::StorageError: Send + Sync + 'static, { // JS: shouldn't we send some contract/validator/gateway message here to actually, you know, // consume it? self.storage .consume_coconut_credential(id) .await .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err))) } } impl Clone for BandwidthController where C: Clone, St: Storage + Clone, { fn clone(&self) -> Self { BandwidthController { storage: self.storage.clone(), client: self.client.clone(), } } }