revamped ticketbook serialisation and exposed additional cli methods
This commit is contained in:
Generated
+3
@@ -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]]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliNativeClient, _>(args).await?;
|
||||
println!("successfully imported coin index signatures!");
|
||||
Ok(())
|
||||
}
|
||||
+2
-2
@@ -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::<CliNativeClient, _>(args).await?;
|
||||
println!("successfully imported credential!");
|
||||
Ok(())
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliNativeClient, _>(args).await?;
|
||||
println!("successfully imported expiration date signatures!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliNativeClient, _>(args).await?;
|
||||
println!("successfully imported master verification key!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<dyn Error + Send + Sync>> {
|
||||
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),
|
||||
}
|
||||
@@ -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<dyn Error + Send + Sync
|
||||
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::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),
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliSocks5Client, _>(args).await?;
|
||||
println!("successfully imported coin index signatures!");
|
||||
Ok(())
|
||||
}
|
||||
+2
-4
@@ -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::<CliSocks5Client, _>(args).await?;
|
||||
println!("successfully imported credential!");
|
||||
Ok(())
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliSocks5Client, _>(args).await?;
|
||||
println!("successfully imported expiration date signatures!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliSocks5Client, _>(args).await?;
|
||||
println!("successfully imported master verification key!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<dyn Error + Send + Sync>> {
|
||||
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),
|
||||
}
|
||||
+2
-2
@@ -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<CommonShowTicketbooksArgs> 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::<CliSocks5Client, _>(args).await?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<dyn Error + Send + Sync
|
||||
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::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),
|
||||
|
||||
@@ -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<St>(
|
||||
@@ -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<St>(
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Vec<u8>> {
|
||||
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<Vec<u8>>,
|
||||
|
||||
/// Specifies the path to file containing binary signatures data
|
||||
#[cfg_attr(feature = "cli", clap(long, group = "signatures_data"))]
|
||||
pub(crate) signatures_path: Option<PathBuf>,
|
||||
|
||||
// currently hidden as there exists only a single serialization standard
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub(crate) version: Option<u8>,
|
||||
}
|
||||
|
||||
pub async fn import_coin_index_signatures<C, A>(args: A) -> Result<(), C::Error>
|
||||
where
|
||||
A: Into<CommonClientImportCoinIndexSignaturesArgs>,
|
||||
C: CliClient,
|
||||
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
|
||||
{
|
||||
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(())
|
||||
}
|
||||
@@ -11,9 +11,14 @@ fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
|
||||
}
|
||||
|
||||
#[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<PathBuf>,
|
||||
|
||||
/// 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<u8>,
|
||||
@@ -33,7 +47,7 @@ pub struct CommonClientImportCredentialArgs {
|
||||
|
||||
pub async fn import_credential<C, A>(args: A) -> Result<(), C::Error>
|
||||
where
|
||||
A: Into<CommonClientImportCredentialArgs>,
|
||||
A: Into<CommonClientImportTicketBookArgs>,
|
||||
C: CliClient,
|
||||
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
|
||||
{
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Vec<u8>> {
|
||||
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<Vec<u8>>,
|
||||
|
||||
/// Specifies the path to file containing binary signatures data
|
||||
#[cfg_attr(feature = "cli", clap(long, group = "signatures_data"))]
|
||||
pub(crate) signatures_path: Option<PathBuf>,
|
||||
|
||||
// currently hidden as there exists only a single serialization standard
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub(crate) version: Option<u8>,
|
||||
}
|
||||
|
||||
pub async fn import_expiration_date_signatures<C, A>(args: A) -> Result<(), C::Error>
|
||||
where
|
||||
A: Into<CommonClientImportExpirationDateSignaturesArgs>,
|
||||
C: CliClient,
|
||||
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
|
||||
{
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Vec<u8>> {
|
||||
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<Vec<u8>>,
|
||||
|
||||
/// Specifies the path to file containing binary key data
|
||||
#[cfg_attr(feature = "cli", clap(long, group = "key_data"))]
|
||||
pub(crate) key_path: Option<PathBuf>,
|
||||
|
||||
// currently hidden as there exists only a single serialization standard
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub(crate) version: Option<u8>,
|
||||
}
|
||||
|
||||
pub async fn import_master_verification_key<C, A>(args: A) -> Result<(), C::Error>
|
||||
where
|
||||
A: Into<CommonClientImportMasterVerificationKeyArgs>,
|
||||
C: CliClient,
|
||||
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
|
||||
{
|
||||
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(())
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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(())
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Vec<u8>> {
|
||||
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<Vec<u8>>,
|
||||
|
||||
/// Specifies the path to file containing binary signatures data
|
||||
#[clap(long, group = "signatures_data")]
|
||||
pub(crate) signatures_path: Option<PathBuf>,
|
||||
|
||||
// currently hidden as there exists only a single serialization standard
|
||||
#[clap(long, hide = true)]
|
||||
pub(crate) version: Option<u8>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn signatures_data(self) -> anyhow::Result<Vec<u8>> {
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Vec<u8>> {
|
||||
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<Vec<u8>>,
|
||||
|
||||
/// Specifies the path to file containing binary signatures data
|
||||
#[clap(long, group = "signatures_data")]
|
||||
pub(crate) signatures_path: Option<PathBuf>,
|
||||
|
||||
// currently hidden as there exists only a single serialization standard
|
||||
#[clap(long, hide = true)]
|
||||
pub(crate) version: Option<u8>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn signatures_data(self) -> anyhow::Result<Vec<u8>> {
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Vec<u8>> {
|
||||
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<Vec<u8>>,
|
||||
|
||||
/// Specifies the path to file containing binary key data
|
||||
#[clap(long, group = "key_data")]
|
||||
pub(crate) key_path: Option<PathBuf>,
|
||||
|
||||
// currently hidden as there exists only a single serialization standard
|
||||
#[clap(long, hide = true)]
|
||||
pub(crate) version: Option<u8>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn key_data(self) -> anyhow::Result<Vec<u8>> {
|
||||
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(())
|
||||
}
|
||||
+42
-11
@@ -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<Vec<u8>> {
|
||||
}
|
||||
|
||||
#[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<PathBuf>,
|
||||
|
||||
/// 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<u8>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn credential_data(self) -> anyhow::Result<Vec<u8>> {
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<PathBuf>,
|
||||
|
||||
/// Output file for the ticketbook
|
||||
#[clap(long, group = "output", requires = "bs58_encoded_client_secret")]
|
||||
pub(crate) output_file: Option<PathBuf>,
|
||||
|
||||
/// 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<String>,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod coconut;
|
||||
pub mod context;
|
||||
pub mod ecash;
|
||||
pub mod utils;
|
||||
pub mod validator;
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
* 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;
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Option<Vec<u8>>, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"SELECT serialised_key FROM master_verification_key WHERE epoch_id = ?",
|
||||
) -> Result<Option<RawVerificationKey>, 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<Option<Vec<u8>>, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"SELECT serialised_signatures FROM coin_indices_signatures WHERE epoch_id = ?",
|
||||
) -> Result<Option<RawCoinIndexSignatures>, 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?;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -62,4 +62,19 @@ pub struct StoredPendingTicketbook {
|
||||
pub struct RawExpirationDateSignatures {
|
||||
pub epoch_id: u32,
|
||||
pub serialised_signatures: Vec<u8>,
|
||||
pub serialization_revision: u8,
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
|
||||
pub struct RawCoinIndexSignatures {
|
||||
pub epoch_id: u32,
|
||||
pub serialised_signatures: Vec<u8>,
|
||||
pub serialization_revision: u8,
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
|
||||
pub struct RawVerificationKey {
|
||||
pub epoch_id: u32,
|
||||
pub serialised_key: Vec<u8>,
|
||||
pub serialization_revision: u8,
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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>(T);
|
||||
|
||||
pub(crate) fn serialise_coin_index_signatures(sigs: &[AnnotatedCoinIndexSignature]) -> Vec<u8> {
|
||||
storage_serialiser()
|
||||
.serialize(&StorageBorrowedSerdeWrapper(&sigs))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn deserialise_coin_index_signatures(
|
||||
raw: &[u8],
|
||||
) -> Result<Vec<AnnotatedCoinIndexSignature>, 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<u8> {
|
||||
storage_serialiser()
|
||||
.serialize(&StorageBorrowedSerdeWrapper(&sigs))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn deserialise_expiration_date_signatures(
|
||||
raw: &[u8],
|
||||
) -> Result<Vec<AnnotatedExpirationDateSignature>, 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()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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>(T);
|
||||
|
||||
pub(crate) fn deserialise_v1_expiration_date_signatures(
|
||||
raw: &[u8],
|
||||
) -> Result<Vec<AnnotatedExpirationDateSignature>, 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<Vec<AnnotatedCoinIndexSignature>, 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, StorageError> {
|
||||
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()
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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(())
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<AggregatedExpirationDateSignatures>,
|
||||
|
||||
pub coin_index_signatures: Option<AggregatedCoinIndicesSignatures>,
|
||||
|
||||
pub master_verification_key: Option<EpochVerificationKey>,
|
||||
}
|
||||
|
||||
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
pub struct ImportableTicketBook {
|
||||
pub serialised_ticketbook: VersionSerialised<IssuedTicketBook>,
|
||||
|
||||
#[zeroize(skip)]
|
||||
pub serialised_expiration_date_signatures:
|
||||
Option<VersionSerialised<AggregatedExpirationDateSignatures>>,
|
||||
|
||||
#[zeroize(skip)]
|
||||
pub serialised_coin_index_signatures:
|
||||
Option<VersionSerialised<AggregatedCoinIndicesSignatures>>,
|
||||
|
||||
#[zeroize(skip)]
|
||||
pub serialised_master_verification_key: Option<VersionSerialised<EpochVerificationKey>>,
|
||||
}
|
||||
|
||||
impl From<IssuedTicketBook> 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<u8> {
|
||||
self.pack().data
|
||||
}
|
||||
|
||||
pub fn try_unpack_full(&self) -> Result<DecodedImportableTicketBook, Error> {
|
||||
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<Option<u8>>) -> Result<Self, Error>
|
||||
where
|
||||
Self: DeserializeOwned,
|
||||
{
|
||||
let revision = revision
|
||||
.into()
|
||||
.unwrap_or(<Self as VersionedSerialise>::CURRENT_SERIALISATION_REVISION);
|
||||
|
||||
match revision {
|
||||
1 => Self::try_unpack_current(b),
|
||||
_ => Err(Error::UnknownSerializationRevision { revision }),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Signature, Error> {
|
||||
// aggregate_signature_shares(shares).map_err(Error::SignatureAggregationError)
|
||||
// }
|
||||
|
||||
pub fn aggregate_signature_shares(
|
||||
&self,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Option<u8>>) -> Result<Self, Error>
|
||||
where
|
||||
Self: DeserializeOwned,
|
||||
{
|
||||
let revision = revision
|
||||
.into()
|
||||
.unwrap_or(<Self as VersionedSerialise>::CURRENT_SERIALISATION_REVISION);
|
||||
|
||||
match revision {
|
||||
1 => Err(Error::UnsupportedSerializationRevision { revision }),
|
||||
2 => Self::try_unpack_current(b),
|
||||
_ => Err(Error::UnknownSerializationRevision { revision }),
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -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<T: ?Sized> {
|
||||
pub data: Vec<u8>,
|
||||
pub revision: u8,
|
||||
|
||||
// still wondering if there's any point in having the phantom in here
|
||||
#[zeroize(skip)]
|
||||
#[serde(skip)]
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> VersionSerialised<T> {
|
||||
pub fn try_unpack(&self) -> Result<T, Error>
|
||||
where
|
||||
T: VersionedSerialise + DeserializeOwned,
|
||||
{
|
||||
T::try_unpack(&self.data, self.revision)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VersionedSerialise {
|
||||
const CURRENT_SERIALISATION_REVISION: u8;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<AnnotatedExpirationDateSignature>,
|
||||
}
|
||||
|
||||
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<Option<u8>>) -> Result<Self, Error>
|
||||
where
|
||||
Self: DeserializeOwned,
|
||||
{
|
||||
let revision = revision
|
||||
.into()
|
||||
.unwrap_or(<Self as VersionedSerialise>::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<AnnotatedCoinIndexSignature>,
|
||||
}
|
||||
|
||||
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<Option<u8>>) -> Result<Self, Error>
|
||||
where
|
||||
Self: DeserializeOwned,
|
||||
{
|
||||
let revision = revision
|
||||
.into()
|
||||
.unwrap_or(<Self as VersionedSerialise>::CURRENT_SERIALISATION_REVISION);
|
||||
|
||||
match revision {
|
||||
1 => Err(Error::UnsupportedSerializationRevision { revision }),
|
||||
2 => Self::try_unpack_current(b),
|
||||
_ => Err(Error::UnknownSerializationRevision { revision }),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<dyn Error + Send + Sync>,
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<S>(
|
||||
credentials_store: S,
|
||||
raw_credential: Vec<u8>,
|
||||
credential_version: impl Into<Option<u8>>,
|
||||
) -> Result<OffsetDateTime, NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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())
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<S>(
|
||||
credentials_store: &S,
|
||||
key: &EpochVerificationKey,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: &S,
|
||||
signatures: &AggregatedExpirationDateSignatures,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: &S,
|
||||
signatures: &AggregatedCoinIndicesSignatures,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: &S,
|
||||
ticketbook: &IssuedTicketBook,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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(())
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<S>(
|
||||
credentials_store: S,
|
||||
raw_key: Vec<u8>,
|
||||
key_version: impl Into<Option<u8>>,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: S,
|
||||
raw_signatures: Vec<u8>,
|
||||
signatures_version: impl Into<Option<u8>>,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: S,
|
||||
raw_signatures: Vec<u8>,
|
||||
signatures_version: impl Into<Option<u8>>,
|
||||
) -> Result<(), NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: S,
|
||||
raw_credential: Vec<u8>,
|
||||
credential_version: impl Into<Option<u8>>,
|
||||
) -> Result<OffsetDateTime, NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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<S>(
|
||||
credentials_store: S,
|
||||
raw_credential: Vec<u8>,
|
||||
credential_version: impl Into<Option<u8>>,
|
||||
) -> Result<OffsetDateTime, NymIdError>
|
||||
where
|
||||
S: Storage,
|
||||
<S as 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())
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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<Self>;
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -524,7 +524,22 @@ pub struct KeyPairUser {
|
||||
public_key: PublicKeyUser,
|
||||
}
|
||||
|
||||
impl From<KeyPairUser> 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<M: AsRef<[u8]>>(seed: M) -> Self {
|
||||
generate_keypair_user_from_seed(seed)
|
||||
}
|
||||
|
||||
pub fn secret_key(&self) -> &SecretKeyUser {
|
||||
&self.secret_key
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"]
|
||||
@@ -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<Date, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
|
||||
Date::parse(&s, DATE_FORMAT).map_err(de::Error::custom)
|
||||
}
|
||||
|
||||
pub fn serialize<S>(datetime: &Date, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+9
-9
@@ -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",
|
||||
|
||||
+4
-5
@@ -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
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
|
||||
@@ -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<Date, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
|
||||
Date::parse(&s, DATE_FORMAT).map_err(de::Error::custom)
|
||||
}
|
||||
|
||||
pub(crate) fn serialize<S>(datetime: &Date, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+1
@@ -3355,6 +3355,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bs58",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliAuthenticatorClient, _>(args).await?;
|
||||
println!("successfully imported coin index signatures!");
|
||||
Ok(())
|
||||
}
|
||||
+2
-4
@@ -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::<CliAuthenticatorClient, _>(args).await?;
|
||||
println!("successfully imported credential!");
|
||||
Ok(())
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliAuthenticatorClient, _>(args).await?;
|
||||
println!("successfully imported expiration date signatures!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliAuthenticatorClient, _>(args).await?;
|
||||
println!("successfully imported master verification key!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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),
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<CommonShowTicketbooksArgs> 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::<CliAuthenticatorClient, _>(args).await?;
|
||||
|
||||
println!("{}", output.format(&res));
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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?,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliIpPacketRouterClient, _>(args).await?;
|
||||
println!("successfully imported coin index signatures!");
|
||||
Ok(())
|
||||
}
|
||||
+2
-4
@@ -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::<CliIpPacketRouterClient, _>(args).await?;
|
||||
println!("successfully imported credential!");
|
||||
Ok(())
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliIpPacketRouterClient, _>(args).await?;
|
||||
println!("successfully imported expiration date signatures!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliIpPacketRouterClient, _>(args).await?;
|
||||
println!("successfully imported master verification key!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<dyn Error + Send + Sync>> {
|
||||
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),
|
||||
}
|
||||
+2
-2
@@ -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<CommonShowTicketbooksArgs> 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::<CliIpPacketRouterClient, _>(args).await?;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliNetworkRequesterClient, _>(args).await?;
|
||||
println!("successfully imported coin index signatures!");
|
||||
Ok(())
|
||||
}
|
||||
+2
-4
@@ -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::<CliNetworkRequesterClient, _>(args).await?;
|
||||
println!("successfully imported credential!");
|
||||
Ok(())
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliNetworkRequesterClient, _>(args).await?;
|
||||
println!("successfully imported expiration date signatures!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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::<CliNetworkRequesterClient, _>(args).await?;
|
||||
println!("successfully imported master verification key!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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),
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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),
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user