zcash_client_backend: Add a Tor client using the Arti library

This commit is contained in:
Jack Grigg 2024-06-14 11:39:44 +00:00
parent 4cefa92c28
commit 1e5b62bfce
9 changed files with 4120 additions and 184 deletions

2642
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,13 @@ proptest = "1"
rand_chacha = "0.3"
rand_xorshift = "0.3"
# Tor
# - `arti-client` depends on `rusqlite`, and a version mismatch there causes a compilation
# failure due to incompatible `libsqlite3-sys` versions.
arti-client = { version = "0.11", default-features = false, features = ["compression", "rustls", "tokio"] }
tokio = "1"
tor-rtcompat = "0.9"
# ZIP 32
aes = "0.8"
fpe = "0.6"

View File

@ -31,16 +31,27 @@ allow = [
]
exceptions = [
{ name = "arrayref", allow = ["BSD-2-Clause"] },
{ name = "async_executors", allow = ["Unlicense"] },
{ name = "bounded-vec-deque", allow = ["BSD-3-Clause"] },
{ name = "coarsetime", allow = ["ISC"] },
{ name = "curve25519-dalek", allow = ["BSD-3-Clause"] },
{ name = "ed25519-dalek", allow = ["BSD-3-Clause"] },
{ name = "matchit", allow = ["BSD-3-Clause"] },
{ name = "minreq", allow = ["ISC"] },
{ name = "option-ext", allow = ["MPL-2.0"] },
{ name = "priority-queue", allow = ["MPL-2.0"] },
{ name = "ring", allow = ["LicenseRef-ring"] },
{ name = "rustls-webpki", allow = ["ISC"] },
{ name = "secp256k1", allow = ["CC0-1.0"] },
{ name = "secp256k1-sys", allow = ["CC0-1.0"] },
{ name = "simple_asn1", allow = ["ISC"] },
{ name = "slotmap", allow = ["Zlib"] },
{ name = "subtle", allow = ["BSD-3-Clause"] },
{ name = "tinystr", allow = ["Unicode-3.0"] },
{ name = "unicode-ident", allow = ["Unicode-DFS-2016"] },
{ name = "untrusted", allow = ["ISC"] },
{ name = "webpki-roots", allow = ["MPL-2.0"] },
{ name = "x25519-dalek", allow = ["BSD-3-Clause"] },
]
[[licenses.clarify]]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@ funds to those addresses. See [ZIP 320](https://zips.z.cash/zip-0320) for detail
- `zcash_client_backend::scanning`:
- `testing` module
- `zcash_client_backend::sync` module, behind the `sync` feature flag.
- `zcash_client_backend::tor` module, behind the `tor` feature flag.
- `zcash_client_backend::wallet::Recipient::map_ephemeral_transparent_outpoint`
### Changed

View File

@ -27,6 +27,7 @@ features = [
"lightwalletd-tonic",
"transparent-inputs",
"test-dependencies",
"tor",
"unstable",
"unstable-serialization",
"unstable-spanning-tree",
@ -94,6 +95,12 @@ jubjub = { workspace = true, optional = true }
# - ZIP 321
nom = "7"
# - Tor
# -- Exposed error types: `arti_client::Error`, `arti_client::config::ConfigBuildError`,
# `hyper::Error`, `hyper::http::Error`, `serde_json::Error`. We could avoid this with
# changes to error handling.
arti-client = { workspace = true, optional = true }
# Dependencies used internally:
# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.)
# - Documentation
@ -107,6 +114,10 @@ percent-encoding.workspace = true
crossbeam-channel.workspace = true
rayon.workspace = true
# - Tor
tokio = { workspace = true, optional = true, features = ["fs"] }
tor-rtcompat = { workspace = true, optional = true }
[build-dependencies]
tonic-build = { workspace = true, features = ["prost"] }
which = "4"
@ -148,6 +159,14 @@ sync = [
"dep:futures-util",
]
## Exposes a Tor client for hiding a wallet's IP address while performing certain wallet
## operations.
tor = [
"dep:arti-client",
"dep:tokio",
"dep:tor-rtcompat",
]
## Exposes APIs that are useful for testing, such as `proptest` strategies.
test-dependencies = [
"dep:proptest",

View File

@ -80,6 +80,9 @@ pub mod sync;
#[cfg(feature = "unstable-serialization")]
pub mod serialization;
#[cfg(feature = "tor")]
pub mod tor;
pub use decrypt::{decrypt_transaction, DecryptedOutput, TransferType};
pub use zcash_protocol::{PoolType, ShieldedProtocol};

View File

@ -0,0 +1,87 @@
//! Tor support for Zcash wallets.
use std::{fmt, io, path::Path};
use arti_client::{config::TorClientConfigBuilder, TorClient};
use tor_rtcompat::PreferredRuntime;
use tracing::debug;
/// A Tor client that exposes capabilities designed for Zcash wallets.
pub struct Client {
inner: TorClient<PreferredRuntime>,
}
impl Client {
/// Creates and bootstraps a Tor client.
///
/// The client's persistent data and cache are both stored in the given directory.
/// Preserving the contents of this directory will speed up subsequent calls to
/// `Client::create`.
///
/// Returns an error if `tor_dir` does not exist, or if bootstrapping fails.
pub async fn create(tor_dir: &Path) -> Result<Self, Error> {
let runtime = PreferredRuntime::current()?;
if !tokio::fs::try_exists(tor_dir).await? {
return Err(Error::MissingTorDirectory);
}
let config = TorClientConfigBuilder::from_directories(
tor_dir.join("arti-data"),
tor_dir.join("arti-cache"),
)
.build()
.expect("all required fields initialized");
let client_builder = TorClient::with_runtime(runtime).config(config);
debug!("Bootstrapping Tor");
let inner = client_builder.create_bootstrapped().await?;
debug!("Tor bootstrapped");
Ok(Self { inner })
}
}
/// Errors that can occur while creating or using a Tor [`Client`].
#[derive(Debug)]
pub enum Error {
/// The directory passed to [`Client::create`] does not exist.
MissingTorDirectory,
/// An IO error occurred while interacting with the filesystem.
Io(io::Error),
/// A Tor-specific error.
Tor(arti_client::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::MissingTorDirectory => write!(f, "Tor directory is missing"),
Error::Io(e) => write!(f, "IO error: {}", e),
Error::Tor(e) => write!(f, "Tor error: {}", e),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::MissingTorDirectory => None,
Error::Io(e) => Some(e),
Error::Tor(e) => Some(e),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Io(e)
}
}
impl From<arti_client::Error> for Error {
fn from(e: arti_client::Error) -> Self {
Error::Tor(e)
}
}