Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI Enhancements: Account creation + config file change #64

Merged
merged 7 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions jstz_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ tezos-smart-rollup-mock = "0.2.1"
tezos-smart-rollup-installer-config = "0.2.1"
serde_yaml = "0.8"
tezos_crypto_rs = "0.5.1"
sha2 = "0.9"
rand = "0.8"
tiny-bip39 = "1.0.0"

[[bin]]
name = "jstz"
Expand Down
34 changes: 34 additions & 0 deletions jstz_cli/src/account/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use anyhow::Result;
use jstz_crypto::{
keypair_from_passphrase, public_key::PublicKey, public_key_hash::PublicKeyHash,
secret_key::SecretKey,
};
use serde::{Deserialize, Serialize};

// Represents an individual account
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Account {
pub alias: String,
pub address: PublicKeyHash,
pub secret_key: SecretKey,
pub public_key: PublicKey,
}

impl Account {
pub fn from_passphrase(passphrase: String, alias: String) -> Result<Self> {
let (sk, pk) = keypair_from_passphrase(passphrase.as_str()).unwrap();

println!("Secret key: {}", sk.to_string());
println!("Public key: {}", pk.to_string());

let address = PublicKeyHash::try_from(&pk)?;
let new_account = Account {
alias,
address,
secret_key: sk,
public_key: pk,
};

Ok(new_account)
}
}
55 changes: 55 additions & 0 deletions jstz_cli/src/account/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use anyhow::Result;
use bip39::{Language, Mnemonic, MnemonicType};
use clap::Subcommand;

pub mod account;

use crate::config::Config;

fn generate_passphrase() -> String {
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
return mnemonic.to_string();
}

fn create_account(
passphrase: Option<String>,
alias: String,
cfg: &mut Config,
) -> Result<()> {
let passphrase = match passphrase {
Some(passphrase) => passphrase,
None => {
let passphrase = generate_passphrase();
println!("Generated passphrase: {}", passphrase);
passphrase
}
};

let account = account::Account::from_passphrase(passphrase, alias)?;

println!("Account created with address: {}", account.address);

cfg.accounts().upsert(account);
cfg.save()?;

Ok(())
}

#[derive(Subcommand)]
pub enum Command {
/// Creates account
Create {
/// User alias
#[arg(value_name = "ALIAS")]
alias: String,
/// User passphrase. If undefined, a random passphrase will be generated.
#[arg(short, long)]
passphrase: Option<String>,
},
}

pub fn exec(command: Command, cfg: &mut Config) -> Result<()> {
match command {
Command::Create { alias, passphrase } => create_account(passphrase, alias, cfg),
}
}
22 changes: 22 additions & 0 deletions jstz_cli/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
collections::HashMap,
fs,
io::{Error, ErrorKind},
path::PathBuf,
Expand All @@ -8,12 +9,26 @@ use std::{
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};

use crate::account::account::Account;

fn home() -> PathBuf {
dirs::home_dir()
.expect("Failed to get home directory")
.join(".jstz")
}

// Represents a collection of accounts
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct AccountConfig {
accounts: HashMap<String, Account>,
}

impl AccountConfig {
pub fn upsert(&mut self, account: Account) {
self.accounts.insert(account.alias.clone(), account);
}
}

#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Config {
/// Path to `jstz` directory
Expand All @@ -26,6 +41,8 @@ pub struct Config {
pub octez_node_rpc_port: u16,
/// Sandbox config (None if sandbox is not running)
pub sandbox: Option<SandboxConfig>,
/// List of accounts
pub accounts: AccountConfig,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -66,6 +83,7 @@ impl Config {
octez_node_port: 18731,
octez_node_rpc_port: 18730,
sandbox: None,
accounts: AccountConfig::default(),
}
}

Expand Down Expand Up @@ -106,4 +124,8 @@ impl Config {
.as_ref()
.ok_or(anyhow!("Sandbox is not running"))
}

pub fn accounts(&mut self) -> &mut AccountConfig {
&mut self.accounts
}
}
5 changes: 5 additions & 0 deletions jstz_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::Result;
use clap::Parser;

mod account;
mod bridge;
mod config;
mod deploy;
Expand All @@ -21,6 +22,9 @@ enum Command {
/// Commands related to the jstz bridge
#[command(subcommand)]
Bridge(bridge::Command),
/// Commands related to the account management
#[command(subcommand)]
Account(account::Command),
/// Deploys a smart function
Deploy {
/// Address used when deploying the contract
Expand Down Expand Up @@ -60,6 +64,7 @@ fn exec(command: Command, cfg: &mut Config) -> Result<()> {
match command {
Command::Sandbox(sandbox_command) => sandbox::exec(cfg, sandbox_command),
Command::Bridge(bridge_command) => bridge::exec(bridge_command, cfg),
Command::Account(account_command) => account::exec(account_command, cfg),
Command::Deploy {
self_address,
function_code,
Expand Down
1 change: 1 addition & 0 deletions jstz_crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ repository.workspace = true
tezos_crypto_rs = { version = "0.5.0", default-features = false }
derive_more = "0.99.17"
serde = "1.0.185"
anyhow = "1.0.75"
6 changes: 6 additions & 0 deletions jstz_crypto/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ impl AsRef<[u8]> for Blake2b {
&self.0
}
}

impl Blake2b {
pub fn as_array(&self) -> &[u8; 32] {
&self.0
}
}
13 changes: 13 additions & 0 deletions jstz_crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,17 @@ pub use error::{Error, Result};
pub mod hash;
pub mod public_key;
pub mod public_key_hash;
pub mod secret_key;
pub mod signature;

use tezos_crypto_rs::bls;

use crate::{hash::Blake2b, public_key::PublicKey, secret_key::SecretKey};

pub fn keypair_from_passphrase(passphrase: &str) -> Result<(SecretKey, PublicKey)> {
let ikm = Blake2b::from(passphrase.as_bytes());

let (sk, pk) = bls::keypair_from_ikm(*ikm.as_array())?;

Ok((SecretKey::Bls(sk), PublicKey::Bls(pk)))
}
2 changes: 1 addition & 1 deletion jstz_crypto/src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use tezos_crypto_rs::hash::PublicKeyBls;

use crate::error::Result;

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum PublicKey {
Bls(PublicKeyBls),
}
Expand Down
28 changes: 28 additions & 0 deletions jstz_crypto/src/secret_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
use tezos_crypto_rs::hash::SecretKeyBls;

use crate::error::Result;

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum SecretKey {
Bls(SecretKeyBls),
}

impl SecretKey {
pub fn to_base58(&self) -> String {
let SecretKey::Bls(pk) = self;
pk.to_base58_check()
}

pub fn from_base58(data: &str) -> Result<Self> {
let bls = SecretKeyBls::from_base58_check(data)?;

Ok(SecretKey::Bls(bls))
}
}

impl ToString for SecretKey {
fn to_string(&self) -> String {
self.to_base58()
}
}
Loading