From fb0b9da14fb3eca59799474db1c7cb49d8e1bc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 30 Aug 2024 17:35:57 +0100 Subject: [PATCH 1/6] revamped ticketbook serialisation and exposed additional cli methods --- Cargo.lock | 3 + .../ecash/import_coin_index_signatures.rs | 16 ++ .../commands/{ => ecash}/import_credential.rs | 4 +- .../import_expiration_date_signatures.rs | 16 ++ .../ecash/import_master_verification_key.rs | 16 ++ clients/native/src/commands/ecash/mod.rs | 59 ++++++ .../commands/{ => ecash}/show_ticketbooks.rs | 0 clients/native/src/commands/mod.rs | 15 +- .../ecash/import_coin_index_signatures.rs | 16 ++ .../commands/{ => ecash}/import_credential.rs | 6 +- .../import_expiration_date_signatures.rs | 16 ++ .../ecash/import_master_verification_key.rs | 16 ++ clients/socks5/src/commands/ecash/mod.rs | 59 ++++++ .../commands/{ => ecash}/show_ticketbooks.rs | 4 +- clients/socks5/src/commands/mod.rs | 15 +- common/bandwidth-controller/src/utils.rs | 32 +++- .../client_import_coin_index_signatures.rs | 68 +++++++ .../cli_helpers/client_import_credential.rs | 35 +++- ...lient_import_expiration_date_signatures.rs | 68 +++++++ .../client_import_master_verification_key.rs | 68 +++++++ common/client-core/src/cli_helpers/mod.rs | 3 + common/commands/Cargo.toml | 3 +- .../commands/src/coconut/issue_ticket_book.rs | 56 ------ .../src/ecash/import_coin_index_signatures.rs | 76 ++++++++ .../import_expiration_date_signatures.rs | 76 ++++++++ .../ecash/import_master_verification_key.rs | 76 ++++++++ .../{coconut => ecash}/import_ticket_book.rs | 53 ++++-- .../commands/src/ecash/issue_ticket_book.rs | 170 ++++++++++++++++++ common/commands/src/{coconut => ecash}/mod.rs | 6 + .../{coconut => ecash}/recover_ticket_book.rs | 0 common/commands/src/lib.rs | 2 +- ...0002_signatures_serialisation_revision.sql | 13 ++ .../credential-storage/src/backends/memory.rs | 25 ++- .../credential-storage/src/backends/sqlite.rs | 49 +++-- .../src/ephemeral_storage.rs | 20 +-- common/credential-storage/src/models.rs | 15 ++ .../src/persistent_storage/helpers.rs | 54 ------ .../src/persistent_storage/legacy_helpers.rs | 44 +++++ .../src/persistent_storage/mod.rs | 75 +++++--- common/credential-storage/src/storage.rs | 14 +- common/credentials-interface/src/lib.rs | 2 +- common/credentials/Cargo.toml | 1 + .../src/ecash/bandwidth/importable.rs | 118 ++++++++++++ .../src/ecash/bandwidth/issuance.rs | 9 +- .../credentials/src/ecash/bandwidth/issued.rs | 5 + common/credentials/src/ecash/bandwidth/mod.rs | 1 + .../src/ecash/bandwidth/serialiser/keys.rs | 35 ++++ .../{serialiser.rs => serialiser/mod.rs} | 18 +- .../ecash/bandwidth/serialiser/signatures.rs | 66 +++++++ common/credentials/src/ecash/utils.rs | 2 +- common/credentials/src/error.rs | 5 +- common/credentials/src/lib.rs | 5 + common/nym-id/src/error.rs | 25 ++- common/nym-id/src/import_credential.rs | 49 ----- .../nym-id/src/import_credential/helpers.rs | 147 +++++++++++++++ common/nym-id/src/import_credential/mod.rs | 119 ++++++++++++ common/nym-id/src/lib.rs | 5 +- .../src/scheme/aggregation.rs | 5 +- .../src/scheme/keygen.rs | 15 ++ .../src/scheme/withdrawal.rs | 3 +- common/serde-helpers/Cargo.toml | 3 +- common/serde-helpers/src/lib.rs | 38 ++++ contracts/Cargo.lock | 18 +- envs/sandbox.env | 9 +- nym-api/nym-api-requests/Cargo.toml | 2 +- nym-api/nym-api-requests/src/helpers.rs | 41 +---- nym-api/src/ecash/comm.rs | 4 +- nym-wallet/Cargo.lock | 1 + .../cli/ecash/import_coin_index_signatures.rs | 16 ++ .../src/cli/{ => ecash}/import_credential.rs | 6 +- .../import_expiration_date_signatures.rs | 16 ++ .../ecash/import_master_verification_key.rs | 16 ++ .../authenticator/src/cli/ecash/mod.rs | 59 ++++++ .../src/cli/ecash/show_ticketbooks.rs | 32 ++++ .../authenticator/src/cli/mod.rs | 10 +- .../cli/ecash/import_coin_index_signatures.rs | 17 ++ .../src/cli/{ => ecash}/import_credential.rs | 6 +- .../import_expiration_date_signatures.rs | 16 ++ .../ecash/import_master_verification_key.rs | 16 ++ .../ip-packet-router/src/cli/ecash/mod.rs | 61 +++++++ .../src/cli/{ => ecash}/show_ticketbooks.rs | 4 +- .../ip-packet-router/src/cli/mod.rs | 13 +- .../cli/ecash/import_coin_index_signatures.rs | 16 ++ .../src/cli/{ => ecash}/import_credential.rs | 6 +- .../import_expiration_date_signatures.rs | 16 ++ .../ecash/import_master_verification_key.rs | 16 ++ .../network-requester/src/cli/ecash/mod.rs | 59 ++++++ .../src/cli/{ => ecash}/show_ticketbooks.rs | 0 .../network-requester/src/cli/mod.rs | 17 +- tools/nym-cli/src/coconut/mod.rs | 24 ++- tools/nym-cli/src/main.rs | 2 +- .../src/commands/import_credential.rs | 4 +- 92 files changed, 2158 insertions(+), 403 deletions(-) create mode 100644 clients/native/src/commands/ecash/import_coin_index_signatures.rs rename clients/native/src/commands/{ => ecash}/import_credential.rs (76%) create mode 100644 clients/native/src/commands/ecash/import_expiration_date_signatures.rs create mode 100644 clients/native/src/commands/ecash/import_master_verification_key.rs create mode 100644 clients/native/src/commands/ecash/mod.rs rename clients/native/src/commands/{ => ecash}/show_ticketbooks.rs (100%) create mode 100644 clients/socks5/src/commands/ecash/import_coin_index_signatures.rs rename clients/socks5/src/commands/{ => ecash}/import_credential.rs (68%) create mode 100644 clients/socks5/src/commands/ecash/import_expiration_date_signatures.rs create mode 100644 clients/socks5/src/commands/ecash/import_master_verification_key.rs create mode 100644 clients/socks5/src/commands/ecash/mod.rs rename clients/socks5/src/commands/{ => ecash}/show_ticketbooks.rs (89%) create mode 100644 common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs create mode 100644 common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs create mode 100644 common/client-core/src/cli_helpers/client_import_master_verification_key.rs delete mode 100644 common/commands/src/coconut/issue_ticket_book.rs create mode 100644 common/commands/src/ecash/import_coin_index_signatures.rs create mode 100644 common/commands/src/ecash/import_expiration_date_signatures.rs create mode 100644 common/commands/src/ecash/import_master_verification_key.rs rename common/commands/src/{coconut => ecash}/import_ticket_book.rs (51%) create mode 100644 common/commands/src/ecash/issue_ticket_book.rs rename common/commands/src/{coconut => ecash}/mod.rs (63%) rename common/commands/src/{coconut => ecash}/recover_ticket_book.rs (100%) create mode 100644 common/credential-storage/migrations/20241104120002_signatures_serialisation_revision.sql delete mode 100644 common/credential-storage/src/persistent_storage/helpers.rs create mode 100644 common/credential-storage/src/persistent_storage/legacy_helpers.rs create mode 100644 common/credentials/src/ecash/bandwidth/importable.rs create mode 100644 common/credentials/src/ecash/bandwidth/serialiser/keys.rs rename common/credentials/src/ecash/bandwidth/{serialiser.rs => serialiser/mod.rs} (83%) create mode 100644 common/credentials/src/ecash/bandwidth/serialiser/signatures.rs delete mode 100644 common/nym-id/src/import_credential.rs create mode 100644 common/nym-id/src/import_credential/helpers.rs create mode 100644 common/nym-id/src/import_credential/mod.rs create mode 100644 service-providers/authenticator/src/cli/ecash/import_coin_index_signatures.rs rename service-providers/authenticator/src/cli/{ => ecash}/import_credential.rs (69%) create mode 100644 service-providers/authenticator/src/cli/ecash/import_expiration_date_signatures.rs create mode 100644 service-providers/authenticator/src/cli/ecash/import_master_verification_key.rs create mode 100644 service-providers/authenticator/src/cli/ecash/mod.rs create mode 100644 service-providers/authenticator/src/cli/ecash/show_ticketbooks.rs create mode 100644 service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs rename service-providers/ip-packet-router/src/cli/{ => ecash}/import_credential.rs (69%) create mode 100644 service-providers/ip-packet-router/src/cli/ecash/import_expiration_date_signatures.rs create mode 100644 service-providers/ip-packet-router/src/cli/ecash/import_master_verification_key.rs create mode 100644 service-providers/ip-packet-router/src/cli/ecash/mod.rs rename service-providers/ip-packet-router/src/cli/{ => ecash}/show_ticketbooks.rs (89%) create mode 100644 service-providers/network-requester/src/cli/ecash/import_coin_index_signatures.rs rename service-providers/network-requester/src/cli/{ => ecash}/import_credential.rs (69%) create mode 100644 service-providers/network-requester/src/cli/ecash/import_expiration_date_signatures.rs create mode 100644 service-providers/network-requester/src/cli/ecash/import_master_verification_key.rs create mode 100644 service-providers/network-requester/src/cli/ecash/mod.rs rename service-providers/network-requester/src/cli/{ => ecash}/show_ticketbooks.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 4efaa347bd5..e8565e92f29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4412,6 +4412,7 @@ dependencies = [ "serde", "serde_json", "tap", + "tempfile", "thiserror", "time", "tokio", @@ -4754,6 +4755,7 @@ dependencies = [ "nym-ecash-contract-common", "nym-ecash-time", "nym-network-defaults", + "nym-serde-helpers", "nym-validator-client", "rand", "serde", @@ -5670,6 +5672,7 @@ dependencies = [ "base64 0.22.1", "bs58", "serde", + "time", ] [[package]] diff --git a/clients/native/src/commands/ecash/import_coin_index_signatures.rs b/clients/native/src/commands/ecash/import_coin_index_signatures.rs new file mode 100644 index 00000000000..bb232771b5d --- /dev/null +++ b/clients/native/src/commands/ecash/import_coin_index_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliNativeClient; +use crate::error::ClientError; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::{ + import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportCoinIndexSignaturesArgs, +) -> Result<(), ClientError> { + import_coin_index_signatures::(args).await?; + println!("successfully imported coin index signatures!"); + Ok(()) +} diff --git a/clients/native/src/commands/import_credential.rs b/clients/native/src/commands/ecash/import_credential.rs similarity index 76% rename from clients/native/src/commands/import_credential.rs rename to clients/native/src/commands/ecash/import_credential.rs index 2e917ed2260..a1951375cc3 100644 --- a/clients/native/src/commands/import_credential.rs +++ b/clients/native/src/commands/ecash/import_credential.rs @@ -4,10 +4,10 @@ use crate::commands::CliNativeClient; use crate::error::ClientError; use nym_client_core::cli_helpers::client_import_credential::{ - import_credential, CommonClientImportCredentialArgs, + import_credential, CommonClientImportTicketBookArgs, }; -pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> { +pub(crate) async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), ClientError> { import_credential::(args).await?; println!("successfully imported credential!"); Ok(()) diff --git a/clients/native/src/commands/ecash/import_expiration_date_signatures.rs b/clients/native/src/commands/ecash/import_expiration_date_signatures.rs new file mode 100644 index 00000000000..dc562f664aa --- /dev/null +++ b/clients/native/src/commands/ecash/import_expiration_date_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliNativeClient; +use crate::error::ClientError; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{ + import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportExpirationDateSignaturesArgs, +) -> Result<(), ClientError> { + import_expiration_date_signatures::(args).await?; + println!("successfully imported expiration date signatures!"); + Ok(()) +} diff --git a/clients/native/src/commands/ecash/import_master_verification_key.rs b/clients/native/src/commands/ecash/import_master_verification_key.rs new file mode 100644 index 00000000000..715efe00e0d --- /dev/null +++ b/clients/native/src/commands/ecash/import_master_verification_key.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliNativeClient; +use crate::error::ClientError; +use nym_client_core::cli_helpers::client_import_master_verification_key::{ + import_master_verification_key, CommonClientImportMasterVerificationKeyArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportMasterVerificationKeyArgs, +) -> Result<(), ClientError> { + import_master_verification_key::(args).await?; + println!("successfully imported master verification key!"); + Ok(()) +} diff --git a/clients/native/src/commands/ecash/mod.rs b/clients/native/src/commands/ecash/mod.rs new file mode 100644 index 00000000000..c1bc47af1ab --- /dev/null +++ b/clients/native/src/commands/ecash/mod.rs @@ -0,0 +1,59 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs; +use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; +use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; +use std::error::Error; + +pub(crate) mod import_coin_index_signatures; +pub(crate) mod import_credential; +pub(crate) mod import_expiration_date_signatures; +pub(crate) mod import_master_verification_key; +pub(crate) mod show_ticketbooks; + +#[derive(Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Ecash { + #[clap(subcommand)] + pub command: EcashCommands, +} + +impl Ecash { + pub async fn execute(self) -> Result<(), Box> { + match self.command { + EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?, + EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?, + EcashCommands::ImportCoinIndexSignatures(args) => { + import_coin_index_signatures::execute(args).await? + } + EcashCommands::ImportExpirationDateSignatures(args) => { + import_expiration_date_signatures::execute(args).await? + } + EcashCommands::ImportMasterVerificationKey(args) => { + import_master_verification_key::execute(args).await? + } + } + Ok(()) + } +} + +#[derive(Subcommand)] +pub enum EcashCommands { + /// Display information associated with the imported ticketbooks, + ShowTicketBooks(show_ticketbooks::Args), + + /// Import a pre-generated ticketbook + ImportTicketBook(CommonClientImportTicketBookArgs), + + /// Import coin index signatures needed for ticketbooks + ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs), + + /// Import expiration date signatures needed for ticketbooks + ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs), + + /// Import master verification key needed for ticketbooks + ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs), +} diff --git a/clients/native/src/commands/show_ticketbooks.rs b/clients/native/src/commands/ecash/show_ticketbooks.rs similarity index 100% rename from clients/native/src/commands/show_ticketbooks.rs rename to clients/native/src/commands/ecash/show_ticketbooks.rs diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 31aa4ae8ece..6c5e8a94976 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -6,13 +6,13 @@ use crate::client::config::old_config_v1_1_20::ConfigV1_1_20; use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2; use crate::client::config::old_config_v1_1_33::ConfigV1_1_33; use crate::client::config::{BaseClientConfig, Config}; +use crate::commands::ecash::Ecash; use crate::error::ClientError; use clap::CommandFactory; use clap::{Parser, Subcommand}; use log::{error, info}; use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; -use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs; use nym_client_core::cli_helpers::CliClient; use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33; use nym_config::OptionalSet; @@ -22,11 +22,10 @@ use std::sync::OnceLock; mod add_gateway; pub(crate) mod build_info; -pub(crate) mod import_credential; +pub(crate) mod ecash; pub(crate) mod init; mod list_gateways; pub(crate) mod run; -mod show_ticketbooks; mod switch_gateway; pub(crate) struct CliNativeClient; @@ -73,8 +72,8 @@ pub(crate) enum Commands { /// Run the Nym client with provided configuration client optionally overriding set parameters Run(run::Run), - /// Import a pre-generated credential - ImportCredential(CommonClientImportCredentialArgs), + /// Ecash-related functionalities + Ecash(Ecash), /// List all registered with gateways ListGateways(list_gateways::Args), @@ -85,9 +84,6 @@ pub(crate) enum Commands { /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), - /// Display information associated with the imported ticketbooks, - ShowTicketbooks(show_ticketbooks::Args), - /// Show build information of this binary BuildInfo(build_info::BuildInfo), @@ -116,11 +112,10 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box init::execute(m).await?, Commands::Run(m) => run::execute(m).await?, - Commands::ImportCredential(m) => import_credential::execute(m).await?, + Commands::Ecash(ecash) => ecash.execute().await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, - Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), diff --git a/clients/socks5/src/commands/ecash/import_coin_index_signatures.rs b/clients/socks5/src/commands/ecash/import_coin_index_signatures.rs new file mode 100644 index 00000000000..39cade10c77 --- /dev/null +++ b/clients/socks5/src/commands/ecash/import_coin_index_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliSocks5Client; +use crate::error::Socks5ClientError; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::{ + import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportCoinIndexSignaturesArgs, +) -> Result<(), Socks5ClientError> { + import_coin_index_signatures::(args).await?; + println!("successfully imported coin index signatures!"); + Ok(()) +} diff --git a/clients/socks5/src/commands/import_credential.rs b/clients/socks5/src/commands/ecash/import_credential.rs similarity index 68% rename from clients/socks5/src/commands/import_credential.rs rename to clients/socks5/src/commands/ecash/import_credential.rs index d8f3d27bc95..e8f4508b832 100644 --- a/clients/socks5/src/commands/import_credential.rs +++ b/clients/socks5/src/commands/ecash/import_credential.rs @@ -4,12 +4,10 @@ use crate::commands::CliSocks5Client; use crate::error::Socks5ClientError; use nym_client_core::cli_helpers::client_import_credential::{ - import_credential, CommonClientImportCredentialArgs, + import_credential, CommonClientImportTicketBookArgs, }; -pub(crate) async fn execute( - args: CommonClientImportCredentialArgs, -) -> Result<(), Socks5ClientError> { +pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), Socks5ClientError> { import_credential::(args).await?; println!("successfully imported credential!"); Ok(()) diff --git a/clients/socks5/src/commands/ecash/import_expiration_date_signatures.rs b/clients/socks5/src/commands/ecash/import_expiration_date_signatures.rs new file mode 100644 index 00000000000..7cc9c1042f0 --- /dev/null +++ b/clients/socks5/src/commands/ecash/import_expiration_date_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliSocks5Client; +use crate::error::Socks5ClientError; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{ + import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportExpirationDateSignaturesArgs, +) -> Result<(), Socks5ClientError> { + import_expiration_date_signatures::(args).await?; + println!("successfully imported expiration date signatures!"); + Ok(()) +} diff --git a/clients/socks5/src/commands/ecash/import_master_verification_key.rs b/clients/socks5/src/commands/ecash/import_master_verification_key.rs new file mode 100644 index 00000000000..51dc108a59b --- /dev/null +++ b/clients/socks5/src/commands/ecash/import_master_verification_key.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliSocks5Client; +use crate::error::Socks5ClientError; +use nym_client_core::cli_helpers::client_import_master_verification_key::{ + import_master_verification_key, CommonClientImportMasterVerificationKeyArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportMasterVerificationKeyArgs, +) -> Result<(), Socks5ClientError> { + import_master_verification_key::(args).await?; + println!("successfully imported master verification key!"); + Ok(()) +} diff --git a/clients/socks5/src/commands/ecash/mod.rs b/clients/socks5/src/commands/ecash/mod.rs new file mode 100644 index 00000000000..c1bc47af1ab --- /dev/null +++ b/clients/socks5/src/commands/ecash/mod.rs @@ -0,0 +1,59 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs; +use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; +use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; +use std::error::Error; + +pub(crate) mod import_coin_index_signatures; +pub(crate) mod import_credential; +pub(crate) mod import_expiration_date_signatures; +pub(crate) mod import_master_verification_key; +pub(crate) mod show_ticketbooks; + +#[derive(Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Ecash { + #[clap(subcommand)] + pub command: EcashCommands, +} + +impl Ecash { + pub async fn execute(self) -> Result<(), Box> { + match self.command { + EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?, + EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?, + EcashCommands::ImportCoinIndexSignatures(args) => { + import_coin_index_signatures::execute(args).await? + } + EcashCommands::ImportExpirationDateSignatures(args) => { + import_expiration_date_signatures::execute(args).await? + } + EcashCommands::ImportMasterVerificationKey(args) => { + import_master_verification_key::execute(args).await? + } + } + Ok(()) + } +} + +#[derive(Subcommand)] +pub enum EcashCommands { + /// Display information associated with the imported ticketbooks, + ShowTicketBooks(show_ticketbooks::Args), + + /// Import a pre-generated ticketbook + ImportTicketBook(CommonClientImportTicketBookArgs), + + /// Import coin index signatures needed for ticketbooks + ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs), + + /// Import expiration date signatures needed for ticketbooks + ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs), + + /// Import master verification key needed for ticketbooks + ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs), +} diff --git a/clients/socks5/src/commands/show_ticketbooks.rs b/clients/socks5/src/commands/ecash/show_ticketbooks.rs similarity index 89% rename from clients/socks5/src/commands/show_ticketbooks.rs rename to clients/socks5/src/commands/ecash/show_ticketbooks.rs index 41a37ef6eb3..1271c0c01f8 100644 --- a/clients/socks5/src/commands/show_ticketbooks.rs +++ b/clients/socks5/src/commands/ecash/show_ticketbooks.rs @@ -9,7 +9,7 @@ use nym_client_core::cli_helpers::client_show_ticketbooks::{ }; #[derive(clap::Args)] -pub(crate) struct Args { +pub struct Args { #[command(flatten)] common_args: CommonShowTicketbooksArgs, @@ -23,7 +23,7 @@ impl AsRef for Args { } } -pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> { +pub async fn execute(args: Args) -> Result<(), Socks5ClientError> { let output = args.output; let res = show_ticketbooks::(args).await?; diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index 34f0738a12b..2c471ec3fad 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -1,6 +1,7 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::commands::ecash::Ecash; use crate::config::old_config_v1_1_13::OldConfigV1_1_13; use crate::config::old_config_v1_1_20::ConfigV1_1_20; use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2; @@ -13,7 +14,6 @@ use clap::{Parser, Subcommand}; use log::{error, info}; use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; -use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs; use nym_client_core::cli_helpers::CliClient; use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33; use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; @@ -26,11 +26,10 @@ use std::sync::OnceLock; mod add_gateway; pub(crate) mod build_info; -mod import_credential; +pub mod ecash; pub mod init; mod list_gateways; pub(crate) mod run; -mod show_ticketbooks; mod switch_gateway; pub(crate) struct CliSocks5Client; @@ -77,8 +76,8 @@ pub(crate) enum Commands { /// Run the Nym client with provided configuration client optionally overriding set parameters Run(run::Run), - /// Import a pre-generated credential - ImportCredential(CommonClientImportCredentialArgs), + /// Ecash-related functionalities + Ecash(Ecash), /// List all registered with gateways ListGateways(list_gateways::Args), @@ -89,9 +88,6 @@ pub(crate) enum Commands { /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), - /// Display information associated with the imported ticketbooks, - ShowTicketbooks(show_ticketbooks::Args), - /// Show build information of this binary BuildInfo(build_info::BuildInfo), @@ -123,11 +119,10 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box init::execute(m).await?, Commands::Run(m) => run::execute(m).await?, - Commands::ImportCredential(m) => import_credential::execute(m).await?, + Commands::Ecash(ecash) => ecash.execute().await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, - Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), diff --git a/common/bandwidth-controller/src/utils.rs b/common/bandwidth-controller/src/utils.rs index 0d2427b2f8c..9c15f5924bc 100644 --- a/common/bandwidth-controller/src/utils.rs +++ b/common/bandwidth-controller/src/utils.rs @@ -4,6 +4,10 @@ use crate::error::BandwidthControllerError; use log::warn; use nym_credential_storage::storage::Storage; +use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use nym_credentials::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; use nym_credentials_interface::{ AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth, }; @@ -94,13 +98,18 @@ where .await? .key; + let full = EpochVerificationKey { + epoch_id, + key: master_vk, + }; + // store the retrieved key storage - .insert_master_verification_key(epoch_id, &master_vk) + .insert_master_verification_key(&full) .await .map_err(BandwidthControllerError::credential_storage_error)?; - Ok(master_vk) + Ok(full.key) } pub(crate) async fn get_coin_index_signatures( @@ -132,13 +141,18 @@ where .await? .signatures; + let aggregated = AggregatedCoinIndicesSignatures { + epoch_id, + signatures: index_sigs, + }; + // store the retrieved key storage - .insert_coin_index_signatures(epoch_id, &index_sigs) + .insert_coin_index_signatures(&aggregated) .await .map_err(BandwidthControllerError::credential_storage_error)?; - Ok(index_sigs) + Ok(aggregated.signatures) } pub(crate) async fn get_expiration_date_signatures( @@ -171,11 +185,17 @@ where .await? .signatures; + let aggregated = AggregatedExpirationDateSignatures { + epoch_id, + expiration_date, + signatures: expiration_sigs, + }; + // store the retrieved key storage - .insert_expiration_date_signatures(epoch_id, expiration_date, &expiration_sigs) + .insert_expiration_date_signatures(&aggregated) .await .map_err(BandwidthControllerError::credential_storage_error)?; - Ok(expiration_sigs) + Ok(aggregated.signatures) } diff --git a/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs b/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs new file mode 100644 index 00000000000..889c34ab4ba --- /dev/null +++ b/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs @@ -0,0 +1,68 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli_helpers::{CliClient, CliClientConfig}; +use std::fs; +use std::path::PathBuf; + +#[cfg(feature = "cli")] +fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +#[cfg_attr(feature = "cli", derive(clap::Args))] +#[cfg_attr(feature = "cli", + clap( + group(clap::ArgGroup::new("signatures_data").required(true)), + )) +] +pub struct CommonClientImportCoinIndexSignaturesArgs { + /// Id of client that is going to import the signatures + #[cfg_attr(feature = "cli", clap(long))] + pub id: String, + + /// Config file of the client that is supposed to use the signatures. + #[cfg_attr(feature = "cli", clap(long))] + pub(crate) client_config: PathBuf, + + /// Explicitly provide the encoded signatures data (as base58) + #[cfg_attr(feature = "cli", clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data))] + pub(crate) signatures_data: Option>, + + /// Specifies the path to file containing binary signatures data + #[cfg_attr(feature = "cli", clap(long, group = "signatures_data"))] + pub(crate) signatures_path: Option, + + // currently hidden as there exists only a single serialization standard + #[cfg_attr(feature = "cli", clap(long, hide = true))] + pub(crate) version: Option, +} + +pub async fn import_coin_index_signatures(args: A) -> Result<(), C::Error> +where + A: Into, + C: CliClient, + C::Error: From + From, +{ + let common_args = args.into(); + let id = &common_args.id; + + let config = C::try_load_current_config(id).await?; + let paths = config.common_paths(); + + let credentials_store = + nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await; + + let version = common_args.version; + let raw_key = match common_args.signatures_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + fs::read(common_args.signatures_path.unwrap())? + } + }; + + nym_id::import_coin_index_signatures(credentials_store, raw_key, version).await?; + + Ok(()) +} diff --git a/common/client-core/src/cli_helpers/client_import_credential.rs b/common/client-core/src/cli_helpers/client_import_credential.rs index 48468771dab..77c6dc16f44 100644 --- a/common/client-core/src/cli_helpers/client_import_credential.rs +++ b/common/client-core/src/cli_helpers/client_import_credential.rs @@ -11,9 +11,14 @@ fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result> { } #[cfg_attr(feature = "cli", derive(clap::Args))] -#[cfg_attr(feature = "cli", clap(group(clap::ArgGroup::new("cred_data").required(true))))] +#[cfg_attr(feature = "cli", + clap( + group(clap::ArgGroup::new("cred_data").required(true)), + group(clap::ArgGroup::new("type").required(true)), + )) +] #[derive(Debug, Clone)] -pub struct CommonClientImportCredentialArgs { +pub struct CommonClientImportTicketBookArgs { /// Id of client that is going to import the credential #[cfg_attr(feature = "cli", clap(long))] pub id: String, @@ -26,6 +31,15 @@ pub struct CommonClientImportCredentialArgs { #[cfg_attr(feature = "cli", clap(long, group = "cred_data"))] pub(crate) credential_path: Option, + /// Specifies whether we're attempting to import a standalone ticketbook (i.e. serialised `IssuedTicketBook`) + #[cfg_attr(feature = "cli", clap(long, group = "type"))] + pub(crate) standalone: bool, + + /// Specifies whether we're attempting to import full ticketboot + /// (i.e. one that **might** contain required global signatures; that is serialised `ImportableTicketBook`) + #[cfg_attr(feature = "cli", clap(long, group = "type"))] + pub(crate) full: bool, + // currently hidden as there exists only a single serialization standard #[cfg_attr(feature = "cli", clap(long, hide = true))] pub(crate) version: Option, @@ -33,7 +47,7 @@ pub struct CommonClientImportCredentialArgs { pub async fn import_credential(args: A) -> Result<(), C::Error> where - A: Into, + A: Into, C: CliClient, C::Error: From + From, { @@ -54,6 +68,19 @@ where } }; - nym_id::import_credential(credentials_store, raw_credential, common_args.version).await?; + if common_args.standalone { + nym_id::import_standalone_ticketbook( + credentials_store, + raw_credential, + common_args.version, + ) + .await?; + } else { + // sanity check; clap should have ensured it + assert!(common_args.full); + nym_id::import_full_ticketbook(credentials_store, raw_credential, common_args.version) + .await?; + } + Ok(()) } diff --git a/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs b/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs new file mode 100644 index 00000000000..7d0a14513ae --- /dev/null +++ b/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs @@ -0,0 +1,68 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli_helpers::{CliClient, CliClientConfig}; +use std::fs; +use std::path::PathBuf; + +#[cfg(feature = "cli")] +fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +#[cfg_attr(feature = "cli", derive(clap::Args))] +#[cfg_attr(feature = "cli", + clap( + group(clap::ArgGroup::new("signatures_data").required(true)), + )) +] +pub struct CommonClientImportExpirationDateSignaturesArgs { + /// Id of client that is going to import the signatures + #[cfg_attr(feature = "cli", clap(long))] + pub id: String, + + /// Config file of the client that is supposed to use the signatures. + #[cfg_attr(feature = "cli", clap(long))] + pub(crate) client_config: PathBuf, + + /// Explicitly provide the encoded signatures data (as base58) + #[cfg_attr(feature = "cli", clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data))] + pub(crate) signatures_data: Option>, + + /// Specifies the path to file containing binary signatures data + #[cfg_attr(feature = "cli", clap(long, group = "signatures_data"))] + pub(crate) signatures_path: Option, + + // currently hidden as there exists only a single serialization standard + #[cfg_attr(feature = "cli", clap(long, hide = true))] + pub(crate) version: Option, +} + +pub async fn import_expiration_date_signatures(args: A) -> Result<(), C::Error> +where + A: Into, + C: CliClient, + C::Error: From + From, +{ + let common_args = args.into(); + let id = &common_args.id; + + let config = C::try_load_current_config(id).await?; + let paths = config.common_paths(); + + let credentials_store = + nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await; + + let version = common_args.version; + let raw_key = match common_args.signatures_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + fs::read(common_args.signatures_path.unwrap())? + } + }; + + nym_id::import_expiration_date_signatures(credentials_store, raw_key, version).await?; + + Ok(()) +} diff --git a/common/client-core/src/cli_helpers/client_import_master_verification_key.rs b/common/client-core/src/cli_helpers/client_import_master_verification_key.rs new file mode 100644 index 00000000000..25a3c421f2f --- /dev/null +++ b/common/client-core/src/cli_helpers/client_import_master_verification_key.rs @@ -0,0 +1,68 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli_helpers::{CliClient, CliClientConfig}; +use std::fs; +use std::path::PathBuf; + +#[cfg(feature = "cli")] +fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +#[cfg_attr(feature = "cli", derive(clap::Args))] +#[cfg_attr(feature = "cli", + clap( + group(clap::ArgGroup::new("key_data").required(true)), + )) +] +pub struct CommonClientImportMasterVerificationKeyArgs { + /// Id of client that is going to import the key + #[cfg_attr(feature = "cli", clap(long))] + pub id: String, + + /// Config file of the client that is supposed to use the key. + #[cfg_attr(feature = "cli", clap(long))] + pub(crate) client_config: PathBuf, + + /// Explicitly provide the encoded key data (as base58) + #[cfg_attr(feature = "cli", clap(long, group = "key_data", value_parser = parse_encoded_key_data))] + pub(crate) key_data: Option>, + + /// Specifies the path to file containing binary key data + #[cfg_attr(feature = "cli", clap(long, group = "key_data"))] + pub(crate) key_path: Option, + + // currently hidden as there exists only a single serialization standard + #[cfg_attr(feature = "cli", clap(long, hide = true))] + pub(crate) version: Option, +} + +pub async fn import_master_verification_key(args: A) -> Result<(), C::Error> +where + A: Into, + C: CliClient, + C::Error: From + From, +{ + let common_args = args.into(); + let id = &common_args.id; + + let config = C::try_load_current_config(id).await?; + let paths = config.common_paths(); + + let credentials_store = + nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await; + + let version = common_args.version; + let raw_key = match common_args.key_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + fs::read(common_args.key_path.unwrap())? + } + }; + + nym_id::import_master_verification_key(credentials_store, raw_key, version).await?; + + Ok(()) +} diff --git a/common/client-core/src/cli_helpers/mod.rs b/common/client-core/src/cli_helpers/mod.rs index a3660a60e62..e1f2e231074 100644 --- a/common/client-core/src/cli_helpers/mod.rs +++ b/common/client-core/src/cli_helpers/mod.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 pub mod client_add_gateway; +pub mod client_import_coin_index_signatures; pub mod client_import_credential; +pub mod client_import_expiration_date_signatures; +pub mod client_import_master_verification_key; pub mod client_init; pub mod client_list_gateways; pub mod client_run; diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index 0754aa6f116..b922baa10bf 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -25,8 +25,9 @@ rand = { workspace = true, features = ["std"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } thiserror = { workspace = true } +tempfile = { workspace = true } time = { workspace = true, features = ["parsing", "formatting"] } -tokio = { workspace = true, features = ["sync"]} +tokio = { workspace = true, features = ["sync"] } toml = "0.5.6" url = { workspace = true } tap = { workspace = true } diff --git a/common/commands/src/coconut/issue_ticket_book.rs b/common/commands/src/coconut/issue_ticket_book.rs deleted file mode 100644 index 79eb67ac0c6..00000000000 --- a/common/commands/src/coconut/issue_ticket_book.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2022-2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::context::SigningClient; -use crate::utils::CommonConfigsWrapper; -use anyhow::bail; -use clap::Parser; -use nym_credential_storage::initialise_persistent_storage; -use nym_credential_utils::utils; -use nym_credentials_interface::TicketType; -use nym_crypto::asymmetric::identity; -use std::path::PathBuf; - -#[derive(Debug, Parser)] -pub struct Args { - /// Specify which type of ticketbook should be issued - #[clap(long, default_value_t = TicketType::V1MixnetEntry)] - pub(crate) ticketbook_type: TicketType, - - /// Config file of the client that is supposed to use the credential. - #[clap(long)] - pub(crate) client_config: PathBuf, -} - -pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> { - let loaded = CommonConfigsWrapper::try_load(args.client_config)?; - - if let Ok(id) = loaded.try_get_id() { - println!("loaded config file for client '{id}'"); - } - - let Ok(credentials_store) = loaded.try_get_credentials_store() else { - bail!("the loaded config does not have a credentials store information") - }; - - let Ok(private_id_key) = loaded.try_get_private_id_key() else { - bail!("the loaded config does not have a public id key information") - }; - - println!( - "using credentials store at '{}'", - credentials_store.display() - ); - - let persistent_storage = initialise_persistent_storage(credentials_store).await; - let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?; - utils::issue_credential( - &client, - &persistent_storage, - &private_id_key.to_bytes(), - args.ticketbook_type, - ) - .await?; - - Ok(()) -} diff --git a/common/commands/src/ecash/import_coin_index_signatures.rs b/common/commands/src/ecash/import_coin_index_signatures.rs new file mode 100644 index 00000000000..91d12aafbbe --- /dev/null +++ b/common/commands/src/ecash/import_coin_index_signatures.rs @@ -0,0 +1,76 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::utils::CommonConfigsWrapper; +use anyhow::bail; +use clap::ArgGroup; +use clap::Parser; +use nym_credential_storage::initialise_persistent_storage; +use nym_id::import_credential::import_expiration_date_signatures; +use std::fs; +use std::path::PathBuf; + +fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +#[derive(Debug, Parser)] +#[clap( + group(ArgGroup::new("signatures_data").required(true)), +)] +pub struct Args { + /// Config file of the client that is supposed to use the signatures. + #[clap(long)] + pub(crate) client_config: PathBuf, + + /// Explicitly provide the encoded signatures data (as base58) + #[clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data)] + pub(crate) signatures_data: Option>, + + /// Specifies the path to file containing binary signatures data + #[clap(long, group = "signatures_data")] + pub(crate) signatures_path: Option, + + // currently hidden as there exists only a single serialization standard + #[clap(long, hide = true)] + pub(crate) version: Option, +} + +impl Args { + fn signatures_data(self) -> anyhow::Result> { + let data = match self.signatures_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + #[allow(clippy::unwrap_used)] + fs::read(self.signatures_path.unwrap())? + } + }; + Ok(data) + } +} + +pub async fn execute(args: Args) -> anyhow::Result<()> { + let loaded = CommonConfigsWrapper::try_load(&args.client_config)?; + + if let Ok(id) = loaded.try_get_id() { + println!("loaded config file for client '{id}'"); + } + + let Ok(credentials_store) = loaded.try_get_credentials_store() else { + bail!("the loaded config does not have a credentials store information") + }; + + println!( + "using credentials store at '{}'", + credentials_store.display() + ); + let credentials_store = initialise_persistent_storage(credentials_store).await; + + let version = args.version; + let raw_signatures = args.signatures_data()?; + + import_expiration_date_signatures(credentials_store, raw_signatures, version).await?; + + Ok(()) +} diff --git a/common/commands/src/ecash/import_expiration_date_signatures.rs b/common/commands/src/ecash/import_expiration_date_signatures.rs new file mode 100644 index 00000000000..91d12aafbbe --- /dev/null +++ b/common/commands/src/ecash/import_expiration_date_signatures.rs @@ -0,0 +1,76 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::utils::CommonConfigsWrapper; +use anyhow::bail; +use clap::ArgGroup; +use clap::Parser; +use nym_credential_storage::initialise_persistent_storage; +use nym_id::import_credential::import_expiration_date_signatures; +use std::fs; +use std::path::PathBuf; + +fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +#[derive(Debug, Parser)] +#[clap( + group(ArgGroup::new("signatures_data").required(true)), +)] +pub struct Args { + /// Config file of the client that is supposed to use the signatures. + #[clap(long)] + pub(crate) client_config: PathBuf, + + /// Explicitly provide the encoded signatures data (as base58) + #[clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data)] + pub(crate) signatures_data: Option>, + + /// Specifies the path to file containing binary signatures data + #[clap(long, group = "signatures_data")] + pub(crate) signatures_path: Option, + + // currently hidden as there exists only a single serialization standard + #[clap(long, hide = true)] + pub(crate) version: Option, +} + +impl Args { + fn signatures_data(self) -> anyhow::Result> { + let data = match self.signatures_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + #[allow(clippy::unwrap_used)] + fs::read(self.signatures_path.unwrap())? + } + }; + Ok(data) + } +} + +pub async fn execute(args: Args) -> anyhow::Result<()> { + let loaded = CommonConfigsWrapper::try_load(&args.client_config)?; + + if let Ok(id) = loaded.try_get_id() { + println!("loaded config file for client '{id}'"); + } + + let Ok(credentials_store) = loaded.try_get_credentials_store() else { + bail!("the loaded config does not have a credentials store information") + }; + + println!( + "using credentials store at '{}'", + credentials_store.display() + ); + let credentials_store = initialise_persistent_storage(credentials_store).await; + + let version = args.version; + let raw_signatures = args.signatures_data()?; + + import_expiration_date_signatures(credentials_store, raw_signatures, version).await?; + + Ok(()) +} diff --git a/common/commands/src/ecash/import_master_verification_key.rs b/common/commands/src/ecash/import_master_verification_key.rs new file mode 100644 index 00000000000..f194c00c8a8 --- /dev/null +++ b/common/commands/src/ecash/import_master_verification_key.rs @@ -0,0 +1,76 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::utils::CommonConfigsWrapper; +use anyhow::bail; +use clap::ArgGroup; +use clap::Parser; +use nym_credential_storage::initialise_persistent_storage; +use nym_id::import_credential::import_master_verification_key; +use std::fs; +use std::path::PathBuf; + +fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +#[derive(Debug, Parser)] +#[clap( + group(ArgGroup::new("key_data").required(true)), +)] +pub struct Args { + /// Config file of the client that is supposed to use the key. + #[clap(long)] + pub(crate) client_config: PathBuf, + + /// Explicitly provide the encoded key data (as base58) + #[clap(long, group = "key_data", value_parser = parse_encoded_key_data)] + pub(crate) key_data: Option>, + + /// Specifies the path to file containing binary key data + #[clap(long, group = "key_data")] + pub(crate) key_path: Option, + + // currently hidden as there exists only a single serialization standard + #[clap(long, hide = true)] + pub(crate) version: Option, +} + +impl Args { + fn key_data(self) -> anyhow::Result> { + let data = match self.key_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + #[allow(clippy::unwrap_used)] + fs::read(self.key_path.unwrap())? + } + }; + Ok(data) + } +} + +pub async fn execute(args: Args) -> anyhow::Result<()> { + let loaded = CommonConfigsWrapper::try_load(&args.client_config)?; + + if let Ok(id) = loaded.try_get_id() { + println!("loaded config file for client '{id}'"); + } + + let Ok(credentials_store) = loaded.try_get_credentials_store() else { + bail!("the loaded config does not have a credentials store information") + }; + + println!( + "using credentials store at '{}'", + credentials_store.display() + ); + let credentials_store = initialise_persistent_storage(credentials_store).await; + + let version = args.version; + let raw_key = args.key_data()?; + + import_master_verification_key(credentials_store, raw_key, version).await?; + + Ok(()) +} diff --git a/common/commands/src/coconut/import_ticket_book.rs b/common/commands/src/ecash/import_ticket_book.rs similarity index 51% rename from common/commands/src/coconut/import_ticket_book.rs rename to common/commands/src/ecash/import_ticket_book.rs index 852f94e34ad..4900b37ef52 100644 --- a/common/commands/src/coconut/import_ticket_book.rs +++ b/common/commands/src/ecash/import_ticket_book.rs @@ -6,7 +6,8 @@ use anyhow::bail; use clap::ArgGroup; use clap::Parser; use nym_credential_storage::initialise_persistent_storage; -use nym_id::import_credential; +use nym_id::import_credential::import_full_ticketbook; +use nym_id::import_standalone_ticketbook; use std::fs; use std::path::PathBuf; @@ -15,7 +16,10 @@ fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result> { } #[derive(Debug, Parser)] -#[clap(group(ArgGroup::new("cred_data").required(true)))] +#[clap( + group(ArgGroup::new("cred_data").required(true)), + group(ArgGroup::new("type").required(true)), +)] pub struct Args { /// Config file of the client that is supposed to use the credential. #[clap(long)] @@ -29,13 +33,36 @@ pub struct Args { #[clap(long, group = "cred_data")] pub(crate) credential_path: Option, + /// Specifies whether we're attempting to import a standalone ticketbook (i.e. serialised `IssuedTicketBook`) + #[clap(long, group = "type")] + pub(crate) standalone: bool, + + /// Specifies whether we're attempting to import full ticketboot + /// (i.e. one that **might** contain required global signatures; that is serialised `ImportableTicketBook`) + #[clap(long, group = "type")] + pub(crate) full: bool, + // currently hidden as there exists only a single serialization standard #[clap(long, hide = true)] pub(crate) version: Option, } +impl Args { + fn credential_data(self) -> anyhow::Result> { + let data = match self.credential_data { + Some(data) => data, + None => { + // SAFETY: one of those arguments must have been set + #[allow(clippy::unwrap_used)] + fs::read(self.credential_path.unwrap())? + } + }; + Ok(data) + } +} + pub async fn execute(args: Args) -> anyhow::Result<()> { - let loaded = CommonConfigsWrapper::try_load(args.client_config)?; + let loaded = CommonConfigsWrapper::try_load(&args.client_config)?; if let Ok(id) = loaded.try_get_id() { println!("loaded config file for client '{id}'"); @@ -51,14 +78,18 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { ); let credentials_store = initialise_persistent_storage(credentials_store).await; - let raw_credential = match args.credential_data { - Some(data) => data, - None => { - // SAFETY: one of those arguments must have been set - fs::read(args.credential_path.unwrap())? - } - }; + let version = args.version; + let standalone = args.standalone; + let full = args.full; + let raw_credential = args.credential_data()?; + + if standalone { + import_standalone_ticketbook(credentials_store, raw_credential, version).await?; + } else { + // sanity check; clap should have ensured it + assert!(full); + import_full_ticketbook(credentials_store, raw_credential, version).await?; + } - import_credential(credentials_store, raw_credential, args.version).await?; Ok(()) } diff --git a/common/commands/src/ecash/issue_ticket_book.rs b/common/commands/src/ecash/issue_ticket_book.rs new file mode 100644 index 00000000000..015522b0165 --- /dev/null +++ b/common/commands/src/ecash/issue_ticket_book.rs @@ -0,0 +1,170 @@ +// Copyright 2022-2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::context::SigningClient; +use crate::utils::CommonConfigsWrapper; +use anyhow::{anyhow, bail}; +use clap::ArgGroup; +use clap::Parser; +use nym_credential_storage::initialise_persistent_storage; +use nym_credential_storage::storage::Storage; +use nym_credential_utils::utils; +use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise; +use nym_credentials::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, EpochVerificationKey, +}; +use nym_credentials_interface::TicketType; +use nym_crypto::asymmetric::identity; +use std::fs; +use std::path::PathBuf; +use tempfile::NamedTempFile; + +#[derive(Debug, Parser)] +#[clap( + group(ArgGroup::new("output").required(true)), +)] +pub struct Args { + /// Specify which type of ticketbook should be issued + #[clap(long, default_value_t = TicketType::V1MixnetEntry)] + pub(crate) ticketbook_type: TicketType, + + /// Config file of the client that is supposed to use the credential. + #[clap(long, group = "output")] + pub(crate) client_config: Option, + + /// Output file for the ticketbook + #[clap(long, group = "output", requires = "bs58_encoded_client_secret")] + pub(crate) output_file: Option, + + /// Specifies whether the output file should use binary or bs58 encoded data + #[clap(long, requires = "output_file")] + pub(crate) bs58_output: bool, + + /// Specifies whether the file output should contain expiration date signatures + #[clap(long, requires = "output_file")] + pub(crate) include_expiration_date_signatures: bool, + + /// Specifies whether the file output should contain coin index signatures + #[clap(long, requires = "output_file")] + pub(crate) include_coin_index_signatures: bool, + + /// Specifies whether the file output should contain master verification key + #[clap(long, requires = "output_file")] + pub(crate) include_master_verification_key: bool, + + /// Secret value that's used for deriving underlying ecash keypair + #[clap(long)] + pub(crate) bs58_encoded_client_secret: Option, +} + +async fn issue_client_ticketbook( + config_path: PathBuf, + ticketbook_type: TicketType, + client: SigningClient, +) -> anyhow::Result<()> { + let loaded = CommonConfigsWrapper::try_load(config_path)?; + + if let Ok(id) = loaded.try_get_id() { + println!("loaded config file for client '{id}'"); + } + + let Ok(credentials_store) = loaded.try_get_credentials_store() else { + bail!("the loaded config does not have a credentials store information") + }; + + let Ok(private_id_key) = loaded.try_get_private_id_key() else { + bail!("the loaded config does not have a public id key information") + }; + + println!( + "using credentials store at '{}'", + credentials_store.display() + ); + + let persistent_storage = initialise_persistent_storage(credentials_store).await; + let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?; + utils::issue_credential( + &client, + &persistent_storage, + &private_id_key.to_bytes(), + ticketbook_type, + ) + .await?; + + Ok(()) +} + +async fn issue_to_file(args: Args, client: SigningClient) -> anyhow::Result<()> { + // those MUST HAVE been specified; clap ensures it + let output_file = args.output_file.unwrap(); + let secret = bs58::decode(&args.bs58_encoded_client_secret.unwrap()).into_vec()?; + + let temp_credential_store_file = NamedTempFile::new()?; + let credential_store_path = temp_credential_store_file.into_temp_path(); + + let credentials_store = initialise_persistent_storage(credential_store_path).await; + + utils::issue_credential(&client, &credentials_store, &secret, args.ticketbook_type).await?; + + let ticketbook = credentials_store + .get_next_unspent_usable_ticketbook(0) + .await? + .ok_or(anyhow!("we just issued a ticketbook, it must be present!"))? + .ticketbook; + + let expiration_date = ticketbook.expiration_date(); + let epoch_id = ticketbook.epoch_id(); + + let mut exported = ticketbook.begin_export(); + + if args.include_expiration_date_signatures { + let signatures = credentials_store + .get_expiration_date_signatures(expiration_date) + .await? + .ok_or(anyhow!("missing expiration date signatures!"))?; + + exported.with_expiration_date_signatures(&AggregatedExpirationDateSignatures { + epoch_id, + expiration_date, + signatures, + }); + } + + if args.include_coin_index_signatures { + let signatures = credentials_store + .get_coin_index_signatures(epoch_id) + .await? + .ok_or(anyhow!("missing coin index signatures!"))?; + exported.with_coin_index_signatures(&AggregatedCoinIndicesSignatures { + epoch_id, + signatures, + }); + } + + if args.include_master_verification_key { + let key = credentials_store + .get_master_verification_key(epoch_id) + .await? + .ok_or(anyhow!("missing master verification key!"))?; + + exported.with_master_verification_key(&EpochVerificationKey { epoch_id, key }); + } + + let data = exported.pack().data; + + if args.bs58_output { + fs::write(output_file, bs58::encode(&data).into_string())?; + } else { + fs::write(output_file, &data)?; + } + + Ok(()) +} + +pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> { + if let Some(client_config) = args.client_config { + return issue_client_ticketbook(client_config, args.ticketbook_type, client).await; + } + + issue_to_file(args, client).await +} diff --git a/common/commands/src/coconut/mod.rs b/common/commands/src/ecash/mod.rs similarity index 63% rename from common/commands/src/coconut/mod.rs rename to common/commands/src/ecash/mod.rs index 74421dd42bd..b307e8dc8ce 100644 --- a/common/commands/src/coconut/mod.rs +++ b/common/commands/src/ecash/mod.rs @@ -3,6 +3,9 @@ use clap::{Args, Subcommand}; +pub mod import_coin_index_signatures; +pub mod import_expiration_date_signatures; +pub mod import_master_verification_key; pub mod import_ticket_book; pub mod issue_ticket_book; pub mod recover_ticket_book; @@ -19,4 +22,7 @@ pub enum EcashCommands { IssueTicketBook(issue_ticket_book::Args), RecoverTicketBook(recover_ticket_book::Args), ImportTicketBook(import_ticket_book::Args), + ImportCoinIndexSignatures(import_coin_index_signatures::Args), + ImportExpirationDateSignatures(import_expiration_date_signatures::Args), + ImportMasterVerificationKey(import_master_verification_key::Args), } diff --git a/common/commands/src/coconut/recover_ticket_book.rs b/common/commands/src/ecash/recover_ticket_book.rs similarity index 100% rename from common/commands/src/coconut/recover_ticket_book.rs rename to common/commands/src/ecash/recover_ticket_book.rs diff --git a/common/commands/src/lib.rs b/common/commands/src/lib.rs index afca2be0f7a..e3670ac5726 100644 --- a/common/commands/src/lib.rs +++ b/common/commands/src/lib.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub mod coconut; pub mod context; +pub mod ecash; pub mod utils; pub mod validator; diff --git a/common/credential-storage/migrations/20241104120002_signatures_serialisation_revision.sql b/common/credential-storage/migrations/20241104120002_signatures_serialisation_revision.sql new file mode 100644 index 00000000000..627ca3ae0f1 --- /dev/null +++ b/common/credential-storage/migrations/20241104120002_signatures_serialisation_revision.sql @@ -0,0 +1,13 @@ +/* + * Copyright 2024 - Nym Technologies SA + * SPDX-License-Identifier: Apache-2.0 + */ + +ALTER TABLE master_verification_key + ADD COLUMN serialization_revision INTEGER NOT NULL default 1; + +ALTER TABLE coin_indices_signatures + ADD COLUMN serialization_revision INTEGER NOT NULL default 1; + +ALTER TABLE expiration_date_signatures + ADD COLUMN serialization_revision INTEGER NOT NULL default 1; diff --git a/common/credential-storage/src/backends/memory.rs b/common/credential-storage/src/backends/memory.rs index 71aea890b0f..d1076778977 100644 --- a/common/credential-storage/src/backends/memory.rs +++ b/common/credential-storage/src/backends/memory.rs @@ -5,6 +5,10 @@ use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, Retr use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature; use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature; use nym_compact_ecash::VerificationKeyAuth; +use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use nym_credentials::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise; use nym_credentials::{IssuanceTicketBook, IssuedTicketBook}; use nym_ecash_time::Date; @@ -192,14 +196,10 @@ impl MemoryEcachTicketbookManager { guard.master_vk.get(&epoch_id).cloned() } - pub(crate) async fn insert_master_verification_key( - &self, - epoch_id: u64, - key: &VerificationKeyAuth, - ) { + pub(crate) async fn insert_master_verification_key(&self, key: &EpochVerificationKey) { let mut guard = self.inner.write().await; - guard.master_vk.insert(epoch_id, key.clone()); + guard.master_vk.insert(key.epoch_id, key.key.clone()); } pub(crate) async fn get_coin_index_signatures( @@ -213,12 +213,13 @@ impl MemoryEcachTicketbookManager { pub(crate) async fn insert_coin_index_signatures( &self, - epoch_id: u64, - sigs: &[AnnotatedCoinIndexSignature], + sigs: &AggregatedCoinIndicesSignatures, ) { let mut guard = self.inner.write().await; - guard.coin_indices_sigs.insert(epoch_id, sigs.to_vec()); + guard + .coin_indices_sigs + .insert(sigs.epoch_id, sigs.signatures.clone()); } pub(crate) async fn get_expiration_date_signatures( @@ -232,14 +233,12 @@ impl MemoryEcachTicketbookManager { pub(crate) async fn insert_expiration_date_signatures( &self, - _epoch_id: u64, - expiration_date: Date, - sigs: &[AnnotatedExpirationDateSignature], + sigs: &AggregatedExpirationDateSignatures, ) { let mut guard = self.inner.write().await; guard .expiration_date_sigs - .insert(expiration_date, sigs.to_vec()); + .insert(sigs.expiration_date, sigs.signatures.clone()); } } diff --git a/common/credential-storage/src/backends/sqlite.rs b/common/credential-storage/src/backends/sqlite.rs index 43f019afa36..39dbdc87022 100644 --- a/common/credential-storage/src/backends/sqlite.rs +++ b/common/credential-storage/src/backends/sqlite.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::models::{ - BasicTicketbookInformation, RawExpirationDateSignatures, StoredIssuedTicketbook, - StoredPendingTicketbook, + BasicTicketbookInformation, RawCoinIndexSignatures, RawExpirationDateSignatures, + RawVerificationKey, StoredIssuedTicketbook, StoredPendingTicketbook, }; use nym_ecash_time::Date; use sqlx::{Executor, Sqlite, Transaction}; @@ -151,25 +151,30 @@ impl SqliteEcashTicketbookManager { pub(crate) async fn get_master_verification_key( &self, epoch_id: i64, - ) -> Result>, sqlx::Error> { - sqlx::query!( - "SELECT serialised_key FROM master_verification_key WHERE epoch_id = ?", + ) -> Result, sqlx::Error> { + sqlx::query_as!( + RawVerificationKey, + r#" + SELECT epoch_id as "epoch_id: u32", serialised_key, serialization_revision as "serialization_revision: u8" + FROM master_verification_key WHERE epoch_id = ? + "#, epoch_id ) .fetch_optional(&self.connection_pool) .await - .map(|maybe_record| maybe_record.map(|r| r.serialised_key)) } pub(crate) async fn insert_master_verification_key( &self, + serialisation_revision: u8, epoch_id: i64, data: &[u8], ) -> Result<(), sqlx::Error> { sqlx::query!( - "INSERT INTO master_verification_key(epoch_id, serialised_key) VALUES (?, ?)", + "INSERT INTO master_verification_key(epoch_id, serialised_key, serialization_revision) VALUES (?, ?, ?)", epoch_id, - data + data, + serialisation_revision ) .execute(&self.connection_pool) .await?; @@ -179,25 +184,30 @@ impl SqliteEcashTicketbookManager { pub(crate) async fn get_coin_index_signatures( &self, epoch_id: i64, - ) -> Result>, sqlx::Error> { - sqlx::query!( - "SELECT serialised_signatures FROM coin_indices_signatures WHERE epoch_id = ?", + ) -> Result, sqlx::Error> { + sqlx::query_as!( + RawCoinIndexSignatures, + r#" + SELECT epoch_id as "epoch_id: u32", serialised_signatures, serialization_revision as "serialization_revision: u8" + FROM coin_indices_signatures WHERE epoch_id = ? + "#, epoch_id ) .fetch_optional(&self.connection_pool) .await - .map(|maybe_record| maybe_record.map(|r| r.serialised_signatures)) } pub(crate) async fn insert_coin_index_signatures( &self, + serialisation_revision: u8, epoch_id: i64, data: &[u8], ) -> Result<(), sqlx::Error> { sqlx::query!( - "INSERT INTO coin_indices_signatures(epoch_id, serialised_signatures) VALUES (?, ?)", + "INSERT INTO coin_indices_signatures(epoch_id, serialised_signatures, serialization_revision) VALUES (?, ?, ?)", epoch_id, - data + data, + serialisation_revision ) .execute(&self.connection_pool) .await?; @@ -211,7 +221,7 @@ impl SqliteEcashTicketbookManager { sqlx::query_as!( RawExpirationDateSignatures, r#" - SELECT epoch_id as "epoch_id: u32", serialised_signatures + SELECT epoch_id as "epoch_id: u32", serialised_signatures, serialization_revision as "serialization_revision: u8" FROM expiration_date_signatures WHERE expiration_date = ? "#, @@ -223,15 +233,20 @@ impl SqliteEcashTicketbookManager { pub(crate) async fn insert_expiration_date_signatures( &self, + serialisation_revision: u8, epoch_id: i64, expiration_date: Date, data: &[u8], ) -> Result<(), sqlx::Error> { sqlx::query!( - "INSERT INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures) VALUES (?, ?, ?)", + r#" + INSERT INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures, serialization_revision) + VALUES (?, ?, ?, ?) + "#, expiration_date, epoch_id, - data + data, + serialisation_revision ) .execute(&self.connection_pool) .await?; diff --git a/common/credential-storage/src/ephemeral_storage.rs b/common/credential-storage/src/ephemeral_storage.rs index e1a18942c22..4b3ef93660a 100644 --- a/common/credential-storage/src/ephemeral_storage.rs +++ b/common/credential-storage/src/ephemeral_storage.rs @@ -9,6 +9,10 @@ use async_trait::async_trait; use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature; use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature; use nym_compact_ecash::VerificationKeyAuth; +use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use nym_credentials::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; use nym_credentials::{IssuanceTicketBook, IssuedTicketBook}; use nym_ecash_time::Date; use std::fmt::{self, Debug, Formatter}; @@ -119,11 +123,10 @@ impl Storage for EphemeralStorage { async fn insert_master_verification_key( &self, - epoch_id: u64, - key: &VerificationKeyAuth, + key: &EpochVerificationKey, ) -> Result<(), Self::StorageError> { self.storage_manager - .insert_master_verification_key(epoch_id, key) + .insert_master_verification_key(key) .await; Ok(()) } @@ -140,11 +143,10 @@ impl Storage for EphemeralStorage { async fn insert_coin_index_signatures( &self, - epoch_id: u64, - data: &[AnnotatedCoinIndexSignature], + signatures: &AggregatedCoinIndicesSignatures, ) -> Result<(), Self::StorageError> { self.storage_manager - .insert_coin_index_signatures(epoch_id, data) + .insert_coin_index_signatures(signatures) .await; Ok(()) } @@ -161,12 +163,10 @@ impl Storage for EphemeralStorage { async fn insert_expiration_date_signatures( &self, - epoch_id: u64, - expiration_date: Date, - data: &[AnnotatedExpirationDateSignature], + signatures: &AggregatedExpirationDateSignatures, ) -> Result<(), Self::StorageError> { self.storage_manager - .insert_expiration_date_signatures(epoch_id, expiration_date, data) + .insert_expiration_date_signatures(signatures) .await; Ok(()) } diff --git a/common/credential-storage/src/models.rs b/common/credential-storage/src/models.rs index 2a3faa2d708..2edf9df3cb7 100644 --- a/common/credential-storage/src/models.rs +++ b/common/credential-storage/src/models.rs @@ -62,4 +62,19 @@ pub struct StoredPendingTicketbook { pub struct RawExpirationDateSignatures { pub epoch_id: u32, pub serialised_signatures: Vec, + pub serialization_revision: u8, +} + +#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))] +pub struct RawCoinIndexSignatures { + pub epoch_id: u32, + pub serialised_signatures: Vec, + pub serialization_revision: u8, +} + +#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))] +pub struct RawVerificationKey { + pub epoch_id: u32, + pub serialised_key: Vec, + pub serialization_revision: u8, } diff --git a/common/credential-storage/src/persistent_storage/helpers.rs b/common/credential-storage/src/persistent_storage/helpers.rs deleted file mode 100644 index 614800d609f..00000000000 --- a/common/credential-storage/src/persistent_storage/helpers.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::error::StorageError; -use bincode::Options; -use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature; -use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize)] -struct StorageBorrowedSerdeWrapper<'a, T>(&'a T); - -#[derive(Serialize, Deserialize)] -struct StorageSerdeWrapper(T); - -pub(crate) fn serialise_coin_index_signatures(sigs: &[AnnotatedCoinIndexSignature]) -> Vec { - storage_serialiser() - .serialize(&StorageBorrowedSerdeWrapper(&sigs)) - .unwrap() -} - -pub(crate) fn deserialise_coin_index_signatures( - raw: &[u8], -) -> Result, StorageError> { - let de: StorageSerdeWrapper<_> = storage_serialiser().deserialize(raw).map_err(|_| { - StorageError::database_inconsistency("malformed stored coin index signatures") - })?; - Ok(de.0) -} - -pub(crate) fn serialise_expiration_date_signatures( - sigs: &[AnnotatedExpirationDateSignature], -) -> Vec { - storage_serialiser() - .serialize(&StorageBorrowedSerdeWrapper(&sigs)) - .unwrap() -} - -pub(crate) fn deserialise_expiration_date_signatures( - raw: &[u8], -) -> Result, StorageError> { - let de: StorageSerdeWrapper<_> = storage_serialiser().deserialize(raw).map_err(|_| { - StorageError::database_inconsistency("malformed expiration date signatures") - })?; - Ok(de.0) -} - -// storage serialiser used for non-critical data, such as global expiration signatures or master verification keys, -// i.e. data that could always be queried for again if malformed -fn storage_serialiser() -> impl bincode::Options { - bincode::DefaultOptions::new() - .with_big_endian() - .with_varint_encoding() -} diff --git a/common/credential-storage/src/persistent_storage/legacy_helpers.rs b/common/credential-storage/src/persistent_storage/legacy_helpers.rs new file mode 100644 index 00000000000..d1feb6ef1a1 --- /dev/null +++ b/common/credential-storage/src/persistent_storage/legacy_helpers.rs @@ -0,0 +1,44 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::StorageError; +use bincode::Options; +use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature; +use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature; +use nym_compact_ecash::VerificationKeyAuth; +use serde::Deserialize; + +#[derive(Deserialize)] +struct StorageSerdeWrapper(T); + +pub(crate) fn deserialise_v1_expiration_date_signatures( + raw: &[u8], +) -> Result, StorageError> { + let de: StorageSerdeWrapper<_> = v1_signatures_serialiser().deserialize(raw).map_err(|_| { + StorageError::database_inconsistency("malformed expiration date signatures") + })?; + Ok(de.0) +} + +pub(crate) fn deserialise_v1_coin_index_signatures( + raw: &[u8], +) -> Result, StorageError> { + let de: StorageSerdeWrapper<_> = v1_signatures_serialiser().deserialize(raw).map_err(|_| { + StorageError::database_inconsistency("malformed stored coin index signatures") + })?; + Ok(de.0) +} + +pub(crate) fn deserialise_v1_master_verification_key( + raw: &[u8], +) -> Result { + VerificationKeyAuth::from_bytes(raw).map_err(|_| { + StorageError::database_inconsistency("malformed stored master verification key") + }) +} + +fn v1_signatures_serialiser() -> impl bincode::Options { + bincode::DefaultOptions::new() + .with_big_endian() + .with_varint_encoding() +} diff --git a/common/credential-storage/src/persistent_storage/mod.rs b/common/credential-storage/src/persistent_storage/mod.rs index 3940d82c3ca..c5daceabd57 100644 --- a/common/credential-storage/src/persistent_storage/mod.rs +++ b/common/credential-storage/src/persistent_storage/mod.rs @@ -1,14 +1,16 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +mod legacy_helpers; + use crate::backends::sqlite::{ get_next_unspent_ticketbook, increase_used_ticketbook_tickets, SqliteEcashTicketbookManager, }; use crate::error::StorageError; use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, RetrievedTicketbook}; -use crate::persistent_storage::helpers::{ - deserialise_coin_index_signatures, deserialise_expiration_date_signatures, - serialise_coin_index_signatures, serialise_expiration_date_signatures, +use crate::persistent_storage::legacy_helpers::{ + deserialise_v1_coin_index_signatures, deserialise_v1_expiration_date_signatures, + deserialise_v1_master_verification_key, }; use crate::storage::Storage; use async_trait::async_trait; @@ -16,6 +18,10 @@ use log::{debug, error}; use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature; use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature; use nym_compact_ecash::VerificationKeyAuth; +use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use nym_credentials::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise; use nym_credentials::{IssuanceTicketBook, IssuedTicketBook}; use nym_ecash_time::{ecash_today, Date, EcashTime}; @@ -23,8 +29,6 @@ use sqlx::ConnectOptions; use std::path::Path; use zeroize::Zeroizing; -mod helpers; - // note that clone here is fine as upon cloning the same underlying pool will be used #[derive(Clone)] pub struct PersistentStorage { @@ -229,21 +233,24 @@ impl Storage for PersistentStorage { return Ok(None); }; - let master_vk = VerificationKeyAuth::from_bytes(&raw).map_err(|_| { - StorageError::database_inconsistency("malformed stored master verification key") - })?; - - Ok(Some(master_vk)) + match raw.serialization_revision { + 1 => deserialise_v1_master_verification_key(&raw.serialised_key).map(Some), + other => { + let deserialised = EpochVerificationKey::try_unpack(&raw.serialised_key, other) + .map_err(|err| StorageError::database_inconsistency(err.to_string()))?; + Ok(Some(deserialised.key)) + } + } } async fn insert_master_verification_key( &self, - epoch_id: u64, - key: &VerificationKeyAuth, + key: &EpochVerificationKey, ) -> Result<(), Self::StorageError> { + let packed = key.pack(); Ok(self .storage_manager - .insert_master_verification_key(epoch_id as i64, &key.to_bytes()) + .insert_master_verification_key(packed.revision, key.epoch_id as i64, &packed.data) .await?) } @@ -259,16 +266,24 @@ impl Storage for PersistentStorage { return Ok(None); }; - Ok(Some(deserialise_coin_index_signatures(&raw)?)) + match raw.serialization_revision { + 1 => deserialise_v1_coin_index_signatures(&raw.serialised_signatures).map(Some), + other => { + let deserialised = + AggregatedCoinIndicesSignatures::try_unpack(&raw.serialised_signatures, other) + .map_err(|err| StorageError::database_inconsistency(err.to_string()))?; + Ok(Some(deserialised.signatures)) + } + } } async fn insert_coin_index_signatures( &self, - epoch_id: u64, - sigs: &[AnnotatedCoinIndexSignature], + signatures: &AggregatedCoinIndicesSignatures, ) -> Result<(), Self::StorageError> { + let packed = signatures.pack(); self.storage_manager - .insert_coin_index_signatures(epoch_id as i64, &serialise_coin_index_signatures(sigs)) + .insert_coin_index_signatures(packed.revision, signatures.epoch_id as i64, &packed.data) .await?; Ok(()) } @@ -285,22 +300,30 @@ impl Storage for PersistentStorage { return Ok(None); }; - Ok(Some(deserialise_expiration_date_signatures( - &raw.serialised_signatures, - )?)) + match raw.serialization_revision { + 1 => deserialise_v1_expiration_date_signatures(&raw.serialised_signatures).map(Some), + other => { + let deserialised = AggregatedExpirationDateSignatures::try_unpack( + &raw.serialised_signatures, + other, + ) + .map_err(|err| StorageError::database_inconsistency(err.to_string()))?; + Ok(Some(deserialised.signatures)) + } + } } async fn insert_expiration_date_signatures( &self, - epoch_id: u64, - expiration_date: Date, - sigs: &[AnnotatedExpirationDateSignature], + signatures: &AggregatedExpirationDateSignatures, ) -> Result<(), Self::StorageError> { + let packed = signatures.pack(); self.storage_manager .insert_expiration_date_signatures( - epoch_id as i64, - expiration_date, - &serialise_expiration_date_signatures(sigs), + packed.revision, + signatures.epoch_id as i64, + signatures.expiration_date, + &packed.data, ) .await?; Ok(()) diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index 8b3ebc74ec1..7033cd6e1fb 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -6,6 +6,10 @@ use async_trait::async_trait; use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature; use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature; use nym_compact_ecash::VerificationKeyAuth; +use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use nym_credentials::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; use nym_credentials::{IssuanceTicketBook, IssuedTicketBook}; use nym_ecash_time::Date; use std::error::Error; @@ -64,8 +68,7 @@ pub trait Storage: Send + Sync { async fn insert_master_verification_key( &self, - epoch_id: u64, - key: &VerificationKeyAuth, + key: &EpochVerificationKey, ) -> Result<(), Self::StorageError>; async fn get_coin_index_signatures( @@ -75,8 +78,7 @@ pub trait Storage: Send + Sync { async fn insert_coin_index_signatures( &self, - epoch_id: u64, - data: &[AnnotatedCoinIndexSignature], + signatures: &AggregatedCoinIndicesSignatures, ) -> Result<(), Self::StorageError>; async fn get_expiration_date_signatures( @@ -86,8 +88,6 @@ pub trait Storage: Send + Sync { async fn insert_expiration_date_signatures( &self, - epoch_id: u64, - expiration_date: Date, - data: &[AnnotatedExpirationDateSignature], + signatures: &AggregatedExpirationDateSignatures, ) -> Result<(), Self::StorageError>; } diff --git a/common/credentials-interface/src/lib.rs b/common/credentials-interface/src/lib.rs index 270cb5f658a..fb0cd1a5df0 100644 --- a/common/credentials-interface/src/lib.rs +++ b/common/credentials-interface/src/lib.rs @@ -28,7 +28,7 @@ pub use nym_compact_ecash::{ withdrawal_request, Base58, BlindedSignature, Bytable, EncodedDate, EncodedTicketType, PartialWallet, PayInfo, PublicKeyUser, SecretKeyUser, VerificationKeyAuth, WithdrawalRequest, }; -use nym_ecash_time::{ecash_today, EcashTime}; +pub use nym_ecash_time::{ecash_today, EcashTime}; #[derive(Debug, Clone)] pub struct CredentialSigningData { diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 569c5736cb0..4240718a371 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -25,6 +25,7 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" } nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" } nym-network-defaults = { path = "../network-defaults" } +nym-serde-helpers = { path = "../serde-helpers", features = ["date"] } [dev-dependencies] rand = { workspace = true } diff --git a/common/credentials/src/ecash/bandwidth/importable.rs b/common/credentials/src/ecash/bandwidth/importable.rs new file mode 100644 index 00000000000..a1f9ac099e9 --- /dev/null +++ b/common/credentials/src/ecash/bandwidth/importable.rs @@ -0,0 +1,118 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use crate::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; +use crate::ecash::bandwidth::{ + issued::IssuedTicketBook, + serialiser::{VersionSerialised, VersionedSerialise}, +}; +use crate::Error; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +pub struct DecodedImportableTicketBook { + pub ticketbook: IssuedTicketBook, + + pub expiration_date_signatures: Option, + + pub coin_index_signatures: Option, + + pub master_verification_key: Option, +} + +#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] +pub struct ImportableTicketBook { + pub serialised_ticketbook: VersionSerialised, + + #[zeroize(skip)] + pub serialised_expiration_date_signatures: + Option>, + + #[zeroize(skip)] + pub serialised_coin_index_signatures: + Option>, + + #[zeroize(skip)] + pub serialised_master_verification_key: Option>, +} + +impl From for ImportableTicketBook { + fn from(ticketbook: IssuedTicketBook) -> Self { + ImportableTicketBook { + serialised_ticketbook: ticketbook.pack(), + serialised_expiration_date_signatures: None, + serialised_coin_index_signatures: None, + serialised_master_verification_key: None, + } + } +} + +impl ImportableTicketBook { + pub fn with_expiration_date_signatures( + &mut self, + signatures: &AggregatedExpirationDateSignatures, + ) -> &mut Self { + self.serialised_expiration_date_signatures = Some(signatures.pack()); + self + } + + pub fn with_coin_index_signatures( + &mut self, + signatures: &AggregatedCoinIndicesSignatures, + ) -> &mut Self { + self.serialised_coin_index_signatures = Some(signatures.pack()); + self + } + + pub fn with_master_verification_key(&mut self, key: &EpochVerificationKey) -> &mut Self { + self.serialised_master_verification_key = Some(key.pack()); + self + } + + pub fn finalize_export(self) -> Vec { + self.pack().data + } + + pub fn try_unpack_full(&self) -> Result { + Ok(DecodedImportableTicketBook { + ticketbook: self.serialised_ticketbook.try_unpack()?, + expiration_date_signatures: self + .serialised_expiration_date_signatures + .as_ref() + .map(|sigs| sigs.try_unpack()) + .transpose()?, + coin_index_signatures: self + .serialised_coin_index_signatures + .as_ref() + .map(|sigs| sigs.try_unpack()) + .transpose()?, + master_verification_key: self + .serialised_master_verification_key + .as_ref() + .map(|key| key.try_unpack()) + .transpose()?, + }) + } +} + +impl VersionedSerialise for ImportableTicketBook { + const CURRENT_SERIALISATION_REVISION: u8 = 1; + + fn try_unpack(b: &[u8], revision: impl Into>) -> Result + where + Self: DeserializeOwned, + { + let revision = revision + .into() + .unwrap_or(::CURRENT_SERIALISATION_REVISION); + + match revision { + 1 => Self::try_unpack_current(b), + _ => Err(Error::UnknownSerializationRevision { revision }), + } + } +} diff --git a/common/credentials/src/ecash/bandwidth/issuance.rs b/common/credentials/src/ecash/bandwidth/issuance.rs index 72b1dd0a251..6e9e217f908 100644 --- a/common/credentials/src/ecash/bandwidth/issuance.rs +++ b/common/credentials/src/ecash/bandwidth/issuance.rs @@ -177,7 +177,7 @@ impl IssuanceTicketBook { } // ideally this would have been generic over credential type, but we really don't need secp256k1 keys for bandwidth vouchers - pub async fn obtain_partial_bandwidth_voucher_credential( + pub async fn obtain_partial_ticketbook_credential( &self, client: &nym_validator_client::client::NymApiClient, signer_index: u64, @@ -191,13 +191,6 @@ impl IssuanceTicketBook { self.unblind_signature(validator_vk, &signing_data, blinded_signature, signer_index) } - // pub fn unchecked_aggregate_signature_shares( - // &self, - // shares: &[SignatureShare], - // ) -> Result { - // aggregate_signature_shares(shares).map_err(Error::SignatureAggregationError) - // } - pub fn aggregate_signature_shares( &self, verification_key: &VerificationKeyAuth, diff --git a/common/credentials/src/ecash/bandwidth/issued.rs b/common/credentials/src/ecash/bandwidth/issued.rs index 8787528ae41..a471e7901de 100644 --- a/common/credentials/src/ecash/bandwidth/issued.rs +++ b/common/credentials/src/ecash/bandwidth/issued.rs @@ -1,6 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::ecash::bandwidth::importable::ImportableTicketBook; use crate::ecash::bandwidth::serialiser::VersionedSerialise; use crate::ecash::bandwidth::CredentialSpendingData; use crate::ecash::utils::ecash_today; @@ -153,6 +154,10 @@ impl IssuedTicketBook { epoch_id: self.epoch_id, }) } + + pub fn begin_export(self) -> ImportableTicketBook { + self.into() + } } impl VersionedSerialise for IssuedTicketBook { diff --git a/common/credentials/src/ecash/bandwidth/mod.rs b/common/credentials/src/ecash/bandwidth/mod.rs index ce75507df6b..5c66d80864a 100644 --- a/common/credentials/src/ecash/bandwidth/mod.rs +++ b/common/credentials/src/ecash/bandwidth/mod.rs @@ -5,6 +5,7 @@ pub use issuance::IssuanceTicketBook; pub use issued::IssuedTicketBook; pub use nym_credentials_interface::{CredentialSigningData, CredentialSpendingData}; +pub mod importable; pub mod issuance; pub mod issued; pub mod serialiser; diff --git a/common/credentials/src/ecash/bandwidth/serialiser/keys.rs b/common/credentials/src/ecash/bandwidth/serialiser/keys.rs new file mode 100644 index 00000000000..ee2e1b0d244 --- /dev/null +++ b/common/credentials/src/ecash/bandwidth/serialiser/keys.rs @@ -0,0 +1,35 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ecash::bandwidth::serialiser::VersionedSerialise; +use crate::Error; +use nym_credentials_interface::VerificationKeyAuth; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EpochVerificationKey { + pub epoch_id: u64, + + pub key: VerificationKeyAuth, +} + +impl VersionedSerialise for EpochVerificationKey { + // we start with revision 2 as the initial, 1, only contained the inner `key` field data + const CURRENT_SERIALISATION_REVISION: u8 = 2; + + fn try_unpack(b: &[u8], revision: impl Into>) -> Result + where + Self: DeserializeOwned, + { + let revision = revision + .into() + .unwrap_or(::CURRENT_SERIALISATION_REVISION); + + match revision { + 1 => Err(Error::UnsupportedSerializationRevision { revision }), + 2 => Self::try_unpack_current(b), + _ => Err(Error::UnknownSerializationRevision { revision }), + } + } +} diff --git a/common/credentials/src/ecash/bandwidth/serialiser.rs b/common/credentials/src/ecash/bandwidth/serialiser/mod.rs similarity index 83% rename from common/credentials/src/ecash/bandwidth/serialiser.rs rename to common/credentials/src/ecash/bandwidth/serialiser/mod.rs index acb76290026..518202187f2 100644 --- a/common/credentials/src/ecash/bandwidth/serialiser.rs +++ b/common/credentials/src/ecash/bandwidth/serialiser/mod.rs @@ -5,17 +5,33 @@ use crate::ecash::bandwidth::issued::CURRENT_SERIALIZATION_REVISION; use crate::Error; use bincode::Options; use serde::de::DeserializeOwned; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::marker::PhantomData; +use zeroize::Zeroize; +pub mod keys; +pub mod signatures; + +#[derive(Zeroize, Serialize, Deserialize)] pub struct VersionSerialised { pub data: Vec, pub revision: u8, // still wondering if there's any point in having the phantom in here + #[zeroize(skip)] + #[serde(skip)] _phantom: PhantomData, } +impl VersionSerialised { + pub fn try_unpack(&self) -> Result + where + T: VersionedSerialise + DeserializeOwned, + { + T::try_unpack(&self.data, self.revision) + } +} + pub trait VersionedSerialise { const CURRENT_SERIALISATION_REVISION: u8; diff --git a/common/credentials/src/ecash/bandwidth/serialiser/signatures.rs b/common/credentials/src/ecash/bandwidth/serialiser/signatures.rs new file mode 100644 index 00000000000..02095ca2c8e --- /dev/null +++ b/common/credentials/src/ecash/bandwidth/serialiser/signatures.rs @@ -0,0 +1,66 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ecash::bandwidth::serialiser::VersionedSerialise; +use crate::Error; +use nym_credentials_interface::{AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use time::Date; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AggregatedExpirationDateSignatures { + pub epoch_id: u64, + + #[serde(with = "nym_serde_helpers::date")] + pub expiration_date: Date, + + pub signatures: Vec, +} + +impl VersionedSerialise for AggregatedExpirationDateSignatures { + // we start with revision 2 as the initial, 1, only contained the inner `signatures` field data + const CURRENT_SERIALISATION_REVISION: u8 = 2; + + fn try_unpack(b: &[u8], revision: impl Into>) -> Result + where + Self: DeserializeOwned, + { + let revision = revision + .into() + .unwrap_or(::CURRENT_SERIALISATION_REVISION); + + match revision { + 1 => Err(Error::UnsupportedSerializationRevision { revision }), + 2 => Self::try_unpack_current(b), + _ => Err(Error::UnknownSerializationRevision { revision }), + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AggregatedCoinIndicesSignatures { + pub epoch_id: u64, + + pub signatures: Vec, +} + +impl VersionedSerialise for AggregatedCoinIndicesSignatures { + // we start with revision 2 as the initial, 1, only contained the inner `signatures` field data + const CURRENT_SERIALISATION_REVISION: u8 = 2; + + fn try_unpack(b: &[u8], revision: impl Into>) -> Result + where + Self: DeserializeOwned, + { + let revision = revision + .into() + .unwrap_or(::CURRENT_SERIALISATION_REVISION); + + match revision { + 1 => Err(Error::UnsupportedSerializationRevision { revision }), + 2 => Self::try_unpack_current(b), + _ => Err(Error::UnknownSerializationRevision { revision }), + } + } +} diff --git a/common/credentials/src/ecash/utils.rs b/common/credentials/src/ecash/utils.rs index 970288b075f..96d24ad018a 100644 --- a/common/credentials/src/ecash/utils.rs +++ b/common/credentials/src/ecash/utils.rs @@ -151,7 +151,7 @@ pub async fn obtain_aggregate_wallet( ); match voucher - .obtain_partial_bandwidth_voucher_credential( + .obtain_partial_ticketbook_credential( &ecash_api_client.api_client, ecash_api_client.node_id, &ecash_api_client.verification_key, diff --git a/common/credentials/src/error.rs b/common/credentials/src/error.rs index c1f99608eed..8f69056de55 100644 --- a/common/credentials/src/error.rs +++ b/common/credentials/src/error.rs @@ -22,7 +22,10 @@ pub enum Error { revision: u8, }, - #[error("unknown credential serializatio revision {revision}. the current (and max supported) version is {CURRENT_SERIALIZATION_REVISION}")] + #[error("unsupported data serialization revision {revision}. the current (and max supported) version is {CURRENT_SERIALIZATION_REVISION}")] + UnsupportedSerializationRevision { revision: u8 }, + + #[error("unknown data serialization revision {revision}. the current (and max supported) version is {CURRENT_SERIALIZATION_REVISION}")] UnknownSerializationRevision { revision: u8 }, #[error("Could not contact any validator")] diff --git a/common/credentials/src/lib.rs b/common/credentials/src/lib.rs index 7f2c3ef2d4d..71549ba59bb 100644 --- a/common/credentials/src/lib.rs +++ b/common/credentials/src/lib.rs @@ -5,6 +5,11 @@ pub mod ecash; pub mod error; pub use ecash::bandwidth::{ + importable::{DecodedImportableTicketBook, ImportableTicketBook}, + serialiser::{ + keys::EpochVerificationKey, + signatures::{AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures}, + }, CredentialSigningData, CredentialSpendingData, IssuanceTicketBook, IssuedTicketBook, }; pub use ecash::utils::{aggregate_verification_keys, obtain_aggregate_wallet}; diff --git a/common/nym-id/src/error.rs b/common/nym-id/src/error.rs index 780658944e6..9ccf73267b7 100644 --- a/common/nym-id/src/error.rs +++ b/common/nym-id/src/error.rs @@ -7,12 +7,33 @@ use time::Date; #[derive(Debug, Error)] pub enum NymIdError { - #[error("failed to deserialize provided credential: {source}")] - CredentialDeserializationFailure { source: nym_credentials::Error }, + #[error("failed to deserialize provided full ticketbook: {source}")] + FullTicketbookDeserializationFailure { source: nym_credentials::Error }, + + #[error("failed to deserialize provided ticketbook: {source}")] + TicketbookDeserializationFailure { source: nym_credentials::Error }, + + #[error("failed to deserialize provided expiration date signatures: {source}")] + ExpirationDateSignaturesDeserializationFailure { source: nym_credentials::Error }, + + #[error("failed to deserialize provided coin index signatures: {source}")] + CoinIndexSignaturesDeserializationFailure { source: nym_credentials::Error }, + + #[error("failed to deserialize provided verification key: {source}")] + VerificationKeyDeserializationFailure { source: nym_credentials::Error }, #[error("attempted to import an expired credential (it expired on {expiration})")] ExpiredCredentialImport { expiration: Date }, + #[error("could not import ticketbook expiring at {date} since we do not have corresponding expiration date signatures")] + MissingExpirationDateSignatures { date: Date }, + + #[error("could not import ticketbook for epoch {epoch_id} since we do not have corresponding coin index signatures")] + MissingCoinIndexSignatures { epoch_id: u64 }, + + #[error("could not import ticketbook for epoch {epoch_id} since we do not have corresponding master verification key")] + MissingMasterVerificationKey { epoch_id: u64 }, + #[error("failed to store credential in the provided store: {source}")] StorageError { source: Box, diff --git a/common/nym-id/src/import_credential.rs b/common/nym-id/src/import_credential.rs deleted file mode 100644 index 867db157490..00000000000 --- a/common/nym-id/src/import_credential.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::NymIdError; -use nym_credential_storage::storage::Storage; -use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise; -use nym_credentials::ecash::utils::EcashTime; -use nym_credentials::IssuedTicketBook; -use time::OffsetDateTime; -use tracing::{debug, warn}; -use zeroize::Zeroizing; - -pub async fn import_credential( - credentials_store: S, - raw_credential: Vec, - credential_version: impl Into>, -) -> Result -where - S: Storage, - ::StorageError: Send + Sync + 'static, -{ - let raw_credential = Zeroizing::new(raw_credential); - - // note: the type itself implements ZeroizeOnDrop - let ticketbook = IssuedTicketBook::try_unpack(&raw_credential, credential_version) - .map_err(|source| NymIdError::CredentialDeserializationFailure { source })?; - - debug!( - "attempting to import credential with expiration date at {}", - ticketbook.expiration_date() - ); - - if ticketbook.expired() { - warn!("the credential has already expired!"); - - // technically we can import it, but the gateway will just reject it so what's the point - return Err(NymIdError::ExpiredCredentialImport { - expiration: ticketbook.expiration_date(), - }); - } - - credentials_store - .insert_issued_ticketbook(&ticketbook) - .await - .map_err(|source| NymIdError::StorageError { - source: Box::new(source), - })?; - Ok(ticketbook.expiration_date().ecash_datetime()) -} diff --git a/common/nym-id/src/import_credential/helpers.rs b/common/nym-id/src/import_credential/helpers.rs new file mode 100644 index 00000000000..dec6228d794 --- /dev/null +++ b/common/nym-id/src/import_credential/helpers.rs @@ -0,0 +1,147 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::NymIdError; +use nym_credential_storage::storage::Storage; +use nym_credentials::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, EpochVerificationKey, + IssuedTicketBook, +}; +use tracing::{debug, warn}; + +pub(crate) async fn import_master_verification_key( + credentials_store: &S, + key: &EpochVerificationKey, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + debug!( + "attempting to import master verification key for epoch {}", + key.epoch_id + ); + + credentials_store + .insert_master_verification_key(key) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })?; + Ok(()) +} + +pub(crate) async fn import_expiration_date_signatures( + credentials_store: &S, + signatures: &AggregatedExpirationDateSignatures, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + debug!( + "attempting to import expiration date signatures with expiration date at {} (epoch: {})", + signatures.expiration_date, signatures.epoch_id + ); + + credentials_store + .insert_expiration_date_signatures(signatures) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })?; + Ok(()) +} + +pub(crate) async fn import_coin_index_signatures( + credentials_store: &S, + signatures: &AggregatedCoinIndicesSignatures, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + debug!( + "attempting to import coin index signatures for epoch {}", + signatures.epoch_id + ); + + credentials_store + .insert_coin_index_signatures(signatures) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })?; + Ok(()) +} + +pub(crate) async fn import_ticketbook( + credentials_store: &S, + ticketbook: &IssuedTicketBook, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + debug!( + "attempting to import ticketbook with expiration date at {}", + ticketbook.expiration_date() + ); + + if ticketbook.expired() { + warn!("the credential has already expired!"); + + // technically we can import it, but the gateway will just reject it so what's the point + return Err(NymIdError::ExpiredCredentialImport { + expiration: ticketbook.expiration_date(), + }); + } + + // in order to import the ticketbook we MUST have the appropriate signatures in the storage already + if credentials_store + .get_expiration_date_signatures(ticketbook.expiration_date()) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })? + .is_none() + { + return Err(NymIdError::MissingExpirationDateSignatures { + date: ticketbook.expiration_date(), + }); + } + + if credentials_store + .get_coin_index_signatures(ticketbook.epoch_id()) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })? + .is_none() + { + return Err(NymIdError::MissingCoinIndexSignatures { + epoch_id: ticketbook.epoch_id(), + }); + } + + if credentials_store + .get_master_verification_key(ticketbook.epoch_id()) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })? + .is_none() + { + return Err(NymIdError::MissingMasterVerificationKey { + epoch_id: ticketbook.epoch_id(), + }); + } + + credentials_store + .insert_issued_ticketbook(ticketbook) + .await + .map_err(|source| NymIdError::StorageError { + source: Box::new(source), + })?; + Ok(()) +} diff --git a/common/nym-id/src/import_credential/mod.rs b/common/nym-id/src/import_credential/mod.rs new file mode 100644 index 00000000000..878b3bc5bc4 --- /dev/null +++ b/common/nym-id/src/import_credential/mod.rs @@ -0,0 +1,119 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::NymIdError; +use nym_credential_storage::storage::Storage; +use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey; +use nym_credentials::ecash::bandwidth::serialiser::signatures::{ + AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, +}; +use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise; +use nym_credentials::ecash::utils::EcashTime; +use nym_credentials::{ImportableTicketBook, IssuedTicketBook}; +use time::OffsetDateTime; +use zeroize::Zeroizing; + +mod helpers; + +pub async fn import_master_verification_key( + credentials_store: S, + raw_key: Vec, + key_version: impl Into>, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + let key = EpochVerificationKey::try_unpack(&raw_key, key_version) + .map_err(|source| NymIdError::VerificationKeyDeserializationFailure { source })?; + + helpers::import_master_verification_key(&credentials_store, &key).await +} + +pub async fn import_expiration_date_signatures( + credentials_store: S, + raw_signatures: Vec, + signatures_version: impl Into>, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + let signatures = + AggregatedExpirationDateSignatures::try_unpack(&raw_signatures, signatures_version) + .map_err( + |source| NymIdError::ExpirationDateSignaturesDeserializationFailure { source }, + )?; + + helpers::import_expiration_date_signatures(&credentials_store, &signatures).await +} + +pub async fn import_coin_index_signatures( + credentials_store: S, + raw_signatures: Vec, + signatures_version: impl Into>, +) -> Result<(), NymIdError> +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + let signatures = + AggregatedCoinIndicesSignatures::try_unpack(&raw_signatures, signatures_version) + .map_err(|source| NymIdError::CoinIndexSignaturesDeserializationFailure { source })?; + + helpers::import_coin_index_signatures(&credentials_store, &signatures).await +} + +pub async fn import_standalone_ticketbook( + credentials_store: S, + raw_credential: Vec, + credential_version: impl Into>, +) -> Result +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + let raw_credential = Zeroizing::new(raw_credential); + + // note: the type itself implements ZeroizeOnDrop + let ticketbook = IssuedTicketBook::try_unpack(&raw_credential, credential_version) + .map_err(|source| NymIdError::TicketbookDeserializationFailure { source })?; + + helpers::import_ticketbook(&credentials_store, &ticketbook).await?; + Ok(ticketbook.expiration_date().ecash_datetime()) +} + +pub async fn import_full_ticketbook( + credentials_store: S, + raw_credential: Vec, + credential_version: impl Into>, +) -> Result +where + S: Storage, + ::StorageError: Send + Sync + 'static, +{ + let raw_credential = Zeroizing::new(raw_credential); + + let importable = ImportableTicketBook::try_unpack(&raw_credential, credential_version) + .map_err(|source| NymIdError::FullTicketbookDeserializationFailure { source })?; + + let decoded = importable + .try_unpack_full() + .map_err(|source| NymIdError::TicketbookDeserializationFailure { source })?; + + if let Some(key) = &decoded.master_verification_key { + helpers::import_master_verification_key(&credentials_store, key).await? + } + + if let Some(sigs) = &decoded.expiration_date_signatures { + helpers::import_expiration_date_signatures(&credentials_store, sigs).await? + } + + if let Some(sigs) = &decoded.coin_index_signatures { + helpers::import_coin_index_signatures(&credentials_store, sigs).await? + } + + helpers::import_ticketbook(&credentials_store, &decoded.ticketbook).await?; + + Ok(decoded.ticketbook.expiration_date().ecash_datetime()) +} diff --git a/common/nym-id/src/lib.rs b/common/nym-id/src/lib.rs index e2e3a74adc8..e535bddd160 100644 --- a/common/nym-id/src/lib.rs +++ b/common/nym-id/src/lib.rs @@ -8,4 +8,7 @@ pub mod error; pub mod import_credential; pub use error::NymIdError; -pub use import_credential::import_credential; +pub use import_credential::{ + import_coin_index_signatures, import_expiration_date_signatures, import_full_ticketbook, + import_master_verification_key, import_standalone_ticketbook, +}; diff --git a/common/nym_offline_compact_ecash/src/scheme/aggregation.rs b/common/nym_offline_compact_ecash/src/scheme/aggregation.rs index 4d7c116121e..b5ca003e22e 100644 --- a/common/nym_offline_compact_ecash/src/scheme/aggregation.rs +++ b/common/nym_offline_compact_ecash/src/scheme/aggregation.rs @@ -14,6 +14,7 @@ use core::iter::Sum; use core::ops::Mul; use group::Curve; use itertools::Itertools; +use zeroize::Zeroizing; pub(crate) trait Aggregatable: Sized { fn aggregate(aggregatable: &[Self], indices: Option<&[SignerIndex]>) -> Result; @@ -138,12 +139,12 @@ pub fn aggregate_wallets( .map(|wallet| SignatureShare::new(*wallet.signature(), wallet.index())) .collect(); - let attributes = vec![ + let attributes = Zeroizing::new(vec![ sk_user.sk, *req_info.get_v(), *req_info.get_expiration_date(), *req_info.get_t_type(), - ]; + ]); let aggregated_signature = aggregate_signature_shares(verification_key, &attributes, &signature_shares)?; diff --git a/common/nym_offline_compact_ecash/src/scheme/keygen.rs b/common/nym_offline_compact_ecash/src/scheme/keygen.rs index ade891b01fb..f5926d9cb69 100644 --- a/common/nym_offline_compact_ecash/src/scheme/keygen.rs +++ b/common/nym_offline_compact_ecash/src/scheme/keygen.rs @@ -524,7 +524,22 @@ pub struct KeyPairUser { public_key: PublicKeyUser, } +impl From for SecretKeyUser { + fn from(value: KeyPairUser) -> Self { + value.secret_key + } +} + impl KeyPairUser { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + generate_keypair_user() + } + + pub fn new_seeded>(seed: M) -> Self { + generate_keypair_user_from_seed(seed) + } + pub fn secret_key(&self) -> &SecretKeyUser { &self.secret_key } diff --git a/common/nym_offline_compact_ecash/src/scheme/withdrawal.rs b/common/nym_offline_compact_ecash/src/scheme/withdrawal.rs index 082f9695088..77d780f589a 100644 --- a/common/nym_offline_compact_ecash/src/scheme/withdrawal.rs +++ b/common/nym_offline_compact_ecash/src/scheme/withdrawal.rs @@ -16,6 +16,7 @@ use bls12_381::{multi_miller_loop, G1Projective, G2Prepared, G2Projective, Scala use group::{Curve, Group, GroupEncoding}; use serde::{Deserialize, Serialize}; use std::ops::Neg; +use zeroize::{Zeroize, ZeroizeOnDrop}; /// Represents a withdrawal request generate by the client who wants to obtain a zk-nym credential. /// @@ -51,7 +52,7 @@ impl WithdrawalRequest { /// /// This structure holds the commitment hash, commitment opening, private attributes openings, /// the wallet secret (scalar), and the expiration date related to a withdrawal request. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] pub struct RequestInfo { joined_commitment_hash: G1Projective, joined_commitment_opening: Scalar, diff --git a/common/serde-helpers/Cargo.toml b/common/serde-helpers/Cargo.toml index 1cb5c76fb74..a45ed0afbd9 100644 --- a/common/serde-helpers/Cargo.toml +++ b/common/serde-helpers/Cargo.toml @@ -15,8 +15,9 @@ license.workspace = true serde = { workspace = true } bs58 = { workspace = true, optional = true } base64 = { workspace = true, optional = true } - +time = { workspace = true, features = ["formatting", "parsing"], optional = true } [features] bs58 = ["dep:bs58"] base64 = ["dep:base64"] +date = ["time"] \ No newline at end of file diff --git a/common/serde-helpers/src/lib.rs b/common/serde-helpers/src/lib.rs index f90a65a2f4a..fd37be86b8b 100644 --- a/common/serde-helpers/src/lib.rs +++ b/common/serde-helpers/src/lib.rs @@ -31,3 +31,41 @@ pub mod bs58 { .map_err(serde::de::Error::custom) } } + +#[cfg(feature = "date")] +pub mod date { + use serde::ser::Error; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use time::format_description::{modifier, BorrowedFormatItem, Component}; + use time::Date; + + // simple YYYY-MM-DD + pub const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[ + BorrowedFormatItem::Component(Component::Year(modifier::Year::default())), + BorrowedFormatItem::Literal(b"-"), + BorrowedFormatItem::Component(Component::Month(modifier::Month::default())), + BorrowedFormatItem::Literal(b"-"), + BorrowedFormatItem::Component(Component::Day(modifier::Day::default())), + ]; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + + Date::parse(&s, DATE_FORMAT).map_err(de::Error::custom) + } + + pub fn serialize(datetime: &Date, serializer: S) -> Result + where + S: Serializer, + { + // serialize it with human-readable format for compatibility with eclipse and nutella clients + // in the future change it back to rfc3339 + datetime + .format(&DATE_FORMAT) + .map_err(S::Error::custom)? + .serialize(serializer) + } +} diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 3bdbdb90224..aecf93c51ee 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -1292,7 +1292,7 @@ dependencies = [ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ - "bs58 0.4.0", + "bs58 0.5.1", "cosmwasm-schema", "cosmwasm-std", "cw2", @@ -1697,9 +1697,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -1724,9 +1724,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", @@ -1933,18 +1933,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", diff --git a/envs/sandbox.env b/envs/sandbox.env index 6deaafdfaa5..4763269a6f3 100644 --- a/envs/sandbox.env +++ b/envs/sandbox.env @@ -13,11 +13,10 @@ DENOMS_EXPONENT=6 REWARDING_VALIDATOR_ADDRESS=n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz -ECASH_CONTRACT_ADDRESS=n1ljlwey4xdj0zs7zueepc48nkr033fca6fjgvurfvttqegm8dvsrswsul70 -GROUP_CONTRACT_ADDRESS=n10v3rjnq4cjyccfykyams68ztce337gksuu6f0lvtl4meuwvkewaqru4uav -MULTISIG_CONTRACT_ADDRESS=n1cemnu8as0ls45v3caunpesl8jlsfw2ff9rlwnltlecp7zrxct4dsqc2y42 -COCONUT_DKG_CONTRACT_ADDRESS=n1zx96qgd88vqlzcxkpwzks7kqs5ctrx36xtzfc58p7q6c4ng9anlqzc4nh8 - +GROUP_CONTRACT_ADDRESS=n1ewmwz97xm0h8rdk8sw7h9mwn866qkx9hl9zlmagqfkhuzvwk5hhq844ue9 +MULTISIG_CONTRACT_ADDRESS=n1tz0setr8vkh9udp8xyxgpqc89ns27k4d0jx2h942hr0ax63yjhmqz6xct8 +COCONUT_DKG_CONTRACT_ADDRESS=n1v3n2ly2dp3a9ng3ff6rh26yfkn0pc5hed7w2shc5u9ca5c865utqj5elvh +ECASH_CONTRACT_ADDRESS=n1v3vydvs2ued84yv3khqwtgldmgwn0elljsdh08dr5s2j9x4rc5fs9jlwz9 STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" EXPLORER_API=https://sandbox-explorer.nymtech.net/api diff --git a/nym-api/nym-api-requests/Cargo.toml b/nym-api/nym-api-requests/Cargo.toml index ca647160e77..8549c073eb9 100644 --- a/nym-api/nym-api-requests/Cargo.toml +++ b/nym-api/nym-api-requests/Cargo.toml @@ -25,7 +25,7 @@ sha2 = "0.10.8" # for serde on secp256k1 signatures ecdsa = { workspace = true, features = ["serde"] } -nym-serde-helpers = { path = "../../common/serde-helpers", features = ["bs58", "base64"] } +nym-serde-helpers = { path = "../../common/serde-helpers", features = ["bs58", "base64", "date"] } nym-credentials-interface = { path = "../../common/credentials-interface" } nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"] } diff --git a/nym-api/nym-api-requests/src/helpers.rs b/nym-api/nym-api-requests/src/helpers.rs index dc2a28c1f57..651030fc746 100644 --- a/nym-api/nym-api-requests/src/helpers.rs +++ b/nym-api/nym-api-requests/src/helpers.rs @@ -2,27 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 use schemars::JsonSchema; -use time::format_description::{modifier, BorrowedFormatItem, Component}; use time::OffsetDateTime; // just to have something, even if not accurate to generate the swagger docs #[derive(JsonSchema)] pub struct PlaceholderJsonSchemaImpl {} -const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[ - BorrowedFormatItem::Component(Component::Year(modifier::Year::default())), - BorrowedFormatItem::Literal(b"-"), - BorrowedFormatItem::Component(Component::Month(modifier::Month::default())), - BorrowedFormatItem::Literal(b"-"), - BorrowedFormatItem::Component(Component::Day(modifier::Day::default())), -]; - pub(crate) const fn unix_epoch() -> OffsetDateTime { OffsetDateTime::UNIX_EPOCH } pub(crate) mod overengineered_offset_date_time_serde { - use crate::helpers::{unix_epoch, DATE_FORMAT}; + use crate::helpers::unix_epoch; + use nym_serde_helpers::date::DATE_FORMAT; use serde::de::Visitor; use serde::ser::Error; use serde::{Deserializer, Serialize, Serializer}; @@ -112,30 +104,5 @@ pub(crate) mod overengineered_offset_date_time_serde { } } -pub(crate) mod date_serde { - use crate::helpers::DATE_FORMAT; - use serde::ser::Error; - use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - use time::Date; - - pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - - Date::parse(&s, DATE_FORMAT).map_err(de::Error::custom) - } - - pub(crate) fn serialize(datetime: &Date, serializer: S) -> Result - where - S: Serializer, - { - // serialize it with human-readable format for compatibility with eclipse and nutella clients - // in the future change it back to rfc3339 - datetime - .format(&DATE_FORMAT) - .map_err(S::Error::custom)? - .serialize(serializer) - } -} +// reimport the module to not break existing imports +pub(crate) use nym_serde_helpers::date as date_serde; diff --git a/nym-api/src/ecash/comm.rs b/nym-api/src/ecash/comm.rs index 20941c484c1..a539cb12c2e 100644 --- a/nym-api/src/ecash/comm.rs +++ b/nym-api/src/ecash/comm.rs @@ -42,7 +42,7 @@ impl CachedEpoch { self.valid_until > OffsetDateTime::now_utc() } - async fn update(&mut self, epoch: Epoch) -> Result<()> { + fn update(&mut self, epoch: Epoch) -> Result<()> { let now = OffsetDateTime::now_utc(); let validity_duration = if let Some(epoch_finish) = epoch.deadline { @@ -85,7 +85,7 @@ impl QueryCommunicationChannel { let epoch = ecash::client::Client::get_current_epoch(&self.nyxd_client).await?; - guard.update(epoch).await?; + guard.update(epoch)?; Ok(guard) } } diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 7117b38868f..e905f79d5f5 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -3355,6 +3355,7 @@ dependencies = [ "base64 0.22.1", "bs58", "serde", + "time", ] [[package]] diff --git a/service-providers/authenticator/src/cli/ecash/import_coin_index_signatures.rs b/service-providers/authenticator/src/cli/ecash/import_coin_index_signatures.rs new file mode 100644 index 00000000000..c3b1aa922e0 --- /dev/null +++ b/service-providers/authenticator/src/cli/ecash/import_coin_index_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliAuthenticatorClient; +use nym_authenticator::error::AuthenticatorError; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::{ + import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportCoinIndexSignaturesArgs, +) -> Result<(), AuthenticatorError> { + import_coin_index_signatures::(args).await?; + println!("successfully imported coin index signatures!"); + Ok(()) +} diff --git a/service-providers/authenticator/src/cli/import_credential.rs b/service-providers/authenticator/src/cli/ecash/import_credential.rs similarity index 69% rename from service-providers/authenticator/src/cli/import_credential.rs rename to service-providers/authenticator/src/cli/ecash/import_credential.rs index 808a1c03630..e525eac5184 100644 --- a/service-providers/authenticator/src/cli/import_credential.rs +++ b/service-providers/authenticator/src/cli/ecash/import_credential.rs @@ -4,12 +4,10 @@ use crate::cli::CliAuthenticatorClient; use nym_authenticator::error::AuthenticatorError; use nym_client_core::cli_helpers::client_import_credential::{ - import_credential, CommonClientImportCredentialArgs, + import_credential, CommonClientImportTicketBookArgs, }; -pub(crate) async fn execute( - args: CommonClientImportCredentialArgs, -) -> Result<(), AuthenticatorError> { +pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), AuthenticatorError> { import_credential::(args).await?; println!("successfully imported credential!"); Ok(()) diff --git a/service-providers/authenticator/src/cli/ecash/import_expiration_date_signatures.rs b/service-providers/authenticator/src/cli/ecash/import_expiration_date_signatures.rs new file mode 100644 index 00000000000..7db61d52388 --- /dev/null +++ b/service-providers/authenticator/src/cli/ecash/import_expiration_date_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliAuthenticatorClient; +use nym_authenticator::error::AuthenticatorError; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{ + import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportExpirationDateSignaturesArgs, +) -> Result<(), AuthenticatorError> { + import_expiration_date_signatures::(args).await?; + println!("successfully imported expiration date signatures!"); + Ok(()) +} diff --git a/service-providers/authenticator/src/cli/ecash/import_master_verification_key.rs b/service-providers/authenticator/src/cli/ecash/import_master_verification_key.rs new file mode 100644 index 00000000000..d53ddacf4c2 --- /dev/null +++ b/service-providers/authenticator/src/cli/ecash/import_master_verification_key.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliAuthenticatorClient; +use nym_authenticator::error::AuthenticatorError; +use nym_client_core::cli_helpers::client_import_master_verification_key::{ + import_master_verification_key, CommonClientImportMasterVerificationKeyArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportMasterVerificationKeyArgs, +) -> Result<(), AuthenticatorError> { + import_master_verification_key::(args).await?; + println!("successfully imported master verification key!"); + Ok(()) +} diff --git a/service-providers/authenticator/src/cli/ecash/mod.rs b/service-providers/authenticator/src/cli/ecash/mod.rs new file mode 100644 index 00000000000..9272ed013cd --- /dev/null +++ b/service-providers/authenticator/src/cli/ecash/mod.rs @@ -0,0 +1,59 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; +use nym_authenticator::error::AuthenticatorError; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs; +use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; +use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; + +pub(crate) mod import_coin_index_signatures; +pub(crate) mod import_credential; +pub(crate) mod import_expiration_date_signatures; +pub(crate) mod import_master_verification_key; +pub(crate) mod show_ticketbooks; + +#[derive(Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Ecash { + #[clap(subcommand)] + pub command: EcashCommands, +} + +impl Ecash { + pub async fn execute(self) -> Result<(), AuthenticatorError> { + match self.command { + EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?, + EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?, + EcashCommands::ImportCoinIndexSignatures(args) => { + import_coin_index_signatures::execute(args).await? + } + EcashCommands::ImportExpirationDateSignatures(args) => { + import_expiration_date_signatures::execute(args).await? + } + EcashCommands::ImportMasterVerificationKey(args) => { + import_master_verification_key::execute(args).await? + } + } + Ok(()) + } +} + +#[derive(Subcommand)] +pub enum EcashCommands { + /// Display information associated with the imported ticketbooks, + ShowTicketBooks(show_ticketbooks::Args), + + /// Import a pre-generated ticketbook + ImportTicketBook(CommonClientImportTicketBookArgs), + + /// Import coin index signatures needed for ticketbooks + ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs), + + /// Import expiration date signatures needed for ticketbooks + ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs), + + /// Import master verification key needed for ticketbooks + ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs), +} diff --git a/service-providers/authenticator/src/cli/ecash/show_ticketbooks.rs b/service-providers/authenticator/src/cli/ecash/show_ticketbooks.rs new file mode 100644 index 00000000000..ca31d398020 --- /dev/null +++ b/service-providers/authenticator/src/cli/ecash/show_ticketbooks.rs @@ -0,0 +1,32 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliAuthenticatorClient; +use nym_authenticator::error::AuthenticatorError; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::cli_helpers::client_show_ticketbooks::{ + show_ticketbooks, CommonShowTicketbooksArgs, +}; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[command(flatten)] + common_args: CommonShowTicketbooksArgs, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +impl AsRef for Args { + fn as_ref(&self) -> &CommonShowTicketbooksArgs { + &self.common_args + } +} + +pub(crate) async fn execute(args: Args) -> Result<(), AuthenticatorError> { + let output = args.output; + let res = show_ticketbooks::(args).await?; + + println!("{}", output.format(&res)); + Ok(()) +} diff --git a/service-providers/authenticator/src/cli/mod.rs b/service-providers/authenticator/src/cli/mod.rs index 685ff62da37..22c88b2710b 100644 --- a/service-providers/authenticator/src/cli/mod.rs +++ b/service-providers/authenticator/src/cli/mod.rs @@ -1,6 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::cli::ecash::Ecash; use clap::{CommandFactory, Parser, Subcommand}; use log::error; use nym_authenticator::{ @@ -9,13 +10,12 @@ use nym_authenticator::{ }; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::{bin_info, version_checker}; -use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs; use nym_client_core::cli_helpers::CliClient; use std::sync::OnceLock; mod add_gateway; mod build_info; -mod import_credential; +pub mod ecash; mod init; mod list_gateways; mod peer_handler; @@ -69,8 +69,8 @@ pub(crate) enum Commands { /// parameters. Run(run::Run), - /// Import a pre-generated credential - ImportCredential(CommonClientImportCredentialArgs), + /// Ecash-related functionalities + Ecash(Ecash), /// List all registered with gateways ListGateways(list_gateways::Args), @@ -127,7 +127,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), AuthenticatorError> { match args.command { Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(&m).await?, - Commands::ImportCredential(m) => import_credential::execute(m).await?, + Commands::Ecash(ecash) => ecash.execute().await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, diff --git a/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs b/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs new file mode 100644 index 00000000000..9ffe5439fa6 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs @@ -0,0 +1,17 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliIpPacketRouterClient; +use crate::error::ClientError; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::{ + import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs, +}; +use nym_ip_packet_router::error::IpPacketRouterError; + +pub(crate) async fn execute( + args: CommonClientImportCoinIndexSignaturesArgs, +) -> Result<(), IpPacketRouterError> { + import_coin_index_signatures::(args).await?; + println!("successfully imported coin index signatures!"); + Ok(()) +} diff --git a/service-providers/ip-packet-router/src/cli/import_credential.rs b/service-providers/ip-packet-router/src/cli/ecash/import_credential.rs similarity index 69% rename from service-providers/ip-packet-router/src/cli/import_credential.rs rename to service-providers/ip-packet-router/src/cli/ecash/import_credential.rs index 3c5fd47e56a..d37a8fa0662 100644 --- a/service-providers/ip-packet-router/src/cli/import_credential.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/import_credential.rs @@ -3,13 +3,11 @@ use crate::cli::CliIpPacketRouterClient; use nym_client_core::cli_helpers::client_import_credential::{ - import_credential, CommonClientImportCredentialArgs, + import_credential, CommonClientImportTicketBookArgs, }; use nym_ip_packet_router::error::IpPacketRouterError; -pub(crate) async fn execute( - args: CommonClientImportCredentialArgs, -) -> Result<(), IpPacketRouterError> { +pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), IpPacketRouterError> { import_credential::(args).await?; println!("successfully imported credential!"); Ok(()) diff --git a/service-providers/ip-packet-router/src/cli/ecash/import_expiration_date_signatures.rs b/service-providers/ip-packet-router/src/cli/ecash/import_expiration_date_signatures.rs new file mode 100644 index 00000000000..7c338d05cb7 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/ecash/import_expiration_date_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliIpPacketRouterClient; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{ + import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs, +}; +use nym_ip_packet_router::error::IpPacketRouterError; + +pub(crate) async fn execute( + args: CommonClientImportExpirationDateSignaturesArgs, +) -> Result<(), IpPacketRouterError> { + import_expiration_date_signatures::(args).await?; + println!("successfully imported expiration date signatures!"); + Ok(()) +} diff --git a/service-providers/ip-packet-router/src/cli/ecash/import_master_verification_key.rs b/service-providers/ip-packet-router/src/cli/ecash/import_master_verification_key.rs new file mode 100644 index 00000000000..654be2ea18a --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/ecash/import_master_verification_key.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliIpPacketRouterClient; +use nym_client_core::cli_helpers::client_import_master_verification_key::{ + import_master_verification_key, CommonClientImportMasterVerificationKeyArgs, +}; +use nym_ip_packet_router::error::IpPacketRouterError; + +pub(crate) async fn execute( + args: CommonClientImportMasterVerificationKeyArgs, +) -> Result<(), IpPacketRouterError> { + import_master_verification_key::(args).await?; + println!("successfully imported master verification key!"); + Ok(()) +} diff --git a/service-providers/ip-packet-router/src/cli/ecash/mod.rs b/service-providers/ip-packet-router/src/cli/ecash/mod.rs new file mode 100644 index 00000000000..b0c7ca0ba96 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/ecash/mod.rs @@ -0,0 +1,61 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs; +use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; +use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; +use std::error::Error; + +pub(crate) mod import_coin_index_signatures; +pub(crate) mod import_credential; +pub mod import_credential; +pub(crate) mod import_expiration_date_signatures; +pub(crate) mod import_master_verification_key; +pub(crate) mod show_ticketbooks; +pub mod show_ticketbooks; + +#[derive(Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Ecash { + #[clap(subcommand)] + pub command: EcashCommands, +} + +impl Ecash { + pub async fn execute(self) -> Result<(), Box> { + match self.command { + EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?, + EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?, + EcashCommands::ImportCoinIndexSignatures(args) => { + import_coin_index_signatures::execute(args).await? + } + EcashCommands::ImportExpirationDateSignatures(args) => { + import_expiration_date_signatures::execute(args).await? + } + EcashCommands::ImportMasterVerificationKey(args) => { + import_master_verification_key::execute(args).await? + } + } + Ok(()) + } +} + +#[derive(Subcommand)] +pub enum EcashCommands { + /// Display information associated with the imported ticketbooks, + ShowTicketBooks(show_ticketbooks::Args), + + /// Import a pre-generated ticketbook + ImportTicketBook(CommonClientImportTicketBookArgs), + + /// Import coin index signatures needed for ticketbooks + ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs), + + /// Import expiration date signatures needed for ticketbooks + ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs), + + /// Import master verification key needed for ticketbooks + ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs), +} diff --git a/service-providers/ip-packet-router/src/cli/show_ticketbooks.rs b/service-providers/ip-packet-router/src/cli/ecash/show_ticketbooks.rs similarity index 89% rename from service-providers/ip-packet-router/src/cli/show_ticketbooks.rs rename to service-providers/ip-packet-router/src/cli/ecash/show_ticketbooks.rs index 20c58a412c4..b3abea30f75 100644 --- a/service-providers/ip-packet-router/src/cli/show_ticketbooks.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/show_ticketbooks.rs @@ -9,7 +9,7 @@ use nym_client_core::cli_helpers::client_show_ticketbooks::{ use nym_ip_packet_router::error::IpPacketRouterError; #[derive(clap::Args)] -pub(crate) struct Args { +pub struct Args { #[command(flatten)] common_args: CommonShowTicketbooksArgs, @@ -23,7 +23,7 @@ impl AsRef for Args { } } -pub(crate) async fn execute(args: Args) -> Result<(), IpPacketRouterError> { +pub async fn execute(args: Args) -> Result<(), IpPacketRouterError> { let output = args.output; let res = show_ticketbooks::(args).await?; diff --git a/service-providers/ip-packet-router/src/cli/mod.rs b/service-providers/ip-packet-router/src/cli/mod.rs index d269d5045d6..fb47e4c09fa 100644 --- a/service-providers/ip-packet-router/src/cli/mod.rs +++ b/service-providers/ip-packet-router/src/cli/mod.rs @@ -1,8 +1,9 @@ +use crate::commands::ecash::Ecash; use clap::{CommandFactory, Parser, Subcommand}; use log::error; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::{bin_info, version_checker}; -use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs; +use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; use nym_client_core::cli_helpers::CliClient; use nym_ip_packet_router::config::helpers::try_upgrade_config; use nym_ip_packet_router::config::{BaseClientConfig, Config}; @@ -11,11 +12,10 @@ use std::sync::OnceLock; mod add_gateway; mod build_info; -mod import_credential; +pub mod ecash; mod init; mod list_gateways; mod run; -mod show_ticketbooks; mod sign; mod switch_gateway; @@ -65,8 +65,8 @@ pub(crate) enum Commands { /// parameters. Run(run::Run), - /// Import a pre-generated credential - ImportCredential(CommonClientImportCredentialArgs), + /// Ecash-related functionalities + Ecash(Ecash), /// List all registered with gateways ListGateways(list_gateways::Args), @@ -132,11 +132,10 @@ pub(crate) async fn execute(args: Cli) -> Result<(), IpPacketRouterError> { match args.command { Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(&m).await?, - Commands::ImportCredential(m) => import_credential::execute(m).await?, + Commands::Ecash(ecash) => ecash.execute().await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, - Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?, Commands::Sign(m) => sign::execute(&m).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), diff --git a/service-providers/network-requester/src/cli/ecash/import_coin_index_signatures.rs b/service-providers/network-requester/src/cli/ecash/import_coin_index_signatures.rs new file mode 100644 index 00000000000..d9fa9aad3c5 --- /dev/null +++ b/service-providers/network-requester/src/cli/ecash/import_coin_index_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliNetworkRequesterClient; +use crate::error::NetworkRequesterError; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::{ + import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportCoinIndexSignaturesArgs, +) -> Result<(), NetworkRequesterError> { + import_coin_index_signatures::(args).await?; + println!("successfully imported coin index signatures!"); + Ok(()) +} diff --git a/service-providers/network-requester/src/cli/import_credential.rs b/service-providers/network-requester/src/cli/ecash/import_credential.rs similarity index 69% rename from service-providers/network-requester/src/cli/import_credential.rs rename to service-providers/network-requester/src/cli/ecash/import_credential.rs index 24b999ccd63..28b9e395451 100644 --- a/service-providers/network-requester/src/cli/import_credential.rs +++ b/service-providers/network-requester/src/cli/ecash/import_credential.rs @@ -4,12 +4,10 @@ use crate::cli::CliNetworkRequesterClient; use crate::error::NetworkRequesterError; use nym_client_core::cli_helpers::client_import_credential::{ - import_credential, CommonClientImportCredentialArgs, + import_credential, CommonClientImportTicketBookArgs, }; -pub(crate) async fn execute( - args: CommonClientImportCredentialArgs, -) -> Result<(), NetworkRequesterError> { +pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), NetworkRequesterError> { import_credential::(args).await?; println!("successfully imported credential!"); Ok(()) diff --git a/service-providers/network-requester/src/cli/ecash/import_expiration_date_signatures.rs b/service-providers/network-requester/src/cli/ecash/import_expiration_date_signatures.rs new file mode 100644 index 00000000000..059df07e76a --- /dev/null +++ b/service-providers/network-requester/src/cli/ecash/import_expiration_date_signatures.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliNetworkRequesterClient; +use crate::error::NetworkRequesterError; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{ + import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportExpirationDateSignaturesArgs, +) -> Result<(), NetworkRequesterError> { + import_expiration_date_signatures::(args).await?; + println!("successfully imported expiration date signatures!"); + Ok(()) +} diff --git a/service-providers/network-requester/src/cli/ecash/import_master_verification_key.rs b/service-providers/network-requester/src/cli/ecash/import_master_verification_key.rs new file mode 100644 index 00000000000..4283291b761 --- /dev/null +++ b/service-providers/network-requester/src/cli/ecash/import_master_verification_key.rs @@ -0,0 +1,16 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliNetworkRequesterClient; +use crate::error::NetworkRequesterError; +use nym_client_core::cli_helpers::client_import_master_verification_key::{ + import_master_verification_key, CommonClientImportMasterVerificationKeyArgs, +}; + +pub(crate) async fn execute( + args: CommonClientImportMasterVerificationKeyArgs, +) -> Result<(), NetworkRequesterError> { + import_master_verification_key::(args).await?; + println!("successfully imported master verification key!"); + Ok(()) +} diff --git a/service-providers/network-requester/src/cli/ecash/mod.rs b/service-providers/network-requester/src/cli/ecash/mod.rs new file mode 100644 index 00000000000..d660cb728f1 --- /dev/null +++ b/service-providers/network-requester/src/cli/ecash/mod.rs @@ -0,0 +1,59 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::NetworkRequesterError; +use clap::{Args, Subcommand}; +use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs; +use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; +use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; +use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; + +pub(crate) mod import_coin_index_signatures; +pub(crate) mod import_credential; +pub(crate) mod import_expiration_date_signatures; +pub(crate) mod import_master_verification_key; +pub(crate) mod show_ticketbooks; + +#[derive(Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Ecash { + #[clap(subcommand)] + pub command: EcashCommands, +} + +impl Ecash { + pub async fn execute(self) -> Result<(), NetworkRequesterError> { + match self.command { + EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?, + EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?, + EcashCommands::ImportCoinIndexSignatures(args) => { + import_coin_index_signatures::execute(args).await? + } + EcashCommands::ImportExpirationDateSignatures(args) => { + import_expiration_date_signatures::execute(args).await? + } + EcashCommands::ImportMasterVerificationKey(args) => { + import_master_verification_key::execute(args).await? + } + } + Ok(()) + } +} + +#[derive(Subcommand)] +pub enum EcashCommands { + /// Display information associated with the imported ticketbooks, + ShowTicketBooks(show_ticketbooks::Args), + + /// Import a pre-generated ticketbook + ImportTicketBook(CommonClientImportTicketBookArgs), + + /// Import coin index signatures needed for ticketbooks + ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs), + + /// Import expiration date signatures needed for ticketbooks + ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs), + + /// Import master verification key needed for ticketbooks + ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs), +} diff --git a/service-providers/network-requester/src/cli/show_ticketbooks.rs b/service-providers/network-requester/src/cli/ecash/show_ticketbooks.rs similarity index 100% rename from service-providers/network-requester/src/cli/show_ticketbooks.rs rename to service-providers/network-requester/src/cli/ecash/show_ticketbooks.rs diff --git a/service-providers/network-requester/src/cli/mod.rs b/service-providers/network-requester/src/cli/mod.rs index 11ef00cfd8f..750575f2fbe 100644 --- a/service-providers/network-requester/src/cli/mod.rs +++ b/service-providers/network-requester/src/cli/mod.rs @@ -1,6 +1,7 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +use crate::cli::ecash::Ecash; use crate::config::helpers::try_upgrade_config_by_id; use crate::{ config::{BaseClientConfig, Config}, @@ -11,18 +12,16 @@ use log::error; use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::version_checker; -use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs; use nym_client_core::cli_helpers::CliClient; use nym_config::OptionalSet; use std::sync::OnceLock; mod add_gateway; mod build_info; -mod import_credential; +pub mod ecash; mod init; mod list_gateways; mod run; -mod show_ticketbooks; mod sign; mod switch_gateway; @@ -72,12 +71,12 @@ pub(crate) enum Commands { /// parameters. Run(run::Run), + /// Ecash-related functionalities + Ecash(Ecash), + /// Sign to prove ownership of this network requester Sign(sign::Sign), - /// Import a pre-generated credential - ImportCredential(CommonClientImportCredentialArgs), - /// List all registered with gateways ListGateways(list_gateways::Args), @@ -87,9 +86,6 @@ pub(crate) enum Commands { /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), - /// Display information associated with the imported ticketbooks, - ShowTicketbooks(show_ticketbooks::Args), - /// Show build information of this binary BuildInfo(build_info::BuildInfo), @@ -151,12 +147,11 @@ pub(crate) async fn execute(args: Cli) -> Result<(), NetworkRequesterError> { match args.command { Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(&m).await?, + Commands::Ecash(ecash) => ecash.execute().await?, Commands::Sign(m) => sign::execute(&m).await?, - Commands::ImportCredential(m) => import_credential::execute(m).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, - Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), diff --git a/tools/nym-cli/src/coconut/mod.rs b/tools/nym-cli/src/coconut/mod.rs index 6fda72e6a0f..ccf0836d05c 100644 --- a/tools/nym-cli/src/coconut/mod.rs +++ b/tools/nym-cli/src/coconut/mod.rs @@ -1,28 +1,38 @@ use nym_cli_commands::context::{create_query_client, create_signing_client, ClientArgs}; +use nym_cli_commands::ecash::EcashCommands; use nym_network_defaults::NymNetworkDetails; pub(crate) async fn execute( global_args: ClientArgs, - coconut: nym_cli_commands::coconut::Ecash, + coconut: nym_cli_commands::ecash::Ecash, network_details: &NymNetworkDetails, ) -> anyhow::Result<()> { match coconut.command { - nym_cli_commands::coconut::EcashCommands::IssueTicketBook(args) => { - nym_cli_commands::coconut::issue_ticket_book::execute( + EcashCommands::IssueTicketBook(args) => { + nym_cli_commands::ecash::issue_ticket_book::execute( args, create_signing_client(global_args, network_details)?, ) .await? } - nym_cli_commands::coconut::EcashCommands::RecoverTicketBook(args) => { - nym_cli_commands::coconut::recover_ticket_book::execute( + EcashCommands::RecoverTicketBook(args) => { + nym_cli_commands::ecash::recover_ticket_book::execute( args, create_query_client(network_details)?, ) .await? } - nym_cli_commands::coconut::EcashCommands::ImportTicketBook(args) => { - nym_cli_commands::coconut::import_ticket_book::execute(args).await? + EcashCommands::ImportTicketBook(args) => { + nym_cli_commands::ecash::import_ticket_book::execute(args).await? + } + EcashCommands::ImportCoinIndexSignatures(args) => { + nym_cli_commands::ecash::import_coin_index_signatures::execute(args).await? + } + EcashCommands::ImportExpirationDateSignatures(args) => { + nym_cli_commands::ecash::import_expiration_date_signatures::execute(args).await? + } + EcashCommands::ImportMasterVerificationKey(args) => { + nym_cli_commands::ecash::import_master_verification_key::execute(args).await? } } Ok(()) diff --git a/tools/nym-cli/src/main.rs b/tools/nym-cli/src/main.rs index 12defe48171..a7255ab9a9f 100644 --- a/tools/nym-cli/src/main.rs +++ b/tools/nym-cli/src/main.rs @@ -63,7 +63,7 @@ pub(crate) enum Commands { /// Sign and verify messages Signature(nym_cli_commands::validator::signature::Signature), /// Ecash related stuff - Ecash(nym_cli_commands::coconut::Ecash), + Ecash(nym_cli_commands::ecash::Ecash), /// Query chain blocks Block(nym_cli_commands::validator::block::Block), /// Manage and execute WASM smart contracts diff --git a/tools/nym-id-cli/src/commands/import_credential.rs b/tools/nym-id-cli/src/commands/import_credential.rs index e04bb89af52..340981006f0 100644 --- a/tools/nym-id-cli/src/commands/import_credential.rs +++ b/tools/nym-id-cli/src/commands/import_credential.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::ArgGroup; -use nym_id::import_credential; +use nym_id::import_standalone_ticketbook; use std::fs; use std::path::PathBuf; @@ -43,6 +43,6 @@ pub(crate) async fn execute(args: Args) -> anyhow::Result<()> { } }; - import_credential(credentials_store, raw_credential, args.version).await?; + import_standalone_ticketbook(credentials_store, raw_credential, args.version).await?; Ok(()) } From 052dbeaef8253d7321d82f24b2de7ff9480aaacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 30 Aug 2024 18:35:02 +0100 Subject: [PATCH 2/6] adjusting the API and fixing CI --- .../commands/src/ecash/issue_ticket_book.rs | 6 +-- .../src/ecash/bandwidth/importable.rs | 44 ++++++++++++++++--- .../cli/ecash/import_coin_index_signatures.rs | 1 - .../ip-packet-router/src/cli/ecash/mod.rs | 1 - .../ip-packet-router/src/cli/mod.rs | 5 +-- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/common/commands/src/ecash/issue_ticket_book.rs b/common/commands/src/ecash/issue_ticket_book.rs index 015522b0165..28e8c3eafcb 100644 --- a/common/commands/src/ecash/issue_ticket_book.rs +++ b/common/commands/src/ecash/issue_ticket_book.rs @@ -123,7 +123,7 @@ async fn issue_to_file(args: Args, client: SigningClient) -> anyhow::Result<()> .await? .ok_or(anyhow!("missing expiration date signatures!"))?; - exported.with_expiration_date_signatures(&AggregatedExpirationDateSignatures { + exported = exported.with_expiration_date_signatures(&AggregatedExpirationDateSignatures { epoch_id, expiration_date, signatures, @@ -135,7 +135,7 @@ async fn issue_to_file(args: Args, client: SigningClient) -> anyhow::Result<()> .get_coin_index_signatures(epoch_id) .await? .ok_or(anyhow!("missing coin index signatures!"))?; - exported.with_coin_index_signatures(&AggregatedCoinIndicesSignatures { + exported = exported.with_coin_index_signatures(&AggregatedCoinIndicesSignatures { epoch_id, signatures, }); @@ -147,7 +147,7 @@ async fn issue_to_file(args: Args, client: SigningClient) -> anyhow::Result<()> .await? .ok_or(anyhow!("missing master verification key!"))?; - exported.with_master_verification_key(&EpochVerificationKey { epoch_id, key }); + exported = exported.with_master_verification_key(&EpochVerificationKey { epoch_id, key }); } let data = exported.pack().data; diff --git a/common/credentials/src/ecash/bandwidth/importable.rs b/common/credentials/src/ecash/bandwidth/importable.rs index a1f9ac099e9..1765248faa8 100644 --- a/common/credentials/src/ecash/bandwidth/importable.rs +++ b/common/credentials/src/ecash/bandwidth/importable.rs @@ -53,28 +53,58 @@ impl From for ImportableTicketBook { impl ImportableTicketBook { pub fn with_expiration_date_signatures( - &mut self, + mut self, signatures: &AggregatedExpirationDateSignatures, - ) -> &mut Self { + ) -> Self { self.serialised_expiration_date_signatures = Some(signatures.pack()); self } pub fn with_coin_index_signatures( - &mut self, + mut self, signatures: &AggregatedCoinIndicesSignatures, - ) -> &mut Self { + ) -> Self { self.serialised_coin_index_signatures = Some(signatures.pack()); self } - pub fn with_master_verification_key(&mut self, key: &EpochVerificationKey) -> &mut Self { + pub fn with_master_verification_key(mut self, key: &EpochVerificationKey) -> Self { self.serialised_master_verification_key = Some(key.pack()); self } - pub fn finalize_export(self) -> Vec { - self.pack().data + pub fn with_maybe_expiration_date_signatures( + self, + signatures: &Option, + ) -> Self { + if let Some(sigs) = signatures { + self.with_expiration_date_signatures(sigs) + } else { + self + } + } + + pub fn with_maybe_coin_index_signatures( + self, + signatures: &Option, + ) -> Self { + if let Some(sigs) = signatures { + self.with_coin_index_signatures(sigs) + } else { + self + } + } + + pub fn with_maybe_master_verification_key(self, key: &Option) -> Self { + if let Some(sigs) = key { + self.with_master_verification_key(sigs) + } else { + self + } + } + + pub fn finalize_export(self) -> VersionSerialised { + self.pack() } pub fn try_unpack_full(&self) -> Result { diff --git a/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs b/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs index 9ffe5439fa6..a06f9e70b8b 100644 --- a/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/import_coin_index_signatures.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::cli::CliIpPacketRouterClient; -use crate::error::ClientError; use nym_client_core::cli_helpers::client_import_coin_index_signatures::{ import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs, }; diff --git a/service-providers/ip-packet-router/src/cli/ecash/mod.rs b/service-providers/ip-packet-router/src/cli/ecash/mod.rs index b0c7ca0ba96..500f0666c5f 100644 --- a/service-providers/ip-packet-router/src/cli/ecash/mod.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/mod.rs @@ -10,7 +10,6 @@ use std::error::Error; pub(crate) mod import_coin_index_signatures; pub(crate) mod import_credential; -pub mod import_credential; pub(crate) mod import_expiration_date_signatures; pub(crate) mod import_master_verification_key; pub(crate) mod show_ticketbooks; diff --git a/service-providers/ip-packet-router/src/cli/mod.rs b/service-providers/ip-packet-router/src/cli/mod.rs index fb47e4c09fa..6a0a9ed19b2 100644 --- a/service-providers/ip-packet-router/src/cli/mod.rs +++ b/service-providers/ip-packet-router/src/cli/mod.rs @@ -1,4 +1,4 @@ -use crate::commands::ecash::Ecash; +use crate::cli::ecash::Ecash; use clap::{CommandFactory, Parser, Subcommand}; use log::error; use nym_bin_common::completions::{fig_generate, ArgShell}; @@ -77,9 +77,6 @@ pub(crate) enum Commands { /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), - /// Display information associated with the imported ticketbooks, - ShowTicketbooks(show_ticketbooks::Args), - /// Sign to prove ownership of this network requester Sign(sign::Sign), From 27afe645c6e293636afbf1f1f7e3a932b1a8ce12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 30 Aug 2024 18:39:10 +0100 Subject: [PATCH 3/6] i hate the ipr build process --- service-providers/ip-packet-router/src/cli/ecash/mod.rs | 4 ++-- service-providers/ip-packet-router/src/cli/mod.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/service-providers/ip-packet-router/src/cli/ecash/mod.rs b/service-providers/ip-packet-router/src/cli/ecash/mod.rs index 500f0666c5f..55db810257f 100644 --- a/service-providers/ip-packet-router/src/cli/ecash/mod.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/mod.rs @@ -7,13 +7,13 @@ use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTi use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; use std::error::Error; +use nym_ip_packet_router::error::IpPacketRouterError; pub(crate) mod import_coin_index_signatures; pub(crate) mod import_credential; pub(crate) mod import_expiration_date_signatures; pub(crate) mod import_master_verification_key; pub(crate) mod show_ticketbooks; -pub mod show_ticketbooks; #[derive(Args)] #[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] @@ -23,7 +23,7 @@ pub struct Ecash { } impl Ecash { - pub async fn execute(self) -> Result<(), Box> { + pub async fn execute(self) -> Result<(), IpPacketRouterError> { match self.command { EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?, EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?, diff --git a/service-providers/ip-packet-router/src/cli/mod.rs b/service-providers/ip-packet-router/src/cli/mod.rs index 6a0a9ed19b2..ee746f48c96 100644 --- a/service-providers/ip-packet-router/src/cli/mod.rs +++ b/service-providers/ip-packet-router/src/cli/mod.rs @@ -3,7 +3,6 @@ use clap::{CommandFactory, Parser, Subcommand}; use log::error; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::{bin_info, version_checker}; -use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; use nym_client_core::cli_helpers::CliClient; use nym_ip_packet_router::config::helpers::try_upgrade_config; use nym_ip_packet_router::config::{BaseClientConfig, Config}; From 43b0b3eb375dfdb532cbad3daf8817b4af8b7687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 30 Aug 2024 18:42:46 +0100 Subject: [PATCH 4/6] i hate the ipr build process vol2 --- service-providers/ip-packet-router/src/cli/ecash/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-providers/ip-packet-router/src/cli/ecash/mod.rs b/service-providers/ip-packet-router/src/cli/ecash/mod.rs index 55db810257f..4e3d0befcd7 100644 --- a/service-providers/ip-packet-router/src/cli/ecash/mod.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/mod.rs @@ -6,8 +6,8 @@ use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonCli use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs; use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; -use std::error::Error; use nym_ip_packet_router::error::IpPacketRouterError; +use std::error::Error; pub(crate) mod import_coin_index_signatures; pub(crate) mod import_credential; From 04a2f59034f4749d76bdda4f26a416642f3bd9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 2 Sep 2024 09:59:21 +0100 Subject: [PATCH 5/6] fixed clap group names --- .../src/cli_helpers/client_import_coin_index_signatures.rs | 6 +++--- .../cli_helpers/client_import_expiration_date_signatures.rs | 6 +++--- .../cli_helpers/client_import_master_verification_key.rs | 6 +++--- common/commands/src/ecash/import_coin_index_signatures.rs | 6 +++--- .../commands/src/ecash/import_expiration_date_signatures.rs | 6 +++--- common/commands/src/ecash/import_master_verification_key.rs | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs b/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs index 889c34ab4ba..5625250ce36 100644 --- a/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs +++ b/common/client-core/src/cli_helpers/client_import_coin_index_signatures.rs @@ -13,7 +13,7 @@ fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { #[cfg_attr(feature = "cli", derive(clap::Args))] #[cfg_attr(feature = "cli", clap( - group(clap::ArgGroup::new("signatures_data").required(true)), + group(clap::ArgGroup::new("sig_data").required(true)), )) ] pub struct CommonClientImportCoinIndexSignaturesArgs { @@ -26,11 +26,11 @@ pub struct CommonClientImportCoinIndexSignaturesArgs { pub(crate) client_config: PathBuf, /// Explicitly provide the encoded signatures data (as base58) - #[cfg_attr(feature = "cli", clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data))] + #[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))] pub(crate) signatures_data: Option>, /// Specifies the path to file containing binary signatures data - #[cfg_attr(feature = "cli", clap(long, group = "signatures_data"))] + #[cfg_attr(feature = "cli", clap(long, group = "sig_data"))] pub(crate) signatures_path: Option, // currently hidden as there exists only a single serialization standard diff --git a/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs b/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs index 7d0a14513ae..4dc875e75c9 100644 --- a/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs +++ b/common/client-core/src/cli_helpers/client_import_expiration_date_signatures.rs @@ -13,7 +13,7 @@ fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { #[cfg_attr(feature = "cli", derive(clap::Args))] #[cfg_attr(feature = "cli", clap( - group(clap::ArgGroup::new("signatures_data").required(true)), + group(clap::ArgGroup::new("sig_data").required(true)), )) ] pub struct CommonClientImportExpirationDateSignaturesArgs { @@ -26,11 +26,11 @@ pub struct CommonClientImportExpirationDateSignaturesArgs { pub(crate) client_config: PathBuf, /// Explicitly provide the encoded signatures data (as base58) - #[cfg_attr(feature = "cli", clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data))] + #[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))] pub(crate) signatures_data: Option>, /// Specifies the path to file containing binary signatures data - #[cfg_attr(feature = "cli", clap(long, group = "signatures_data"))] + #[cfg_attr(feature = "cli", clap(long, group = "sig_data"))] pub(crate) signatures_path: Option, // currently hidden as there exists only a single serialization standard diff --git a/common/client-core/src/cli_helpers/client_import_master_verification_key.rs b/common/client-core/src/cli_helpers/client_import_master_verification_key.rs index 25a3c421f2f..b44334b49e0 100644 --- a/common/client-core/src/cli_helpers/client_import_master_verification_key.rs +++ b/common/client-core/src/cli_helpers/client_import_master_verification_key.rs @@ -13,7 +13,7 @@ fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result> { #[cfg_attr(feature = "cli", derive(clap::Args))] #[cfg_attr(feature = "cli", clap( - group(clap::ArgGroup::new("key_data").required(true)), + group(clap::ArgGroup::new("key_data_group").required(true)), )) ] pub struct CommonClientImportMasterVerificationKeyArgs { @@ -26,11 +26,11 @@ pub struct CommonClientImportMasterVerificationKeyArgs { pub(crate) client_config: PathBuf, /// Explicitly provide the encoded key data (as base58) - #[cfg_attr(feature = "cli", clap(long, group = "key_data", value_parser = parse_encoded_key_data))] + #[cfg_attr(feature = "cli", clap(long, group = "key_data_group", value_parser = parse_encoded_key_data))] pub(crate) key_data: Option>, /// Specifies the path to file containing binary key data - #[cfg_attr(feature = "cli", clap(long, group = "key_data"))] + #[cfg_attr(feature = "cli", clap(long, group = "key_data_group"))] pub(crate) key_path: Option, // currently hidden as there exists only a single serialization standard diff --git a/common/commands/src/ecash/import_coin_index_signatures.rs b/common/commands/src/ecash/import_coin_index_signatures.rs index 91d12aafbbe..8fb746937e9 100644 --- a/common/commands/src/ecash/import_coin_index_signatures.rs +++ b/common/commands/src/ecash/import_coin_index_signatures.rs @@ -16,7 +16,7 @@ fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { #[derive(Debug, Parser)] #[clap( - group(ArgGroup::new("signatures_data").required(true)), + group(ArgGroup::new("sig_data").required(true)), )] pub struct Args { /// Config file of the client that is supposed to use the signatures. @@ -24,11 +24,11 @@ pub struct Args { pub(crate) client_config: PathBuf, /// Explicitly provide the encoded signatures data (as base58) - #[clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data)] + #[clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data)] pub(crate) signatures_data: Option>, /// Specifies the path to file containing binary signatures data - #[clap(long, group = "signatures_data")] + #[clap(long, group = "sig_data")] pub(crate) signatures_path: Option, // currently hidden as there exists only a single serialization standard diff --git a/common/commands/src/ecash/import_expiration_date_signatures.rs b/common/commands/src/ecash/import_expiration_date_signatures.rs index 91d12aafbbe..8fb746937e9 100644 --- a/common/commands/src/ecash/import_expiration_date_signatures.rs +++ b/common/commands/src/ecash/import_expiration_date_signatures.rs @@ -16,7 +16,7 @@ fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result> { #[derive(Debug, Parser)] #[clap( - group(ArgGroup::new("signatures_data").required(true)), + group(ArgGroup::new("sig_data").required(true)), )] pub struct Args { /// Config file of the client that is supposed to use the signatures. @@ -24,11 +24,11 @@ pub struct Args { pub(crate) client_config: PathBuf, /// Explicitly provide the encoded signatures data (as base58) - #[clap(long, group = "signatures_data", value_parser = parse_encoded_signatures_data)] + #[clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data)] pub(crate) signatures_data: Option>, /// Specifies the path to file containing binary signatures data - #[clap(long, group = "signatures_data")] + #[clap(long, group = "sig_data")] pub(crate) signatures_path: Option, // currently hidden as there exists only a single serialization standard diff --git a/common/commands/src/ecash/import_master_verification_key.rs b/common/commands/src/ecash/import_master_verification_key.rs index f194c00c8a8..1e8118ca9d3 100644 --- a/common/commands/src/ecash/import_master_verification_key.rs +++ b/common/commands/src/ecash/import_master_verification_key.rs @@ -16,7 +16,7 @@ fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result> { #[derive(Debug, Parser)] #[clap( - group(ArgGroup::new("key_data").required(true)), + group(ArgGroup::new("key_data_group").required(true)), )] pub struct Args { /// Config file of the client that is supposed to use the key. @@ -24,11 +24,11 @@ pub struct Args { pub(crate) client_config: PathBuf, /// Explicitly provide the encoded key data (as base58) - #[clap(long, group = "key_data", value_parser = parse_encoded_key_data)] + #[clap(long, group = "key_data_group", value_parser = parse_encoded_key_data)] pub(crate) key_data: Option>, /// Specifies the path to file containing binary key data - #[clap(long, group = "key_data")] + #[clap(long, group = "key_data_group")] pub(crate) key_path: Option, // currently hidden as there exists only a single serialization standard From 45e60119619af7b66aa1fb5c24289027c2eec6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 2 Sep 2024 10:08:33 +0100 Subject: [PATCH 6/6] vol3 --- service-providers/ip-packet-router/src/cli/ecash/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/service-providers/ip-packet-router/src/cli/ecash/mod.rs b/service-providers/ip-packet-router/src/cli/ecash/mod.rs index 4e3d0befcd7..33a4ddd6678 100644 --- a/service-providers/ip-packet-router/src/cli/ecash/mod.rs +++ b/service-providers/ip-packet-router/src/cli/ecash/mod.rs @@ -7,7 +7,6 @@ use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTi use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs; use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs; use nym_ip_packet_router::error::IpPacketRouterError; -use std::error::Error; pub(crate) mod import_coin_index_signatures; pub(crate) mod import_credential;