use clap::{Parser, Subcommand};
use colored::*;
use dark_crystal_web3_core::secret::Secret;
use dark_crystal_web3_core::{recover, share, RecoveredShare, SHARE_LENGTH};
use directories::BaseDirs;
use hex;
use qr_code;
use std::convert::TryInto;
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
#[clap(arg_required_else_help = true)]
Share {
#[clap(short, long)]
secret: String,
#[clap(short, long)]
pk: Vec<String>,
#[clap(short, long)]
lookup: String,
},
#[clap(arg_required_else_help = true)]
Combine { shares: Vec<String> },
Serve {
#[clap(long)]
port: Option<u16>,
#[clap(long)]
ui_path: Option<String>,
},
}
fn main() {
let args = Cli::parse();
match &args.command {
Commands::Share { secret, pk, lookup } => {
let mut public_keys: Vec<[u8; 32]> = Vec::new();
for pk in pk.iter() {
public_keys.push(hex::decode(&pk).unwrap().try_into().unwrap());
}
let secret = Secret::detect_from_string(secret.to_string())
.to_bytes()
.unwrap();
let lookup_key = &lookup.as_bytes().to_vec();
match share(public_keys, secret.to_vec(), lookup_key.to_vec()) {
Ok(shares) => {
println!("\n{}", shares.green());
let qr_code = qr_code::QrCode::new(shares.as_bytes()).unwrap();
println!("\n\n{}", qr_code.to_string(true, 3));
}
Err(err) => error(format!("{:?}", err)),
}
}
Commands::Combine { shares } => {
let mut recovered_shares: Vec<RecoveredShare> = Vec::new();
for share in shares.iter() {
if share.len() < SHARE_LENGTH * 2 {
return error("Share too short".to_string());
}
match hex::decode(&share) {
Ok(share_bytes) => {
recovered_shares.push(RecoveredShare::from_concat(&share_bytes))
}
Err(_) => return error("Cannot decode hex share".to_string()),
};
}
match recover(recovered_shares) {
Ok(secret_raw) => match Secret::from_bytes(secret_raw) {
Ok(secret) => {
let secret_string = match secret {
Secret::Raw(bytes) => hex::encode(bytes),
Secret::Utf8(string) => string,
Secret::Bip39(string) => string,
};
println!("\n{}", secret_string.green())
}
Err(_) => error("Cannot decode secret".to_string()),
},
Err(recovery_error) => error(recovery_error.message),
}
}
Commands::Serve { port, ui_path } => {
let path_option = match ui_path {
Some(given_path) => Some(PathBuf::from(given_path)),
None => match BaseDirs::new() {
None => None,
Some(base_dirs) => Some(base_dirs.data_local_dir().join("dark-crystal-web3")),
},
};
if path_option.is_none() {
error("Cannot get local data directory.".to_string());
std::process::exit(1);
}
let path = path_option.unwrap();
match std::fs::metadata(&path) {
Ok(ui_path) => match ui_path.is_dir() {
true => {
let server = match port {
Some(given_port) => file_serve::ServerBuilder::new(&path)
.port(*given_port)
.build(),
None => file_serve::Server::new(&path),
};
println!("Serving {}", path.display());
println!("http://{}", server.addr());
println!("Hit CTRL-C to stop");
server.serve().unwrap();
}
_ => {
error(format!("Cannot find web ui at {}", path.display()));
std::process::exit(1);
}
},
Err(_) => {
error(format!("Cannot find web ui at {}", path.display()));
std::process::exit(1);
}
}
}
}
}
fn error(message: String) {
println!("{}", message.red());
}