Skip to content

Commit

Permalink
Merge branch 'rc/v0.53' into unified-blackbox-rust-testing
Browse files Browse the repository at this point in the history
  • Loading branch information
BiancaIalangi committed Aug 20, 2024
2 parents a8f1377 + 8d5a21e commit 5c115d3
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 6 deletions.
20 changes: 14 additions & 6 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions framework/meta/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ zip = { version = "2.1", features = ["deflate"], default-features = false }
copy_dir = "0.1.2"
pathdiff = "0.2.1"
common-path = "1.0.0"
bip39 = "2.0.0"

[dependencies.multiversx-sc-meta-lib]
version = "=0.52.3"
Expand Down
61 changes: 61 additions & 0 deletions framework/meta/src/cli/cli_args_standalone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ pub enum StandaloneCliAction {
about = "Generates a report on the local depedencies of contract crates. Will explore indirect depdencies too."
)]
LocalDeps(LocalDepsArgs),

#[command(
name = "wallet",
about = "Generates a new wallet or performs actions on an existing wallet."
)]
Wallet(WalletArgs),
}

#[derive(Default, Clone, PartialEq, Eq, Debug, Args)]
Expand Down Expand Up @@ -411,3 +417,58 @@ pub struct AccountArgs {
#[arg(long = "address", verbatim_doc_comment)]
pub address: String,
}

#[derive(Clone, PartialEq, Eq, Debug, Subcommand)]
pub enum WalletAction {
#[command(name = "new", about = "Creates a new wallet")]
New(WalletNewArgs),

#[command(
name = "bech32",
about = "Encodes/decodes a bech32 address to/from hex"
)]
Bech32(WalletBech32Args),
#[command(name = "convert", about = "Converts a wallet")]
Convert(WalletConvertArgs),
}

#[derive(Clone, PartialEq, Eq, Debug, Parser)]
#[command(propagate_version = true)]
pub struct WalletArgs {
#[command(subcommand)]
pub command: WalletAction,
}

#[derive(Default, Clone, PartialEq, Eq, Debug, Args)]
pub struct WalletNewArgs {
/// The type of wallet to create.
#[arg(long = "format", verbatim_doc_comment)]
pub wallet_format: Option<String>,

/// The name of the wallet to create.
#[arg(long = "outfile", verbatim_doc_comment)]
pub outfile: Option<String>,
}

#[derive(Default, Clone, PartialEq, Eq, Debug, Args)]
pub struct WalletConvertArgs {
#[arg(long = "in-format", verbatim_doc_comment)]
pub from: String,

#[arg(long = "out-format", verbatim_doc_comment)]
pub to: String,

#[arg(long = "infile", verbatim_doc_comment)]
pub infile: Option<String>,

#[arg(long = "outfile", verbatim_doc_comment)]
pub outfile: Option<String>,
}

#[derive(Default, Clone, PartialEq, Eq, Debug, Args)]
pub struct WalletBech32Args {
#[arg(long = "encode", verbatim_doc_comment)]
pub hex_address: Option<String>,
#[arg(long = "decode", verbatim_doc_comment)]
pub bech32_address: Option<String>,
}
4 changes: 4 additions & 0 deletions framework/meta/src/cli/cli_standalone_main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::cli::{StandaloneCliAction, StandaloneCliArgs};
use crate::cmd::retrieve_address::retrieve_address;
use crate::cmd::wallet::wallet;
use clap::Parser;

use crate::cmd::all::call_all_meta;
Expand Down Expand Up @@ -46,6 +47,9 @@ pub async fn cli_main_standalone() {
Some(StandaloneCliAction::LocalDeps(args)) => {
local_deps(args);
},
Some(StandaloneCliAction::Wallet(args)) => {
wallet(args);
},
None => {},
}
}
1 change: 1 addition & 0 deletions framework/meta/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pub mod template;
pub mod test;
pub mod test_coverage;
pub mod upgrade;
pub mod wallet;
163 changes: 163 additions & 0 deletions framework/meta/src/cmd/wallet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use core::str;
use multiversx_sc::types;
use std::{
fs::{self, File},
io::{self, Read, Write},
};

use crate::cli::{WalletAction, WalletArgs, WalletBech32Args, WalletConvertArgs, WalletNewArgs};
use bip39::Mnemonic;
use multiversx_sc_snippets::sdk::{
crypto::public_key::PublicKey, data::address::Address, utils::base64_encode, wallet::Wallet,
};
use multiversx_sc_snippets::{hex, imports::Bech32Address};
pub fn wallet(args: &WalletArgs) {
let command = &args.command;
match command {
WalletAction::New(new_args) => new(new_args),
WalletAction::Bech32(bech32_args) => bech32_conversion(bech32_args),
WalletAction::Convert(convert_args) => convert(convert_args),
}
}

