Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions src/commands/balance.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::path::Path;

use anyhow::anyhow;
use gumdrop::Options;

Expand All @@ -11,8 +9,9 @@ use zcash_client_sqlite::WalletDb;
use zcash_protocol::value::{Zatoshis, COIN};

use crate::{
data::{get_db_paths, get_tor_dir, get_wallet_network},
data::{get_db_paths, get_wallet_network},
error,
remote::tor_client,
ui::format_zec,
MIN_CONFIRMATIONS,
};
Expand Down Expand Up @@ -40,7 +39,8 @@ impl Command {
.ok_or(error::Error::InvalidRecipient)?;

let printer = if let Some(currency) = self.convert {
ValuePrinter::with_exchange_rate(&get_tor_dir(wallet_dir), currency).await?
let tor = tor_client(wallet_dir.as_ref()).await?;
ValuePrinter::with_exchange_rate(&tor, currency).await?
} else {
ValuePrinter::ZecOnly
};
Expand Down Expand Up @@ -88,12 +88,7 @@ enum ValuePrinter {
}

impl ValuePrinter {
async fn with_exchange_rate(tor_dir: &Path, currency: Currency) -> anyhow::Result<Self> {
// Ensure Tor directory exists.
tokio::fs::create_dir_all(tor_dir).await?;

let tor = tor::Client::create(tor_dir).await?;

async fn with_exchange_rate(tor: &tor::Client, currency: Currency) -> anyhow::Result<Self> {
info!("Fetching {:?}/ZEC exchange rate", currency);
let exchanges = tor::http::cryptex::Exchanges::unauthenticated_known_with_gemini_trusted();
let usd_zec = tor.get_latest_zec_to_usd_rate(&exchanges).await?;
Expand Down
13 changes: 11 additions & 2 deletions src/commands/enhance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use zcash_protocol::consensus::{BlockHeight, BranchId, Network};

use crate::{
data::{get_db_paths, get_wallet_network},
remote::{connect_to_lightwalletd, Servers},
remote::{tor_client, Servers},
};

// Options accepted for the `enhance` command
Expand Down Expand Up @@ -89,7 +89,16 @@ impl Command {
anyhow!("Chain height must be available to perform transaction enhancement.")
})?;

let mut client = connect_to_lightwalletd(self.server.pick(params)?).await?;
// TODO:
// - Create a shared Tor client.
// - Create an isolated `lightwalletd` connection for each transaction.
// - Spread transactions across all available servers.
// - Fetch transactions in parallel, with timing noise.
let mut client = self
.server
.pick(params)?
.connect(|| tor_client(wallet_dir.as_ref()))
.await?;

let mut satisfied_requests = BTreeSet::new();
loop {
Expand Down
10 changes: 7 additions & 3 deletions src/commands/import_ufvk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use zcash_primitives::consensus;
use crate::{
data::get_db_paths,
error,
remote::{connect_to_lightwalletd, Servers},
remote::{tor_client, Servers},
};

// Options accepted for the `import-ufvk` command
Expand Down Expand Up @@ -49,14 +49,18 @@ impl Command {
}
}?;

let (_, db_data) = get_db_paths(wallet_dir);
let (_, db_data) = get_db_paths(wallet_dir.as_ref());
let mut db_data = WalletDb::for_path(db_data, params)?;

// Construct an `AccountBirthday` for the account's birthday.
let birthday = {
// Fetch the tree state corresponding to the last block prior to the wallet's
// birthday height. NOTE: THIS APPROACH LEAKS THE BIRTHDAY TO THE SERVER!
let mut client = connect_to_lightwalletd(self.server.pick(params)?).await?;
let mut client = self
.server
.pick(params)?
.connect(|| tor_client(wallet_dir))
.await?;
let request = service::BlockId {
height: (self.birthday - 1).into(),
..Default::default()
Expand Down
8 changes: 6 additions & 2 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use zcash_primitives::consensus::{self, Parameters};
use crate::{
data::{get_db_paths, init_wallet_config, Network},
error,
remote::{connect_to_lightwalletd, Servers},
remote::{tor_client, Servers},
};

// Options accepted for the `init` command
Expand Down Expand Up @@ -50,7 +50,11 @@ impl Command {
let params = consensus::Network::from(opts.network);

// Get the current chain height (for the wallet's birthday).
let mut client = connect_to_lightwalletd(opts.server.pick(params)?).await?;
let mut client = opts
.server
.pick(params)?
.connect(|| tor_client(wallet_dir.as_ref()))
.await?;
let birthday = if let Some(birthday) = opts.birthday {
birthday
} else {
Expand Down
8 changes: 6 additions & 2 deletions src/commands/reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use gumdrop::Options;

use crate::{
data::{erase_wallet_state, read_config},
remote::{connect_to_lightwalletd, Servers},
remote::{tor_client, Servers},
};

// Options accepted for the `reset` command
Expand All @@ -27,7 +27,11 @@ impl Command {
let params = keys.network();

// Connect to the client (for re-initializing the wallet).
let client = connect_to_lightwalletd(self.server.pick(params)?).await?;
let client = self
.server
.pick(params)?
.connect(|| tor_client(wallet_dir.as_ref()))
.await?;

// Erase the wallet state (excluding key material).
erase_wallet_state(wallet_dir.as_ref()).await;
Expand Down
10 changes: 7 additions & 3 deletions src/commands/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
commands::propose::{parse_fee_rule, FeeRule},
data::{get_db_paths, read_config},
error,
remote::{connect_to_lightwalletd, Servers},
remote::{tor_client, Servers},
MIN_CONFIRMATIONS,
};

Expand Down Expand Up @@ -62,7 +62,7 @@ impl Command {
let keys = read_config(wallet_dir.as_ref())?;
let params = keys.network();

let (_, db_data) = get_db_paths(wallet_dir);
let (_, db_data) = get_db_paths(wallet_dir.as_ref());
let mut db_data = WalletDb::for_path(db_data, params)?;
let account_id = *db_data
.get_account_ids()?
Expand All @@ -87,7 +87,11 @@ impl Command {
)
.map_err(error::Error::from)?;

let mut client = connect_to_lightwalletd(self.server.pick(params)?).await?;
let mut client = self
.server
.pick(params)?
.connect(|| tor_client(wallet_dir.as_ref()))
.await?;

// Create the transaction.
println!("Creating transaction...");
Expand Down
4 changes: 2 additions & 2 deletions src/commands/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use zcash_protocol::consensus::{BlockHeight, Parameters};
use crate::{
data::{get_block_path, get_db_paths, get_wallet_network},
error,
remote::{connect_to_lightwalletd, Servers},
remote::Servers,
ShutdownListener,
};

Expand Down Expand Up @@ -80,7 +80,7 @@ impl Command {
let fsblockdb_root = fsblockdb_root.as_path();
let mut db_cache = FsBlockDb::for_path(fsblockdb_root).map_err(error::Error::from)?;
let mut db_data = WalletDb::for_path(db_data, params)?;
let mut client = connect_to_lightwalletd(self.server.pick(params)?).await?;
let mut client = self.server.pick(params)?.connect_direct().await?;

#[cfg(any(feature = "transparent-inputs", feature = "tui"))]
let wallet_birthday = db_data
Expand Down
77 changes: 60 additions & 17 deletions src/remote.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use std::{borrow::Cow, fmt};
use std::{borrow::Cow, fmt, future::Future, path::Path};

use anyhow::anyhow;
use tonic::transport::{Channel, ClientTlsConfig};

use tracing::info;
use zcash_client_backend::proto::service::compact_tx_streamer_client::CompactTxStreamerClient;
use zcash_client_backend::{
proto::service::compact_tx_streamer_client::CompactTxStreamerClient, tor,
};
use zcash_protocol::consensus::Network;

use crate::data::get_tor_dir;

const ECC_TESTNET: &[Server<'_>] = &[Server::fixed("lightwalletd.testnet.electriccoin.co", 9067)];

const YWALLET_MAINNET: &[Server<'_>] = &[
Expand Down Expand Up @@ -131,23 +135,62 @@ impl<'a> Server<'a> {
self.port
)
}
}

pub(crate) async fn connect_to_lightwalletd(
server: &Server<'_>,
) -> Result<CompactTxStreamerClient<Channel>, anyhow::Error> {
info!("Connecting to {}", server);
pub(crate) async fn connect_direct(&self) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
info!("Connecting to {}", self);

let channel = Channel::from_shared(self.endpoint())?;

let channel = if self.use_tls() {
let tls = ClientTlsConfig::new()
.domain_name(self.host.to_string())
.with_webpki_roots();
channel.tls_config(tls)?
} else {
channel
};

Ok(CompactTxStreamerClient::new(channel.connect().await?))
}

async fn connect_over_tor(
&self,
tor: &tor::Client,
) -> Result<CompactTxStreamerClient<Channel>, anyhow::Error> {
if !self.use_tls() {
return Err(anyhow!(
"Cannot connect to local lightwalletd server over Tor"
));
}

info!("Connecting to {} over Tor", self);
let endpoint = self.endpoint().try_into()?;
Ok(tor.connect_to_lightwalletd(endpoint).await?)
}

/// Connects to the server over Tor, unless it is running on localhost without HTTPS.
pub(crate) async fn connect<F>(
&self,
tor: impl FnOnce() -> F,
) -> Result<CompactTxStreamerClient<Channel>, anyhow::Error>
where
F: Future<Output = anyhow::Result<tor::Client>>,
{
if self.use_tls() {
self.connect_over_tor(&tor().await?).await
} else {
self.connect_direct().await
}
}
}

let channel = Channel::from_shared(server.endpoint())?;
pub(crate) async fn tor_client<P: AsRef<Path>>(
wallet_dir: Option<P>,
) -> anyhow::Result<tor::Client> {
let tor_dir = get_tor_dir(wallet_dir);

let channel = if server.use_tls() {
let tls = ClientTlsConfig::new()
.domain_name(server.host.to_string())
.with_webpki_roots();
channel.tls_config(tls)?
} else {
channel
};
// Ensure Tor directory exists.
tokio::fs::create_dir_all(&tor_dir).await?;

Ok(CompactTxStreamerClient::new(channel.connect().await?))
Ok(tor::Client::create(&tor_dir).await?)
}