From 695f3241f17be6fbcd420e32cc57867299d19848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Fri, 1 Nov 2024 23:57:41 +0100 Subject: [PATCH] wip: importing --- nym-vpn-core/Cargo.lock | 1 + .../src/commands/zknym.rs | 22 +++--- .../src/controller.rs | 71 ++++++++++++++++--- .../nym-vpn-account-controller/src/error.rs | 3 + .../crates/nym-vpn-api-client/Cargo.toml | 1 + .../crates/nym-vpn-api-client/src/client.rs | 71 ++++++++++++++++--- .../crates/nym-vpn-api-client/src/response.rs | 40 +++++++++-- .../crates/nym-vpn-api-client/src/routes.rs | 1 + 8 files changed, 178 insertions(+), 32 deletions(-) diff --git a/nym-vpn-core/Cargo.lock b/nym-vpn-core/Cargo.lock index d56abfa657..96fce25a65 100644 --- a/nym-vpn-core/Cargo.lock +++ b/nym-vpn-core/Cargo.lock @@ -4627,6 +4627,7 @@ dependencies = [ "nym-compact-ecash", "nym-config", "nym-contracts-common", + "nym-credential-proxy-requests", "nym-credentials-interface", "nym-crypto", "nym-ecash-time", diff --git a/nym-vpn-core/crates/nym-vpn-account-controller/src/commands/zknym.rs b/nym-vpn-core/crates/nym-vpn-account-controller/src/commands/zknym.rs index d33a6ed630..6e2182dcd1 100644 --- a/nym-vpn-core/crates/nym-vpn-account-controller/src/commands/zknym.rs +++ b/nym-vpn-core/crates/nym-vpn-account-controller/src/commands/zknym.rs @@ -8,7 +8,7 @@ use nym_credentials::IssuedTicketBook; use nym_credentials_interface::{PublicKeyUser, RequestInfo, TicketType}; use nym_ecash_time::EcashTime; use nym_vpn_api_client::{ - response::{NymVpnZkNym, NymVpnZkNymStatus}, + response::{NymVpnZkNym, NymVpnZkNym2, NymVpnZkNymStatus}, types::{Device, VpnApiAccount}, VpnApiClientError, }; @@ -81,6 +81,7 @@ pub(crate) async fn poll_zk_nym( api_client: nym_vpn_api_client::VpnApiClient, ) -> PollingResult { tracing::info!("Starting zk-nym polling task for {}", response.id); + tracing::info!("which had response : {:#?}", response); let start_time = Instant::now(); loop { tokio::time::sleep(std::time::Duration::from_secs(5)).await; @@ -90,7 +91,7 @@ pub(crate) async fn poll_zk_nym( .get_zk_nym_by_id(&account, &device, &response.id) .await { - Ok(poll_response) if response.status != NymVpnZkNymStatus::Pending => { + Ok(poll_response) if poll_response.status != NymVpnZkNymStatus::Pending => { tracing::info!("zk-nym polling finished: {:#?}", poll_response); return PollingResult::Finished( poll_response, @@ -121,7 +122,7 @@ pub(crate) async fn poll_zk_nym( } pub(crate) async fn unblind_and_aggregate( - response: NymVpnZkNym, + response: NymVpnZkNym2, ticketbook_type: TicketType, request_info: RequestInfo, account: VpnApiAccount, @@ -132,20 +133,21 @@ pub(crate) async fn unblind_and_aggregate( .map_err(Error::CreateEcashKeyPair)?; let mut partial_wallets = Vec::new(); - for blinded_share in response.blinded_shares { + let blinded_shares = response.blinded_shares.unwrap(); + for share in blinded_shares.shares { // TODO: remove unwrap - let blinded_share: WalletShare = serde_json::from_str(&blinded_share).unwrap(); + // let blinded_share: WalletShare = serde_json::from_str(&share).unwrap(); // TODO: remove unwrap let blinded_sig = - BlindedSignature::try_from_bs58(&blinded_share.bs58_encoded_share).unwrap(); + BlindedSignature::try_from_bs58(&share.bs58_encoded_share).unwrap(); match nym_compact_ecash::issue_verify( &vk_auth, ecash_keypair.secret_key(), &blinded_sig, &request_info, - blinded_share.node_index, + share.node_index, ) { Ok(partial_wallet) => partial_wallets.push(partial_wallet), Err(err) => { @@ -169,7 +171,7 @@ pub(crate) async fn unblind_and_aggregate( let ticketbook = IssuedTicketBook::new( aggregated_wallets.into_wallet_signatures(), - response.epoch.unwrap(), + blinded_shares.epoch_id, ecash_keypair.into(), ticketbook_type, expiration_date.ecash_date(), @@ -180,8 +182,8 @@ pub(crate) async fn unblind_and_aggregate( #[derive(Debug)] pub(crate) enum PollingResult { - Finished(NymVpnZkNym, TicketType, Box), - Timeout(NymVpnZkNym), + Finished(NymVpnZkNym2, TicketType, Box), + Timeout(NymVpnZkNym2), Error(PollingError), } diff --git a/nym-vpn-core/crates/nym-vpn-account-controller/src/controller.rs b/nym-vpn-core/crates/nym-vpn-account-controller/src/controller.rs index 448cbde1ba..27a6bd0089 100644 --- a/nym-vpn-core/crates/nym-vpn-account-controller/src/controller.rs +++ b/nym-vpn-core/crates/nym-vpn-account-controller/src/controller.rs @@ -4,12 +4,15 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use futures::StreamExt; +use nym_compact_ecash::Base58; use nym_config::defaults::NymNetworkDetails; -use nym_credentials_interface::{RequestInfo, TicketType}; +use nym_credentials::EpochVerificationKey; +use nym_credentials_interface::{RequestInfo, TicketType, VerificationKeyAuth}; use nym_http_api_client::UserAgent; use nym_vpn_api_client::{ response::{ - NymVpnAccountSummaryResponse, NymVpnDevicesResponse, NymVpnZkNym, NymVpnZkNymStatus, + NymVpnAccountSummaryResponse, NymVpnDevicesResponse, NymVpnZkNym, NymVpnZkNym2, + NymVpnZkNymStatus, }, types::{Device, VpnApiAccount}, }; @@ -23,7 +26,6 @@ use url::Url; use crate::{ commands::{ - self, zknym::{ construct_zk_nym_request_data, poll_zk_nym, request_zk_nym, PollingResult, ZkNymRequestData, @@ -177,18 +179,44 @@ where async fn import_zk_nym( &mut self, - response: NymVpnZkNym, + response: NymVpnZkNym2, ticketbook_type: TicketType, request_info: RequestInfo, ) -> Result<(), Error> { + tracing::info!("Importing zk-nym: {:#?}", response); + let account = self.account_storage.load_account().await?; - let master_verification_key = self + + let Some(ref blinded_shares) = response.blinded_shares else { + return Err(Error::MissingBlindedShares); + }; + + let master_verification_key = match self .credential_storage - .get_master_verification_key(response.epoch.unwrap()) + .get_master_verification_key(blinded_shares.epoch_id) .await? - .unwrap(); + { + Some(master_verification_key) => master_verification_key.clone(), + None => { + let vk_bs58 = blinded_shares + .master_verification_key + .as_ref() + .unwrap() + .bs58_encoded_key + .clone(); + let vk = VerificationKeyAuth::try_from_bs58(&vk_bs58).unwrap(); + let epoch_verification_key = EpochVerificationKey { + epoch_id: blinded_shares.epoch_id, + key: vk.clone(), + }; + self.credential_storage + .insert_master_verification_key(&epoch_verification_key) + .await?; + vk + } + }; - let issued_ticketbook = commands::zknym::unblind_and_aggregate( + let issued_ticketbook = crate::commands::zknym::unblind_and_aggregate( response, ticketbook_type, request_info, @@ -228,7 +256,7 @@ where .collect::>(); // For testing: uncomment to only request zk-nyms for a specific ticket type - //let ticket_types_needed_to_request = vec![TicketType::V1MixnetEntry]; + let ticket_types_needed_to_request = vec![TicketType::V1MixnetEntry]; // Request zk-nyms for each ticket type that we need let responses = futures::stream::iter(ticket_types_needed_to_request) @@ -338,6 +366,26 @@ where Ok(()) } + async fn handle_get_zk_nyms_available_for_download(&self) -> Result<(), Error> { + tracing::info!("Getting zk-nyms available for download from API"); + + let account = self.account_storage.load_account().await?; + let device = self.account_storage.load_device_keys().await?; + + let reported_device_zk_nyms = self + .vpn_api_client + .get_zk_nyms_available_for_download(&account, &device) + .await + .map_err(Error::GetZkNyms)?; + + tracing::info!("The device as the following zk-nyms available to download:"); + // TODO: pagination + for zk_nym in &reported_device_zk_nyms.items { + tracing::info!("{:?}", zk_nym); + } + Ok(()) + } + // Once we finish polling the result of the zk-nym request, we now import the zk-nym into the // local credential store async fn handle_polling_result(&mut self, result: Result) { @@ -402,7 +450,10 @@ where AccountCommand::UpdateAccountState => self.handle_update_account_state().await, AccountCommand::RegisterDevice => self.handle_register_device().await, AccountCommand::RequestZkNym => self.handle_request_zk_nym().await, - AccountCommand::GetDeviceZkNym => self.handle_get_device_zk_nym().await, + // AccountCommand::GetDeviceZkNym => self.handle_get_device_zk_nym().await, + AccountCommand::GetDeviceZkNym => { + self.handle_get_zk_nyms_available_for_download().await + } } } diff --git a/nym-vpn-core/crates/nym-vpn-account-controller/src/error.rs b/nym-vpn-core/crates/nym-vpn-account-controller/src/error.rs index 4260eb6df5..12e1790344 100644 --- a/nym-vpn-core/crates/nym-vpn-account-controller/src/error.rs +++ b/nym-vpn-core/crates/nym-vpn-account-controller/src/error.rs @@ -68,6 +68,9 @@ pub enum Error { #[error(transparent)] NymSdk(#[from] nym_sdk::Error), + + #[error("succesfull zknym response is missing blinded shares")] + MissingBlindedShares, } impl Error { diff --git a/nym-vpn-core/crates/nym-vpn-api-client/Cargo.toml b/nym-vpn-core/crates/nym-vpn-api-client/Cargo.toml index e76190b2f9..1e2d4ee153 100644 --- a/nym-vpn-core/crates/nym-vpn-api-client/Cargo.toml +++ b/nym-vpn-core/crates/nym-vpn-api-client/Cargo.toml @@ -18,6 +18,7 @@ itertools.workspace = true nym-compact-ecash.workspace = true nym-config.workspace = true nym-contracts-common.workspace = true +nym-credential-proxy-requests.workspace = true nym-credentials-interface.workspace = true nym-crypto = { workspace = true, features = ["asymmetric"] } nym-ecash-time.workspace = true diff --git a/nym-vpn-core/crates/nym-vpn-api-client/src/client.rs b/nym-vpn-core/crates/nym-vpn-api-client/src/client.rs index 504dd2821c..62f51aa881 100644 --- a/nym-vpn-core/crates/nym-vpn-api-client/src/client.rs +++ b/nym-vpn-core/crates/nym-vpn-api-client/src/client.rs @@ -15,9 +15,7 @@ use crate::{ RegisterDeviceRequestBody, RequestZkNymRequestBody, }, response::{ - NymDirectoryGatewayCountriesResponse, NymDirectoryGatewaysResponse, NymVpnAccountResponse, - NymVpnAccountSummaryResponse, NymVpnDevice, NymVpnDevicesResponse, NymVpnSubscription, - NymVpnSubscriptionResponse, NymVpnSubscriptionsResponse, NymVpnZkNym, NymVpnZkNymResponse, + NymDirectoryGatewayCountriesResponse, NymDirectoryGatewaysResponse, NymVpnAccountResponse, NymVpnAccountSummaryResponse, NymVpnDevice, NymVpnDevicesResponse, NymVpnSubscription, NymVpnSubscriptionResponse, NymVpnSubscriptionsResponse, NymVpnZkNym, NymVpnZkNym2, NymVpnZkNymResponse }, routes, types::{Device, GatewayMinPerformance, GatewayType, VpnApiAccount}, @@ -78,6 +76,37 @@ impl VpnApiClient { nym_http_api_client::parse_response(response, false).await } + async fn get_authorized_debug( + &self, + path: PathSegments<'_>, + account: &VpnApiAccount, + device: Option<&Device>, + ) -> std::result::Result> + where + T: DeserializeOwned, + E: fmt::Display + DeserializeOwned, + { + let request = self + .inner + .create_get_request(path, NO_PARAMS) + .bearer_auth(account.jwt().to_string()); + + let request = match device { + Some(device) => request.header( + DEVICE_AUTHORIZATION_HEADER, + format!("Bearer {}", device.jwt()), + ), + None => request, + }; + + let response = request.send().await?; + let r = response.text().await; + tracing::info!("Response: {:#?}", r); + todo!(); + + nym_http_api_client::parse_response(response, false).await + } + async fn get_json_with_retry( &self, path: PathSegments<'_>, @@ -125,6 +154,9 @@ impl VpnApiClient { }; let response = request.send().await?; + // let r = response.text().await; + // tracing::info!("Response: {:#?}", r); + // todo!(); nym_http_api_client::parse_response(response, false).await } @@ -305,11 +337,11 @@ impl VpnApiClient { .map_err(VpnApiClientError::FailedToRequestZkNym) } - pub async fn get_active_zk_nym( + pub async fn get_zk_nyms_available_for_download( &self, account: &VpnApiAccount, device: &Device, - ) -> Result { + ) -> Result { self.get_authorized( &[ routes::PUBLIC, @@ -319,21 +351,44 @@ impl VpnApiClient { routes::DEVICE, &device.identity_key().to_string(), routes::ZKNYM, - routes::ACTIVE, + routes::AVAILABLE, ], account, Some(device), ) .await - .map_err(VpnApiClientError::FailedToGetActiveZkNym) + .map_err(VpnApiClientError::FailedToGetDeviceZkNyms) } + //pub async fn get_active_zk_nym( + // &self, + // account: &VpnApiAccount, + // device: &Device, + //) -> Result { + // self.get_authorized( + // &[ + // routes::PUBLIC, + // routes::V1, + // routes::ACCOUNT, + // &account.id(), + // routes::DEVICE, + // &device.identity_key().to_string(), + // routes::ZKNYM, + // routes::ACTIVE, + // ], + // account, + // Some(device), + // ) + // .await + // .map_err(VpnApiClientError::FailedToGetActiveZkNym) + //} + pub async fn get_zk_nym_by_id( &self, account: &VpnApiAccount, device: &Device, id: &str, - ) -> Result { + ) -> Result { self.get_authorized( &[ routes::PUBLIC, diff --git a/nym-vpn-core/crates/nym-vpn-api-client/src/response.rs b/nym-vpn-core/crates/nym-vpn-api-client/src/response.rs index 5240ed03b9..3aea945705 100644 --- a/nym-vpn-core/crates/nym-vpn-api-client/src/response.rs +++ b/nym-vpn-core/crates/nym-vpn-api-client/src/response.rs @@ -5,6 +5,7 @@ use std::fmt; use itertools::Itertools; use nym_contracts_common::Percent; +use nym_credential_proxy_requests::api::v1::ticketbook::models::TicketbookWalletSharesResponse; use serde::{Deserialize, Serialize}; const MAX_PROBE_RESULT_AGE_MINUTES: i64 = 60; @@ -129,19 +130,50 @@ pub enum NymVpnRefundUserReason { Other, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct NymVpnZkNym { pub created_on_utc: String, pub last_updated_utc: String, pub id: String, + pub ticketbook_type: String, pub valid_until_utc: String, pub valid_from_utc: String, pub issued_bandwidth_in_gb: f64, - pub blinded_shares: Vec, + pub blinded_shares: Option>>, pub status: NymVpnZkNymStatus, - pub epoch: Option, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct NymVpnZkNym2 { + pub created_on_utc: String, + pub last_updated_utc: String, + pub id: String, + pub ticketbook_type: String, + pub valid_until_utc: String, + pub valid_from_utc: String, + pub issued_bandwidth_in_gb: f64, + pub blinded_shares: Option, + pub status: NymVpnZkNymStatus, +} + +//"{ +// \"totalItems\":1, +// \"page\":1, +// \"pageSize\":100, +// \"items\":[ +// { +// \"id\":\"k09ww30bcaqv2j5\", +// \"status\":\"active\", +// \"last_updated_utc\":\"2024-11-01 21:27:29.205Z\", +// \"created_on_utc\":\"2024-11-01 21:27:22.842Z\", +// \"valid_until_utc\":\"2024-12-01 21:26:07.909Z\", +// \"valid_from_utc\":\"2024-11-01 21:27:22.503Z\", +// \"issued_bandwidth_in_gb\":25 +// } +// ] +//}", + + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum NymVpnZkNymStatus { @@ -152,7 +184,7 @@ pub enum NymVpnZkNymStatus { Error, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NymVpnZkNymResponse { pub total_items: u64, diff --git a/nym-vpn-core/crates/nym-vpn-api-client/src/routes.rs b/nym-vpn-core/crates/nym-vpn-api-client/src/routes.rs index eefda53607..c6418ad6de 100644 --- a/nym-vpn-core/crates/nym-vpn-api-client/src/routes.rs +++ b/nym-vpn-core/crates/nym-vpn-api-client/src/routes.rs @@ -8,6 +8,7 @@ pub(crate) const SUMMARY: &str = "summary"; pub(crate) const DEVICE: &str = "device"; pub(crate) const ACTIVE: &str = "active"; pub(crate) const ZKNYM: &str = "zknym"; +pub(crate) const AVAILABLE: &str = "available"; pub(crate) const FREEPASS: &str = "freepass"; pub(crate) const SUBSCRIPTION: &str = "subscription"; pub(crate) const DIRECTORY: &str = "directory";