Browse Source

Merge pull request #9 from DenioD/master

Change to 2 conf, build fixes, merge upstream updates
checkpoints
Duke Leto 5 years ago
committed by GitHub
parent
commit
18d711600d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 24
      docker/Dockerfile
  3. 67
      lib/src/commands.rs
  4. 2
      lib/src/lib.rs
  5. 45
      lib/src/lightclient.rs
  6. 239
      lib/src/lightwallet.rs
  7. 107
      lib/src/lightwallet/tests.rs
  8. 25
      mkrelease.sh

2
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 2 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)

24
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 && \

67
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<HashMap<String, Box<dyn Command>>> {
let mut map: HashMap<String, Box<dyn Command>> = 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)
}

2
lib/src/lib.rs

@ -11,7 +11,7 @@ pub mod commands;
#[folder = "zcash-params/"]
pub struct SaplingParams;
pub const ANCHOR_OFFSET: u32 = 4;
pub const ANCHOR_OFFSET: u32 = 2;
pub mod grpc_client {

45
lib/src/lightclient.rs

@ -75,6 +75,13 @@ impl LightClientConfig {
}
pub fn create(server: http::Uri, dangerous: bool) -> io::Result<(LightClientConfig, u64)> {
use std::net::ToSocketAddrs;
// Test for a connection first
format!("{}:{}", server.host().unwrap(), server.port_part().unwrap())
.to_socket_addrs()?
.next()
.ok_or(std::io::Error::new(ErrorKind::ConnectionRefused, "Couldn't resolve server!"))?;
// Do a getinfo first, before opening the wallet
let info = grpcconnector::get_info(server.clone(), dangerous)
.map_err(|e| std::io::Error::new(ErrorKind::ConnectionRefused, e))?;
@ -639,9 +646,18 @@ 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();
// Create a list of TransactionItems
// Create a list of TransactionItems from wallet txns
let mut tx_list = wallet.txs.read().unwrap().iter()
.flat_map(| (_k, v) | {
let mut txns: Vec<JsonValue> = vec![];
@ -709,6 +725,33 @@ impl LightClient {
})
.collect::<Vec<JsonValue>>();
// Add in all mempool txns
tx_list.extend(wallet.mempool_txs.read().unwrap().iter().map( |(_, wtx)| {
use zcash_primitives::transaction::components::amount::DEFAULT_FEE;
use std::convert::TryInto;
let amount: u64 = wtx.outgoing_metadata.iter().map(|om| om.value).sum::<u64>();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
// Collect outgoing metadata
let outgoing_json = wtx.outgoing_metadata.iter()
.map(|om|
object!{
"address" => om.address.clone(),
"value" => om.value,
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
}).collect::<Vec<JsonValue>>();
object! {
"block_height" => wtx.block,
"datetime" => wtx.datetime,
"txid" => format!("{}", wtx.txid),
"amount" => -1 * (fee + amount) as i64,
"unconfirmed" => true,
"outgoing_metadata" => outgoing_json,
}
}));
tx_list.sort_by( |a, b| if a["block_height"] == b["block_height"] {
a["txid"].as_str().cmp(&b["txid"].as_str())
} else {

239
lib/src/lightwallet.rs

@ -119,6 +119,10 @@ pub struct LightWallet {
blocks: Arc<RwLock<Vec<BlockData>>>,
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
// Transactions that are only in the mempool, but haven't been confirmed yet.
// This is not stored to disk.
pub mempool_txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
// The block at which this wallet was born. Rescans
// will start from here.
birthday: u64,
@ -199,20 +203,21 @@ impl LightWallet {
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
Ok(LightWallet {
encrypted: false,
unlocked: true,
enc_seed: [0u8; 48],
nonce: vec![],
seed: seed_bytes,
extsks: Arc::new(RwLock::new(vec![extsk])),
extfvks: Arc::new(RwLock::new(vec![extfvk])),
zaddress: Arc::new(RwLock::new(vec![address])),
tkeys: Arc::new(RwLock::new(vec![tpk])),
taddresses: Arc::new(RwLock::new(vec![taddr])),
blocks: Arc::new(RwLock::new(vec![])),
txs: Arc::new(RwLock::new(HashMap::new())),
config: config.clone(),
birthday: latest_block,
encrypted: false,
unlocked: true,
enc_seed: [0u8; 48],
nonce: vec![],
seed: seed_bytes,
extsks: Arc::new(RwLock::new(vec![extsk])),
extfvks: Arc::new(RwLock::new(vec![extfvk])),
zaddress: Arc::new(RwLock::new(vec![address])),
tkeys: Arc::new(RwLock::new(vec![tpk])),
taddresses: Arc::new(RwLock::new(vec![taddr])),
blocks: Arc::new(RwLock::new(vec![])),
txs: Arc::new(RwLock::new(HashMap::new())),
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
config: config.clone(),
birthday: latest_block,
})
}
@ -297,19 +302,20 @@ impl LightWallet {
let birthday = reader.read_u64::<LittleEndian>()?;
Ok(LightWallet{
encrypted: encrypted,
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
enc_seed: enc_seed,
nonce: nonce,
seed: seed_bytes,
extsks: Arc::new(RwLock::new(extsks)),
extfvks: Arc::new(RwLock::new(extfvks)),
zaddress: Arc::new(RwLock::new(addresses)),
tkeys: Arc::new(RwLock::new(tkeys)),
taddresses: Arc::new(RwLock::new(taddresses)),
blocks: Arc::new(RwLock::new(blocks)),
txs: Arc::new(RwLock::new(txs)),
config: config.clone(),
encrypted: encrypted,
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
enc_seed: enc_seed,
nonce: nonce,
seed: seed_bytes,
extsks: Arc::new(RwLock::new(extsks)),
extfvks: Arc::new(RwLock::new(extfvks)),
zaddress: Arc::new(RwLock::new(addresses)),
tkeys: Arc::new(RwLock::new(tkeys)),
taddresses: Arc::new(RwLock::new(taddresses)),
blocks: Arc::new(RwLock::new(blocks)),
txs: Arc::new(RwLock::new(txs)),
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
config: config.clone(),
birthday,
})
}
@ -470,6 +476,7 @@ impl LightWallet {
pub fn clear_blocks(&self) {
self.blocks.write().unwrap().clear();
self.txs.write().unwrap().clear();
self.mempool_txs.write().unwrap().clear();
}
pub fn set_initial_block(&self, height: i32, hash: &str, sapling_tree: &str) -> bool {
@ -763,8 +770,7 @@ impl LightWallet {
}
})
.map(|nd| if nd.spent.is_none() { nd.note.value } else { 0 })
.sum::<u64>()
.sum::<u64>()
})
.sum::<u64>() as u64
@ -796,7 +802,7 @@ impl LightWallet {
pub fn verified_zbalance(&self, addr: Option<String>) -> u64 {
let anchor_height = match self.get_target_height_and_anchor_offset() {
Some((height, anchor_offset)) => height - anchor_offset as u32 - 1,
Some((height, anchor_offset)) => height - anchor_offset as u32 ,
None => return 0,
};
@ -1174,62 +1180,68 @@ impl LightWallet {
.map(|block| block.tree.clone())
.unwrap_or(CommitmentTree::new()),
};
// These are filled in inside the block
let new_txs;
let nfs: Vec<_>;
{
// Create a write lock
let mut txs = self.txs.write().unwrap();
// Create a write lock that will last for the rest of the function.
let mut txs = self.txs.write().unwrap();
// Create a Vec containing all unspent nullifiers.
// Include only the confirmed spent nullifiers, since unconfirmed ones still need to be included
// during scan_block below.
let nfs: Vec<_> = txs
.iter()
.map(|(txid, tx)| {
let txid = *txid;
tx.notes.iter().filter_map(move |nd| {
if nd.spent.is_none() {
Some((nd.nullifier, nd.account, txid))
} else {
None
}
// Create a Vec containing all unspent nullifiers.
// Include only the confirmed spent nullifiers, since unconfirmed ones still need to be included
// during scan_block below.
nfs = txs
.iter()
.map(|(txid, tx)| {
let txid = *txid;
tx.notes.iter().filter_map(move |nd| {
if nd.spent.is_none() {
Some((nd.nullifier, nd.account, txid))
} else {
None
}
})
})
})
.flatten()
.collect();
.flatten()
.collect();
// Prepare the note witnesses for updating
for tx in txs.values_mut() {
for nd in tx.notes.iter_mut() {
// Duplicate the most recent witness
if let Some(witness) = nd.witnesses.last() {
let clone = witness.clone();
nd.witnesses.push(clone);
// Prepare the note witnesses for updating
for tx in txs.values_mut() {
for nd in tx.notes.iter_mut() {
// Duplicate the most recent witness
if let Some(witness) = nd.witnesses.last() {
let clone = witness.clone();
nd.witnesses.push(clone);
}
// Trim the oldest witnesses
nd.witnesses = nd
.witnesses
.split_off(nd.witnesses.len().saturating_sub(100));
}
// Trim the oldest witnesses
nd.witnesses = nd
.witnesses
.split_off(nd.witnesses.len().saturating_sub(100));
}
}
let new_txs = {
let nf_refs: Vec<_> = nfs.iter().map(|(nf, acc, _)| (&nf[..], *acc)).collect();
// Create a single mutable slice of all the newly-added witnesses.
let mut witness_refs: Vec<_> = txs
.values_mut()
.map(|tx| tx.notes.iter_mut().filter_map(|nd| nd.witnesses.last_mut()))
.flatten()
.collect();
scan_block(
block.clone(),
&self.extfvks.read().unwrap(),
&nf_refs[..],
&mut block_data.tree,
&mut witness_refs[..],
)
};
new_txs = {
let nf_refs: Vec<_> = nfs.iter().map(|(nf, acc, _)| (&nf[..], *acc)).collect();
// Create a single mutable slice of all the newly-added witnesses.
let mut witness_refs: Vec<_> = txs
.values_mut()
.map(|tx| tx.notes.iter_mut().filter_map(|nd| nd.witnesses.last_mut()))
.flatten()
.collect();
scan_block(
block.clone(),
&self.extfvks.read().unwrap(),
&nf_refs[..],
&mut block_data.tree,
&mut witness_refs[..],
)
};
}
// If this block had any new Txs, return the list of ALL txids in this block,
// so the wallet can fetch them all as a decoy.
let all_txs = if !new_txs.is_empty() {
@ -1243,6 +1255,9 @@ impl LightWallet {
};
for tx in new_txs {
// Create a write lock
let mut txs = self.txs.write().unwrap();
// Mark notes as spent.
let mut total_shielded_value_spent: u64 = 0;
@ -1305,6 +1320,11 @@ impl LightWallet {
}
}
{
// Cleanup mempool tx after adding a block, to remove all txns that got mined
self.cleanup_mempool();
}
// Print info about the block every 10,000 blocks
if height % 10_000 == 0 {
match self.get_sapling_tree() {
@ -1350,7 +1370,7 @@ impl LightWallet {
);
// Convert address (str) to RecepientAddress and value to Amount
let tos = tos.iter().map(|to| {
let recepients = tos.iter().map(|to| {
let ra = match address::RecipientAddress::from_str(to.0,
self.config.hrp_sapling_address(),
self.config.base58_pubkey_address(),
@ -1451,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
);
error!("{}", e);
return Err(e);
@ -1485,7 +1505,7 @@ impl LightWallet {
// TODO: We're using the first ovk to encrypt outgoing Txns. Is that Ok?
let ovk = self.extfvks.read().unwrap()[0].fvk.ovk;
for (to, value, memo) in tos {
for (to, value, memo) in recepients {
// Compute memo if it exists
let encoded_memo = memo.map(|s| Memo::from_str(&s).unwrap());
@ -1541,11 +1561,66 @@ impl LightWallet {
}
}
// Add this Tx to the mempool structure
{
let mut mempool_txs = self.mempool_txs.write().unwrap();
match mempool_txs.get_mut(&tx.txid()) {
None => {
// Collect the outgoing metadata
let outgoing_metadata = tos.iter().map(|(addr, amt, maybe_memo)| {
OutgoingTxMetadata {
address: addr.to_string(),
value: *amt,
memo: match maybe_memo {
None => Memo::default(),
Some(s) => Memo::from_str(&s).unwrap(),
},
}
}).collect::<Vec<_>>();
// Create a new WalletTx
let mut wtx = WalletTx::new(height as i32, now() as u64, &tx.txid());
wtx.outgoing_metadata = outgoing_metadata;
// Add it into the mempool
mempool_txs.insert(tx.txid(), wtx);
},
Some(_) => {
warn!("A newly created Tx was already in the mempool! How's that possible? Txid: {}", tx.txid());
}
}
}
// Return the encoded transaction, so the caller can send it.
let mut raw_tx = vec![];
tx.write(&mut raw_tx).unwrap();
Ok(raw_tx.into_boxed_slice())
}
// After some blocks have been mined, we need to remove the Txns from the mempool_tx structure
// if they :
// 1. Have expired
// 2. The Tx has been added to the wallet via a mined block
pub fn cleanup_mempool(&self) {
const DEFAULT_TX_EXPIRY_DELTA: i32 = 20;
let current_height = self.blocks.read().unwrap().last().map(|b| b.height).unwrap_or(0);
{
// Remove all expired Txns
self.mempool_txs.write().unwrap().retain( | _, wtx| {
current_height < (wtx.block + DEFAULT_TX_EXPIRY_DELTA)
});
}
{
// Remove all txns where the txid is added to the wallet directly
self.mempool_txs.write().unwrap().retain ( |txid, _| {
self.txs.read().unwrap().get(txid).is_none()
});
}
}
}
#[cfg(test)]

107
lib/src/lightwallet/tests.rs

@ -690,7 +690,7 @@ fn get_test_wallet(amount: u64) -> (LightWallet, TxId, BlockHash) {
}
#[test]
fn test_z_spend() {
fn test_z_spend_to_z() {
const AMOUNT1: u64 = 50000;
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT1);
@ -703,9 +703,15 @@ fn test_z_spend() {
let outgoing_memo = "Outgoing Memo".to_string();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
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();
@ -723,6 +729,25 @@ fn test_z_spend() {
assert_eq!(txs[&txid1].notes[0].unconfirmed_spent, Some(sent_txid));
}
// It should also be in the mempool structure
{
let mem = wallet.mempool_txs.read().unwrap();
assert_eq!(mem[&sent_txid].block, 2); // block number is next block
assert! (mem[&sent_txid].datetime > 0);
assert_eq!(mem[&sent_txid].txid, sent_txid);
assert_eq!(mem[&sent_txid].outgoing_metadata.len(), 1);
assert_eq!(mem[&sent_txid].outgoing_metadata[0].address, ext_address);
assert_eq!(mem[&sent_txid].outgoing_metadata[0].value, AMOUNT_SENT);
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();
@ -738,11 +763,18 @@ fn test_z_spend() {
// 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);
}
{
// And the mempool tx should disappear
let mem = wallet.mempool_txs.read().unwrap();
assert!(mem.get(&sent_txid).is_none());
}
// Now, full scan the Tx, which should populate the Outgoing Meta data
wallet.scan_full_tx(&sent_tx, 2, 0);
@ -771,7 +803,7 @@ fn test_multi_z() {
let outgoing_memo = "Outgoing Memo".to_string();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) =get_sapling_params().unwrap();
// Create a tx and send to address
@ -864,7 +896,7 @@ fn test_z_spend_to_taddr() {
const AMOUNT1: u64 = 50000;
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT1);
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
@ -959,7 +991,7 @@ fn test_t_spend_to_z() {
let outgoing_memo = "Outgoing Memo".to_string();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) =get_sapling_params().unwrap();
// Create a tx and send to address. This should consume both the UTXO and the note
@ -1033,7 +1065,7 @@ fn test_z_incoming_memo() {
let memo = "Incoming Memo".to_string();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
// Create a tx and send to address
@ -1072,7 +1104,7 @@ fn test_z_to_t_withinwallet() {
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
// Create a tx and send to address
@ -1132,7 +1164,7 @@ fn test_multi_t() {
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
// Create a Tx and send to the second t address
@ -1267,7 +1299,7 @@ fn test_multi_spends() {
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
let tos = vec![ (zaddr2.as_str(), ZAMOUNT2, Some(outgoing_memo2.clone())),
@ -1394,7 +1426,7 @@ fn test_bad_send() {
let (wallet, _txid1, _block_hash) = get_test_wallet(AMOUNT1);
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
let ext_taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
@ -1426,7 +1458,7 @@ fn test_bad_params() {
let (wallet, _, _) = get_test_wallet(100000);
let ext_taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
// Bad params
let _ = wallet.send_to_address(branch_id, &[], &[],
vec![(&ext_taddr, 10, None)]);
@ -1448,6 +1480,53 @@ fn add_blocks(wallet: &LightWallet, start: i32, num: i32, mut prev_hash: BlockHa
Ok(new_blk.hash())
}
#[test]
fn test_z_mempool_expiry() {
const AMOUNT1: u64 = 50000;
let (wallet, _, block_hash) = get_test_wallet(AMOUNT1);
let fvk = ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[1u8; 32]));
let ext_address = encode_payment_address(wallet.config.hrp_sapling_address(),
&fvk.default_address().unwrap().1);
const AMOUNT_SENT: u64 = 20;
let outgoing_memo = "Outgoing Memo".to_string();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
// 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();
let sent_tx = Transaction::read(&raw_tx[..]).unwrap();
let sent_txid = sent_tx.txid();
// It should also be in the mempool structure
{
let mem = wallet.mempool_txs.read().unwrap();
assert_eq!(mem[&sent_txid].block, 2); // block number is next block
assert! (mem[&sent_txid].datetime > 0);
assert_eq!(mem[&sent_txid].txid, sent_txid);
assert_eq!(mem[&sent_txid].outgoing_metadata.len(), 1);
assert_eq!(mem[&sent_txid].outgoing_metadata[0].address, ext_address);
assert_eq!(mem[&sent_txid].outgoing_metadata[0].value, AMOUNT_SENT);
assert_eq!(mem[&sent_txid].outgoing_metadata[0].memo.to_utf8().unwrap().unwrap(), outgoing_memo);
}
// Don't mine the Tx, but just add several blocks
add_blocks(&wallet, 2, 21, block_hash).unwrap();
// After 21 blocks, it should disappear (expiry is 20 blocks) since it was not mined
{
let mem = wallet.mempool_txs.read().unwrap();
assert!(mem.get(&sent_txid).is_none());
}
}
#[test]
fn test_block_limit() {
const AMOUNT: u64 = 500000;
@ -1529,7 +1608,7 @@ fn test_rollback() {
// Now do a Tx
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
// Create a tx and send to address
@ -1795,7 +1874,7 @@ fn test_encrypted_zreceive() {
let outgoing_memo = "Outgoing Memo".to_string();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
// Create a tx and send to address
@ -1901,7 +1980,7 @@ fn test_encrypted_treceive() {
let password: String = "password".to_string();
let (mut wallet, txid1, block_hash) = get_test_wallet(AMOUNT1);
let branch_id = u32::from_str_radix("2bb40e60", 16).unwrap();
let branch_id = u32::from_str_radix("76b809bb", 16).unwrap();
let (ss, so) = get_sapling_params().unwrap();
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());

25
mkrelease.sh

@ -43,7 +43,7 @@ mkdir -p target/macOS-silentdragonlite-cli-v$APP_VERSION
cp target/release/silentdragonlite-cli target/macOS-silentdragonlite-cli-v$APP_VERSION/
# For Windows and Linux, build via docker
docker run --rm -v $(pwd)/:/opt/silentdragonlite-light-cli rustbuild:latest bash -c "cd /opt/silentdragonlite-light-cli && cargo build --release && SODIUM_LIB_DIR='/opt/libsodium-win64/lib/' cargo build --release --target x86_64-pc-windows-gnu"
docker run --rm -v $(pwd)/:/opt/silentdragonlite-cli rustbuild:latest bash -c "cd /opt/silentdragonlite-cli && cargo build --release && cargo build --release --target armv7-unknown-linux-gnueabihf && cargo build --release --target aarch64-unknown-linux-gnu && SODIUM_LIB_DIR='/opt/libsodium-win64/lib/' cargo build --release --target x86_64-pc-windows-gnu"
# Now sign and zip the binaries
# macOS
@ -82,4 +82,27 @@ zip -r Windows-silentdragonlite-cli-v$APP_VERSION.zip Windows-silentdragonlite-c
cd ..
#Armv7
rm -rf target/Armv7-silentdragonlite-cli-v$APP_VERSION
mkdir -p target/Armv7-silentdragonlite-cli-v$APP_VERSION
cp target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli target/Armv7-silentdragonlite-cli-v$APP_VERSION/
gpg --batch --output target/Armv7-silentdragonlite-cli-v$APP_VERSION/silentdragonlite-cli.sig --detach-sig target/Armv7-silentdragonlite-cli-v$APP_VERSION/silentdragonlite-cli
cd target
cd Armv7-silentdragonlite-cli-v$APP_VERSION
gsha256sum silentdragonlite-cli > sha256sum.txt
cd ..
zip -r Armv7-silentdragonlite-cli-v$APP_VERSION.zip Armv7-silentdragonlite-cli-v$APP_VERSION
cd ..
#AARCH64
rm -rf target/aarch64-silentdragonlite-cli-v$APP_VERSION
mkdir -p target/aarch64-silentdragonlite-cli-v$APP_VERSION
cp target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli target/aarch64-silentdragonlite-cli-v$APP_VERSION/
gpg --batch --output target/aarch64-silentdragonlite-cli-v$APP_VERSION/silentdragonlite-cli.sig --detach-sig target/aarch64-silentdragonlite-cli-v$APP_VERSION/silentdragonlite-cli
cd target
cd aarch64-silentdragonlite-cli-v$APP_VERSION
gsha256sum silentdragonlite-cli > sha256sum.txt
cd ..
zip -r aarch64-silentdragonlite-cli-v$APP_VERSION.zip aarch64-silentdragonlite-cli-v$APP_VERSION
cd ..

Loading…
Cancel
Save