p2panda_net/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3#![cfg_attr(doctest, doc=include_str!("../README.md"))]
4
5//! `p2panda-net` is a data-type-agnostic p2p networking layer offering robust, direct
6//! communication to any device, no matter where they are.
7//!
8//! It provides a stream-based API for higher layers: Applications subscribe to any "topic" they
9//! are interested in and `p2panda-net` will automatically discover similar peers and transport raw
10//! bytes between them.
11//!
12//! Additionally `p2panda-net` can be extended with custom sync protocols for all data types,
13//! allowing applications to "catch up on past data", eventually converging to the same state.
14//!
15//! ## Features
16//!
17//! Most of the lower-level networking of `p2panda-net` is made possible by the work of
18//! [iroh](https://github.com/n0-computer/iroh/) utilising well-established and known standards,
19//! like QUIC for transport, (self-certified) TLS for transport encryption, STUN for establishing
20//! direct connections between devices, Tailscale's DERP (Designated Encrypted Relay for Packets)
21//! for relay fallbacks, PlumTree and HyParView for broadcast-based gossip overlays.
22//!
23//! p2panda adds crucial functionality on top of iroh for peer-to-peer application development,
24//! without tying developers too closely to any pre-defined data types and allowing plenty of space
25//! for customisation:
26//!
27//! 1. Data of any kind can be exchanged efficiently via gossip broadcast ("live mode") or via sync
28//!    protocols between two peers ("catching up on past state")
29//! 2. Custom network-wide queries to express interest in certain data of applications
30//! 3. Ambient peer discovery: Learning about new, previously unknown peers in the network
31//! 4. Ambient topic discovery: Learning what peers are interested in, automatically forming
32//!    overlay networks per topic
33//! 5. Sync protocol API, providing an eventual-consistency guarantee that peers will converge on
34//!    the same state over time
35//! 6. Manages connections, automatically syncs with discovered peers and re-tries on faults
36//! 7. Extension for networks to handle efficient [sync of large
37//!    files](https://docs.rs/p2panda-blobs)
38//!
39//! ## Offline-First
40//!
41//! This networking crate is designed to run on top of bi-directional, ordered connections on the
42//! IP layer (aka "The Internet"), with robustness to work in environments with unstable
43//! connectivity or offline time-periods.
44//!
45//! While this IP-based networking implementation should provide for many "modern" use-cases,
46//! p2panda data-types are designed for more extreme scenarios where connectivity can _never_ be
47//! assumed and data transmission needs to be highly "delay tolerant": For example "broadcast-only"
48//! topologies on top of BLE (Bluetooth Low Energy), LoRa or even Digital Radio Communication
49//! infrastructure.
50//!
51//! ## Extensions
52//!
53//! `p2panda-net` is agnostic to any data type (sending and receiving raw byte streams) and can
54//! seamlessly be extended with external or official p2panda implementations for different parts of
55//! the application:
56//!
57//! 1. Custom Data types exchanged over the network
58//! 2. Optional relay nodes to aid connection establishment when peers are behind firewalls etc.
59//! 3. Custom sync protocol for any data types, with managed re-attempts on connection failures and
60//!    optional re-sync schedules
61//! 4. Custom peer discovery strategies (multiple approaches can be used at the same time)
62//! 5. Sync and storage of (very) large blobs
63//! 6. Fine-tune gossipping behaviour
64//! 7. Additional custom protocol handlers
65//!
66//! ## Integration with other p2panda solutions
67//!
68//! We provide p2panda's fork-tolerant and prunable append-only logs in `p2panda-core`, offering
69//! single-writer and multi-writer streams, authentication, deletion, ordering and more. This can
70//! be further extended with an efficient sync implementation in `p2panda-sync` and validation and
71//! fast stream-based ingest solutions in `p2panda-streams`.
72//!
73//! For discovery of peers on the local network, we provide an mDNS-based implementation in
74//! `p2panda-discovery`, planned next are additional techniques like "rendesvouz" nodes and random
75//! walk algorithms.
76//!
77//! Lastly we maintain persistance layer APIs in `p2panda-store` for in-memory storage or
78//! embeddable, SQL-based databases.
79//!
80//! In the future we will provide additional implementations for managing access control and group
81//! encryption.
82//!
83//! ## Example
84//!
85//! ```
86//! # use anyhow::Result;
87//! use p2panda_core::{PrivateKey, Hash};
88//! use p2panda_discovery::mdns::LocalDiscovery;
89//! use p2panda_net::{NetworkBuilder, TopicId};
90//! use p2panda_sync::TopicQuery;
91//! use serde::{Serialize, Deserialize};
92//! # #[tokio::main]
93//! # async fn main() -> Result<()> {
94//!
95//! // Peers using the same "network id" will eventually find each other. This is the most global
96//! // identifier to group peers into multiple networks when necessary.
97//! let network_id = [1; 32];
98//!
99//! // The network can be used to automatically find and ask other peers about any data the
100//! // application is interested in. This is expressed through "network-wide queries" over topics.
101//! //
102//! // In this example we would like to be able to query messages from each chat group, identified
103//! // by a BLAKE3 hash.
104//! #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
105//! struct ChatGroup(Hash);
106//!
107//! impl ChatGroup {
108//!     pub fn new(name: &str) -> Self {
109//!         Self(Hash::new(name.as_bytes()))
110//!     }
111//! }
112//!
113//! impl TopicQuery for ChatGroup {}
114//!
115//! impl TopicId for ChatGroup {
116//!     fn id(&self) -> [u8; 32] {
117//!         self.0.into()
118//!     }
119//! }
120//!
121//! // Generate an Ed25519 private key which will be used to authenticate your peer towards others.
122//! let private_key = PrivateKey::new();
123//!
124//! // Use mDNS to discover other peers on the local network.
125//! let mdns_discovery = LocalDiscovery::new();
126//!
127//! // Establish the p2p network which will automatically connect you to any discovered peers.
128//! let network = NetworkBuilder::new(network_id)
129//!     .private_key(private_key)
130//!     .discovery(mdns_discovery)
131//!     .build()
132//!     .await?;
133//!
134//! // Subscribe to network events.
135//! let mut event_rx = network.events().await?;
136//!
137//! // From now on we can send and receive bytes to any peer interested in the same chat.
138//! let my_friends_group = ChatGroup::new("me-and-my-friends");
139//! let (tx, mut rx, ready) = network.subscribe(my_friends_group).await?;
140//! # Ok(())
141//! # }
142//! ```
143mod addrs;
144mod bytes;
145pub mod config;
146mod engine;
147mod events;
148pub mod network;
149mod protocols;
150mod sync;
151
152pub use addrs::{NodeAddress, RelayUrl};
153pub use config::Config;
154pub use events::SystemEvent;
155pub use network::{FromNetwork, Network, NetworkBuilder, RelayMode, ToNetwork};
156pub use protocols::ProtocolHandler;
157pub use sync::{ResyncConfiguration, SyncConfiguration};
158
159#[cfg(feature = "log-sync")]
160pub use p2panda_sync::log_sync::LogSyncProtocol;
161
162/// Unique 32 byte identifier for a network.
163///
164/// Peers operating on the same network identifier will eventually discover each other. This is the
165/// most global identifier to group peers into networks. Different applications may choose to share
166/// the same underlying network infrastructure by using the same network identifier.
167///
168/// Please note that the network identifier should _never_ be the same as any other topic
169/// identifier.
170pub type NetworkId = [u8; 32];
171
172/// Topic ids are announced on the network and used to identify peers with overlapping interests.
173///
174/// Once other peers are discovered who are interested in the same topic id, the application will
175/// join the gossip overlay under that identifier.
176///
177/// If an optional sync protocol has been provided, the application will attempt to synchronise
178/// past state before entering the gossip overlay.
179///
180/// ## Designing topic identifiers for applications
181///
182/// Networked applications, such as p2p systems, usually want to converge to the same state over
183/// time so that all users eventually see the same data.
184///
185/// If we're considering the totality of "all data" the application can create as the "global
186/// state", we might want to categorise it into logical "sub-sections", especially when the
187/// application gets complex. In an example chat application we might not want to sync _all_ chat
188/// group data which has ever been created by all peers, but only a subset of the ones our peer is
189/// actually a member of.
190///
191/// In this case we could separate the application state into distinct topic identifiers, one for
192/// each chat group. Now peers can announce their interest in a specific chat group and only sync
193/// that particular data.
194///
195/// ## `TopicQuery` vs. `TopicId`
196///
197/// Next to topic identifiers p2panda offers a `TopicQuery` trait which allows for even more
198/// sophisticated "network queries".
199///
200/// `TopicId` is a tool for general topic discovery and establishing gossip network overlays.
201/// `TopicQuery` is a query for sync protocols to ask for a specific piece of information.
202///
203/// Consult the `TopicQuery` documentation in `p2panda-sync` for further information.
204pub trait TopicId {
205    fn id(&self) -> [u8; 32];
206}
207
208/// Converts an `iroh` public key type to the `p2panda-core` implementation.
209pub(crate) fn to_public_key(key: iroh_base::PublicKey) -> p2panda_core::PublicKey {
210    p2panda_core::PublicKey::from_bytes(key.as_bytes()).expect("already validated public key")
211}
212
213/// Converts a `p2panda-core` public key to the "iroh" type.
214pub(crate) fn from_public_key(key: p2panda_core::PublicKey) -> iroh_base::PublicKey {
215    iroh_base::PublicKey::from_bytes(key.as_bytes()).expect("already validated public key")
216}
217
218/// Converts a `p2panda-core` private key to the "iroh" type.
219pub(crate) fn from_private_key(key: p2panda_core::PrivateKey) -> iroh_base::SecretKey {
220    iroh_base::SecretKey::from_bytes(key.as_bytes())
221}