Skip to content

Commit

Permalink
Connect to non-local lightwalletd over Tor for everything except sync
Browse files Browse the repository at this point in the history
Most commands are single-shot, only making a couple of correlated
requests to the server. Enhancement makes requests that could be made
uncorrelated; that is left as a TODO.

Syncing currently requires a long-running connection, so we don't
migrate it yet.
  • Loading branch information
str4d committed Aug 20, 2024
1 parent 93e8c44 commit d3e51e0
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 13 deletions.
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::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 = self.server.pick(params)?.connect_direct().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
14 changes: 11 additions & 3 deletions src/commands/import_ufvk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ use zcash_client_sqlite::WalletDb;
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_primitives::consensus;

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

// Options accepted for the `import-ufvk` command
#[derive(Debug, Options)]
Expand Down Expand Up @@ -45,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 = self.server.pick(params)?.connect_direct().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::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 = opts.server.pick(params)?.connect_direct().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::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 = self.server.pick(params)?.connect_direct().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::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 = self.server.pick(params)?.connect_direct().await?;
let mut client = self
.server
.pick(params)?
.connect(|| tor_client(wallet_dir.as_ref()))
.await?;

// Create the transaction.
println!("Creating transaction...");
Expand Down
32 changes: 31 additions & 1 deletion src/remote.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{borrow::Cow, fmt, path::Path};
use std::{borrow::Cow, fmt, future::Future, path::Path};

use anyhow::anyhow;
use tonic::transport::{Channel, ClientTlsConfig};
Expand Down Expand Up @@ -152,6 +152,36 @@ impl<'a> Server<'a> {

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
}
}
}

pub(crate) async fn tor_client<P: AsRef<Path>>(
Expand Down

0 comments on commit d3e51e0

Please sign in to comment.