diff --git a/common/credentials/src/coconut/bandwidth/freepass.rs b/common/credentials/src/coconut/bandwidth/freepass.rs index 5bb971c2cf..57893b259d 100644 --- a/common/credentials/src/coconut/bandwidth/freepass.rs +++ b/common/credentials/src/coconut/bandwidth/freepass.rs @@ -14,7 +14,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; pub const MAX_FREE_PASS_VALIDITY: Duration = Duration::WEEK; // 1 week -#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] +#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] pub struct FreePassIssuedData { /// the plain validity value of this credential expressed as unix timestamp #[zeroize(skip)] diff --git a/common/credentials/src/coconut/bandwidth/issued.rs b/common/credentials/src/coconut/bandwidth/issued.rs index b9d3ee02b3..486fd7c7ee 100644 --- a/common/credentials/src/coconut/bandwidth/issued.rs +++ b/common/credentials/src/coconut/bandwidth/issued.rs @@ -20,7 +20,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; pub const CURRENT_SERIALIZATION_REVISION: u8 = 1; -#[derive(Zeroize, Serialize, Deserialize)] +#[derive(Debug, Zeroize, Serialize, Deserialize)] pub enum BandwidthCredentialIssuedDataVariant { Voucher(BandwidthVoucherIssuedData), FreePass(FreePassIssuedData), diff --git a/common/credentials/src/coconut/bandwidth/voucher.rs b/common/credentials/src/coconut/bandwidth/voucher.rs index f61f5c3544..acce944fae 100644 --- a/common/credentials/src/coconut/bandwidth/voucher.rs +++ b/common/credentials/src/coconut/bandwidth/voucher.rs @@ -13,7 +13,7 @@ use nym_validator_client::nyxd::{Coin, Hash}; use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; -#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] +#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] pub struct BandwidthVoucherIssuedData { /// the plain value (e.g., bandwidth) encoded in this voucher // note: for legacy reasons we're only using the value of the coin and ignoring the denom diff --git a/gateway/src/node/client_handling/bandwidth.rs b/gateway/src/node/client_handling/bandwidth.rs index 47c6447a7a..10d1f50b3c 100644 --- a/gateway/src/node/client_handling/bandwidth.rs +++ b/gateway/src/node/client_handling/bandwidth.rs @@ -36,6 +36,7 @@ pub enum BandwidthError { }, } +#[derive(Debug, Copy, Clone)] pub struct Bandwidth { value: u64, } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index 1c22df93b7..6c9456224b 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -21,7 +21,7 @@ use futures::{ }; use log::*; use nym_credentials::coconut::bandwidth::{bandwidth_credential_params, CredentialType}; -use nym_credentials_interface::CoconutError; +use nym_credentials_interface::{Base58, CoconutError}; use nym_gateway_requests::models::CredentialSpendingRequest; use nym_gateway_requests::{ iv::{IVConversionError, IV}, @@ -227,15 +227,23 @@ where ) -> Result { // check if the credential hasn't been spent before let serial_number = credential.data.blinded_serial_number(); + trace!("processing credential {}", serial_number.to_bs58()); + let already_spent = self .inner .storage .contains_credential(&serial_number) .await?; if already_spent { + trace!("the credential has already been spent before"); return Err(RequestHandlingError::BandwidthCredentialAlreadySpent); } + trace!( + "attempting to obtain aggregate verification key for epoch {}", + credential.data.epoch_id + ); + let aggregated_verification_key = self .inner .coconut_verifier @@ -243,26 +251,32 @@ where .await?; if !credential.data.validate_type_attribute() { + trace!("mismatch in the type attribute"); return Err(RequestHandlingError::InvalidTypeAttribute); } let Some(bandwidth_attribute) = credential.data.get_bandwidth_attribute() else { + trace!("missing bandwidth attribute"); return Err(RequestHandlingError::MissingBandwidthAttribute); }; // this will extract token amounts out of bandwidth vouchers and validate expiry of free passes let bandwidth = Bandwidth::try_from_raw_value(bandwidth_attribute, credential.data.typ)?; + trace!("embedded bandwidth: {bandwidth:?}"); + // locally verify the credential let params = bandwidth_credential_params(); if !credential.data.verify(params, &aggregated_verification_key) { + trace!("the credential did not verify correctly"); return Err(RequestHandlingError::InvalidBandwidthCredential( - String::from("credential failed to verify on gateway"), + String::from("local credential verification has failed"), )); } let was_freepass = match credential.data.typ { CredentialType::Voucher => { + trace!("the credential is a bandwidth voucher. attempting to release the funds"); let api_clients = self .inner .coconut_verifier @@ -291,11 +305,13 @@ where // mark the credential as spent // TODO: technically this should be done under a storage transaction so that if we experience any // failures later on, it'd get reverted + trace!("storing serial number information"); self.inner .storage .insert_spent_credential(serial_number, was_freepass, self.client.address) .await?; + trace!("increasing client bandwidth"); self.increase_bandwidth(bandwidth).await?; let available_total = self.get_available_bandwidth().await?; @@ -307,6 +323,8 @@ where enc_credential: Vec, iv: Vec, ) -> Result { + debug!("handling v1 bandwidth request"); + let iv = IV::try_from_bytes(&iv)?; let credential = ClientControlRequest::try_from_enc_coconut_bandwidth_credential_v1( enc_credential, @@ -329,6 +347,8 @@ where enc_credential: Vec, iv: Vec, ) -> Result { + debug!("handling v2 bandwidth request"); + let iv = IV::try_from_bytes(&iv)?; let credential = ClientControlRequest::try_from_enc_coconut_bandwidth_credential_v2( enc_credential, @@ -342,6 +362,8 @@ where async fn handle_claim_testnet_bandwidth( &mut self, ) -> Result { + debug!("handling testnet bandwidth request"); + if self.inner.only_coconut_credentials { return Err(RequestHandlingError::OnlyCoconutCredentials); } @@ -389,6 +411,7 @@ where /// /// * `bin_msg`: raw message to handle. async fn handle_binary(&self, bin_msg: Vec) -> Message { + trace!("binary request"); // this function decrypts the request and checks the MAC match BinaryRequest::try_from_encrypted_tagged_bytes(bin_msg, &self.client.shared_keys) { Err(e) => { @@ -413,6 +436,7 @@ where /// /// * `raw_request`: raw message to handle. async fn handle_text(&mut self, raw_request: String) -> Message { + trace!("text request"); match ClientControlRequest::try_from(raw_request) { Err(e) => RequestHandlingError::InvalidTextRequest(e).into_error_message(), Ok(request) => match request { @@ -465,6 +489,12 @@ where /// /// * `raw_request`: raw received websocket message. async fn handle_request(&mut self, raw_request: Message) -> Option { + // TODO: this should be added via tracing + debug!( + "handling request from {}", + self.client.address.as_base58_string() + ); + // apparently tungstenite auto-handles ping/pong/close messages so for now let's ignore // them and let's test that claim. If that's not the case, just copy code from // desktop nym-client websocket as I've manually handled everything there diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index d013f8fda7..d075e01bb8 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -112,10 +112,15 @@ impl CoconutVerifier { // the key was already in the map if let Ok(mapped) = RwLockReadGuard::try_map(guard, |clients| clients.get(&epoch_id)) { + trace!("we already had cached api clients for epoch {epoch_id}"); return Ok(mapped); } let api_clients = self.query_api_clients(epoch_id).await?; + trace!( + "obtained {} api clients for epoch {epoch_id} from the contract", + api_clients.len() + ); // EDGE CASE: // if this epoch is from the past, we can't query for its threshold @@ -131,6 +136,7 @@ impl CoconutVerifier { let mut guard = self.api_clients.write().await; guard.insert(epoch_id, api_clients); let guard = guard.downgrade(); + trace!("stored api clients for epoch {epoch_id}"); // SAFETY: // we just inserted the entry into the map while NEVER dropping the lock (only downgraded it) @@ -149,10 +155,15 @@ impl CoconutVerifier { // the key was already in the map if let Ok(mapped) = RwLockReadGuard::try_map(guard, |keys| keys.get(&epoch_id)) { + trace!("we already had cached verification key for epoch {epoch_id}"); return Ok(mapped); } let api_clients = self.api_clients(epoch_id).await?; + trace!( + "attempting to obtain verification key from {} api clients", + api_clients.len() + ); let aggregated_verification_key = nym_credentials::obtain_aggregate_verification_key(&api_clients)?; @@ -160,6 +171,7 @@ impl CoconutVerifier { let mut guard = self.master_keys.write().await; guard.insert(epoch_id, aggregated_verification_key); let guard = guard.downgrade(); + trace!("stored aggregated verification key for epoch {epoch_id}"); // SAFETY: // we just inserted the entry into the map while NEVER dropping the lock (only downgraded it)