diff --git a/rust-lightclient/src/commands.rs b/rust-lightclient/src/commands.rs index 3b7ecd4..3bb7637 100644 --- a/rust-lightclient/src/commands.rs +++ b/rust-lightclient/src/commands.rs @@ -7,7 +7,7 @@ pub trait Command { fn short_help(&self) -> String; - fn exec(&self, _args: &[&str], lightclient: &LightClient); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String; } struct SyncCommand {} @@ -20,8 +20,8 @@ impl Command for SyncCommand { "Download CompactBlocks and sync to the server".to_string() } - fn exec(&self, _args: &[&str], lightclient: &LightClient) { - lightclient.do_sync(); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_sync() } } @@ -35,8 +35,8 @@ impl Command for RescanCommand { "Rescan the wallet, downloading and scanning all blocks and transactions".to_string() } - fn exec(&self, _args: &[&str], lightclient: &LightClient) { - lightclient.do_rescan(); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_rescan() } } @@ -51,12 +51,15 @@ impl Command for HelpCommand { "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 - println!("Available commands:"); + responses.push(format!("Available commands:")); 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() } - fn exec(&self, _args: &[&str], lightclient: &LightClient) { - lightclient.do_info(); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_info() } } @@ -85,9 +88,9 @@ impl Command for AddressCommand { "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(); - println!("{}", res.pretty(2)); + format!("{}", res.pretty(2)) } } @@ -103,26 +106,24 @@ impl Command for SendCommand { "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. // 1 - Destination address. T or Z address if args.len() < 2 || args.len() > 3 { - self.help(); - return; + return self.short_help(); } // Make sure we can parse the amount let value = match args[1].parse::() { Ok(amt) => amt, Err(e) => { - println!("Couldn't parse amount: {}", e); - return; + return format!("Couldn't parse amount: {}", e);; } }; 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() } - fn exec(&self, _args: &[&str], lightclient: &LightClient) { - lightclient.do_save(); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_save() } } @@ -151,13 +152,8 @@ impl Command for SeedCommand { "Display the seed phrase".to_string() } - fn exec(&self, _args: &[&str], lightclient: &LightClient) { - let phrase = lightclient.do_seed_phrase(); - - println!("PLEASE SAVE YOUR SEED PHRASE CAREFULLY AND DO NOT SHARE IT"); - println!(); - println!("{}", phrase); - println!(); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_seed_phrase() } } @@ -171,9 +167,9 @@ impl Command for TransactionsCommand { "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(); - 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() } - fn exec(&self, args: &[&str], lightclient: &LightClient) { + fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { // Parse the args. if args.len() > 1 { - self.help(); - return; + return self.short_help(); } // Make sure we can parse the amount let all_notes = if args.len() == 1 { match args[0] { "all" => true, - _ => { println!("Invalid argument. Specify 'all' to include unspent notes"); - return; } + _ => return "Invalid argument. Specify 'all' to include unspent notes".to_string() } } else { false }; 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() } - fn exec(&self, _args: &[&str], lightclient: &LightClient) { - lightclient.do_save(); + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_save() } } @@ -248,11 +242,9 @@ pub fn get_commands() -> Box>> { 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) { Some(cmd) => cmd.exec(args, lightclient), - None => { - println!("Unknown command : {}. Type 'help' for a list of commands", cmd); - } + None => format!("Unknown command : {}. Type 'help' for a list of commands", cmd) } } diff --git a/rust-lightclient/src/lightclient.rs b/rust-lightclient/src/lightclient.rs index f618b69..9334775 100644 --- a/rust-lightclient/src/lightclient.rs +++ b/rust-lightclient/src/lightclient.rs @@ -121,28 +121,32 @@ impl LightClient { } } - pub fn do_save(&self) { - print!("Saving wallet..."); + pub fn do_save(&self) -> String { io::stdout().flush().ok().expect("Could not flush stdout"); let mut file_buffer = BufWriter::with_capacity( 1_000_000, // 1 MB write buffer File::create("wallet.dat").unwrap()); self.wallet.write(&mut file_buffer).unwrap(); - println!("[OK]"); + format!("Saved Wallet") } - pub fn do_info(&self) { - let uri: http::Uri = format!("http://127.0.0.1:9067").parse().unwrap(); + pub fn do_info(&self) -> String { + use std::cell::RefCell; + let uri: http::Uri = format!("http://127.0.0.1:9067").parse().unwrap(); + let infostr = Arc::new(RefCell::::default()); + + let infostrinner = infostr.clone(); let say_hello = self.make_grpc_client(uri).unwrap() .and_then(move |mut client| { client.get_lightd_info(Request::new(Empty{})) }) .and_then(move |response| { //let tx = Transaction::read(&response.into_inner().data[..]).unwrap(); - println!("{:?}", response.into_inner()); + //println!("{:?}", response.into_inner()); + infostrinner.replace(format!("{:?}", response.into_inner())); Ok(()) }) @@ -151,6 +155,9 @@ impl LightClient { }); 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 { @@ -341,7 +348,7 @@ impl LightClient { JsonValue::Array(tx_list) } - pub fn do_rescan(&self) { + pub fn do_rescan(&self) -> String { // First, clear the state from the wallet self.wallet.clear_blocks(); @@ -349,10 +356,10 @@ impl LightClient { self.set_wallet_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 // 1. Get the latest block // 2. Get all the blocks that we don't have @@ -410,8 +417,8 @@ impl LightClient { } } - println!("Synced to {}, Downloaded {} kB \r", - last_block, bytes_downloaded.load(Ordering::SeqCst) / 1024); + let mut responses = vec![]; + responses.push(format!("Synced to {}, Downloaded {} kB", last_block, bytes_downloaded.load(Ordering::SeqCst) / 1024)); // Get the Raw transaction for all the wallet transactions @@ -448,7 +455,7 @@ impl LightClient { // read the memos for (txid, height) in txids_to_fetch { 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] | { let tx = Transaction::read(tx_bytes).unwrap(); @@ -470,9 +477,11 @@ impl LightClient { wallet.add_utxo(&utxo); }); }); + + responses.join("\n") } - pub fn do_send(&self, addr: &str, value: u64, memo: Option) { + pub fn do_send(&self, addr: &str, value: u64, memo: Option) -> String { let rawtx = self.wallet.send_to_address( u32::from_str_radix("2bb40e60", 16).unwrap(), // Blossom ID &self.sapling_spend, &self.sapling_output, @@ -481,8 +490,8 @@ impl LightClient { match rawtx { Some(txbytes) => self.broadcast_raw_tx(txbytes), - None => eprintln!("No Tx to broadcast") - }; + None => format!("No Tx to broadcast") + } } pub fn fetch_blocks(&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(); } - 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 infostr = Arc::new(RefCell::::default()); + let infostrinner = infostr.clone(); + let say_hello = self.make_grpc_client(uri).unwrap() .and_then(move |mut client| { client.send_transaction(Request::new(RawTransaction {data: tx_bytes.to_vec(), height: 0})) }) .and_then(move |response| { - println!("{:?}", response.into_inner()); + infostrinner.replace(format!("{:?}", response.into_inner())); Ok(()) }) .map_err(|e| { @@ -687,6 +701,9 @@ impl LightClient { }); tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap(); + + let ans = infostr.borrow().clone(); + ans } pub fn fetch_latest_block(&self, mut c : F) diff --git a/rust-lightclient/src/main.rs b/rust-lightclient/src/main.rs index ac5a13c..5f6be16 100644 --- a/rust-lightclient/src/main.rs +++ b/rust-lightclient/src/main.rs @@ -48,8 +48,8 @@ pub fn main() { match command_rx.recv() { Ok((cmd, args)) => { let args = args.iter().map(|s| s.as_ref()).collect(); - commands::do_user_command(&cmd, &args, &lc); - resp_tx.send("Finished command".to_string()).unwrap(); + let cmd_response = commands::do_user_command(&cmd, &args, &lc); + resp_tx.send(cmd_response).unwrap(); if cmd == "quit" { break;