Commands return responses

This commit is contained in:
Aditya Kulkarni
2019-09-15 16:29:20 -07:00
parent d21c87acef
commit 059db8cd1c
3 changed files with 69 additions and 60 deletions

View File

@@ -7,7 +7,7 @@ pub trait Command {
fn short_help(&self) -> String; fn short_help(&self) -> String;
fn exec(&self, _args: &[&str], lightclient: &LightClient); fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String;
} }
struct SyncCommand {} struct SyncCommand {}
@@ -20,8 +20,8 @@ impl Command for SyncCommand {
"Download CompactBlocks and sync to the server".to_string() "Download CompactBlocks and sync to the server".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
lightclient.do_sync(); lightclient.do_sync()
} }
} }
@@ -35,8 +35,8 @@ impl Command for RescanCommand {
"Rescan the wallet, downloading and scanning all blocks and transactions".to_string() "Rescan the wallet, downloading and scanning all blocks and transactions".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
lightclient.do_rescan(); lightclient.do_rescan()
} }
} }
@@ -51,12 +51,15 @@ impl Command for HelpCommand {
"Lists all available commands".to_string() "Lists all available commands".to_string()
} }
fn exec(&self, _args: &[&str], _: &LightClient) { fn exec(&self, _args: &[&str], _: &LightClient) -> String {
let mut responses = vec![];
// Print a list of all commands // Print a list of all commands
println!("Available commands:"); responses.push(format!("Available commands:"));
get_commands().iter().for_each(| (cmd, obj) | { get_commands().iter().for_each(| (cmd, obj) | {
println!("{} - {}", cmd, obj.short_help()); responses.push(format!("{} - {}", cmd, obj.short_help()));
}); });
responses.join("\n")
} }
} }
@@ -70,8 +73,8 @@ impl Command for InfoCommand {
"Get the lightwalletd server's info".to_string() "Get the lightwalletd server's info".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
lightclient.do_info(); lightclient.do_info()
} }
} }
@@ -85,9 +88,9 @@ impl Command for AddressCommand {
"List all current addresses".to_string() "List all current addresses".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
let res = lightclient.do_address(); let res = lightclient.do_address();
println!("{}", res.pretty(2)); format!("{}", res.pretty(2))
} }
} }
@@ -103,26 +106,24 @@ impl Command for SendCommand {
"Send ZEC to the given address".to_string() "Send ZEC to the given address".to_string()
} }
fn exec(&self, args: &[&str], lightclient: &LightClient) { fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
// Parse the args. // Parse the args.
// 1 - Destination address. T or Z address // 1 - Destination address. T or Z address
if args.len() < 2 || args.len() > 3 { if args.len() < 2 || args.len() > 3 {
self.help(); return self.short_help();
return;
} }
// Make sure we can parse the amount // Make sure we can parse the amount
let value = match args[1].parse::<u64>() { let value = match args[1].parse::<u64>() {
Ok(amt) => amt, Ok(amt) => amt,
Err(e) => { Err(e) => {
println!("Couldn't parse amount: {}", e); return format!("Couldn't parse amount: {}", e);;
return;
} }
}; };
let memo = if args.len() == 3 { Some(args[2].to_string()) } else {None}; let memo = if args.len() == 3 { Some(args[2].to_string()) } else {None};
lightclient.do_send(args[0], value, memo); lightclient.do_send(args[0], value, memo)
} }
} }
@@ -136,8 +137,8 @@ impl Command for SaveCommand {
"Save wallet file to disk".to_string() "Save wallet file to disk".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
lightclient.do_save(); lightclient.do_save()
} }
} }
@@ -151,13 +152,8 @@ impl Command for SeedCommand {
"Display the seed phrase".to_string() "Display the seed phrase".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
let phrase = lightclient.do_seed_phrase(); lightclient.do_seed_phrase()
println!("PLEASE SAVE YOUR SEED PHRASE CAREFULLY AND DO NOT SHARE IT");
println!();
println!("{}", phrase);
println!();
} }
} }
@@ -171,9 +167,9 @@ impl Command for TransactionsCommand {
"List all transactions in the wallet".to_string() "List all transactions in the wallet".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
let txns = lightclient.do_list_transactions(); let txns = lightclient.do_list_transactions();
println!("{}", txns.pretty(2)); format!("{}", txns.pretty(2))
} }
} }
@@ -188,26 +184,24 @@ impl Command for NotesCommand {
"List all sapling notes in the wallet".to_string() "List all sapling notes in the wallet".to_string()
} }
fn exec(&self, args: &[&str], lightclient: &LightClient) { fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
// Parse the args. // Parse the args.
if args.len() > 1 { if args.len() > 1 {
self.help(); return self.short_help();
return;
} }
// Make sure we can parse the amount // Make sure we can parse the amount
let all_notes = if args.len() == 1 { let all_notes = if args.len() == 1 {
match args[0] { match args[0] {
"all" => true, "all" => true,
_ => { println!("Invalid argument. Specify 'all' to include unspent notes"); _ => return "Invalid argument. Specify 'all' to include unspent notes".to_string()
return; }
} }
} else { } else {
false false
}; };
let txns = lightclient.do_list_notes(all_notes); let txns = lightclient.do_list_notes(all_notes);
println!("{}", txns.pretty(2)); format!("{}", txns.pretty(2))
} }
} }
@@ -222,8 +216,8 @@ impl Command for QuitCommand {
"Quit the lightwallet, saving state to disk".to_string() "Quit the lightwallet, saving state to disk".to_string()
} }
fn exec(&self, _args: &[&str], lightclient: &LightClient) { fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
lightclient.do_save(); lightclient.do_save()
} }
} }
@@ -248,11 +242,9 @@ pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
Box::new(map) Box::new(map)
} }
pub fn do_user_command(cmd: &str, args: &Vec<&str>, lightclient: &LightClient) { pub fn do_user_command(cmd: &str, args: &Vec<&str>, lightclient: &LightClient) -> String {
match get_commands().get(cmd) { match get_commands().get(cmd) {
Some(cmd) => cmd.exec(args, lightclient), Some(cmd) => cmd.exec(args, lightclient),
None => { None => format!("Unknown command : {}. Type 'help' for a list of commands", cmd)
println!("Unknown command : {}. Type 'help' for a list of commands", cmd);
}
} }
} }

