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