diff --git a/nym-credential-proxy/Cargo.lock b/nym-credential-proxy/Cargo.lock index 01f25f0932..50e39249dc 100644 --- a/nym-credential-proxy/Cargo.lock +++ b/nym-credential-proxy/Cargo.lock @@ -2382,7 +2382,7 @@ dependencies = [ [[package]] name = "nym-credential-proxy" -version = "0.1.1" +version = "0.1.3" dependencies = [ "anyhow", "async-trait", @@ -2691,7 +2691,6 @@ dependencies = [ "flate2", "futures", "itertools 0.13.0", - "log", "nym-api-requests", "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", @@ -2715,6 +2714,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tracing", "url", "wasmtimer", "zeroize", @@ -3791,9 +3791,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -4917,6 +4917,7 @@ dependencies = [ "quote", "regex", "syn 2.0.79", + "uuid", ] [[package]] diff --git a/nym-credential-proxy/nym-credential-proxy-requests/src/api/v1/ticketbook/models.rs b/nym-credential-proxy/nym-credential-proxy-requests/src/api/v1/ticketbook/models.rs index 46a65060ad..1d88b28d00 100644 --- a/nym-credential-proxy/nym-credential-proxy-requests/src/api/v1/ticketbook/models.rs +++ b/nym-credential-proxy/nym-credential-proxy-requests/src/api/v1/ticketbook/models.rs @@ -10,7 +10,7 @@ use schemars::schema::Schema; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::ops::{Deref, DerefMut}; -use time::Date; +use time::{Date, OffsetDateTime}; #[cfg(feature = "query-types")] use nym_http_api_common::Output; @@ -236,22 +236,28 @@ pub struct TicketbookWalletSharesAsyncResponse { #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] -pub struct BlindedWalletSharesResponse { +pub struct WebhookTicketbookWalletShares { pub id: i64, pub status: String, pub device_id: String, pub credential_id: String, pub data: Option, pub error_message: Option, - pub created: String, - pub updated: String, + + #[schemars(with = "String")] + #[serde(with = "time::serde::rfc3339")] + pub created: OffsetDateTime, + + #[schemars(with = "String")] + #[serde(with = "time::serde::rfc3339")] + pub updated: OffsetDateTime, } #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] -pub struct WebhookBlindedSharesResponse { - pub blinded_shares: BlindedWalletSharesResponse, +pub struct WebhookTicketbookWalletSharesRequest { + pub ticketbook_wallet_shares: WebhookTicketbookWalletShares, pub secret: String, } diff --git a/nym-credential-proxy/nym-credential-proxy/Cargo.toml b/nym-credential-proxy/nym-credential-proxy/Cargo.toml index f04586b452..143e163f54 100644 --- a/nym-credential-proxy/nym-credential-proxy/Cargo.toml +++ b/nym-credential-proxy/nym-credential-proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-credential-proxy" -version = "0.1.2" +version = "0.1.3" authors.workspace = true repository.workspace = true homepage.workspace = true diff --git a/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs b/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs index 217aa6e89b..3a91724a4f 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs @@ -3,10 +3,12 @@ use crate::error::VpnApiError; use crate::http::state::ApiState; +use crate::storage::models::BlindedShares; use futures::{stream, StreamExt}; use nym_credential_proxy_requests::api::v1::ticketbook::models::{ TicketbookAsyncRequest, TicketbookObtainQueryParams, TicketbookRequest, - TicketbookWalletSharesResponse, WalletShare, + TicketbookWalletSharesResponse, WalletShare, WebhookTicketbookWalletShares, + WebhookTicketbookWalletSharesRequest, }; use nym_credentials::IssuanceTicketBook; use nym_credentials_interface::Base58; @@ -217,11 +219,13 @@ async fn try_obtain_blinded_ticketbook_async_inner( requested_on: OffsetDateTime, request_data: TicketbookAsyncRequest, params: TicketbookObtainQueryParams, + pending: &BlindedShares, ) -> Result<(), VpnApiError> { let epoch_id = state.current_epoch_id().await?; let device_id = &request_data.device_id; let credential_id = &request_data.credential_id; + let secret = request_data.secret.clone(); // 1. try to obtain global data let ( @@ -259,19 +263,70 @@ async fn try_obtain_blinded_ticketbook_async_inner( error!(uuid = %request, "failed to update db with issued information: {err}") } - // 4. build the response - let response = TicketbookWalletSharesResponse { + // 4. build the webhook request body + let data = Some(TicketbookWalletSharesResponse { epoch_id, shares, master_verification_key, aggregated_coin_index_signatures, aggregated_expiration_date_signatures, + }); + + let ticketbook_wallet_shares = WebhookTicketbookWalletShares { + id: pending.id, + status: pending.status.to_string(), + device_id: device_id.clone(), + credential_id: credential_id.clone(), + data, + error_message: None, + created: pending.created, + updated: pending.updated, + }; + + let webhook_request = WebhookTicketbookWalletSharesRequest { + ticketbook_wallet_shares, + secret, }; // 5. call the webhook state .zk_nym_web_hook() - .try_trigger(request, &response) + .try_trigger(request, &webhook_request) + .await; + + Ok(()) +} + +async fn try_trigger_webhook_request_for_error( + state: &ApiState, + request: Uuid, + request_data: TicketbookAsyncRequest, + pending: &BlindedShares, + error_message: String, +) -> Result<(), VpnApiError> { + let device_id = &request_data.device_id; + let credential_id = &request_data.credential_id; + let secret = request_data.secret.clone(); + + let ticketbook_wallet_shares = WebhookTicketbookWalletShares { + id: pending.id, + status: "error".to_string(), + device_id: device_id.clone(), + credential_id: credential_id.clone(), + data: None, + error_message: Some(error_message), + created: pending.created, + updated: pending.updated, + }; + + let webhook_request = WebhookTicketbookWalletSharesRequest { + ticketbook_wallet_shares, + secret, + }; + + state + .zk_nym_web_hook() + .try_trigger(request, &webhook_request) .await; Ok(()) @@ -285,16 +340,30 @@ pub(crate) async fn try_obtain_blinded_ticketbook_async( requested_on: OffsetDateTime, request_data: TicketbookAsyncRequest, params: TicketbookObtainQueryParams, + pending: BlindedShares, ) { if let Err(err) = try_obtain_blinded_ticketbook_async_inner( &state, request, requested_on, - request_data, + request_data.clone(), params, + &pending, ) .await { + // post to the webhook to notify of errors on this side + if let Err(webhook_err) = try_trigger_webhook_request_for_error( + &state, + request, + request_data, + &pending, + format!("Failed to get ticketbook: {err}"), + ) + .await + { + error!(uuid = %request, "failed to make webhook request to report error: {webhook_err}") + } error!(uuid = %request, "failed to resolve the blinded ticketbook issuance: {err}") } else { info!(uuid = %request, "managed to resolve the blinded ticketbook issuance") diff --git a/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/openapi.rs b/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/openapi.rs index 9769ab415c..2315a658a1 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/openapi.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/openapi.rs @@ -84,8 +84,8 @@ pub(crate) struct ApiDoc; api_requests::v1::ticketbook::models::WalletShare, api_requests::v1::ticketbook::models::TicketbookWalletSharesResponse, api_requests::v1::ticketbook::models::TicketbookWalletSharesAsyncResponse, - api_requests::v1::ticketbook::models::BlindedWalletSharesResponse, - api_requests::v1::ticketbook::models::WebhookBlindedSharesResponse, + api_requests::v1::ticketbook::models::WebhookTicketbookWalletShares, + api_requests::v1::ticketbook::models::WebhookTicketbookWalletSharesRequest, api_requests::v1::ticketbook::models::TicketbookObtainQueryParams, api_requests::v1::ticketbook::models::SharesQueryParams, api_requests::v1::ticketbook::models::PlaceholderJsonSchemaImpl, diff --git a/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/ticketbook/mod.rs b/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/ticketbook/mod.rs index 670b6aaba9..2dccc8d349 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/ticketbook/mod.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/http/router/api/v1/ticketbook/mod.rs @@ -192,6 +192,7 @@ pub(crate) async fn obtain_ticketbook_shares_async( } Ok(pending) => pending, }; + let id = pending.id; // 3. try to spawn a new task attempting to resolve the request if state @@ -201,6 +202,7 @@ pub(crate) async fn obtain_ticketbook_shares_async( requested_on, payload, params, + pending, )) .is_none() { @@ -213,10 +215,7 @@ pub(crate) async fn obtain_ticketbook_shares_async( } // 4. in the meantime, return the id to the user - Ok(output.to_response(TicketbookWalletSharesAsyncResponse { - id: pending.id, - uuid, - })) + Ok(output.to_response(TicketbookWalletSharesAsyncResponse { id, uuid })) } /// Obtain the current value of the bandwidth voucher deposit diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index fe3e2b65d8..159d861a3f 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -3428,7 +3428,6 @@ dependencies = [ "flate2", "futures", "itertools 0.13.0", - "log", "nym-api-requests", "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", @@ -3452,6 +3451,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tracing", "url", "wasmtimer", "zeroize",