View File

@@ -121,28 +121,32 @@ impl LightClient {
} }
} }
pub fn do_save(&self) { pub fn do_save(&self) -> String {
print!("Saving wallet...");
io::stdout().flush().ok().expect("Could not flush stdout"); io::stdout().flush().ok().expect("Could not flush stdout");
let mut file_buffer = BufWriter::with_capacity( let mut file_buffer = BufWriter::with_capacity(
1_000_000, // 1 MB write buffer 1_000_000, // 1 MB write buffer
File::create("wallet.dat").unwrap()); File::create("wallet.dat").unwrap());
self.wallet.write(&mut file_buffer).unwrap(); self.wallet.write(&mut file_buffer).unwrap();
println!("[OK]"); format!("Saved Wallet")
} }
pub fn do_info(&self) { pub fn do_info(&self) -> String {
let uri: http::Uri = format!("http://127.0.0.1:9067").parse().unwrap(); use std::cell::RefCell;
let uri: http::Uri = format!("http://127.0.0.1:9067").parse().unwrap();
let infostr = Arc::new(RefCell::<String>::default());
let infostrinner = infostr.clone();
let say_hello = self.make_grpc_client(uri).unwrap() let say_hello = self.make_grpc_client(uri).unwrap()
.and_then(move |mut client| { .and_then(move |mut client| {
client.get_lightd_info(Request::new(Empty{})) client.get_lightd_info(Request::new(Empty{}))
}) })
.and_then(move |response| { .and_then(move |response| {
//let tx = Transaction::read(&response.into_inner().data[..]).unwrap(); //let tx = Transaction::read(&response.into_inner().data[..]).unwrap();
println!("{:?}", response.into_inner()); //println!("{:?}", response.into_inner());
infostrinner.replace(format!("{:?}", response.into_inner()));
Ok(()) Ok(())
}) })
@@ -151,6 +155,9 @@ impl LightClient {
}); });
tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap(); tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap();
let ans = infostr.borrow().clone();
ans
} }
pub fn do_seed_phrase(&self) -> String { pub fn do_seed_phrase(&self) -> String {
@@ -341,7 +348,7 @@ impl LightClient {
JsonValue::Array(tx_list) JsonValue::Array(tx_list)
} }
pub fn do_rescan(&self) { pub fn do_rescan(&self) -> String {
// First, clear the state from the wallet // First, clear the state from the wallet
self.wallet.clear_blocks(); self.wallet.clear_blocks();
@@ -349,10 +356,10 @@ impl LightClient {
self.set_wallet_initial_state(); self.set_wallet_initial_state();
// Then, do a sync, which will force a full rescan from the initial state // Then, do a sync, which will force a full rescan from the initial state
self.do_sync(); self.do_sync()
} }
pub fn do_sync(&self) { pub fn do_sync(&self) -> String {
// Sync is 3 parts // Sync is 3 parts
// 1. Get the latest block // 1. Get the latest block
// 2. Get all the blocks that we don't have // 2. Get all the blocks that we don't have
@@ -410,8 +417,8 @@ impl LightClient {
} }
} }
println!("Synced to {}, Downloaded {} kB \r", let mut responses = vec![];
last_block, bytes_downloaded.load(Ordering::SeqCst) / 1024); responses.push(format!("Synced to {}, Downloaded {} kB", last_block, bytes_downloaded.load(Ordering::SeqCst) / 1024));
// Get the Raw transaction for all the wallet transactions // Get the Raw transaction for all the wallet transactions
@@ -448,7 +455,7 @@ impl LightClient {
// read the memos // read the memos
for (txid, height) in txids_to_fetch { for (txid, height) in txids_to_fetch {
let light_wallet_clone = self.wallet.clone(); let light_wallet_clone = self.wallet.clone();
println!("Fetching full Tx: {}", txid); responses.push(format!("Fetching full Tx: {}", txid));
self.fetch_full_tx(txid, move |tx_bytes: &[u8] | { self.fetch_full_tx(txid, move |tx_bytes: &[u8] | {
let tx = Transaction::read(tx_bytes).unwrap(); let tx = Transaction::read(tx_bytes).unwrap();
@@ -470,9 +477,11 @@ impl LightClient {
wallet.add_utxo(&utxo); wallet.add_utxo(&utxo);
}); });
}); });
responses.join("\n")
} }
pub fn do_send(&self, addr: &str, value: u64, memo: Option<String>) { pub fn do_send(&self, addr: &str, value: u64, memo: Option<String>) -> String {
let rawtx = self.wallet.send_to_address( let rawtx = self.wallet.send_to_address(
u32::from_str_radix("2bb40e60", 16).unwrap(), // Blossom ID u32::from_str_radix("2bb40e60", 16).unwrap(), // Blossom ID
&self.sapling_spend, &self.sapling_output, &self.sapling_spend, &self.sapling_output,
@@ -481,8 +490,8 @@ impl LightClient {
match rawtx { match rawtx {
Some(txbytes) => self.broadcast_raw_tx(txbytes), Some(txbytes) => self.broadcast_raw_tx(txbytes),
None => eprintln!("No Tx to broadcast") None => format!("No Tx to broadcast")
}; }
} }
pub fn fetch_blocks<F : 'static + std::marker::Send>(&self, start_height: u64, end_height: u64, c: F) pub fn fetch_blocks<F : 'static + std::marker::Send>(&self, start_height: u64, end_height: u64, c: F)
@@ -671,15 +680,20 @@ impl LightClient {
tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap(); tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap();
} }
pub fn broadcast_raw_tx(&self, tx_bytes: Box<[u8]>) { pub fn broadcast_raw_tx(&self, tx_bytes: Box<[u8]>) -> String {
use std::cell::RefCell;
let uri: http::Uri = format!("http://127.0.0.1:9067").parse().unwrap(); let uri: http::Uri = format!("http://127.0.0.1:9067").parse().unwrap();
let infostr = Arc::new(RefCell::<String>::default());
let infostrinner = infostr.clone();
let say_hello = self.make_grpc_client(uri).unwrap() let say_hello = self.make_grpc_client(uri).unwrap()
.and_then(move |mut client| { .and_then(move |mut client| {
client.send_transaction(Request::new(RawTransaction {data: tx_bytes.to_vec(), height: 0})) client.send_transaction(Request::new(RawTransaction {data: tx_bytes.to_vec(), height: 0}))
}) })
.and_then(move |response| { .and_then(move |response| {
println!("{:?}", response.into_inner()); infostrinner.replace(format!("{:?}", response.into_inner()));
Ok(()) Ok(())
}) })
.map_err(|e| { .map_err(|e| {
@@ -687,6 +701,9 @@ impl LightClient {
}); });
tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap(); tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap();
let ans = infostr.borrow().clone();
ans
} }
pub fn fetch_latest_block<F : 'static + std::marker::Send>(&self, mut c : F) pub fn fetch_latest_block<F : 'static + std::marker::Send>(&self, mut c : F)

View File

@@ -48,8 +48,8 @@ pub fn main() {
match command_rx.recv() { match command_rx.recv() {
Ok((cmd, args)) => { Ok((cmd, args)) => {
let args = args.iter().map(|s| s.as_ref()).collect(); let args = args.iter().map(|s| s.as_ref()).collect();
commands::do_user_command(&cmd, &args, &lc); let cmd_response = commands::do_user_command(&cmd, &args, &lc);
resp_tx.send("Finished command".to_string()).unwrap(); resp_tx.send(cmd_response).unwrap();
if cmd == "quit" { if cmd == "quit" {
break; break;