//! 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; 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")] #[cfg(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")))] #[cfg(tor)] 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; }