locally marking credentials as spent

This commit is contained in:
Jędrzej Stuczyński
2024-01-26 15:43:48 +00:00
parent 0890407886
commit ecf85049c6
5 changed files with 60 additions and 17 deletions
+5 -1
View File
@@ -49,7 +49,11 @@ impl Credential {
}
}
pub fn blinded_serial_number(&self) -> String {
pub fn blinded_serial_number(&self) -> &BlindedSerialNumber {
&self.theta.blinded_serial_number
}
pub fn blinded_serial_number_bs58(&self) -> String {
self.theta.blinded_serial_number_bs58()
}
@@ -19,8 +19,10 @@ use thiserror::Error;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
use std::cmp::max;
use std::{convert::TryFrom, process, time::Duration};
use crate::node::client_handling::websocket::connection_handler::coconut::BANDWIDTH_PER_CREDENTIAL;
use crate::node::{
client_handling::{
bandwidth::Bandwidth,
@@ -58,6 +60,9 @@ pub(crate) enum RequestHandlingError {
#[error("Provided bandwidth credential did not verify correctly on {0}")]
InvalidBandwidthCredential(String),
#[error("the provided bandwidth credential has already been spent before at this gateway")]
BandwidthCredentialAlreadySpent,
#[error("This gateway is only accepting coconut credentials for bandwidth")]
OnlyCoconutCredentials,
@@ -229,6 +234,17 @@ where
iv,
)?;
// check if the credential hasn't been spent before
let already_spent = self
.inner
.storage
.contains_credential(credential.blinded_serial_number())
.await?;
if already_spent {
return Err(RequestHandlingError::BandwidthCredentialAlreadySpent);
}
// locally verify the credential
let aggregated_verification_key = self
.inner
.coconut_verifier
@@ -241,19 +257,36 @@ where
));
}
let api_clients = self
.inner
.coconut_verifier
.api_clients(*credential.epoch_id())
// technically this is not atomic, i.e. checking for the spending and then marking as spent,
// but because we have the `UNIQUE` constraint on the database table
// if somebody attempts to spend the same credential in another, parallel request,
// one of them will fail
//
// 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
self.inner
.storage
.insert_spent_credential(*credential.blinded_serial_number(), self.client.address)
.await?;
self.inner
.coconut_verifier
.release_funds(&api_clients, &credential)
.await?;
// OLD CODE FOR RELEASING FUNDS
// let api_clients = self
// .inner
// .coconut_verifier
// .api_clients(*credential.epoch_id())
// .await?;
//
// self.inner
// .coconut_verifier
// .release_funds(&api_clients, &credential)
// .await?;
let bandwidth = Bandwidth::from(credential);
let bandwidth_value = bandwidth.value();
// if somebody decided to use a credential with bunch of tokens in it, sure, grant them that bandwidth
// otherwise use the default value
let bandwidth_value = max(bandwidth.value(), BANDWIDTH_PER_CREDENTIAL);
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
@@ -20,6 +20,8 @@ use std::collections::HashMap;
use std::ops::Deref;
use tokio::sync::{RwLock, RwLockReadGuard};
pub(crate) const BANDWIDTH_PER_CREDENTIAL: u64 = 1024 * 1024 * 1024; // 1GB
pub(crate) struct CoconutVerifier {
address: AccountId,
nyxd_client: RwLock<DirectSigningHttpRpcNyxdClient>,
@@ -165,7 +167,7 @@ impl CoconutVerifier {
credential.voucher_value().into(),
self.mix_denom_base.clone(),
),
credential.blinded_serial_number(),
credential.blinded_serial_number_bs58(),
self.address.to_string(),
None,
)
+5 -1
View File
@@ -110,7 +110,11 @@ pub async fn verify_bandwidth_credential(
// Credential has not been spent before, and is on its way of being spent
let credential_status = state
.client
.get_spent_credential(verify_credential_body.credential.blinded_serial_number())
.get_spent_credential(
verify_credential_body
.credential
.blinded_serial_number_bs58(),
)
.await?
.spend_credential
.ok_or(CoconutError::InvalidCredentialStatus {
+5 -5
View File
@@ -1062,7 +1062,7 @@ async fn verification_of_bandwidth_credential() {
);
// Test the endpoint with no msg in the proposal action
proposal.description = credential.blinded_serial_number();
proposal.description = credential.blinded_serial_number_bs58();
proposal_db
.write()
.unwrap()
@@ -1117,7 +1117,7 @@ async fn verification_of_bandwidth_credential() {
);
spent_credential_db.write().unwrap().insert(
credential.blinded_serial_number(),
credential.blinded_serial_number_bs58(),
SpendCredentialResponse::new(None),
);
let response = client
@@ -1140,11 +1140,11 @@ async fn verification_of_bandwidth_credential() {
// Test the endpoint with a credential that doesn't verify correctly
let mut spent_credential = SpendCredential::new(
funds.clone().into(),
credential.blinded_serial_number(),
credential.blinded_serial_number_bs58(),
Addr::unchecked("unimportant"),
);
spent_credential_db.write().unwrap().insert(
credential.blinded_serial_number(),
credential.blinded_serial_number_bs58(),
SpendCredentialResponse::new(Some(spent_credential.clone())),
);
let bad_credential = Credential::new(
@@ -1259,7 +1259,7 @@ async fn verification_of_bandwidth_credential() {
// Test the endpoint with the credential marked as Spent in the Coconut Bandwidth Contract
spent_credential.mark_as_spent();
spent_credential_db.write().unwrap().insert(
credential.blinded_serial_number(),
credential.blinded_serial_number_bs58(),
SpendCredentialResponse::new(Some(spent_credential)),
);
let response = client