1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
//! Networking code for Zebra.
//!
//! ## Network Protocol Design
//!
//! The Zcash network protocol is inherited from Bitcoin, which uses a
//! stateful network protocol in which messages can arrive in any
//! order (even before a handshake is complete!). The same Bitcoin message
//! may be a request or a response depending on context.
//!
//! ### Achieving Concurrency
//!
//! This crate translates the legacy Zcash network protocol
//! into a stateless, request-response oriented protocol defined by
//! the [`Request`] and [`Response`] enums. `zebra-network` completely
//! encapsulates all peer handling code behind a single
//! [`tower::Service`] representing "the network", which load-balances
//! outbound [`Request`]s over available peers.
//!
//! Unlike the underlying legacy network protocol, Zebra's `PeerSet`
//! [`tower::Service`] guarantees that each `Request` future will resolve to the
//! correct `Response`, rather than an unrelated `Response` message.
//!
//! Each peer connection is handled by a distinct [`peer::Connection`] task.
//! The Zcash network protocol is bidirectional, so Zebra interprets incoming
//! Zcash messages as either:
//! - [`Response`]s to previously sent outbound [`Request`]s, or
//! - inbound [`Request`]s to an internal [`tower::Service`] representing "this node".
//!
//! All connection state is isolated to individual peers, so this
//! design is structurally immune to the recent `ping` attack.
//!
//! ### Connection Pool
//!
//! Because [`tower::Service`]s provide backpressure information, we
//! can dynamically manage the size of the connection pool according
//! to inbound and outbound demand. The inbound service can shed load
//! when it is not ready for requests, causing those peer connections
//! to close, and the outbound service can connect to additional peers
//! when it is overloaded.
//!
//! ## `zebra-network` Structure
//!
//! [`zebra-network::init`] is the main entry point for `zebra-network`.
//! It uses the following services, tasks, and endpoints:
//!
//! ### Low-Level Network Connections
//!
//! Inbound Zcash Listener Task:
//! * accepts inbound connections on the listener port
//! * initiates Zcash [`peer::Handshake`]s, which creates [`peer::Connection`]
//! tasks for each inbound connection
//!
//! Outbound Zcash Connector Service:
//! * initiates outbound connections to peer addresses
//! * initiates Zcash [`peer::Handshake`]s, which creates [`peer::Connection`]
//! tasks for each outbound connection
//!
//! Zebra uses direct TCP connections to share blocks and mempool transactions with other peers.
//!
//! The [`isolated`] APIs provide anonymised TCP and [Tor](https://crates.io/crates/arti)
//! connections to individual peers.
//! These isolated connections can be used to send user-generated transactions anonymously.
//! Tor connections are currently disabled until `arti-client`'s dependency `x25519-dalek v1.2.0`
//! is updated to a higher version. See #5492.
//!
//! ### Individual Peer Connections
//!
//! Each new peer connection spawns the following tasks:
//!
//! [`peer::Client`] Service:
//! * provides an interface for outbound requests to an individual peer
//! * accepts [`Request`]s assigned to this peer by the `PeerSet`
//! * sends each request to the peer as Zcash [`Message`][1]
//! * waits for the inbound response [`Message`][1] from the peer, and returns it as a [`Response`]
//!
//! [`peer::Connection`] Service:
//! * manages connection state: awaiting a request, or handling an inbound or outbound response
//! * provides an interface for inbound requests from an individual peer
//! * accepts inbound Zcash [`Message`][1]s from this peer
//! * handles each message as a [`Request`] to the inbound service
//! * sends the [`Response`] to the peer as Zcash [`Message`][1]s
//! * drops peer connections if the inbound request queue is overloaded
//!
//! Since the Zcash network protocol is bidirectional,
//! inbound and outbound connections are handled using the same logic.
//!
//! ### Connection Pool
//!
//! `PeerSet` Network Service:
//! * provides an interface for other services and tasks running within this node
//! to make requests to remote peers ("the rest of the network")
//! * accepts [`Request`]s from the local node
//! * sends each request to a [`peer::Client`] using randomised load-balancing
//! * returns the [`Response`] from the [`peer::Client`]
//!
//! Inbound Network Service:
//! * provides an interface for remote peers to request data held by this node
//! * accepts inbound Zcash [`Request`]s from [`peer::Connection`]s
//! * handles each message as a [`Request`] to the local node
//! * sends the [`Response`] to the [`peer::Connection`]
//!
//! Note: the inbound service is implemented by the [`zebra-network::init`] caller.
//!
//! Peer Inventory Service:
//! * tracks gossiped `inv` advertisements for each peer
//! * updated before each `PeerSet` request is processed
//! * tracks missing inventory for each peer
//! * used by the `PeerSet` to route block and transaction requests
//! to peers that have the requested data
//!
//! ### Peer Discovery
//!
//! [`AddressBook`] Service:
//! * maintains a list of peer addresses and associated connection attempt metadata
//! * address book metadata is used to prioritise peer connection attempts
//! * updated by an independent thread based on peer connection status changes
//! * caches peer addresses to disk regularly using an independent task
//!
//! Initial Seed Peer Task:
//! On startup:
//! * loads seed peers from the config, resolving them via DNS if required
//! * loads cached peer addresses from disk
//! * initiates new outbound peer connections to seed and cached peers
//! * adds seed and cached peer addresses to the [`AddressBook`]
//!
//! Peer Crawler Task:
//! * discovers new peer addresses by sending `Addr` requests to connected peers
//! * initiates new outbound peer connections in response to application demand
//!
//! [1]: protocol::external::Message
#![doc(html_favicon_url = "https://zfnd.org/wp-content/uploads/2022/03/zebra-favicon-128.png")]
#![doc(html_logo_url = "https://zfnd.org/wp-content/uploads/2022/03/zebra-icon.png")]
#![doc(html_root_url = "https://docs.rs/zebra_network")]
#[macro_use]
extern crate pin_project;
#[macro_use]
extern crate serde;
#[macro_use]
extern crate tracing;
#[macro_use]
extern crate bitflags;
/// Type alias to make working with tower traits easier.
///
/// Note: the 'static lifetime bound means that the *type* cannot have any
/// non-'static lifetimes, (e.g., when a type contains a borrow and is
/// parameterized by 'a), *not* that the object itself has 'static lifetime.
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub mod address_book_peers;
pub mod config;
pub mod constants;
mod address_book;
mod address_book_updater;
mod isolated;
mod meta_addr;
mod peer;
mod peer_cache_updater;
mod peer_set;
mod policies;
mod protocol;
// Wait until `arti-client`'s dependency `x25519-dalek v1.2.0` is updated to a higher version. (#5492)
// #[cfg(feature = "tor")]
// pub use crate::isolated::tor::connect_isolated_tor;
// Wait until `arti-client`'s dependency `x25519-dalek v1.2.0` is updated to a higher version. (#5492)
// #[cfg(all(feature = "tor", any(test, feature = "proptest-impl")))]
// pub use crate::isolated::tor::connect_isolated_tor_with_inbound;
#[cfg(any(test, feature = "proptest-impl"))]
pub use crate::{
isolated::{connect_isolated_tcp_direct_with_inbound, connect_isolated_with_inbound},
protocol::external::canonical_peer_addr,
};
pub use crate::{
address_book::AddressBook,
address_book_peers::AddressBookPeers,
config::{CacheDir, Config},
isolated::{connect_isolated, connect_isolated_tcp_direct},
meta_addr::{PeerAddrState, PeerSocketAddr},
peer::{Client, ConnectedAddr, ConnectionInfo, HandshakeError, PeerError, SharedPeerError},
peer_set::init,
policies::RetryLimit,
protocol::{
external::{Version, VersionMessage, MAX_TX_INV_IN_SENT_MESSAGE},
internal::{InventoryResponse, Request, Response},
},
};
/// Types used in the definition of [`Request`], [`Response`], and [`VersionMessage`].
pub mod types {
pub use crate::{
meta_addr::MetaAddr,
protocol::{
external::{AddrInVersion, Nonce},
types::PeerServices,
},
};
#[cfg(any(test, feature = "proptest-impl"))]
pub use crate::protocol::external::InventoryHash;
}