fn convert(convert_args: &WalletConvertArgs) {
let infile = convert_args.infile.as_ref();
let outfile = convert_args.outfile.as_ref();
let in_format = &convert_args.from;
let out_format = &convert_args.to;

let mut mnemonic_str = String::new();

match (in_format.as_str(), out_format.as_str()) {
("mnemonic", "pem") => match infile {
Some(file) => {
mnemonic_str = fs::read_to_string(file).unwrap();
mnemonic_str = mnemonic_str.replace('\n', "");
},
None => {
println!("Insert text below. Press 'Ctrl-D' (Linux / MacOS) or 'Ctrl-Z' (Windows) when done.");
_ = io::stdin().read_to_string(&mut mnemonic_str).unwrap()
},
},
_ => {
println!("Unsupported conversion");
},
}

let mnemonic = Mnemonic::parse(mnemonic_str).unwrap();

let (private_key_str, public_key_str) = get_wallet_keys(mnemonic);
let address = get_wallet_address(private_key_str.as_str());

match outfile {
Some(outfile) => {
generate_pem(
&address,
private_key_str.as_str(),
public_key_str.as_str(),
outfile,
);
},
None => {
let pem_content =
generate_pem_content(&address, private_key_str.as_str(), public_key_str.as_str());
print!("{}", pem_content);
},
}
}

fn bech32_conversion(bech32_args: &WalletBech32Args) {
let encode_address = bech32_args.hex_address.as_ref();
let decode_address = bech32_args.bech32_address.as_ref();

match (encode_address, decode_address) {
(Some(hex), None) => {
let bytes_from_hex = hex::decode(hex).unwrap();
let bytes_arr: [u8; 32] = bytes_from_hex.try_into().unwrap();

let addr = types::Address::from(&bytes_arr);
let bech32_addr = Bech32Address::from(addr).to_bech32_str().to_string();
println!("{}", bech32_addr);
},
(None, Some(bech32)) => {
let hex_addr = Bech32Address::from_bech32_string(bech32.to_string()).to_hex();
println!("{}", hex_addr);
},
(Some(_), Some(_)) => {
println!("error: only one of --encode or --decode can be used in the same command");
},
_ => {},
}
}

fn get_wallet_keys(mnemonic: Mnemonic) -> (String, String) {
let private_key = Wallet::get_private_key_from_mnemonic(mnemonic, 0u32, 0u32);
let public_key = PublicKey::from(&private_key);

let public_key_str: &str = &public_key.to_string();
let private_key_str: &str = &private_key.to_string();

(private_key_str.to_string(), public_key_str.to_string())
}

fn get_wallet_address(private_key: &str) -> Address {
let wallet = Wallet::from_private_key(private_key).unwrap();
wallet.address()
}

fn new(new_args: &WalletNewArgs) {
let format = new_args.wallet_format.as_ref();
let outfile = new_args.outfile.as_ref();
let mnemonic = Wallet::generate_mnemonic();
println!("Mnemonic: {}", mnemonic);

let (private_key_str, public_key_str) = get_wallet_keys(mnemonic);
let address = get_wallet_address(private_key_str.as_str());

println!("Wallet address: {}", address);

if let Some(f) = format {
match (f.as_str(), outfile) {
("pem", Some(file)) => {
generate_pem(
&address,
private_key_str.as_str(),
public_key_str.as_str(),
file,
);
},
("pem", None) => {
println!("Output file is required for PEM format");
},
_ => {},
}
}
}

fn generate_pem(address: &Address, private_key: &str, public_key: &str, outfile: &String) {
let pem_content = generate_pem_content(address, private_key, public_key);
let mut file = File::create(outfile).unwrap();
file.write_all(pem_content.as_bytes()).unwrap()
}

fn generate_pem_content(address: &Address, private_key: &str, public_key: &str) -> String {
let concat_keys = format!("{}{}", private_key, public_key);
let concat_keys_b64 = base64_encode(concat_keys);

// Split the base64 string into 64-character lines
let formatted_key = concat_keys_b64
.as_bytes()
.chunks(64)
.map(|chunk| std::str::from_utf8(chunk).unwrap())
.collect::<Vec<&str>>()
.join("\n");

let pem_content = format!(
"-----BEGIN PRIVATE KEY for {}-----\n{}\n-----END PRIVATE KEY for {}-----\n",
address.to_bech32_string().unwrap(),
formatted_key,
address.to_bech32_string().unwrap()
);

pem_content
}

0 comments on commit 5c115d3

Please sign in to comment.