zebra_network/
isolated.rs

1//! Creating isolated connections to specific peers.
2
3use std::future::Future;
4
5use futures::future::TryFutureExt;
6use tokio::io::{AsyncRead, AsyncWrite};
7use tower::{util::Oneshot, Service};
8
9use zebra_chain::{chain_tip::NoChainTip, parameters::Network};
10
11use crate::{
12    peer::{self, Client, ConnectedAddr, HandshakeRequest},
13    peer_set::ActiveConnectionCounter,
14    BoxError, Config, PeerSocketAddr, Request, Response,
15};
16
17// Wait until `arti-client`'s dependency `x25519-dalek v1.2.0` is updated to a higher version. (#5492)
18// #[cfg(feature = "tor")]
19// pub(crate) mod tor;
20
21#[cfg(test)]
22mod tests;
23
24/// Creates a Zcash peer connection using the provided data stream.
25/// This connection is completely isolated from all other node state.
26///
27/// The connection pool returned by [`init`](crate::init)
28/// should be used for all requests that
29/// don't require isolated state or use of an existing TCP connection. However,
30/// this low-level API is useful for custom network crawlers or Tor connections.
31///
32/// In addition to being completely isolated from all other node state, this
33/// function also aims to be minimally distinguishable from other clients.
34///
35/// SECURITY TODO: check if the timestamp field can be zeroed, to remove another distinguisher (#3300)
36///
37/// Note that this function does not implement any timeout behavior, so callers may
38/// want to layer it with a timeout as appropriate for their application.
39///
40/// # Inputs
41///
42/// - `network`: the Zcash [`Network`] used for this connection.
43///
44/// - `data_stream`: an existing data stream. This can be a non-anonymised TCP connection,
45///   or a Tor client `arti_client::DataStream`.
46///
47/// - `user_agent`: a valid BIP14 user-agent, e.g., the empty string.
48pub fn connect_isolated<PeerTransport>(
49    network: &Network,
50    data_stream: PeerTransport,
51    user_agent: String,
52) -> impl Future<Output = Result<Client, BoxError>>
53where
54    PeerTransport: AsyncRead + AsyncWrite + Unpin + Send + 'static,
55{
56    let nil_inbound_service =
57        tower::service_fn(|_req| async move { Ok::<Response, BoxError>(Response::Nil) });
58
59    connect_isolated_with_inbound(network, data_stream, user_agent, nil_inbound_service)
60}
61
62/// Creates an isolated Zcash peer connection using the provided data stream.
63/// This function is for testing purposes only.
64///
65/// See [`connect_isolated`] for details.
66///
67/// # Additional Inputs
68///
69/// - `inbound_service`: a [`tower::Service`] that answers inbound requests from the connected peer.
70///
71/// # Privacy
72///
73/// This function can make the isolated connection send different responses to peers,
74/// which makes it stand out from other isolated connections from other peers.
75pub fn connect_isolated_with_inbound<PeerTransport, InboundService>(
76    network: &Network,
77    data_stream: PeerTransport,
78    user_agent: String,
79    inbound_service: InboundService,
80) -> impl Future<Output = Result<Client, BoxError>>
81where
82    PeerTransport: AsyncRead + AsyncWrite + Unpin + Send + 'static,
83    InboundService:
84        Service<Request, Response = Response, Error = BoxError> + Clone + Send + 'static,
85    InboundService::Future: Send,
86{
87    let config = Config {
88        network: network.clone(),
89        ..Config::default()
90    };
91
92    let handshake = peer::Handshake::builder()
93        .with_config(config)
94        .with_inbound_service(inbound_service)
95        .with_user_agent(user_agent)
96        .with_latest_chain_tip(NoChainTip)
97        .finish()
98        .expect("provided mandatory builder parameters");
99
100    // Don't send or track any metadata about the connection
101    let connected_addr = ConnectedAddr::new_isolated();
102    let connection_tracker = ActiveConnectionCounter::new_counter().track_connection();
103
104    Oneshot::new(
105        handshake,
106        HandshakeRequest {
107            data_stream,
108            connected_addr,
109            connection_tracker,
110        },
111    )
112}
113
114/// Creates a direct TCP Zcash peer connection to `addr`.
115/// This connection is completely isolated from all other node state.
116///
117/// See [`connect_isolated`] for details.
118///
119/// # Privacy
120///
121/// Transactions sent over this connection can be linked to the sending and receiving IP address
122/// by passive internet observers.
123///
124///
125/// Prefer `connect_isolated_tor` if available.
126pub fn connect_isolated_tcp_direct(
127    network: &Network,
128    addr: impl Into<PeerSocketAddr>,
129    user_agent: String,
130) -> impl Future<Output = Result<Client, BoxError>> {
131    let nil_inbound_service =
132        tower::service_fn(|_req| async move { Ok::<Response, BoxError>(Response::Nil) });
133
134    connect_isolated_tcp_direct_with_inbound(network, addr, user_agent, nil_inbound_service)
135}
136
137/// Creates an isolated Zcash peer connection using the provided data stream.
138/// This function is for testing purposes only.
139///
140/// See [`connect_isolated_with_inbound`] and [`connect_isolated_tcp_direct`] for details.
141///
142/// # Privacy
143///
144/// This function can make the isolated connection send different responses to peers,
145/// which makes it stand out from other isolated connections from other peers.
146pub fn connect_isolated_tcp_direct_with_inbound<InboundService>(
147    network: &Network,
148    addr: impl Into<PeerSocketAddr>,
149    user_agent: String,
150    inbound_service: InboundService,
151) -> impl Future<Output = Result<Client, BoxError>>
152where
153    InboundService:
154        Service<Request, Response = Response, Error = BoxError> + Clone + Send + 'static,
155    InboundService::Future: Send,
156{
157    let addr = addr.into();
158    let network = network.clone();
159
160    tokio::net::TcpStream::connect(*addr)
161        .err_into()
162        .and_then(move |tcp_stream| {
163            connect_isolated_with_inbound(&network, tcp_stream, user_agent, inbound_service)
164        })
165}