diff --git a/Cargo.lock b/Cargo.lock index 7dfcda47ff..e81281023f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -923,7 +923,6 @@ dependencies = [ name = "credential" version = "0.1.0" dependencies = [ - "async-trait", "bip39", "cfg-if 0.1.10", "clap 3.2.8", @@ -935,7 +934,6 @@ dependencies = [ "crypto", "network-defaults", "pemstore", - "pickledb", "rand 0.7.3", "serde", "thiserror", @@ -2745,12 +2743,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - [[package]] name = "lioness" version = "0.1.2" @@ -3934,19 +3926,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "pickledb" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9161694d67f6c5163519d42be942ae36bbdb55f439460144f105bc4f9f7d1d61" -dependencies = [ - "bincode", - "serde", - "serde_cbor", - "serde_json", - "serde_yaml", -] - [[package]] name = "pin-project" version = "1.0.10" @@ -5057,18 +5036,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_yaml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" -dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", -] - [[package]] name = "sha-1" version = "0.8.2" @@ -6938,15 +6905,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "yansi" version = "0.5.1" diff --git a/clients/client-core/src/config/mod.rs b/clients/client-core/src/config/mod.rs index 277fff30b9..b3dc70eb28 100644 --- a/clients/client-core/src/config/mod.rs +++ b/clients/client-core/src/config/mod.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use config::NymConfig; +use config::{NymConfig, DB_FILE_NAME}; use nymsphinx::params::PacketSize; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; @@ -412,7 +412,7 @@ impl Client { T::default_data_directory(Some(id)).join("reply_key_store") } fn default_database_path(id: &str) -> PathBuf { - T::default_data_directory(Some(id)).join("db.sqlite") + T::default_data_directory(Some(id)).join(DB_FILE_NAME) } } diff --git a/clients/credential/Cargo.toml b/clients/credential/Cargo.toml index 81fe361ad0..ccf241b1cb 100644 --- a/clients/credential/Cargo.toml +++ b/clients/credential/Cargo.toml @@ -6,11 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.52" bip39 = "1.0.1" cfg-if = "0.1" clap = { version = "3.2", features = ["cargo", "derive"] } -pickledb = "0.4.1" rand = "0.7.3" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" diff --git a/clients/credential/src/commands.rs b/clients/credential/src/commands.rs index 7f9d3ae2e1..5ed4bd8dad 100644 --- a/clients/credential/src/commands.rs +++ b/clients/credential/src/commands.rs @@ -1,14 +1,12 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use async_trait::async_trait; use clap::{Args, Subcommand}; use completions::ArgShell; -use pickledb::PickleDb; use rand::rngs::OsRng; use std::str::FromStr; -use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters}; +use coconut_interface::{Base58, Parameters}; use credential_storage::storage::Storage; use credential_storage::PersistentStorage; use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; @@ -20,16 +18,12 @@ use validator_client::{CoconutApiClient, Config}; use crate::client::Client; use crate::error::{CredentialClientError, Result}; -use crate::state::{KeyPair, RequestData, State}; +use crate::state::{KeyPair, State}; #[derive(Subcommand)] -pub(crate) enum Commands { - /// Deposit funds for buying coconut credential - Deposit(Deposit), - /// Lists the tx hashes of previous deposits - ListDeposits(ListDeposits), - /// Get a credential for a given deposit - GetCredential(GetCredential), +pub(crate) enum Command { + /// Run the binary + Run(Run), /// Generate shell completions Completions(ArgShell), @@ -38,169 +32,81 @@ pub(crate) enum Commands { GenerateFigSpec, } -#[async_trait] -pub(crate) trait Execute { - async fn execute(&self, db: &mut PickleDb, shared_storage: PersistentStorage) -> Result<()>; -} +#[derive(Args)] +pub(crate) struct Run { + /// Home directory of the client that is supposed to use the credential. + #[clap(long)] + pub(crate) client_home_directory: std::path::PathBuf, -#[derive(Args, Clone)] -pub(crate) struct Deposit { /// The nymd URL that should be used #[clap(long)] - nymd_url: String, - /// A mnemonic for the account that does the deposit + pub(crate) nymd_url: String, + + /// A mnemonic for the account that buys the credential #[clap(long)] - mnemonic: String, - /// The amount that needs to be deposited + pub(crate) mnemonic: String, + + /// The amount of utokens the credential will hold #[clap(long)] - amount: u64, + pub(crate) amount: u64, } -#[async_trait] -impl Execute for Deposit { - async fn execute(&self, db: &mut PickleDb, _shared_storage: PersistentStorage) -> Result<()> { - let mut rng = OsRng; - let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); - let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); +pub(crate) async fn deposit(nymd_url: &str, mnemonic: &str, amount: u64) -> Result { + let mut rng = OsRng; + let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); + let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); - let client = Client::new(&self.nymd_url, &self.mnemonic); - let tx_hash = client - .deposit( - self.amount, - signing_keypair.public_key.clone(), - encryption_keypair.public_key.clone(), - None, - ) - .await?; - - let state = State { - amount: self.amount, - tx_hash: tx_hash.clone(), - signing_keypair, - encryption_keypair, - blind_request_data: None, - signature: None, - }; - db.set(&tx_hash, &state).unwrap(); - - println!("{:?}", state); - - Ok(()) - } -} - -#[derive(Args, Clone)] -pub(crate) struct ListDeposits {} - -#[async_trait] -impl Execute for ListDeposits { - async fn execute(&self, db: &mut PickleDb, _shared_storage: PersistentStorage) -> Result<()> { - for kv in db.iter() { - println!("{:?}", kv.get_value::()); - } - - Ok(()) - } -} - -#[derive(Args, Clone)] -pub(crate) struct GetCredential { - /// The hash of a successful deposit transaction - #[clap(long)] - tx_hash: String, - /// The nymd URL that should be used - #[clap(long)] - nymd_url: String, - /// If we want to get the signature without attaching a blind sign request; it is expected that - /// there is already a signature stored on the signer - #[clap(long, parse(from_flag))] - __no_request: bool, -} - -#[async_trait] -impl Execute for GetCredential { - async fn execute(&self, db: &mut PickleDb, shared_storage: PersistentStorage) -> Result<()> { - let mut state = db - .get::(&self.tx_hash) - .ok_or(CredentialClientError::NoDeposit)?; - - let network_details = NymNetworkDetails::new_from_env(); - let config = Config::try_from_nym_network_details(&network_details)?; - let client = validator_client::Client::new_query(config)?; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?; - - let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); - let bandwidth_credential_attributes = if self.__no_request { - if let Some(blind_request_data) = state.blind_request_data { - let serial_number = - Attribute::try_from_byte_slice(&blind_request_data.serial_number) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?; - let binding_number = - Attribute::try_from_byte_slice(&blind_request_data.binding_number) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?; - let pedersen_commitments_openings = vec![ - Attribute::try_from_byte_slice(&blind_request_data.first_attribute) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?, - Attribute::try_from_byte_slice(&blind_request_data.second_attribute) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?, - ]; - let blind_sign_request = - BlindSignRequest::from_bytes(blind_request_data.blind_sign_req.as_slice()) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?; - BandwidthVoucher::new_with_blind_sign_req( - [serial_number, binding_number], - [&state.amount.to_string(), VOUCHER_INFO], - Hash::from_str(&self.tx_hash) - .map_err(|_| CredentialClientError::InvalidTxHash)?, - identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?, - encryption::PrivateKey::from_base58_string( - &state.encryption_keypair.private_key, - )?, - pedersen_commitments_openings, - blind_sign_request, - ) - } else { - return Err(CredentialClientError::NoLocalBlindSignRequest); - } - } else { - BandwidthVoucher::new( - ¶ms, - state.amount.to_string(), - VOUCHER_INFO.to_string(), - Hash::from_str(&self.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?, - identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?, - encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?, - ) - }; - - // Back up the blind sign req data, in case of sporadic failures - state.blind_request_data = Some(RequestData::new( - bandwidth_credential_attributes.get_private_attributes(), - bandwidth_credential_attributes.pedersen_commitments_openings(), - bandwidth_credential_attributes.blind_sign_request(), - )?); - db.set(&self.tx_hash, &state).unwrap(); - - let signature = obtain_aggregate_signature( - ¶ms, - &bandwidth_credential_attributes, - &coconut_api_clients, + let client = Client::new(nymd_url, mnemonic); + let tx_hash = client + .deposit( + amount, + signing_keypair.public_key.clone(), + encryption_keypair.public_key.clone(), + None, ) .await?; - shared_storage - .insert_coconut_credential( - state.amount.to_string(), - VOUCHER_INFO.to_string(), - bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(), - bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(), - signature.to_bs58(), - ) - .await?; - state.signature = Some(signature.to_bs58()); - db.set(&self.tx_hash, &state).unwrap(); - println!("Signature: {:?}", state.signature); + let state = State { + amount, + tx_hash: tx_hash.clone(), + signing_keypair, + encryption_keypair, + }; - Ok(()) - } + Ok(state) +} + +pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStorage) -> Result<()> { + let network_details = NymNetworkDetails::new_from_env(); + let config = Config::try_from_nym_network_details(&network_details)?; + let client = validator_client::Client::new_query(config)?; + let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?; + + let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); + let bandwidth_credential_attributes = BandwidthVoucher::new( + ¶ms, + state.amount.to_string(), + VOUCHER_INFO.to_string(), + Hash::from_str(&state.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?, + identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?, + encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?, + ); + + let signature = obtain_aggregate_signature( + ¶ms, + &bandwidth_credential_attributes, + &coconut_api_clients, + ) + .await?; + shared_storage + .insert_coconut_credential( + state.amount.to_string(), + VOUCHER_INFO.to_string(), + bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(), + bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(), + signature.to_bs58(), + ) + .await?; + + Ok(()) } diff --git a/clients/credential/src/error.rs b/clients/credential/src/error.rs index 08bd0001e9..5cbc636e58 100644 --- a/clients/credential/src/error.rs +++ b/clients/credential/src/error.rs @@ -23,18 +23,6 @@ pub enum CredentialClientError { #[error("Credential error: {0}")] Credential(#[from] CredentialError), - #[error("No previous deposit with that tx hash")] - NoDeposit, - - #[error("Wrong number of attributes")] - WrongAttributeNumber, - - #[error("Could not find any backed up blind sign request data")] - NoLocalBlindSignRequest, - - #[error("The local blind sign request data is corrupted")] - CorruptedBlindSignRequest, - #[error("The tx hash provided is not valid")] InvalidTxHash, diff --git a/clients/credential/src/main.rs b/clients/credential/src/main.rs index cc0263e048..f413673626 100644 --- a/clients/credential/src/main.rs +++ b/clients/credential/src/main.rs @@ -9,14 +9,13 @@ cfg_if::cfg_if! { mod error; mod state; - use commands::{Commands, Execute}; use error::Result; use network_defaults::setup_env; - use clap::CommandFactory; use completions::fig_generate; + use commands::*; + use config::{DATA_DIR, DB_FILE_NAME}; - use clap::Parser; - use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod}; + use clap::{CommandFactory, Parser}; #[derive(Parser)] #[clap(author = "Nymtech", version, about)] @@ -25,43 +24,26 @@ cfg_if::cfg_if! { #[clap(short, long)] pub(crate) config_env_file: Option, - /// Path where the sqlite credental database will be located. - /// It should point to a $HOME/$CLIENT_ID/data/db.sqlite file of - /// the client that is supposed to use the credential. - #[clap(long)] - pub(crate) credential_db_path: std::path::PathBuf, - #[clap(subcommand)] - command: Commands, + pub(crate) command: Command, } #[tokio::main] async fn main() -> Result<()> { let args = Cli::parse(); setup_env(args.config_env_file.clone()); - - let shared_storage = credential_storage::initialise_storage(args.credential_db_path.clone()).await; - let mut db = match PickleDb::load( - "credential.db", - PickleDbDumpPolicy::AutoDump, - SerializationMethod::Json, - ) { - Ok(db) => db, - Err(_) => PickleDb::new( - "credential.db", - PickleDbDumpPolicy::AutoDump, - SerializationMethod::Json, - ), - }; - let bin_name = "nym-credential-client"; - match &args.command { - Commands::Deposit(m) => m.execute(&mut db, shared_storage).await?, - Commands::ListDeposits(m) => m.execute(&mut db, shared_storage).await?, - Commands::GetCredential(m) => m.execute(&mut db, shared_storage).await?, - Commands::Completions(s) => s.generate(&mut crate::Cli::into_app(), bin_name), - Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::into_app(), bin_name) + match args.command { + Command::Run(r) => { + let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME); + let shared_storage = credential_storage::initialise_storage(db_path).await; + + let state = deposit(&r.nymd_url, &r.mnemonic, r.amount).await?; + get_credential(&state, shared_storage).await?; + } + Command::Completions(c) => c.generate(&mut crate::Cli::into_app(), bin_name), + Command::GenerateFigSpec => fig_generate(&mut crate::Cli::into_app(), bin_name) } Ok(()) diff --git a/clients/credential/src/state.rs b/clients/credential/src/state.rs index 6da1ae6faa..98cbb307df 100644 --- a/clients/credential/src/state.rs +++ b/clients/credential/src/state.rs @@ -1,13 +1,10 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use coconut_interface::{Attribute, BlindSignRequest, Bytable, PrivateAttribute}; use serde::{Deserialize, Serialize}; use crypto::asymmetric::{encryption, identity}; -use crate::error::{CredentialClientError, Result}; - #[derive(Clone, Debug, Deserialize, Serialize)] pub(crate) struct KeyPair { pub public_key: String, @@ -38,35 +35,4 @@ pub(crate) struct State { pub tx_hash: String, pub signing_keypair: KeyPair, pub encryption_keypair: KeyPair, - pub blind_request_data: Option, - pub signature: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub(crate) struct RequestData { - pub serial_number: Vec, - pub binding_number: Vec, - pub first_attribute: Vec, - pub second_attribute: Vec, - pub blind_sign_req: Vec, -} - -impl RequestData { - pub fn new( - private_attributes: Vec, - attributes: &[Attribute], - blind_sign_request: &BlindSignRequest, - ) -> Result { - if private_attributes.len() != 2 || attributes.len() != 2 { - Err(CredentialClientError::WrongAttributeNumber) - } else { - Ok(RequestData { - serial_number: private_attributes[0].to_byte_vec(), - binding_number: private_attributes[1].to_byte_vec(), - first_attribute: attributes[0].to_byte_vec(), - second_attribute: attributes[1].to_byte_vec(), - blind_sign_req: blind_sign_request.to_bytes(), - }) - } - } } diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 1f72896d9c..f7bf7c2149 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -11,6 +11,10 @@ use std::{fs, io}; pub mod defaults; +pub const CONFIG_DIR: &str = "config"; +pub const DATA_DIR: &str = "data"; +pub const DB_FILE_NAME: &str = "db.sqlite"; + pub trait NymConfig: Default + Serialize + DeserializeOwned { fn template() -> &'static str; @@ -23,17 +27,17 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { // default, most probable, implementations; can be easily overridden where required fn default_config_directory(id: Option<&str>) -> PathBuf { if let Some(id) = id { - Self::default_root_directory().join(id).join("config") + Self::default_root_directory().join(id).join(CONFIG_DIR) } else { - Self::default_root_directory().join("config") + Self::default_root_directory().join(CONFIG_DIR) } } fn default_data_directory(id: Option<&str>) -> PathBuf { if let Some(id) = id { - Self::default_root_directory().join(id).join("data") + Self::default_root_directory().join(id).join(DATA_DIR) } else { - Self::default_root_directory().join("data") + Self::default_root_directory().join(DATA_DIR) } } @@ -47,17 +51,17 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { fn try_default_config_directory(id: Option<&str>) -> Option { if let Some(id) = id { - Self::try_default_root_directory().map(|d| d.join(id).join("config")) + Self::try_default_root_directory().map(|d| d.join(id).join(CONFIG_DIR)) } else { - Self::try_default_root_directory().map(|d| d.join("config")) + Self::try_default_root_directory().map(|d| d.join(CONFIG_DIR)) } } fn try_default_data_directory(id: Option<&str>) -> Option { if let Some(id) = id { - Self::try_default_root_directory().map(|d| d.join(id).join("data")) + Self::try_default_root_directory().map(|d| d.join(id).join(DATA_DIR)) } else { - Self::try_default_root_directory().map(|d| d.join("data")) + Self::try_default_root_directory().map(|d| d.join(DATA_DIR)) } }