From 5668e123d9da407b6c1bffd63b6a2f9f15dd960c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 25 Nov 2024 15:41:49 +0000 Subject: [PATCH] introduced initial internal commands for nym-cli: ecash key and request generation (#5174) * introduced initial internal commands for nym-cli: ecash key and request generation * reduced args logging level --- Cargo.lock | 2 + common/commands/Cargo.toml | 2 + .../internal/ecash/generate_keypair/mod.rs | 41 ++++++++++ common/commands/src/internal/ecash/mod.rs | 23 ++++++ .../src/internal/ecash/withdrawal_request.rs | 78 +++++++++++++++++++ common/commands/src/internal/mod.rs | 19 +++++ common/commands/src/lib.rs | 1 + .../src/scheme/keygen.rs | 9 +++ tools/nym-cli/src/internal/ecash/mod.rs | 26 +++++++ tools/nym-cli/src/internal/mod.rs | 20 +++++ tools/nym-cli/src/main.rs | 9 +++ 11 files changed, 230 insertions(+) create mode 100644 common/commands/src/internal/ecash/generate_keypair/mod.rs create mode 100644 common/commands/src/internal/ecash/mod.rs create mode 100644 common/commands/src/internal/ecash/withdrawal_request.rs create mode 100644 common/commands/src/internal/mod.rs create mode 100644 tools/nym-cli/src/internal/ecash/mod.rs create mode 100644 tools/nym-cli/src/internal/mod.rs diff --git a/Cargo.lock b/Cargo.lock index d004324f62..ab3aa84ddf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4752,12 +4752,14 @@ dependencies = [ "nym-coconut-dkg-common", "nym-config", "nym-contracts-common", + "nym-credential-proxy-requests", "nym-credential-storage", "nym-credential-utils", "nym-credentials", "nym-credentials-interface", "nym-crypto", "nym-ecash-contract-common", + "nym-ecash-time", "nym-id", "nym-mixnet-contract-common", "nym-multisig-contract-common", diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index c9bd11775d..d382cb696f 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -48,6 +48,7 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" } nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" } nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" } +nym-ecash-time = { path = "../../common/ecash-time" } nym-sphinx = { path = "../../common/nymsphinx" } nym-client-core = { path = "../../common/client-core" } nym-config = { path = "../../common/config" } @@ -56,6 +57,7 @@ nym-credentials-interface = { path = "../../common/credentials-interface" } nym-credential-storage = { path = "../../common/credential-storage" } nym-credential-utils = { path = "../../common/credential-utils" } nym-id = { path = "../nym-id" } +nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" } nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" } nym-types = { path = "../../common/types" } diff --git a/common/commands/src/internal/ecash/generate_keypair/mod.rs b/common/commands/src/internal/ecash/generate_keypair/mod.rs new file mode 100644 index 0000000000..7be40d986d --- /dev/null +++ b/common/commands/src/internal/ecash/generate_keypair/mod.rs @@ -0,0 +1,41 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use log::trace; +use nym_credentials_interface::{generate_keypair_user, generate_keypair_user_from_seed, Base58}; +use serde::{Deserialize, Serialize}; +use std::io::stdout; + +#[derive(Serialize, Deserialize)] +pub struct Bs58EncodedKeys { + pub secret_key: String, + pub public_key: String, +} + +#[derive(Debug, Parser)] +pub struct Args { + /// Secret value that's used for deriving underlying ecash keypair + #[clap(long)] + pub(crate) bs58_encoded_client_secret: Option, +} + +pub fn generate_ecash_keypair(args: Args) -> anyhow::Result<()> { + trace!("args: {args:?}"); + + let keypair = if let Some(secret) = args.bs58_encoded_client_secret { + let seed = bs58::decode(&secret).into_vec()?; + generate_keypair_user_from_seed(&seed) + } else { + generate_keypair_user() + }; + + let encoded = Bs58EncodedKeys { + secret_key: keypair.secret_key().to_bs58(), + public_key: keypair.public_key().to_bs58(), + }; + + serde_json::to_writer_pretty(stdout(), &encoded)?; + + Ok(()) +} diff --git a/common/commands/src/internal/ecash/mod.rs b/common/commands/src/internal/ecash/mod.rs new file mode 100644 index 0000000000..7a9ab05d9b --- /dev/null +++ b/common/commands/src/internal/ecash/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; + +pub mod generate_keypair; +pub mod withdrawal_request; + +#[derive(Debug, Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct InternalEcash { + #[clap(subcommand)] + pub command: InternalEcashCommands, +} + +#[derive(Debug, Subcommand)] +pub enum InternalEcashCommands { + /// Generate a dummy withdrawal request + GenerateWithdrawalRequest(withdrawal_request::Args), + + /// Generate dummy ecash keypair + GenerateKeypair(generate_keypair::Args), +} diff --git a/common/commands/src/internal/ecash/withdrawal_request.rs b/common/commands/src/internal/ecash/withdrawal_request.rs new file mode 100644 index 0000000000..bf56babdd9 --- /dev/null +++ b/common/commands/src/internal/ecash/withdrawal_request.rs @@ -0,0 +1,78 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use log::trace; +use nym_credential_proxy_requests::api::v1::ticketbook::models::TicketbookRequest; +use nym_credentials_interface::{ + generate_keypair_user, withdrawal_request, Base58, SecretKeyUser, TicketType, +}; +use nym_ecash_time::{ecash_default_expiration_date, EcashTime}; +use serde::{Deserialize, Serialize}; +use std::io::stdout; +use time::macros::format_description; +use time::Date; +use zeroize::Zeroizing; + +fn parse_date(raw: &str) -> Result { + let format = format_description!("[year]-[month]-[day]"); + Date::parse(raw, &format) +} + +#[derive(Serialize, Deserialize)] +pub struct Bs58EncodedOutput { + pub ecash_proxy_request: TicketbookRequest, + pub ecash_secret: String, + + /// Needed to later unblind shares + pub ecash_request_info_bs58: String, +} + +#[derive(Debug, Parser)] +pub struct Args { + /// Specify which type of ticketbook + #[clap(long, default_value_t = TicketType::V1MixnetEntry)] + pub(crate) ticketbook_type: TicketType, + + /// Set expiration date for the ticketbook + #[clap(long, value_parser = parse_date, default_value_t = ecash_default_expiration_date())] + pub(crate) expiration_date: Date, + + /// Provide ecash secret key (or generate a fresh one) + #[clap(long)] + pub(crate) ecash_secret_key_bs58: Option, +} + +pub async fn generate_withdrawal_request(args: Args) -> anyhow::Result<()> { + trace!("args: {args:?}"); + + let ecash_keypair = if let Some(secret_key) = args.ecash_secret_key_bs58 { + let secret_key = Zeroizing::new(bs58::decode(Zeroizing::new(secret_key)).into_vec()?); + let sk = SecretKeyUser::from_bytes(&secret_key)?; + sk.into() + } else { + generate_keypair_user() + }; + + let (withdrawal_request, request_info) = withdrawal_request( + ecash_keypair.secret_key(), + args.expiration_date.ecash_unix_timestamp(), + args.ticketbook_type.encode(), + )?; + + let encoded = Bs58EncodedOutput { + ecash_proxy_request: TicketbookRequest { + withdrawal_request: withdrawal_request.into(), + ecash_pubkey: ecash_keypair.public_key(), + expiration_date: args.expiration_date, + ticketbook_type: args.ticketbook_type, + is_freepass_request: false, + }, + ecash_secret: ecash_keypair.secret_key().to_bs58(), + ecash_request_info_bs58: request_info.to_bs58(), + }; + + serde_json::to_writer_pretty(stdout(), &encoded)?; + + Ok(()) +} diff --git a/common/commands/src/internal/mod.rs b/common/commands/src/internal/mod.rs new file mode 100644 index 0000000000..44fa4d2659 --- /dev/null +++ b/common/commands/src/internal/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; + +pub mod ecash; + +#[derive(Debug, Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Internal { + #[clap(subcommand)] + pub command: InternalCommands, +} + +#[derive(Debug, Subcommand)] +pub enum InternalCommands { + /// Ecash related internal commands + Ecash(ecash::InternalEcash), +} diff --git a/common/commands/src/lib.rs b/common/commands/src/lib.rs index e3670ac572..f48415d7a6 100644 --- a/common/commands/src/lib.rs +++ b/common/commands/src/lib.rs @@ -3,5 +3,6 @@ pub mod context; pub mod ecash; +pub mod internal; pub mod utils; pub mod validator; diff --git a/common/nym_offline_compact_ecash/src/scheme/keygen.rs b/common/nym_offline_compact_ecash/src/scheme/keygen.rs index f5926d9cb6..e49c6309df 100644 --- a/common/nym_offline_compact_ecash/src/scheme/keygen.rs +++ b/common/nym_offline_compact_ecash/src/scheme/keygen.rs @@ -530,6 +530,15 @@ impl From for SecretKeyUser { } } +impl From for KeyPairUser { + fn from(value: SecretKeyUser) -> Self { + KeyPairUser { + public_key: value.public_key(), + secret_key: value, + } + } +} + impl KeyPairUser { #[allow(clippy::new_without_default)] pub fn new() -> Self { diff --git a/tools/nym-cli/src/internal/ecash/mod.rs b/tools/nym-cli/src/internal/ecash/mod.rs new file mode 100644 index 0000000000..ee9f8ef47c --- /dev/null +++ b/tools/nym-cli/src/internal/ecash/mod.rs @@ -0,0 +1,26 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_cli_commands::context::ClientArgs; +use nym_cli_commands::internal::ecash::InternalEcashCommands; +use nym_network_defaults::NymNetworkDetails; + +pub(super) async fn execute( + global_args: ClientArgs, + ecash: nym_cli_commands::internal::ecash::InternalEcash, + nym_network_details: &NymNetworkDetails, +) -> anyhow::Result<()> { + // I reckon those will be needed later + let _ = global_args; + let _ = nym_network_details; + + match ecash.command { + InternalEcashCommands::GenerateWithdrawalRequest(args) => { + nym_cli_commands::internal::ecash::withdrawal_request::generate_withdrawal_request(args) + .await + } + InternalEcashCommands::GenerateKeypair(args) => { + nym_cli_commands::internal::ecash::generate_keypair::generate_ecash_keypair(args) + } + } +} diff --git a/tools/nym-cli/src/internal/mod.rs b/tools/nym-cli/src/internal/mod.rs new file mode 100644 index 0000000000..253c45b227 --- /dev/null +++ b/tools/nym-cli/src/internal/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_cli_commands::context::ClientArgs; +use nym_cli_commands::internal::InternalCommands; +use nym_network_defaults::NymNetworkDetails; + +mod ecash; + +pub(super) async fn execute( + global_args: ClientArgs, + internal: nym_cli_commands::internal::Internal, + nym_network_details: &NymNetworkDetails, +) -> anyhow::Result<()> { + match internal.command { + InternalCommands::Ecash(ecash_commands) => { + ecash::execute(global_args, ecash_commands, nym_network_details).await + } + } +} diff --git a/tools/nym-cli/src/main.rs b/tools/nym-cli/src/main.rs index c5b4cc51e3..7854dbb0d2 100644 --- a/tools/nym-cli/src/main.rs +++ b/tools/nym-cli/src/main.rs @@ -9,6 +9,7 @@ use nym_validator_client::nyxd::AccountId; mod completion; mod ecash; +mod internal; mod validator; #[derive(Debug, Parser)] @@ -74,6 +75,11 @@ pub(crate) enum Commands { VestingSchedule(nym_cli_commands::validator::vesting::VestingSchedule), /// Manage your mixnet infrastructure, delegate stake or query the directory Mixnet(nym_cli_commands::validator::mixnet::Mixnet), + + #[clap(hide = true)] + /// Internal commands used for testing and debugging + Internal(nym_cli_commands::internal::Internal), + /// Generates shell completion GenerateFig, } @@ -118,6 +124,9 @@ async fn execute(cli: Cli) -> anyhow::Result<()> { Commands::Mixnet(mixnet) => { validator::mixnet::execute(args, mixnet, &network_details).await? } + Commands::Internal(internal_commands) => { + internal::execute(args, internal_commands, &network_details).await? + } Commands::GenerateFig => { let mut cmd = Cli::command(); completion::print_fig(&mut cmd);