Merge pull request #9 from DenioD/master
Change to 2 conf, build fixes, merge upstream updates
This commit is contained in:
@@ -21,7 +21,7 @@ Run `silentdragonlite-cli help` to see a list of all commands.
|
|||||||
### Note Management
|
### 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:
|
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
|
* 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
|
* Can select funds from multiple shielded addresses in the same transaction
|
||||||
* Will automatically shield your transparent funds at the first opportunity
|
* 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)
|
* 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)
|
||||||
|
|||||||
@@ -1,35 +1,41 @@
|
|||||||
FROM rust:1.38
|
FROM ubuntu:16.04
|
||||||
LABEL Description="Rust compile env for Linux + Windows (cross)"
|
LABEL Description="Rust compile env for Linux + Windows (cross)"
|
||||||
|
|
||||||
RUN apt update
|
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 x86_64-pc-windows-gnu
|
||||||
RUN rustup target add aarch64-unknown-linux-gnu
|
RUN rustup target add aarch64-unknown-linux-gnu
|
||||||
RUN rustup target add armv7-unknown-linux-gnueabihf
|
RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||||
|
|
||||||
# Append the linker to the cargo config for Windows cross compile
|
# Append the linker to the cargo config for Windows cross compile
|
||||||
RUN echo "[target.x86_64-pc-windows-gnu]" >> /usr/local/cargo/config && \
|
RUN echo "[target.x86_64-pc-windows-gnu]" >> /root/.cargo/config && \
|
||||||
echo "linker = '/usr/bin/x86_64-w64-mingw32-gcc'" >> /usr/local/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 && \
|
RUN echo "[target.aarch64-unknown-linux-gnu]" >> /root/.cargo/config && \
|
||||||
echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /usr/local/cargo/config
|
echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /root/.cargo/config
|
||||||
|
|
||||||
RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /usr/local/cargo/config && \
|
RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /root/.cargo/config && \
|
||||||
echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /usr/local/cargo/config
|
echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /root/.cargo/config
|
||||||
|
|
||||||
ENV CC_x86_64_unknown_linux_musl="gcc"
|
ENV CC_x86_64_unknown_linux_musl="gcc"
|
||||||
ENV CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc"
|
ENV CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc"
|
||||||
ENV CC_armv7_unknown_linux_gnueabhihf="arm-linux-gnueabihf-gcc"
|
ENV CC_armv7_unknown_linux_gnueabhihf="arm-linux-gnueabihf-gcc"
|
||||||
|
|
||||||
# This is a bug fix for the windows cross compiler for Rust.
|
# 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
|
# For windows cross compilation, use a pre-build binary. Remember to set the
|
||||||
# SODIUM_LIB_DIR for windows cross compilation
|
# SODIUM_LIB_DIR for windows cross compilation
|
||||||
RUN cd /opt && wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.17-mingw.tar.gz && \
|
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
|
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
|
# 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 && \
|
RUN cd /tmp && git clone https://github.com/adityapk00/silentdragonlite-light-cli.git && \
|
||||||
cd silentdragonlite-light-cli && \
|
cd silentdragonlite-light-cli && \
|
||||||
|
|||||||
@@ -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 {}
|
struct SyncStatusCommand {}
|
||||||
impl Command for SyncStatusCommand {
|
impl Command for SyncStatusCommand {
|
||||||
@@ -440,9 +460,7 @@ impl Command for SendCommand {
|
|||||||
|
|
||||||
// Check for a single argument that can be parsed as JSON
|
// Check for a single argument that can be parsed as JSON
|
||||||
let send_args = if args.len() == 1 {
|
let send_args = if args.len() == 1 {
|
||||||
// Sometimes on the command line, people use "'" for the quotes, which json::parse doesn't
|
let arg_list = args[0];
|
||||||
// understand. So replace it with double-quotes
|
|
||||||
let arg_list = args[0].replace("'", "\"");
|
|
||||||
|
|
||||||
let json_args = match json::parse(&arg_list) {
|
let json_args = match json::parse(&arg_list) {
|
||||||
Ok(j) => j,
|
Ok(j) => j,
|
||||||
@@ -731,27 +749,28 @@ impl Command for QuitCommand {
|
|||||||
pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
|
pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
|
||||||
let mut map: HashMap<String, Box<dyn Command>> = HashMap::new();
|
let mut map: HashMap<String, Box<dyn Command>> = HashMap::new();
|
||||||
|
|
||||||
map.insert("sync".to_string(), Box::new(SyncCommand{}));
|
map.insert("sync".to_string(), Box::new(SyncCommand{}));
|
||||||
map.insert("syncstatus".to_string(), Box::new(SyncStatusCommand{}));
|
map.insert("syncstatus".to_string(), Box::new(SyncStatusCommand{}));
|
||||||
map.insert("rescan".to_string(), Box::new(RescanCommand{}));
|
map.insert("encryptionstatus".to_string(), Box::new(EncryptionStatusCommand{}));
|
||||||
map.insert("help".to_string(), Box::new(HelpCommand{}));
|
map.insert("rescan".to_string(), Box::new(RescanCommand{}));
|
||||||
map.insert("balance".to_string(), Box::new(BalanceCommand{}));
|
map.insert("help".to_string(), Box::new(HelpCommand{}));
|
||||||
map.insert("addresses".to_string(), Box::new(AddressCommand{}));
|
map.insert("balance".to_string(), Box::new(BalanceCommand{}));
|
||||||
map.insert("height".to_string(), Box::new(HeightCommand{}));
|
map.insert("addresses".to_string(), Box::new(AddressCommand{}));
|
||||||
map.insert("export".to_string(), Box::new(ExportCommand{}));
|
map.insert("height".to_string(), Box::new(HeightCommand{}));
|
||||||
map.insert("info".to_string(), Box::new(InfoCommand{}));
|
map.insert("export".to_string(), Box::new(ExportCommand{}));
|
||||||
map.insert("send".to_string(), Box::new(SendCommand{}));
|
map.insert("info".to_string(), Box::new(InfoCommand{}));
|
||||||
map.insert("save".to_string(), Box::new(SaveCommand{}));
|
map.insert("send".to_string(), Box::new(SendCommand{}));
|
||||||
map.insert("quit".to_string(), Box::new(QuitCommand{}));
|
map.insert("save".to_string(), Box::new(SaveCommand{}));
|
||||||
map.insert("list".to_string(), Box::new(TransactionsCommand{}));
|
map.insert("quit".to_string(), Box::new(QuitCommand{}));
|
||||||
map.insert("notes".to_string(), Box::new(NotesCommand{}));
|
map.insert("list".to_string(), Box::new(TransactionsCommand{}));
|
||||||
map.insert("new".to_string(), Box::new(NewAddressCommand{}));
|
map.insert("notes".to_string(), Box::new(NotesCommand{}));
|
||||||
map.insert("seed".to_string(), Box::new(SeedCommand{}));
|
map.insert("new".to_string(), Box::new(NewAddressCommand{}));
|
||||||
map.insert("encrypt".to_string(), Box::new(EncryptCommand{}));
|
map.insert("seed".to_string(), Box::new(SeedCommand{}));
|
||||||
map.insert("decrypt".to_string(), Box::new(DecryptCommand{}));
|
map.insert("encrypt".to_string(), Box::new(EncryptCommand{}));
|
||||||
map.insert("unlock".to_string(), Box::new(UnlockCommand{}));
|
map.insert("decrypt".to_string(), Box::new(DecryptCommand{}));
|
||||||
map.insert("lock".to_string(), Box::new(LockCommand{}));
|
map.insert("unlock".to_string(), Box::new(UnlockCommand{}));
|
||||||
map.insert("fixbip39bug".to_string(), Box::new(FixBip39BugCommand{}));
|
map.insert("lock".to_string(), Box::new(LockCommand{}));
|
||||||
|
map.insert("fixbip39bug".to_string(), Box::new(FixBip39BugCommand{}));
|
||||||
|
|
||||||
Box::new(map)
|
Box::new(map)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub mod commands;
|
|||||||
#[folder = "zcash-params/"]
|
#[folder = "zcash-params/"]
|
||||||
pub struct SaplingParams;
|
pub struct SaplingParams;
|
||||||
|
|
||||||
pub const ANCHOR_OFFSET: u32 = 4;
|
pub const ANCHOR_OFFSET: u32 = 2;
|
||||||
|
|
||||||
|
|
||||||
pub mod grpc_client {
|
pub mod grpc_client {
|
||||||
|
|||||||
@@ -75,6 +75,13 @@ impl LightClientConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(server: http::Uri, dangerous: bool) -> io::Result<(LightClientConfig, u64)> {
|
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
|
// Do a getinfo first, before opening the wallet
|
||||||
let info = grpcconnector::get_info(server.clone(), dangerous)
|
let info = grpcconnector::get_info(server.clone(), dangerous)
|
||||||
.map_err(|e| std::io::Error::new(ErrorKind::ConnectionRefused, e))?;
|
.map_err(|e| std::io::Error::new(ErrorKind::ConnectionRefused, e))?;
|
||||||
@@ -639,9 +646,18 @@ impl LightClient {
|
|||||||
res
|
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 {
|
pub fn do_list_transactions(&self) -> JsonValue {
|
||||||
let wallet = self.wallet.read().unwrap();
|
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()
|
let mut tx_list = wallet.txs.read().unwrap().iter()
|
||||||
.flat_map(| (_k, v) | {
|
.flat_map(| (_k, v) | {
|
||||||
let mut txns: Vec<JsonValue> = vec![];
|
let mut txns: Vec<JsonValue> = vec![];
|
||||||
@@ -709,6 +725,33 @@ impl LightClient {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<JsonValue>>();
|
.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"] {
|
tx_list.sort_by( |a, b| if a["block_height"] == b["block_height"] {
|
||||||
a["txid"].as_str().cmp(&b["txid"].as_str())
|
a["txid"].as_str().cmp(&b["txid"].as_str())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ pub struct LightWallet {
|
|||||||
blocks: Arc<RwLock<Vec<BlockData>>>,
|
blocks: Arc<RwLock<Vec<BlockData>>>,
|
||||||
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
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
|
// The block at which this wallet was born. Rescans
|
||||||
// will start from here.
|
// will start from here.
|
||||||
birthday: u64,
|
birthday: u64,
|
||||||
@@ -199,20 +203,21 @@ impl LightWallet {
|
|||||||
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||||
|
|
||||||
Ok(LightWallet {
|
Ok(LightWallet {
|
||||||
encrypted: false,
|
encrypted: false,
|
||||||
unlocked: true,
|
unlocked: true,
|
||||||
enc_seed: [0u8; 48],
|
enc_seed: [0u8; 48],
|
||||||
nonce: vec![],
|
nonce: vec![],
|
||||||
seed: seed_bytes,
|
seed: seed_bytes,
|
||||||
extsks: Arc::new(RwLock::new(vec![extsk])),
|
extsks: Arc::new(RwLock::new(vec![extsk])),
|
||||||
extfvks: Arc::new(RwLock::new(vec![extfvk])),
|
extfvks: Arc::new(RwLock::new(vec![extfvk])),
|
||||||
zaddress: Arc::new(RwLock::new(vec![address])),
|
zaddress: Arc::new(RwLock::new(vec![address])),
|
||||||
tkeys: Arc::new(RwLock::new(vec![tpk])),
|
tkeys: Arc::new(RwLock::new(vec![tpk])),
|
||||||
taddresses: Arc::new(RwLock::new(vec![taddr])),
|
taddresses: Arc::new(RwLock::new(vec![taddr])),
|
||||||
blocks: Arc::new(RwLock::new(vec![])),
|
blocks: Arc::new(RwLock::new(vec![])),
|
||||||
txs: Arc::new(RwLock::new(HashMap::new())),
|
txs: Arc::new(RwLock::new(HashMap::new())),
|
||||||
config: config.clone(),
|
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
||||||
birthday: latest_block,
|
config: config.clone(),
|
||||||
|
birthday: latest_block,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,19 +302,20 @@ impl LightWallet {
|
|||||||
let birthday = reader.read_u64::<LittleEndian>()?;
|
let birthday = reader.read_u64::<LittleEndian>()?;
|
||||||
|
|
||||||
Ok(LightWallet{
|
Ok(LightWallet{
|
||||||
encrypted: encrypted,
|
encrypted: encrypted,
|
||||||
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
|
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
|
||||||
enc_seed: enc_seed,
|
enc_seed: enc_seed,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
seed: seed_bytes,
|
seed: seed_bytes,
|
||||||
extsks: Arc::new(RwLock::new(extsks)),
|
extsks: Arc::new(RwLock::new(extsks)),
|
||||||
extfvks: Arc::new(RwLock::new(extfvks)),
|
extfvks: Arc::new(RwLock::new(extfvks)),
|
||||||
zaddress: Arc::new(RwLock::new(addresses)),
|
zaddress: Arc::new(RwLock::new(addresses)),
|
||||||
tkeys: Arc::new(RwLock::new(tkeys)),
|
tkeys: Arc::new(RwLock::new(tkeys)),
|
||||||
taddresses: Arc::new(RwLock::new(taddresses)),
|
taddresses: Arc::new(RwLock::new(taddresses)),
|
||||||
blocks: Arc::new(RwLock::new(blocks)),
|
blocks: Arc::new(RwLock::new(blocks)),
|
||||||
txs: Arc::new(RwLock::new(txs)),
|
txs: Arc::new(RwLock::new(txs)),
|
||||||
config: config.clone(),
|
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
config: config.clone(),
|
||||||
birthday,
|
birthday,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -470,6 +476,7 @@ impl LightWallet {
|
|||||||
pub fn clear_blocks(&self) {
|
pub fn clear_blocks(&self) {
|
||||||
self.blocks.write().unwrap().clear();
|
self.blocks.write().unwrap().clear();
|
||||||
self.txs.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 {
|
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 })
|
.map(|nd| if nd.spent.is_none() { nd.note.value } else { 0 })
|
||||||
.sum::<u64>()
|
.sum::<u64>()
|
||||||
|
|
||||||
})
|
})
|
||||||
.sum::<u64>() as u64
|
.sum::<u64>() as u64
|
||||||
|
|
||||||
@@ -796,7 +802,7 @@ impl LightWallet {
|
|||||||
|
|
||||||
pub fn verified_zbalance(&self, addr: Option<String>) -> u64 {
|
pub fn verified_zbalance(&self, addr: Option<String>) -> u64 {
|
||||||
let anchor_height = match self.get_target_height_and_anchor_offset() {
|
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,
|
None => return 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1175,60 +1181,66 @@ impl LightWallet {
|
|||||||
.unwrap_or(CommitmentTree::new()),
|
.unwrap_or(CommitmentTree::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a write lock that will last for the rest of the function.
|
// These are filled in inside the block
|
||||||
let mut txs = self.txs.write().unwrap();
|
let new_txs;
|
||||||
|
let nfs: Vec<_>;
|
||||||
|
{
|
||||||
|
// Create a write lock
|
||||||
|
let mut txs = self.txs.write().unwrap();
|
||||||
|
|
||||||
// Create a Vec containing all unspent nullifiers.
|
// Create a Vec containing all unspent nullifiers.
|
||||||
// Include only the confirmed spent nullifiers, since unconfirmed ones still need to be included
|
// Include only the confirmed spent nullifiers, since unconfirmed ones still need to be included
|
||||||
// during scan_block below.
|
// during scan_block below.
|
||||||
let nfs: Vec<_> = txs
|
nfs = txs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(txid, tx)| {
|
.map(|(txid, tx)| {
|
||||||
let txid = *txid;
|
let txid = *txid;
|
||||||
tx.notes.iter().filter_map(move |nd| {
|
tx.notes.iter().filter_map(move |nd| {
|
||||||
if nd.spent.is_none() {
|
if nd.spent.is_none() {
|
||||||
Some((nd.nullifier, nd.account, txid))
|
Some((nd.nullifier, nd.account, txid))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
// 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()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
scan_block(
|
// Prepare the note witnesses for updating
|
||||||
block.clone(),
|
for tx in txs.values_mut() {
|
||||||
&self.extfvks.read().unwrap(),
|
for nd in tx.notes.iter_mut() {
|
||||||
&nf_refs[..],
|
// Duplicate the most recent witness
|
||||||
&mut block_data.tree,
|
if let Some(witness) = nd.witnesses.last() {
|
||||||
&mut witness_refs[..],
|
let clone = witness.clone();
|
||||||
)
|
nd.witnesses.push(clone);
|
||||||
};
|
}
|
||||||
|
// Trim the oldest witnesses
|
||||||
|
nd.witnesses = nd
|
||||||
|
.witnesses
|
||||||
|
.split_off(nd.witnesses.len().saturating_sub(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
// 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.
|
// so the wallet can fetch them all as a decoy.
|
||||||
@@ -1243,6 +1255,9 @@ impl LightWallet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for tx in new_txs {
|
for tx in new_txs {
|
||||||
|
// Create a write lock
|
||||||
|
let mut txs = self.txs.write().unwrap();
|
||||||
|
|
||||||
// Mark notes as spent.
|
// Mark notes as spent.
|
||||||
let mut total_shielded_value_spent: u64 = 0;
|
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
|
// Print info about the block every 10,000 blocks
|
||||||
if height % 10_000 == 0 {
|
if height % 10_000 == 0 {
|
||||||
match self.get_sapling_tree() {
|
match self.get_sapling_tree() {
|
||||||
@@ -1350,7 +1370,7 @@ impl LightWallet {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Convert address (str) to RecepientAddress and value to Amount
|
// 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,
|
let ra = match address::RecipientAddress::from_str(to.0,
|
||||||
self.config.hrp_sapling_address(),
|
self.config.hrp_sapling_address(),
|
||||||
self.config.base58_pubkey_address(),
|
self.config.base58_pubkey_address(),
|
||||||
@@ -1485,7 +1505,7 @@ impl LightWallet {
|
|||||||
// TODO: We're using the first ovk to encrypt outgoing Txns. Is that Ok?
|
// TODO: We're using the first ovk to encrypt outgoing Txns. Is that Ok?
|
||||||
let ovk = self.extfvks.read().unwrap()[0].fvk.ovk;
|
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
|
// Compute memo if it exists
|
||||||
let encoded_memo = memo.map(|s| Memo::from_str(&s).unwrap());
|
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.
|
// Return the encoded transaction, so the caller can send it.
|
||||||
let mut raw_tx = vec![];
|
let mut raw_tx = vec![];
|
||||||
tx.write(&mut raw_tx).unwrap();
|
tx.write(&mut raw_tx).unwrap();
|
||||||
Ok(raw_tx.into_boxed_slice())
|
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)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -690,7 +690,7 @@ fn get_test_wallet(amount: u64) -> (LightWallet, TxId, BlockHash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_z_spend() {
|
fn test_z_spend_to_z() {
|
||||||
const AMOUNT1: u64 = 50000;
|
const AMOUNT1: u64 = 50000;
|
||||||
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT1);
|
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 outgoing_memo = "Outgoing Memo".to_string();
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
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 (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
|
// Create a tx and send to address
|
||||||
let raw_tx = wallet.send_to_address(branch_id, &ss, &so,
|
let raw_tx = wallet.send_to_address(branch_id, &ss, &so,
|
||||||
vec![(&ext_address, AMOUNT_SENT, Some(outgoing_memo.clone()))]).unwrap();
|
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));
|
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);
|
let mut cb3 = FakeCompactBlock::new(2, block_hash);
|
||||||
cb3.add_tx(&sent_tx);
|
cb3.add_tx(&sent_tx);
|
||||||
wallet.scan_block(&cb3.as_bytes()).unwrap();
|
wallet.scan_block(&cb3.as_bytes()).unwrap();
|
||||||
@@ -738,11 +763,18 @@ fn test_z_spend() {
|
|||||||
// The sent tx should generate change
|
// The sent tx should generate change
|
||||||
assert_eq!(txs[&sent_txid].notes.len(), 1);
|
assert_eq!(txs[&sent_txid].notes.len(), 1);
|
||||||
assert_eq!(txs[&sent_txid].notes[0].note.value, AMOUNT1 - AMOUNT_SENT - fee);
|
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].is_change, true);
|
||||||
assert_eq!(txs[&sent_txid].notes[0].spent, None);
|
assert_eq!(txs[&sent_txid].notes[0].spent, None);
|
||||||
assert_eq!(txs[&sent_txid].notes[0].unconfirmed_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
|
// Now, full scan the Tx, which should populate the Outgoing Meta data
|
||||||
wallet.scan_full_tx(&sent_tx, 2, 0);
|
wallet.scan_full_tx(&sent_tx, 2, 0);
|
||||||
|
|
||||||
@@ -771,7 +803,7 @@ fn test_multi_z() {
|
|||||||
let outgoing_memo = "Outgoing Memo".to_string();
|
let outgoing_memo = "Outgoing Memo".to_string();
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
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 (ss, so) =get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a tx and send to address
|
// Create a tx and send to address
|
||||||
@@ -864,7 +896,7 @@ fn test_z_spend_to_taddr() {
|
|||||||
const AMOUNT1: u64 = 50000;
|
const AMOUNT1: u64 = 50000;
|
||||||
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT1);
|
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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).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 outgoing_memo = "Outgoing Memo".to_string();
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
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 (ss, so) =get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a tx and send to address. This should consume both the UTXO and the note
|
// 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 memo = "Incoming Memo".to_string();
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a tx and send to address
|
// 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 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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a tx and send to address
|
// Create a tx and send to address
|
||||||
@@ -1132,7 +1164,7 @@ fn test_multi_t() {
|
|||||||
|
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a Tx and send to the second t address
|
// 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 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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
let tos = vec![ (zaddr2.as_str(), ZAMOUNT2, Some(outgoing_memo2.clone())),
|
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 (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 (ss, so) = get_sapling_params().unwrap();
|
||||||
let ext_taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).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 (wallet, _, _) = get_test_wallet(100000);
|
||||||
let ext_taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
|
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
|
// Bad params
|
||||||
let _ = wallet.send_to_address(branch_id, &[], &[],
|
let _ = wallet.send_to_address(branch_id, &[], &[],
|
||||||
vec![(&ext_taddr, 10, None)]);
|
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())
|
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]
|
#[test]
|
||||||
fn test_block_limit() {
|
fn test_block_limit() {
|
||||||
const AMOUNT: u64 = 500000;
|
const AMOUNT: u64 = 500000;
|
||||||
@@ -1529,7 +1608,7 @@ fn test_rollback() {
|
|||||||
// Now do a Tx
|
// Now do a Tx
|
||||||
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
|
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();
|
let (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a tx and send to address
|
// Create a tx and send to address
|
||||||
@@ -1795,7 +1874,7 @@ fn test_encrypted_zreceive() {
|
|||||||
let outgoing_memo = "Outgoing Memo".to_string();
|
let outgoing_memo = "Outgoing Memo".to_string();
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
// Create a tx and send to address
|
// Create a tx and send to address
|
||||||
@@ -1901,7 +1980,7 @@ fn test_encrypted_treceive() {
|
|||||||
let password: String = "password".to_string();
|
let password: String = "password".to_string();
|
||||||
let (mut wallet, txid1, block_hash) = get_test_wallet(AMOUNT1);
|
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 (ss, so) = get_sapling_params().unwrap();
|
||||||
|
|
||||||
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
|
let taddr = wallet.address_from_sk(&SecretKey::from_slice(&[1u8; 32]).unwrap());
|
||||||
|
|||||||
25
mkrelease.sh
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/
|
cp target/release/silentdragonlite-cli target/macOS-silentdragonlite-cli-v$APP_VERSION/
|
||||||
|
|
||||||
# For Windows and Linux, build via docker
|
# 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
|
# Now sign and zip the binaries
|
||||||
# macOS
|
# macOS
|
||||||
@@ -82,4 +82,27 @@ zip -r Windows-silentdragonlite-cli-v$APP_VERSION.zip Windows-silentdragonlite-c
|
|||||||
cd ..
|
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 ..
|
||||||
|
|||||||
Reference in New Issue
Block a user