diff --git a/README.md b/README.md index 80d8e55..0e87dfc 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Run `silentdragonlite-cli help` to see a list of all commands. ### Note Management silentdragonlite does automatic note and utxo management, which means it doesn't allow you to manually select which address to send outgoing transactions from. It follows these principles: * Defaults to sending shielded transactions, even if you're sending to a transparent address -* Sapling funds need at least 4 confirmations before they can be spent +* Sapling funds need at least 5 confirmations before they can be spent * Can select funds from multiple shielded addresses in the same transaction * Will automatically shield your transparent funds at the first opportunity * When sending an outgoing transaction to a shielded address, silentdragonlite can decide to use the transaction to additionally shield your transparent funds (i.e., send your transparent funds to your own shielded address in the same transaction) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5a88d0f..e782017 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,35 +1,41 @@ -FROM rust:1.38 +FROM ubuntu:16.04 LABEL Description="Rust compile env for Linux + Windows (cross)" RUN apt update -RUN apt install -y build-essential mingw-w64 gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf +RUN apt install -y build-essential mingw-w64 gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf curl vim wget + +# Get Rust +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" RUN rustup target add x86_64-pc-windows-gnu RUN rustup target add aarch64-unknown-linux-gnu RUN rustup target add armv7-unknown-linux-gnueabihf # Append the linker to the cargo config for Windows cross compile -RUN echo "[target.x86_64-pc-windows-gnu]" >> /usr/local/cargo/config && \ - echo "linker = '/usr/bin/x86_64-w64-mingw32-gcc'" >> /usr/local/cargo/config +RUN echo "[target.x86_64-pc-windows-gnu]" >> /root/.cargo/config && \ + echo "linker = '/usr/bin/x86_64-w64-mingw32-gcc'" >> /root/.cargo/config -RUN echo "[target.aarch64-unknown-linux-gnu]" >> /usr/local/cargo/config && \ - echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /usr/local/cargo/config +RUN echo "[target.aarch64-unknown-linux-gnu]" >> /root/.cargo/config && \ + echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /root/.cargo/config -RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /usr/local/cargo/config && \ - echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /usr/local/cargo/config +RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /root/.cargo/config && \ + echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /root/.cargo/config ENV CC_x86_64_unknown_linux_musl="gcc" ENV CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc" ENV CC_armv7_unknown_linux_gnueabhihf="arm-linux-gnueabihf-gcc" # This is a bug fix for the windows cross compiler for Rust. -RUN cp /usr/x86_64-w64-mingw32/lib/crt2.o /usr/local/rustup/toolchains/1.38.0-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o +RUN cp /usr/x86_64-w64-mingw32/lib/crt2.o /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o # For windows cross compilation, use a pre-build binary. Remember to set the # SODIUM_LIB_DIR for windows cross compilation RUN cd /opt && wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.17-mingw.tar.gz && \ tar xvf libsodium-1.0.17-mingw.tar.gz +RUN apt install -y git + # Cargo fetch the dependencies so we don't download them over and over again RUN cd /tmp && git clone https://github.com/adityapk00/silentdragonlite-light-cli.git && \ cd silentdragonlite-light-cli && \ diff --git a/lib/src/commands.rs b/lib/src/commands.rs index 997e118..a2d74a8 100644 --- a/lib/src/commands.rs +++ b/lib/src/commands.rs @@ -35,6 +35,26 @@ impl Command for SyncCommand { } } +struct EncryptionStatusCommand {} +impl Command for EncryptionStatusCommand { + fn help(&self) -> String { + let mut h = vec![]; + h.push("Check if the wallet is encrypted and if it is locked"); + h.push("Usage:"); + h.push("encryptionstatus"); + h.push(""); + + h.join("\n") + } + + fn short_help(&self) -> String { + "Check if the wallet is encrypted and if it is locked".to_string() + } + + fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + lightclient.do_encryption_status().pretty(2) + } +} struct SyncStatusCommand {} impl Command for SyncStatusCommand { @@ -440,9 +460,7 @@ impl Command for SendCommand { // Check for a single argument that can be parsed as JSON let send_args = if args.len() == 1 { - // Sometimes on the command line, people use "'" for the quotes, which json::parse doesn't - // understand. So replace it with double-quotes - let arg_list = args[0].replace("'", "\""); + let arg_list = args[0]; let json_args = match json::parse(&arg_list) { Ok(j) => j, @@ -731,27 +749,28 @@ impl Command for QuitCommand { pub fn get_commands() -> Box>> { let mut map: HashMap> = HashMap::new(); - map.insert("sync".to_string(), Box::new(SyncCommand{})); - map.insert("syncstatus".to_string(), Box::new(SyncStatusCommand{})); - map.insert("rescan".to_string(), Box::new(RescanCommand{})); - map.insert("help".to_string(), Box::new(HelpCommand{})); - map.insert("balance".to_string(), Box::new(BalanceCommand{})); - map.insert("addresses".to_string(), Box::new(AddressCommand{})); - map.insert("height".to_string(), Box::new(HeightCommand{})); - map.insert("export".to_string(), Box::new(ExportCommand{})); - map.insert("info".to_string(), Box::new(InfoCommand{})); - map.insert("send".to_string(), Box::new(SendCommand{})); - map.insert("save".to_string(), Box::new(SaveCommand{})); - map.insert("quit".to_string(), Box::new(QuitCommand{})); - map.insert("list".to_string(), Box::new(TransactionsCommand{})); - map.insert("notes".to_string(), Box::new(NotesCommand{})); - map.insert("new".to_string(), Box::new(NewAddressCommand{})); - map.insert("seed".to_string(), Box::new(SeedCommand{})); - map.insert("encrypt".to_string(), Box::new(EncryptCommand{})); - map.insert("decrypt".to_string(), Box::new(DecryptCommand{})); - map.insert("unlock".to_string(), Box::new(UnlockCommand{})); - map.insert("lock".to_string(), Box::new(LockCommand{})); - map.insert("fixbip39bug".to_string(), Box::new(FixBip39BugCommand{})); + map.insert("sync".to_string(), Box::new(SyncCommand{})); + map.insert("syncstatus".to_string(), Box::new(SyncStatusCommand{})); + map.insert("encryptionstatus".to_string(), Box::new(EncryptionStatusCommand{})); + map.insert("rescan".to_string(), Box::new(RescanCommand{})); + map.insert("help".to_string(), Box::new(HelpCommand{})); + map.insert("balance".to_string(), Box::new(BalanceCommand{})); + map.insert("addresses".to_string(), Box::new(AddressCommand{})); + map.insert("height".to_string(), Box::new(HeightCommand{})); + map.insert("export".to_string(), Box::new(ExportCommand{})); + map.insert("info".to_string(), Box::new(InfoCommand{})); + map.insert("send".to_string(), Box::new(SendCommand{})); + map.insert("save".to_string(), Box::new(SaveCommand{})); + map.insert("quit".to_string(), Box::new(QuitCommand{})); + map.insert("list".to_string(), Box::new(TransactionsCommand{})); + map.insert("notes".to_string(), Box::new(NotesCommand{})); + map.insert("new".to_string(), Box::new(NewAddressCommand{})); + map.insert("seed".to_string(), Box::new(SeedCommand{})); + map.insert("encrypt".to_string(), Box::new(EncryptCommand{})); + map.insert("decrypt".to_string(), Box::new(DecryptCommand{})); + map.insert("unlock".to_string(), Box::new(UnlockCommand{})); + map.insert("lock".to_string(), Box::new(LockCommand{})); + map.insert("fixbip39bug".to_string(), Box::new(FixBip39BugCommand{})); Box::new(map) } diff --git a/lib/src/lightclient.rs b/lib/src/lightclient.rs index 3e1fcc0..89f4cae 100644 --- a/lib/src/lightclient.rs +++ b/lib/src/lightclient.rs @@ -639,6 +639,14 @@ impl LightClient { res } + pub fn do_encryption_status(&self) -> JsonValue { + let wallet = self.wallet.read().unwrap(); + object!{ + "encrypted" => wallet.is_encrypted(), + "locked" => !wallet.is_unlocked_for_spending() + } + } + pub fn do_list_transactions(&self) -> JsonValue { let wallet = self.wallet.read().unwrap(); diff --git a/lib/src/lightwallet.rs b/lib/src/lightwallet.rs index 059501e..5bd8862 100644 --- a/lib/src/lightwallet.rs +++ b/lib/src/lightwallet.rs @@ -769,7 +769,7 @@ impl LightWallet { None => true } }) - .map(|nd| if nd.spent.is_none() && nd.unconfirmed_spent.is_none() { nd.note.value } else { 0 }) + .map(|nd| if nd.spent.is_none() { nd.note.value } else { 0 }) .sum::() }) .sum::() as u64 @@ -1471,7 +1471,7 @@ impl LightWallet { if selected_value < u64::from(target_value) { let e = format!( "Insufficient verified funds (have {}, need {:?}). NOTE: funds need {} confirmations before they can be spent.", - selected_value, target_value, self.config.anchor_offset + selected_value, target_value, self.config.anchor_offset + 1 ); error!("{}", e); return Err(e); diff --git a/lib/src/lightwallet/tests.rs b/lib/src/lightwallet/tests.rs index 96ac79f..2b733cd 100644 --- a/lib/src/lightwallet/tests.rs +++ b/lib/src/lightwallet/tests.rs @@ -706,6 +706,12 @@ fn test_z_spend_to_z() { let branch_id = u32::from_str_radix("76b809bb", 16).unwrap(); let (ss, so) = get_sapling_params().unwrap(); + // Make sure that the balance exists + { + assert_eq!(wallet.zbalance(None), AMOUNT1); + assert_eq!(wallet.verified_zbalance(None), AMOUNT1); + } + // Create a tx and send to address let raw_tx = wallet.send_to_address(branch_id, &ss, &so, vec![(&ext_address, AMOUNT_SENT, Some(outgoing_memo.clone()))]).unwrap(); @@ -736,6 +742,12 @@ fn test_z_spend_to_z() { assert_eq!(mem[&sent_txid].outgoing_metadata[0].memo.to_utf8().unwrap().unwrap(), outgoing_memo); } + { + // The wallet should deduct this from the verified balance. The zbalance still includes it + assert_eq!(wallet.zbalance(None), AMOUNT1); + assert_eq!(wallet.verified_zbalance(None), 0); + } + let mut cb3 = FakeCompactBlock::new(2, block_hash); cb3.add_tx(&sent_tx); wallet.scan_block(&cb3.as_bytes()).unwrap(); @@ -751,6 +763,7 @@ fn test_z_spend_to_z() { // The sent tx should generate change assert_eq!(txs[&sent_txid].notes.len(), 1); assert_eq!(txs[&sent_txid].notes[0].note.value, AMOUNT1 - AMOUNT_SENT - fee); + assert_eq!(wallet.zbalance(None), AMOUNT1 - AMOUNT_SENT - fee); assert_eq!(txs[&sent_txid].notes[0].is_change, true); assert_eq!(txs[&sent_txid].notes[0].spent, None); assert_eq!(txs[&sent_txid].notes[0].unconfirmed_spent, None);