From 64d8087c124aa21f2eb819ca2d7aa5824157780b Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 15:46:10 +0200
Subject: [PATCH 001/122] add docs stuff
---
README.md | 54 ++++++++++++++++++++++++++++++++++++++++-
swarm_nl/src/lib.rs | 7 +++---
swarm_nl/src/prelude.rs | 8 +++---
swarm_nl/src/setup.rs | 9 +++----
swarm_nl/src/util.rs | 8 +++---
5 files changed, 71 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index 823230398..d6c879c9a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,54 @@
+
+
# SwarmNL
-SwarmNL is a configurable p2p networking layer for transporting arbitrary data between nodes in a network.
+## a library to build custom networking layers for decentralized web applications
+
+![swarmnl](/swarm-img.png)
+
+SwarmNL is a Rust library that provides a highly configurable P2P networking layer to be used in distributed system architectures that require data transfer solutions for off-chain communication.
+It is designed to offer developers a lightweight, scalable and configurable networking stack, easy to integrate to any decentralized application.
+
+
+
+## Features
+
+**Node Configuration**
+
+SwarmNL provides a simple interface to configure a node and specify parameters to dictate its behaviour. This includes:
+
+- Selection and configuration of the transport layers to be supported by the node.
+- Selection of the cryptographic keypair to use for identity generation e.g Edwards.
+- Storage and retrieval of keypair locally.
+- PeerID and multiaddress generation.
+- Protocol specification and handlers.
+- Event handlers for network events and logging.
+
+**Node Communication**
+
+SwarmNL uses the Gossipsub 1.1 protocol, specified by the libp2p spec.
+
+**Node Failure Handling**
+
+SwarmNL provides customizable options for developers to define reconnection strategies, automatic peer discovery, and failover mechanisms. This ensures that the network can gracefully adapt to failures without compromising overall system performance.
+
+**Scaling**
+
+SwarmNL needs to efficiently handle a growing (or shrinking) number of nodes while maintaining performance and reliability. Here's what we plan to implement to this effect:
+
+- *Sharding* -- implementation of a flexible generic sharding protocol that allows application specify configurations like sharding hash functions and locations for shards.
+- *Data Forwarding* -- definition of a protocol for forwarding messages between nodes in different shards and establishment of efficient routing mechanisms for inter-shard communication.
+- *Fault Tolerance* -- implementation of fault-tolerant mechanisms for detecting (and recovering from) node failures. This might involve redundancy, node replication, erasure encoding/decoding or re-routing strategies.
+
+**IPFS**
+
+- *Upload* -- provision of interfaces to upload to IPFS, pin on current node and post arbitrary data to remote servers. Encryption is also easily pluggable and will be provided.
+- *Download* -- retrieval and possible decryption of data from the IPFS network.
+
+## Technology Stack
+
+- [Libp2p](https://libp2p.io/)
+- [Rust](https://www.rust-lang.org/)
+
+## License
+
+Apache 2.0
\ No newline at end of file
diff --git a/swarm_nl/src/lib.rs b/swarm_nl/src/lib.rs
index 87d657063..f69c034e9 100644
--- a/swarm_nl/src/lib.rs
+++ b/swarm_nl/src/lib.rs
@@ -1,6 +1,7 @@
-/// Copyright (c) 2024 Algorealm
-///
-/// This file is part of the SwarmNL library.
+// Copyright 2024 Algorealm
+// Apache 2.0 License
+
+#![doc = include_str!("../../README.md")]
/// Re-exports
pub use crate::prelude::*;
diff --git a/swarm_nl/src/prelude.rs b/swarm_nl/src/prelude.rs
index 7090aa08b..c02a5c370 100644
--- a/swarm_nl/src/prelude.rs
+++ b/swarm_nl/src/prelude.rs
@@ -1,8 +1,10 @@
+// Copyright 2024 Algorealm
+// Apache 2.0 License
+
+//! Types and traits that are used throughout the library.
+
use libp2p_identity::{KeyType, PeerId};
use std::net::Ipv4Addr;
-/// Copyright (c) 2024 Algorealm
-///
-/// This file is part of the SwarmNL library.
use thiserror::Error;
/// Default IP address when no address is specified.
diff --git a/swarm_nl/src/setup.rs b/swarm_nl/src/setup.rs
index 57bc51be8..2e1c8817b 100644
--- a/swarm_nl/src/setup.rs
+++ b/swarm_nl/src/setup.rs
@@ -1,9 +1,8 @@
-/// Copyright (c) 2024 Algorealm
+// Copyright 2024 Algorealm
+// Apache 2.0 License
+
+//! Data structures and functions to setup a node identity and configure it for networking.
-// The module containing the data structures and functions to setup a node identity and
-/// configure it for networking.
-///
-/// This file is part of the SwarmNl library.
use std::collections::HashMap;
use libp2p_identity::rsa;
diff --git a/swarm_nl/src/util.rs b/swarm_nl/src/util.rs
index 0b636caf3..197e49d2f 100644
--- a/swarm_nl/src/util.rs
+++ b/swarm_nl/src/util.rs
@@ -1,6 +1,8 @@
-/// Copyright (c) 2024 Algorealm
-///
-/// This file is part of the SwarmNl library.
+// Copyright 2024 Algorealm
+// Apache 2.0 License
+
+//! Utility helpers for reading from and writing to `.ini` files.
+
use crate::{prelude::*, setup::BootstrapConfig};
use base58::FromBase58;
use ini::Ini;
From b497428ab68e056362ae45f576fcc5ee6ea316e9 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 15:59:24 +0200
Subject: [PATCH 002/122] docs: add doc folder structure
---
swarm_nl/doc/core/ApplicationInteraction.md | 7 ++
swarm_nl/doc/core/NetworkBuilder.md | 36 ++++++
swarm_nl/doc/setup/NodeSetup.md | 126 ++++++++++++++++++++
3 files changed, 169 insertions(+)
create mode 100644 swarm_nl/doc/core/ApplicationInteraction.md
create mode 100644 swarm_nl/doc/core/NetworkBuilder.md
create mode 100644 swarm_nl/doc/setup/NodeSetup.md
diff --git a/swarm_nl/doc/core/ApplicationInteraction.md b/swarm_nl/doc/core/ApplicationInteraction.md
new file mode 100644
index 000000000..175f572b9
--- /dev/null
+++ b/swarm_nl/doc/core/ApplicationInteraction.md
@@ -0,0 +1,7 @@
+# Application Interaction
+
+How the application interacts with the swarm (different tasks and stream data).
+
+Request / response protocol
+
+TODO
\ No newline at end of file
diff --git a/swarm_nl/doc/core/NetworkBuilder.md b/swarm_nl/doc/core/NetworkBuilder.md
new file mode 100644
index 000000000..125c3a927
--- /dev/null
+++ b/swarm_nl/doc/core/NetworkBuilder.md
@@ -0,0 +1,36 @@
+# Network builder
+
+The [`CoreBuilder::with_config`] method takes in one parameter for the bootstrap config done by [`setup::BootstrapConfig`] and another parameter to pass in an [`EventHandler`] to handle network events. With this you can build a swarm like this:
+
+```rust
+ let config = BootstrapConfig::default();
+ let handler = DefaultHandler; // from the core library
+ let mut network = swarm_nl::core::CoreBuilder::with_config(config, complex_handler).build().await.unwrap();
+```
+
+## Event Handlers
+
+You could always just use the [`DefaultHandler`] provided by the library. But if you wanted to add any custom logic around how you want to handle network events, you must implement the methods you want from [`EventHandler`]. For example:
+
+```rust
+use swarm_nl::core::EventHandler;
+
+struct ComplexHandler;
+
+impl EventHandler for ComplexHandler {
+ fn new_listen_addr(&mut self, _listener_id: ListenerId, addr: Multiaddr) {
+ // Log the address we begin listening on
+ println!("We're now listening on: {}", addr);
+ }
+}
+```
+
+## Custom configurations
+
+You can specify custom transport layers for TCP and QUIC (in the future we will be able to handle other transport options such as WebRTC).
+
+# Implementing your own protocols
+
+For now, the protocols we've implemented are Ping, Kademlia and Identify. You could always introduce your own custom protocol, for example:
+
+TODO
\ No newline at end of file
diff --git a/swarm_nl/doc/setup/NodeSetup.md b/swarm_nl/doc/setup/NodeSetup.md
new file mode 100644
index 000000000..f48fa74de
--- /dev/null
+++ b/swarm_nl/doc/setup/NodeSetup.md
@@ -0,0 +1,126 @@
+# Node setup
+
+When we say "node setup" we mean the requirements to launch a single or a set of peers that can bootstrap the network. This requires passing in an `.ini` file with bootstrap configuration data such as bootstrap nodes, TCP/UDP ports and cryptographic types for keypair generation. Have a look at the template you can use which outlines the different fields you can include.
+
+If you're setting up a new network for the first time, you don't need to pass in any bootnodes. If you're joining an exisiting network, you need to ask someone for their bootnode addresses to connect to. The [`BootstrapConfig`] object will handle reading the `.ini` file to build a configuration. Then the [`CoreBuilder`] object launches the network with that config.
+
+Once the configuration is setup, a stream can be polled.
+
+## Examples
+
+For any node setup, you need a valid `.ini` file to create the bootstrap config object and a `network_handler` object to specify what events you would like to listen to and how you want to handle them.
+
+1. In this example, we'll setup a new network that uses `Ed25519` for keypair generation and uses the [`DefaultHandler`] from the core library.
+
+Put this `.ini` file at the root of your project:
+
+```ini
+[ports]
+; TCP/IP port to listen on
+tcp=3000
+; UDP port to listen on
+udp=4000
+
+[auth]
+; Type of keypair to generate for node identity and message auth e.g RSA, EDSA, Ed25519
+crypto=Ed25519
+; The protobuf serialized format of the node's cryptographic keypair
+protobuf_keypair=[]
+```
+
+Run the following:
+
+```rust
+use swarm_nl::core::DefaultHandler;
+
+#[tokio::main]
+async fn main() {
+
+ // network handler object
+ let handler = DefaultHandler;
+
+ // use the default setup, TCP
+ let default_config = swarm_nl::setup::BootstrapConfig::default();
+
+ // set up network core
+ let mut network = swarm_nl::core::CoreBuilder::with_config(default_config, handler)
+ .build()
+ .await
+ .unwrap();
+
+ // read first (ready) message
+ if let Some(StreamData::Ready) = network.application_receiver.next().await {
+ println!("Database is online");
+
+ // begin listening
+ loop {
+ if let Some(data) = network.application_receiver.next().await {
+ println!("{:?}", data);
+ }
+ }
+ }
+}
+```
+
+1. In this example, we'll implement our own event handler to override the default handler bahavior and explicitly connect to known bootnodes:
+
+```rust
+use swarm_nl::core::DefaultHandler;
+
+struct ComplexHandler;
+
+impl EventHandler for ComplexHandler {
+ fn new_listen_addr(&mut self, _listener_id: ListenerId, addr: Multiaddr) {
+ // Log the address we begin listening on
+ println!("We're now listening on: {}", addr);
+ }
+}
+
+#[tokio::main]
+async fn main() {
+ // handler for events happening in the network layer (majorly for technical use)
+ // use default handler
+ let handler = DefaultHandler;
+ let complex_handler = ComplexHandler;
+
+ // set up node
+ let mut bootnodes: HashMap = HashMap::new();
+ bootnodes.insert(
+ "12D3KooWBmwXN3rsVfnLsZKbXeBrSLfczHxZHwVjPrbKwpLfYm3t".to_string(),
+ "/ip4/127.0.0.1/tcp/63307".to_string(),
+ );
+
+ // configure default data
+ let config = swarm_nl::setup::BootstrapConfig::new().with_bootnodes(bootnodes);
+
+ // set up network core
+ let mut network = swarm_nl::core::CoreBuilder::with_config(config, complex_handler)
+ .build()
+ .await
+ .unwrap();
+
+ // read first (ready) message
+ if let Some(StreamData::Ready) = network.application_receiver.next().await {
+ println!("Database is online");
+
+ // begin listening
+ loop {
+ if let Some(data) = network.application_receiver.next().await {
+ println!("{:?}", data);
+ }
+ }
+ }
+}
+```
+
+
+## Fallback behaviour
+
+Node setup will fallback to default behavior if:
+* The `ini` file doesn't contain any `[ports]` section, the fallback ports for TCP is [`MIN_PORT`] and for UDP is [`MAX_PORT`].
+* No key type is specified, the default is always `Ed25519`
+* No bootnodes are passed in, an empty hashmap is created
+
+## Panics
+* If no `.ini` file is supplied
+* If the `.ini` file does not contain a valid keypair
\ No newline at end of file
From bb270fbaaa9a420f4c2490b1bcf0d72f544c9540 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 15:59:37 +0200
Subject: [PATCH 003/122] docs: minor fixes
---
swarm_nl/src/core/mod.rs | 20 +++++++++++++-------
swarm_nl/src/prelude.rs | 31 ++++++++++++++++---------------
swarm_nl/src/setup.rs | 3 ++-
3 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/swarm_nl/src/core/mod.rs b/swarm_nl/src/core/mod.rs
index c40249222..5a44b5227 100644
--- a/swarm_nl/src/core/mod.rs
+++ b/swarm_nl/src/core/mod.rs
@@ -1,5 +1,10 @@
-/// Copyright (c) 2024 Algorealm
-/// Core data structures and protocol implementations for building a swarm.
+// Copyright 2024 Algorealm
+// Apache 2.0 License
+
+//! Core data structures and protocol implementations for building a swarm.
+
+#![doc = include_str!("../../doc/core/NetworkBuilder.md")]
+
use std::{
collections::{HashMap, HashSet},
net::{IpAddr, Ipv4Addr},
@@ -607,16 +612,17 @@ impl CoreBuilder {
}
}
-/// The core interface for the application layer to interface with the networking layer
+
+/// The core interface for the application layer to interface with the networking layer.
#[derive(Clone)]
pub struct Core {
keypair: Keypair,
/// The producing end of the stream that sends data to the network layer from the
- /// application
+ /// application.
application_sender: Sender,
- /// The consuming end of the stream that recieves data from the network layer
+ /// The consuming end of the stream that recieves data from the network layer.
// application_receiver: Receiver,
- /// The producing end of the stream that sends data from the network layer to the application
+ /// The producing end of the stream that sends data from the network layer to the application.
// network_sender: Sender,
/// This serves as a buffer for the results of the requests to the network layer.
/// With this, applications can make async requests and fetch their results at a later time
@@ -826,7 +832,7 @@ impl Core {
}
}
- /// Handle async operations, which basically involved handling two major data sources:
+ /// Handle async operations, which basically involves handling two major data sources:
/// - Streams coming from the application layer.
/// - Events generated by (libp2p) network activities.
/// Important information are sent to the application layer over a (mpsc) stream
diff --git a/swarm_nl/src/prelude.rs b/swarm_nl/src/prelude.rs
index c02a5c370..56d1cdc57 100644
--- a/swarm_nl/src/prelude.rs
+++ b/swarm_nl/src/prelude.rs
@@ -1,7 +1,7 @@
// Copyright 2024 Algorealm
// Apache 2.0 License
-//! Types and traits that are used throughout the library.
+//! Types and traits that are used throughout SwarmNL.
use libp2p_identity::{KeyType, PeerId};
use std::net::Ipv4Addr;
@@ -34,19 +34,20 @@ pub enum SwarmNlError {
NetworkIdParseError(String),
}
-/// Generic SwarmNl result type
+/// Generic SwarmNl result type.
pub type SwarmNlResult = Result;
-/// Port type
+/// Port type.
pub type Port = u16;
-/// Seconds type
+/// Seconds type.
pub type Seconds = u64;
-/// The stringified PeerId type
+/// The stringified PeerId type.
pub type PeerIdString = String;
-/// The stringified Multiaddr type
+/// The stringified Multiaddr type.
pub type MultiaddrString = String;
-/// Port ranges
+/// Lower bound port range (u16::MIN).
pub const MIN_PORT: u16 = 49152;
+/// Upper bound port range (u16::MAX).
pub const MAX_PORT: u16 = 65535;
/// Default network id
@@ -56,8 +57,9 @@ pub static DEFAULT_NETWORK_ID: &str = "/swarmnl/1.0";
/// separates it from others.
pub static MIN_NETWORK_ID_LENGTH: u8 = 4;
-/// Implement From<&str> for libp2p2_identity::KeyType.
-/// We'll define a custom trait because of the Rust visibility rule to solve this problem
+/// An implementation of [`From<&str>`] for [`KeyType`] to read a key type from a file.
+///
+/// We define a custom trait because of the Rust visibility rule.
pub trait CustomFrom {
fn from(string: &str) -> Option
where
@@ -76,15 +78,14 @@ impl CustomFrom for KeyType {
}
}
-/// Supported transport protocols
+/// Supported transport protocols.
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub enum TransportOpts {
- /// QUIC transport protocol enabled with TCP/IP as fallback.
- /// DNS lookup is also configured by default
+ /// QUIC transport protocol enabled with TCP/IP as fallback. DNS lookup is also configured by default.
TcpQuic { tcp_config: TcpConfig },
}
-/// TCP setup Config
+/// TCP setup configuration.
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub enum TcpConfig {
/// Default configuration specified in the [libp2p docs](https://docs.rs/libp2p/latest/libp2p/tcp/struct.Config.html#method.new).
@@ -101,8 +102,8 @@ pub enum TcpConfig {
},
}
-/// A unique type that indicates that a struct is not yet initialized to its default state
+/// A unique type that indicates that a struct is not yet initialized to its default state.
pub struct NotInitialiazed;
-/// A unique type that indicates that a struct has been default configured
+/// A unique type that indicates that a struct has been default configured.
pub struct Initialized;
diff --git a/swarm_nl/src/setup.rs b/swarm_nl/src/setup.rs
index 2e1c8817b..a2b129b54 100644
--- a/swarm_nl/src/setup.rs
+++ b/swarm_nl/src/setup.rs
@@ -77,12 +77,13 @@ impl BootstrapConfig {
}
/// Generate a Cryptographic Keypair.
+ ///
/// An RSA keypair cannot be generated on-the-fly. It has to be generated from a `.pk8` file.
/// Hence the `Option` parameter is always `None` except in the case of RSA.
/// Please note that calling this function overrides whatever might have been read from the
/// `.ini` file
///
- /// # Panics (Only applies to the RSA keypair instance)
+ /// # Panics
///
/// This function will panic if:
/// 1. The RSA key type is specified and the `rsa_pk8_filepath` is set to `None`.
From c5204f5f1a6bfce6fdabe29e603237a9d63d77de Mon Sep 17 00:00:00 2001
From: thewoodfish
Date: Fri, 10 May 2024 15:35:02 +0100
Subject: [PATCH 004/122] chore: complete gossipsub impl
---
client/src/main.rs | 32 +++--
swarm_nl/Cargo.toml | 2 +-
swarm_nl/src/core/mod.rs | 237 +++++++++++++++++++++++++++--------
swarm_nl/src/core/prelude.rs | 148 ++++++++++++----------
swarm_nl/src/lib.rs | 1 -
swarm_nl/src/prelude.rs | 2 +
6 files changed, 295 insertions(+), 127 deletions(-)
diff --git a/client/src/main.rs b/client/src/main.rs
index 414f36ce8..6ca4fe755 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -6,7 +6,6 @@
/// Enjoy!
use std::{borrow::Cow, num::NonZeroU32, time::Duration};
use swarm_nl::{
- async_trait,
core::{EventHandler, AppResponse},
core::{AppData, Core, CoreBuilder},
setup::BootstrapConfig,
@@ -44,9 +43,8 @@ impl Empire {
}
}
-#[async_trait]
impl EventHandler for Empire {
- async fn new_listen_addr(
+ fn new_listen_addr(
&mut self,
local_peer_id: PeerId,
_listener_id: swarm_nl::ListenerId,
@@ -61,7 +59,7 @@ impl EventHandler for Empire {
);
}
- async fn connection_established(
+ fn connection_established(
&mut self,
peer_id: PeerId,
_connection_id: ConnectionId,
@@ -145,11 +143,6 @@ pub async fn play_game() {
// TODO! FUNCTION TO CHECK NODES I'M CONNECTED WITH
- // TODO: Wait a little to help the network boot
-
- // Let them connect first
- tokio::time::sleep(Duration::from_secs(6)).await;
-
let request = vec!["military_status".as_bytes().to_vec()];
// Spartan Empire
@@ -162,11 +155,11 @@ pub async fn play_game() {
};
// Send request
- let stream_id = core.send_to_network(status_request).await.unwrap();
+ // let stream_id = core.send_to_network(status_request).await.unwrap();
// Get response
// AppData::Fetch returns a Vec>, hence we can parse the response from it
- if let Ok(status_response) = core.recv_from_network(stream_id).await {
+ if let Ok(status_response) = core.fetch_from_network(status_request).await {
if let AppResponse::FetchData(status) = status_response {
let empire_name = String::from_utf8_lossy(&status[0]);
let military_status = status[1][0];
@@ -181,3 +174,20 @@ pub async fn play_game() {
// Keep looping so we can record network events
loop {}
}
+
+
+// make pr
+// merge to main
+// loggings
+// network data
+// gossip
+// examples
+// appdata
+// configure logger
+
+
+// TEST
+// Events, dailing, AppData, RPC, Kad, Ping, Gossip
+// check for rexeports e.g to initialize gossipsub
+
+// check if i'm subscribed to topics
\ No newline at end of file
diff --git a/swarm_nl/Cargo.toml b/swarm_nl/Cargo.toml
index 8e6424893..d2608d2c2 100644
--- a/swarm_nl/Cargo.toml
+++ b/swarm_nl/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
rust-ini = "0.20.0"
thiserror = "1.0.58"
rand = "0.8.5"
-libp2p = { version="0.53.2", "features"=["async-std", "macros", "ping", "tokio", "tcp", "noise", "yamux", "quic", "tls", "dns", "kad", "identify", "request-response", "cbor"] }
+libp2p = { version="0.53.2", "features"=["async-std", "macros", "ping", "tokio", "tcp", "noise", "yamux", "quic", "tls", "dns", "kad", "identify", "request-response", "cbor", "gossipsub"] }
libp2p-identity = { version="0.2.8", "features"=["secp256k1", "ecdsa", "rsa", "ed25519"] }
futures = "0.3.30"
futures-time = "3.0.0"
diff --git a/swarm_nl/src/core/mod.rs b/swarm_nl/src/core/mod.rs
index c40249222..d24534523 100644
--- a/swarm_nl/src/core/mod.rs
+++ b/swarm_nl/src/core/mod.rs
@@ -15,8 +15,8 @@ use futures::{
channel::mpsc::{self, Receiver, Sender},
select, SinkExt, StreamExt,
};
-use futures_time::time::Duration as AsyncDuration;
use libp2p::{
+ gossipsub::{self, IdentTopic, Topic, TopicHash},
identify::{self, Info},
kad::{self, store::MemoryStore, Record},
multiaddr::Protocol,
@@ -50,6 +50,7 @@ struct CoreBehaviour {
kademlia: kad::Behaviour,
identify: identify::Behaviour,
request_response: request_response::cbor::Behaviour,
+ gossipsub: gossipsub::Behaviour,
}
/// Network events generated as a result of supported and configured `NetworkBehaviour`'s
@@ -59,6 +60,7 @@ enum CoreEvent {
Kademlia(kad::Event),
Identify(identify::Event),
RequestResponse(request_response::Event),
+ Gossipsub(gossipsub::Event),
}
/// Implement ping events for [`CoreEvent`]
@@ -89,6 +91,13 @@ impl From> for CoreEvent {
}
}
+/// Implement gossipsub events for [`CoreEvent`]
+impl From for CoreEvent {
+ fn from(event: gossipsub::Event) -> Self {
+ CoreEvent::Gossipsub(event)
+ }
+}
+
/// Structure containing necessary data to build [`Core`]
pub struct CoreBuilder {
network_id: StreamProtocol,
@@ -97,8 +106,6 @@ pub struct CoreBuilder {
boot_nodes: HashMap,
/// the network event handler
handler: T,
- /// Prevents blocking forever due to absence of expected data from the network layer
- network_read_delay: AsyncDuration,
/// The size of the stream buffers to use to track application requests to the network layer
/// internally.
stream_size: usize,
@@ -116,6 +123,8 @@ pub struct CoreBuilder {
/// The `Behaviour` of the `Request-Response` protocol.
/// The second field value is the function to handle an incoming request from a peer
request_response: Behaviour,
+ /// The `Behaviour` of the `GossipSub` protocol
+ gossipsub: gossipsub::Behaviour,
}
impl CoreBuilder {
@@ -152,6 +161,15 @@ impl CoreBuilder {
request_response::Config::default(),
);
+ // Set up default config for gossiping
+ let cfg = gossipsub::Config::default();
+ let gossipsub = gossipsub::Behaviour::new(
+ gossipsub::MessageAuthenticity::Signed(config.keypair()),
+ cfg,
+ )
+ .map_err(|_| SwarmNlError::GossipConfigError)
+ .unwrap();
+
// Initialize struct with information from `BootstrapConfig`
CoreBuilder {
network_id: StreamProtocol::new(network_id),
@@ -159,8 +177,6 @@ impl CoreBuilder {
tcp_udp_port: config.ports(),
boot_nodes: config.bootnodes(),
handler,
- // Timeout defaults to 60 seconds
- network_read_delay: AsyncDuration::from_secs(NETWORK_READ_TIMEOUT),
stream_size: usize::MAX,
// Default is to listen on all interfaces (ipv4)
ip_address: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
@@ -175,6 +191,7 @@ impl CoreBuilder {
kademlia,
identify,
request_response,
+ gossipsub,
}
}
@@ -199,17 +216,6 @@ impl CoreBuilder {
CoreBuilder { ip_address, ..self }
}
- /// Configure the timeout for requests to read from the network layer.
- /// Reading from the network layer could potentially block if the data corresponding to the
- /// [`StreamId`] specified could not be found (or has been read already). This prevents the
- /// future from `await`ing forever. Defaults to 60 seconds
- pub fn with_network_read_delay(self, network_read_delay: AsyncDuration) -> Self {
- CoreBuilder {
- network_read_delay,
- ..self
- }
- }
-
/// Configure how long to keep a connection alive (in seconds) once it is idling.
pub fn with_idle_connection_timeout(self, keep_alive_duration: Seconds) -> Self {
CoreBuilder {
@@ -272,6 +278,22 @@ impl CoreBuilder {
CoreBuilder { kademlia, ..self }
}
+ /// Configure the `Gossipsub` protocol for the network
+ /// # Panics
+ ///
+ /// THis function panics if `Gossipsub` cannot be configured properly
+ pub fn with_gossipsub(
+ self,
+ config: gossipsub::Config,
+ auth: gossipsub::MessageAuthenticity,
+ ) -> Self {
+ let gossipsub = gossipsub::Behaviour::new(auth, config)
+ .map_err(|_| SwarmNlError::GossipConfigError)
+ .unwrap();
+
+ CoreBuilder { gossipsub, ..self }
+ }
+
/// Configure the transports to support.
pub fn with_transports(self, transport: TransportOpts) -> Self {
CoreBuilder { transport, ..self }
@@ -367,7 +389,8 @@ impl CoreBuilder {
ping: self.ping.0,
kademlia: self.kademlia,
identify: self.identify,
- request_response: self.request_response
+ request_response: self.request_response,
+ gossipsub: self.gossipsub
})
.map_err(|_| SwarmNlError::ProtocolConfigError)?
.with_swarm_config(|cfg| {
@@ -439,7 +462,8 @@ impl CoreBuilder {
ping: self.ping.0,
kademlia: self.kademlia,
identify: self.identify,
- request_response: self.request_response
+ request_response: self.request_response,
+ gossipsub: self.gossipsub
})
.map_err(|_| SwarmNlError::ProtocolConfigError)?
.with_swarm_config(|cfg| {
@@ -513,8 +537,10 @@ impl CoreBuilder {
// application and the application will comsume it (single consumer) The second stream
// will have SwarmNl (being the consumer) recieve data and commands from multiple areas
// in the application;
- let (application_sender, network_receiver) = mpsc::channel::(100);
- let (network_sender, application_receiver) = mpsc::channel::(100);
+ let (application_sender, network_receiver) =
+ mpsc::channel::(STREAM_BUFFER_CAPACITY);
+ let (network_sender, application_receiver) =
+ mpsc::channel::(STREAM_BUFFER_CAPACITY);
// Set up the ping network info.
// `PeerId` does not implement `Default` so we will add the peerId of this node as seed
@@ -563,7 +589,6 @@ impl CoreBuilder {
// application_receiver,
stream_request_buffer: stream_request_buffer.clone(),
stream_response_buffer: stream_response_buffer.clone(),
- network_read_delay: self.network_read_delay,
current_stream_id: Arc::new(Mutex::new(stream_id)),
// Save handler as the state of the application
state: self.handler,
@@ -603,6 +628,14 @@ impl CoreBuilder {
network_core.clone(),
));
+ // Wait for a few seconds before passing control to the application
+ #[cfg(feature = "async-std-runtime")]
+ async_std::task::sleep(Duration::from_secs(BOOT_WAIT_TIME)).await;
+
+ // Wait for a few seconds before passing control to the application
+ #[cfg(feature = "tokio-runtime")]
+ tokio::time::sleep(Duration::from_secs(BOOT_WAIT_TIME)).await;
+
Ok(network_core)
}
}
@@ -625,8 +658,6 @@ pub struct Core {
stream_response_buffer: Arc>,
/// Store a [`StreamId`] representing a network request
stream_request_buffer: Arc>,
- /// The network read timeout
- network_read_delay: AsyncDuration,
/// Current stream id. Useful for opening new streams, we just have to bump the number by 1
current_stream_id: Arc>,
/// The state of the application
@@ -737,7 +768,7 @@ impl Core {
return Err(NetworkError::NetworkReadTimeout);
}
- // Failed to acquire the lock, sleep and retry
+ // Response has not arrived, sleep and retry
async_std::task::sleep(Duration::from_secs(TASK_SLEEP_DURATION)).await;
}
});
@@ -770,7 +801,7 @@ impl Core {
return Err(NetworkError::NetworkReadTimeout);
}
- // Failed to acquire the lock, sleep and retry
+ // Response has not arrived, sleep and retry
tokio::time::sleep(Duration::from_secs(TASK_SLEEP_DURATION)).await;
}
});
@@ -809,6 +840,8 @@ impl Core {
match response {
// Send response to request operations specified by the application layer
StreamData::ToApplication(stream_id, response) => match response {
+ // Error
+ AppResponse::Error(error) => buffer_guard.insert(stream_id, Err(error)),
res @ AppResponse::Echo(..) => buffer_guard.insert(stream_id, Ok(res)),
res @ AppResponse::DailPeer(..) => buffer_guard.insert(stream_id, Ok(res)),
res @ AppResponse::KademliaStoreRecordSuccess => buffer_guard.insert(stream_id, Ok(res)),
@@ -816,8 +849,12 @@ impl Core {
res @ AppResponse::KademliaGetProviders{..} => buffer_guard.insert(stream_id, Ok(res)),
res @ AppResponse::KademliaGetRoutingTableInfo { .. } => buffer_guard.insert(stream_id, Ok(res)),
res @ AppResponse::FetchData(..) => buffer_guard.insert(stream_id, Ok(res)),
- // Error
- AppResponse::Error(error) => buffer_guard.insert(stream_id, Err(error))
+ res @ AppResponse::GetNetworkInfo{..} => buffer_guard.insert(stream_id, Ok(res)),
+ res @ AppResponse::GossipsubBroadcastSuccess => buffer_guard.insert(stream_id, Ok(res)),
+ res @ AppResponse::GossipsubJoinSuccess => buffer_guard.insert(stream_id, Ok(res)),
+ res @ AppResponse::GossipsubExitSuccess => buffer_guard.insert(stream_id, Ok(res)),
+ res @ AppResponse::GossipsubBlacklistSuccess => buffer_guard.insert(stream_id, Ok(res)),
+ res @ AppResponse::GossipsubGetInfo{..} => buffer_guard.insert(stream_id, Ok(res)),
},
_ => false
};
@@ -837,7 +874,6 @@ impl Core {
mut receiver: Receiver,
mut network_core: Core,
) {
-
let mut exec_queue_1 = ExecQueue::new();
let mut exec_queue_2 = ExecQueue::new();
let mut exec_queue_3 = ExecQueue::new();
@@ -938,7 +974,97 @@ impl Core {
// Send streamId to libp2p events, to track response
exec_queue_4.push(stream_id).await;
+ },
+ // Return important information about the node
+ AppData::GetNetworkInfo => {
+ // Connected peers
+ let connected_peers = swarm.connected_peers().map(|peer| peer.to_owned()).collect::>();
+
+ // External Addresses
+ let external_addresses = swarm.listeners().map(|multiaddr| multiaddr.to_string()).collect::>();
+
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GetNetworkInfo { peer_id: swarm.local_peer_id().clone(), connected_peers, external_addresses })).await;
+ },
+ // Send gossip message to peers
+ AppData::GossipsubBroadcastMessage { message, peers: _, topic } => {
+ // Get the topic hash
+ let topic_hash = TopicHash::from_raw(topic);
+
+ // Marshall message into a single string
+ let message = message.join("~#~");
+
+ // Check if we're already subscribed to the topic
+ let is_subscribed = swarm.behaviour().gossipsub.mesh_peers(&topic_hash).any(|peer| peer == swarm.local_peer_id());
+
+ // Gossip
+ if swarm
+ .behaviour_mut().gossipsub
+ .publish(topic_hash, message.as_bytes()).is_ok() && !is_subscribed {
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GossipsubBroadcastSuccess)).await;
+ } else {
+ // Return error
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::Error(NetworkError::GossipsubBroadcastMessageError))).await;
+ }
+ },
+ // Join a mesh network
+ AppData::GossipsubJoinNetwork(topic) => {
+ // Create a new topic
+ let topic = IdentTopic::new(topic);
+
+ // Subscribe
+ if swarm.behaviour_mut().gossipsub.subscribe(&topic).is_ok() {
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GossipsubJoinSuccess)).await;
+ } else {
+ // Return error
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::Error(NetworkError::GossipsubJoinNetworkError))).await;
+ }
+ },
+ // Get information concerning our gossiping
+ AppData::GossipsubGetInfo => {
+ // Topics we're subscribed to
+ let subscribed_topics = swarm.behaviour().gossipsub.topics().map(|topic| topic.clone().into_string()).collect::>();
+
+ // Peers we know and the topics they are subscribed too
+ let mesh_peers = swarm.behaviour().gossipsub.all_peers().map(|(peer, topics)| {
+ (peer.to_owned(), topics.iter().map(|&t| t.clone().as_str().to_owned()).collect::>())
+ }).collect::>();
+
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GossipsubGetInfo { topics: subscribed_topics, mesh_peers })).await;
+ },
+ // Exit a network we're a part of
+ AppData::GossipsubExitNetwork(topic) => {
+ // Create a new topic
+ let topic = IdentTopic::new(topic);
+
+ // Subscribe
+ if swarm.behaviour_mut().gossipsub.unsubscribe(&topic).is_ok() {
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GossipsubExitSuccess)).await;
+ } else {
+ // Return error
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::Error(NetworkError::GossipsubJoinNetworkError))).await;
+ }
}
+ // Blacklist a peer explicitly
+ AppData::GossipsubBlacklistPeer(peer) => {
+ // Add to list
+ swarm.behaviour_mut().gossipsub.blacklist_peer(&peer);
+
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GossipsubBlacklistSuccess)).await;
+ },
+ // Remove a peer from the blacklist
+ AppData::GossipsubFilterBlacklist(peer) => {
+ // Add to list
+ swarm.behaviour_mut().gossipsub.remove_blacklisted_peer(&peer);
+
+ // Send the response back to the application layer
+ let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::GossipsubBlacklistSuccess)).await;
+ },
}
}
_ => {}
@@ -956,7 +1082,7 @@ impl Core {
address,
} => {
// call configured handler
- network_core.state.new_listen_addr(swarm.local_peer_id().to_owned(), listener_id, address).await;
+ network_core.state.new_listen_addr(swarm.local_peer_id().to_owned(), listener_id, address);
}
SwarmEvent::Behaviour(event) => match event {
// Ping
@@ -996,7 +1122,7 @@ impl Core {
}
// Call custom handler
- network_core.state.inbound_ping_success(peer, duration).await;
+ network_core.state.inbound_ping_success(peer, duration);
}
// Outbound ping failure
Err(err_type) => {
@@ -1065,7 +1191,7 @@ impl Core {
}
// Call custom handler
- network_core.state.outbound_ping_error(peer, err_type).await;
+ network_core.state.outbound_ping_error(peer, err_type);
}
}
}
@@ -1120,7 +1246,7 @@ impl Core {
}
// Call handler
- network_core.state.kademlia_put_record_success(key.to_vec()).await;
+ network_core.state.kademlia_put_record_success(key.to_vec());
}
kad::QueryResult::PutRecord(Err(e)) => {
let key = match e {
@@ -1134,17 +1260,17 @@ impl Core {
}
// Call handler
- network_core.state.kademlia_put_record_error().await;
+ network_core.state.kademlia_put_record_error();
}
kad::QueryResult::StartProviding(Ok(kad::AddProviderOk {
key,
})) => {
// Call handler
- network_core.state.kademlia_start_providing_success(key.to_vec()).await;
+ network_core.state.kademlia_start_providing_success(key.to_vec());
}
kad::QueryResult::StartProviding(Err(_)) => {
// Call handler
- network_core.state.kademlia_start_providing_error().await;
+ network_core.state.kademlia_start_providing_error();
}
_ => {}
},
@@ -1155,7 +1281,7 @@ impl Core {
CoreEvent::Identify(event) => match event {
identify::Event::Received { peer_id, info } => {
// We just recieved an `Identify` info from a peer.s
- network_core.state.identify_info_recieved(peer_id, info.clone()).await;
+ network_core.state.identify_info_recieved(peer_id, info.clone());
// disconnect from peer of the network id is different
if info.protocol_version != network_info.id.as_ref() {
@@ -1206,9 +1332,22 @@ impl Core {
if let Some(stream_id) = exec_queue_4.pop().await {
// Send the error back to the application layer
let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::Error(NetworkError::RpcDataFetchError))).await;
- }
+ }
},
_ => {}
+ },
+ // Gossipsub
+ CoreEvent::Gossipsub(event) => match event {
+ // We've recieved an inbound message
+ gossipsub::Event::Message { propagation_source, message_id, message } => {
+
+ },
+ // A peer just subscribed
+ gossipsub::Event::Subscribed { peer_id, topic } => {
+ // We want to check that we care about the topic and then add it to our mesh
+ },
+ gossipsub::Event::Unsubscribed { peer_id, topic } => todo!(),
+ _ => {},
}
},
SwarmEvent::ConnectionEstablished {
@@ -1226,7 +1365,7 @@ impl Core {
&endpoint,
num_established,
established_in,
- ).await;
+ );
}
SwarmEvent::ConnectionClosed {
peer_id,
@@ -1242,14 +1381,14 @@ impl Core {
&endpoint,
num_established,
cause,
- ).await;
+ );
}
SwarmEvent::ExpiredListenAddr {
listener_id,
address,
} => {
// call configured handler
- network_core.state.expired_listen_addr(listener_id, address).await;
+ network_core.state.expired_listen_addr(listener_id, address);
}
SwarmEvent::ListenerClosed {
listener_id,
@@ -1257,33 +1396,33 @@ impl Core {
reason: _,
} => {
// call configured handler
- network_core.state.listener_closed(listener_id, addresses).await;
+ network_core.state.listener_closed(listener_id, addresses);
}
SwarmEvent::ListenerError {
listener_id,
error: _,
} => {
// call configured handler
- network_core.state.listener_error(listener_id).await;
+ network_core.state.listener_error(listener_id);
}
SwarmEvent::Dialing {
peer_id,
connection_id,
} => {
// call configured handler
- network_core.state.dialing(peer_id, connection_id).await;
+ network_core.state.dialing(peer_id, connection_id);
}
SwarmEvent::NewExternalAddrCandidate { address } => {
// call configured handler
- network_core.state.new_external_addr_candidate(address).await;
+ network_core.state.new_external_addr_candidate(address);
}
SwarmEvent::ExternalAddrConfirmed { address } => {
// call configured handler
- network_core.state.external_addr_confirmed(address).await;
+ network_core.state.external_addr_confirmed(address);
}
SwarmEvent::ExternalAddrExpired { address } => {
// call configured handler
- network_core.state.external_addr_expired(address).await;
+ network_core.state.external_addr_expired(address);
}
SwarmEvent::IncomingConnection {
connection_id,
@@ -1291,7 +1430,7 @@ impl Core {
send_back_addr,
} => {
// call configured handler
- network_core.state.incoming_connection(connection_id, local_addr, send_back_addr).await;
+ network_core.state.incoming_connection(connection_id, local_addr, send_back_addr);
}
SwarmEvent::IncomingConnectionError {
connection_id,
@@ -1304,7 +1443,7 @@ impl Core {
connection_id,
local_addr,
send_back_addr,
- ).await;
+ );
}
SwarmEvent::OutgoingConnectionError {
connection_id,
@@ -1312,7 +1451,7 @@ impl Core {
error: _,
} => {
// call configured handler
- network_core.state.outgoing_connection_error(connection_id, peer_id).await;
+ network_core.state.outgoing_connection_error(connection_id, peer_id);
}
_ => todo!(),
}
diff --git a/swarm_nl/src/core/prelude.rs b/swarm_nl/src/core/prelude.rs
index 0056904ea..92055cf60 100644
--- a/swarm_nl/src/core/prelude.rs
+++ b/swarm_nl/src/core/prelude.rs
@@ -1,7 +1,6 @@
-use async_trait::async_trait;
/// Copyright (c) 2024 Algorealm
use serde::{Deserialize, Serialize};
-use std::{time::Instant, collections::VecDeque};
+use std::{collections::VecDeque, time::Instant};
use thiserror::Error;
use self::ping_config::PingInfo;
@@ -15,9 +14,16 @@ pub const NETWORK_READ_TIMEOUT: u64 = 60;
/// The time it takes for the task to sleep before it can recheck if an output has been placed in
/// the repsonse buffer (7 seconds)
pub const TASK_SLEEP_DURATION: u64 = 7;
+
/// Type that represents the response of the network layer to the application layer's event handler
pub type AppResponseResult = Result;
+/// Time to wait (in seconds) for node (network layer) to boot
+pub const BOOT_WAIT_TIME: u64 = 1;
+
+/// The buffer capacity of an mpsc stream
+pub const STREAM_BUFFER_CAPACITY: usize = 100;
+
/// Data exchanged over a stream between the application and network layer
#[derive(Debug, Clone)]
pub(super) enum StreamData {
@@ -55,8 +61,26 @@ pub enum AppData {
KademliaGetRoutingTableInfo,
/// Fetch data(s) quickly from a peer over the network
FetchData { keys: Vec>, peer: PeerId },
- // Get network information
- // Gossip related requests
+ /// Get network information about the node
+ GetNetworkInfo,
+ // Send message to gossip peers in a mesh network
+ GossipsubBroadcastMessage {
+ /// Topic to send messages to
+ topic: String,
+ message: Vec,
+ /// Explicit peers to gossip to
+ peers: Option>,
+ },
+ /// Join a mesh network
+ GossipsubJoinNetwork(String),
+ /// Get gossip information about node
+ GossipsubGetInfo,
+ /// Leave a network we are a part of
+ GossipsubExitNetwork(String),
+ /// Blacklist a peer explicitly
+ GossipsubBlacklistPeer(PeerId),
+ /// Remove a peer from the blacklist
+ GossipsubFilterBlacklist(PeerId),
}
/// Response to requests sent from the aplication to the network layer
@@ -81,6 +105,27 @@ pub enum AppResponse {
FetchData(Vec>),
/// A network error occured while executing the request
Error(NetworkError),
+ /// Important information about the node
+ GetNetworkInfo {
+ peer_id: PeerId,
+ connected_peers: Vec,
+ external_addresses: Vec,
+ },
+ /// Successfully broadcast the network
+ GossipsubBroadcastSuccess,
+ /// Successfully joined a mesh network
+ GossipsubJoinSuccess,
+ /// Successfully exited a mesh network
+ GossipsubExitSuccess,
+ /// Get gossip information about node
+ GossipsubGetInfo {
+ /// Topics that the node is currently subscribed to
+ topics: Vec,
+ /// Peers we know about and their corresponding topics
+ mesh_peers: Vec<(PeerId, Vec)>,
+ },
+ /// Blacklist operation success
+ GossipsubBlacklistSuccess
}
/// Network error type containing errors encountered during network operations
@@ -100,6 +145,10 @@ pub enum NetworkError {
InternalTaskError,
#[error("failed to dail peer")]
DailPeerError,
+ #[error("failed to broadcast message to peers in the topic")]
+ GossipsubBroadcastMessageError,
+ #[error("failed to join a mesh network")]
+ GossipsubJoinNetworkError,
}
/// A simple struct used to track requests sent from the application layer to the network layer
@@ -203,12 +252,11 @@ pub struct RpcConfig {
/// The high level trait that provides default implementations to handle most supported network
/// swarm events.
-#[async_trait]
pub trait EventHandler {
/// Event that informs the network core that we have started listening on a new multiaddr.
- async fn new_listen_addr(
+ fn new_listen_addr(
&mut self,
-
+
_local_peer_id: PeerId,
_listener_id: ListenerId,
_addr: Multiaddr,
@@ -217,9 +265,9 @@ pub trait EventHandler {
}
/// Event that informs the network core about a newly established connection to a peer.
- async fn connection_established(
+ fn connection_established(
&mut self,
-
+
_peer_id: PeerId,
_connection_id: ConnectionId,
_endpoint: &ConnectedPoint,
@@ -230,9 +278,9 @@ pub trait EventHandler {
}
/// Event that informs the network core about a closed connection to a peer.
- async fn connection_closed(
+ fn connection_closed(
&mut self,
-
+
_peer_id: PeerId,
_connection_id: ConnectionId,
_endpoint: &ConnectedPoint,
@@ -243,60 +291,45 @@ pub trait EventHandler {
}
/// Event that announces expired listen address.
- async fn expired_listen_addr(
- &mut self,
-
- _listener_id: ListenerId,
- _address: Multiaddr,
- ) {
+ fn expired_listen_addr(&mut self, _listener_id: ListenerId, _address: Multiaddr) {
// Default implementation
}
/// Event that announces a closed listener.
- async fn listener_closed(
- &mut self,
-
- _listener_id: ListenerId,
- _addresses: Vec,
- ) {
+ fn listener_closed(&mut self, _listener_id: ListenerId, _addresses: Vec) {
// Default implementation
}
/// Event that announces a listener error.
- async fn listener_error(&mut self, _listener_id: ListenerId) {
+ fn listener_error(&mut self, _listener_id: ListenerId) {
// Default implementation
}
/// Event that announces a dialing attempt.
- async fn dialing(
- &mut self,
-
- _peer_id: Option,
- _connection_id: ConnectionId,
- ) {
+ fn dialing(&mut self, _peer_id: Option, _connection_id: ConnectionId) {
// Default implementation
}
/// Event that announces a new external address candidate.
- async fn new_external_addr_candidate(&mut self, _address: Multiaddr) {
+ fn new_external_addr_candidate(&mut self, _address: Multiaddr) {
// Default implementation
}
/// Event that announces a confirmed external address.
- async fn external_addr_confirmed(&mut self, _address: Multiaddr) {
+ fn external_addr_confirmed(&mut self, _address: Multiaddr) {
// Default implementation
}
/// Event that announces an expired external address.
- async fn external_addr_expired(&mut self, _address: Multiaddr) {
+ fn external_addr_expired(&mut self, _address: Multiaddr) {
// Default implementation
}
/// Event that announces new connection arriving on a listener and in the process of
/// protocol negotiation.
- async fn incoming_connection(
+ fn incoming_connection(
&mut self,
-
+
_connection_id: ConnectionId,
_local_addr: Multiaddr,
_send_back_addr: Multiaddr,
@@ -306,9 +339,9 @@ pub trait EventHandler {
/// Event that announces an error happening on an inbound connection during its initial
/// handshake.
- async fn incoming_connection_error(
+ fn incoming_connection_error(
&mut self,
-
+
_connection_id: ConnectionId,
_local_addr: Multiaddr,
_send_back_addr: Multiaddr,
@@ -318,9 +351,9 @@ pub trait EventHandler {
/// Event that announces an error happening on an outbound connection during its initial
/// handshake.
- async fn outgoing_connection_error(
+ fn outgoing_connection_error(
&mut self,
-
+
_connection_id: ConnectionId,
_peer_id: Option,
) {
@@ -329,52 +362,37 @@ pub trait EventHandler {
/// Event that announces the arrival of a ping message from a peer.
/// The duration it took for a round trip is also returned
- async fn inbound_ping_success(
- &mut self,
-
- _peer_id: PeerId,
- _duration: Duration,
- ) {
+ fn inbound_ping_success(&mut self, _peer_id: PeerId, _duration: Duration) {
// Default implementation
}
/// Event that announces a `Ping` error
- async fn outbound_ping_error(
- &mut self,
-
- _peer_id: PeerId,
- _err_type: Failure,
- ) {
+ fn outbound_ping_error(&mut self, _peer_id: PeerId, _err_type: Failure) {
// Default implementation
}
/// Event that announces the arrival of a `PeerInfo` via the `Identify` protocol
- async fn identify_info_recieved(
- &mut self,
-
- _peer_id: PeerId,
- _info: Info,
- ) {
+ fn identify_info_recieved(&mut self, _peer_id: PeerId, _info: Info) {
// Default implementation
}
/// Event that announces the successful write of a record to the DHT
- async fn kademlia_put_record_success(&mut self, _key: Vec) {
+ fn kademlia_put_record_success(&mut self, _key: Vec) {
// Default implementation
}
/// Event that announces the failure of a node to save a record
- async fn kademlia_put_record_error(&mut self) {
+ fn kademlia_put_record_error(&mut self) {
// Default implementation
}
/// Event that announces a node as a provider of a record in the DHT
- async fn kademlia_start_providing_success(&mut self, _key: Vec) {
+ fn kademlia_start_providing_success(&mut self, _key: Vec) {
// Default implementation
}
/// Event that announces the failure of a node to become a provider of a record in the DHT
- async fn kademlia_start_providing_error(&mut self) {
+ fn kademlia_start_providing_error(&mut self) {
// Default implementation
}
@@ -451,14 +469,14 @@ pub mod ping_config {
/// Network queue that tracks the execution of application requests in the network layer
pub(super) struct ExecQueue {
- buffer: Mutex>
-}
+ buffer: Mutex>,
+}
impl ExecQueue {
// Create new execution queue
pub fn new() -> Self {
Self {
- buffer: Mutex::new(VecDeque::new())
+ buffer: Mutex::new(VecDeque::new()),
}
}
@@ -471,4 +489,4 @@ impl ExecQueue {
pub async fn push(&mut self, stream_id: StreamId) {
self.buffer.lock().await.push_back(stream_id);
}
-}
\ No newline at end of file
+}
diff --git a/swarm_nl/src/lib.rs b/swarm_nl/src/lib.rs
index 87d657063..acbcbcf5f 100644
--- a/swarm_nl/src/lib.rs
+++ b/swarm_nl/src/lib.rs
@@ -13,7 +13,6 @@ pub use libp2p::{
swarm::ConnectionId,
};
pub use libp2p_identity::{rsa::Keypair as RsaKeypair, KeyType, Keypair, PeerId};
-pub use async_trait::async_trait;
pub mod core;
mod prelude;
diff --git a/swarm_nl/src/prelude.rs b/swarm_nl/src/prelude.rs
index 7090aa08b..bb5382095 100644
--- a/swarm_nl/src/prelude.rs
+++ b/swarm_nl/src/prelude.rs
@@ -30,6 +30,8 @@ pub enum SwarmNlError {
RemotePeerDialError(String),
#[error("could not parse provided network id")]
NetworkIdParseError(String),
+ #[error("could not configure node for gossiping")]
+ GossipConfigError
}
/// Generic SwarmNl result type
From 6128ab511525589eb73c29d6a11c834ee536e6f3 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 16:53:12 +0200
Subject: [PATCH 005/122] docs: update README
---
README.md | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index d6c879c9a..009c10064 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
# SwarmNL
## a library to build custom networking layers for decentralized web applications
-![swarmnl](/swarm-img.png)
+![swarmnl](/swarm_nl/doc/assets/swarmnl.svg)
SwarmNL is a Rust library that provides a highly configurable P2P networking layer to be used in distributed system architectures that require data transfer solutions for off-chain communication.
It is designed to offer developers a lightweight, scalable and configurable networking stack, easy to integrate to any decentralized application.
@@ -17,7 +17,7 @@ It is designed to offer developers a lightweight, scalable and configurable netw
SwarmNL provides a simple interface to configure a node and specify parameters to dictate its behaviour. This includes:
- Selection and configuration of the transport layers to be supported by the node.
-- Selection of the cryptographic keypair to use for identity generation e.g Edwards.
+- Selection of the cryptographic keypairs (ed25519, RSA, secp256k1, ecdsa)
- Storage and retrieval of keypair locally.
- PeerID and multiaddress generation.
- Protocol specification and handlers.
@@ -25,7 +25,7 @@ SwarmNL provides a simple interface to configure a node and specify parameters t
**Node Communication**
-SwarmNL uses the Gossipsub 1.1 protocol, specified by the libp2p spec.
+SwarmNL uses the Gossipsub 1.1 protocol, specified by the [libp2p spec](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md).
**Node Failure Handling**
@@ -33,9 +33,9 @@ SwarmNL provides customizable options for developers to define reconnection stra
**Scaling**
-SwarmNL needs to efficiently handle a growing (or shrinking) number of nodes while maintaining performance and reliability. Here's what we plan to implement to this effect:
+Here's how SwarmNL handles a growing (or shrinking) number of nodes while maintaining performance and reliability (_note: this is currently under development👷_):
-- *Sharding* -- implementation of a flexible generic sharding protocol that allows application specify configurations like sharding hash functions and locations for shards.
+- *Sharding* -- a flexible generic sharding protocol that allows application specify configurations like sharding hash functions and locations for shards.
- *Data Forwarding* -- definition of a protocol for forwarding messages between nodes in different shards and establishment of efficient routing mechanisms for inter-shard communication.
- *Fault Tolerance* -- implementation of fault-tolerant mechanisms for detecting (and recovering from) node failures. This might involve redundancy, node replication, erasure encoding/decoding or re-routing strategies.
@@ -44,11 +44,6 @@ SwarmNL needs to efficiently handle a growing (or shrinking) number of nodes whi
- *Upload* -- provision of interfaces to upload to IPFS, pin on current node and post arbitrary data to remote servers. Encryption is also easily pluggable and will be provided.
- *Download* -- retrieval and possible decryption of data from the IPFS network.
-## Technology Stack
-
-- [Libp2p](https://libp2p.io/)
-- [Rust](https://www.rust-lang.org/)
-
## License
Apache 2.0
\ No newline at end of file
From 5c651b5705bfd9d25b22673a3d09e495d3a24b86 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 17:10:45 +0200
Subject: [PATCH 006/122] docs: update networkbuilder
---
swarm_nl/doc/core/NetworkBuilder.md | 28 +++++++++++--------------
swarm_nl/src/setup.rs | 32 +++++++++++++++--------------
2 files changed, 29 insertions(+), 31 deletions(-)
diff --git a/swarm_nl/doc/core/NetworkBuilder.md b/swarm_nl/doc/core/NetworkBuilder.md
index 125c3a927..094dc8e9b 100644
--- a/swarm_nl/doc/core/NetworkBuilder.md
+++ b/swarm_nl/doc/core/NetworkBuilder.md
@@ -1,16 +1,22 @@
# Network builder
-The [`CoreBuilder::with_config`] method takes in one parameter for the bootstrap config done by [`setup::BootstrapConfig`] and another parameter to pass in an [`EventHandler`] to handle network events. With this you can build a swarm like this:
+To build a swarm you first need to create a [`CoreBuilder`] object (i.e. the bootstrap node) which is then used to build the [`Core`] network.
+
+The [`CoreBuilder::with_config`] method takes two parameters:
+1. [`BootstrapConfig`] to pass in a bootstrap node configuration
+2. [`EventHandler`] to handle network events.
+
+Here's how you would build a bootstrap node with the default library settings:
```rust
- let config = BootstrapConfig::default();
- let handler = DefaultHandler; // from the core library
- let mut network = swarm_nl::core::CoreBuilder::with_config(config, complex_handler).build().await.unwrap();
+let config = BootstrapConfig::default();
+let handler = DefaultHandler;
+let mut network = swarm_nl::core::CoreBuilder::with_config(config, handler).build().await.unwrap();
```
-## Event Handlers
+To add any custom logic around how you want to handle network events, you must implement the methods you want from [`EventHandler`].
-You could always just use the [`DefaultHandler`] provided by the library. But if you wanted to add any custom logic around how you want to handle network events, you must implement the methods you want from [`EventHandler`]. For example:
+For example:
```rust
use swarm_nl::core::EventHandler;
@@ -24,13 +30,3 @@ impl EventHandler for ComplexHandler {
}
}
```
-
-## Custom configurations
-
-You can specify custom transport layers for TCP and QUIC (in the future we will be able to handle other transport options such as WebRTC).
-
-# Implementing your own protocols
-
-For now, the protocols we've implemented are Ping, Kademlia and Identify. You could always introduce your own custom protocol, for example:
-
-TODO
\ No newline at end of file
diff --git a/swarm_nl/src/setup.rs b/swarm_nl/src/setup.rs
index a2b129b54..359757913 100644
--- a/swarm_nl/src/setup.rs
+++ b/swarm_nl/src/setup.rs
@@ -10,21 +10,21 @@ use libp2p_identity::rsa;
/// Import the contents of the exported modules into this module
use super::*;
-/// Configuration data required for node bootstrap
+/// Configuration data required for node bootstrap.
#[derive(Debug)]
pub struct BootstrapConfig {
- /// The port to listen on if using the TCP/IP protocol
+ /// The port to listen on if using the TCP/IP protocol.
tcp_port: Port,
- /// The port to listen on if using the UDP or QUIC protocol
+ /// The port to listen on if using the UDP or QUIC protocol.
udp_port: Port,
- /// The Cryptographic Keypair for node identification and message auth
+ /// The Cryptographic Keypair for node identification and message auth.
keypair: Keypair,
- /// Bootstrap peers
+ /// Bootstrap peers.
boot_nodes: HashMap,
}
impl BootstrapConfig {
- /// Read from a bootstrap config file on disk
+ /// Read from a bootstrap config file on disk.
///
/// # Panics
///
@@ -36,28 +36,29 @@ impl BootstrapConfig {
/// Return a new `BootstrapConfig` struct populated by default (empty) values.
///
/// Must be called first if the config is to be explicitly built without reading `.ini` file
- /// from disk
+ /// from disk.
pub fn new() -> Self {
BootstrapConfig {
- // Default TCP/IP port if not specified
+ // Default TCP/IP port if not specified.
tcp_port: MIN_PORT,
- // Default UDP port if not specified
+ // Default UDP port if not specified.
udp_port: MAX_PORT,
- // Default node keypair type i.e Ed25519
+ // Default node keypair type i.e Ed25519.
keypair: Keypair::generate_ed25519(),
boot_nodes: Default::default(),
}
}
- /// Configure available bootnodes
+ /// Configure available bootnodes.
pub fn with_bootnodes(mut self, boot_nodes: HashMap) -> Self {
// additive operation
self.boot_nodes.extend(boot_nodes.into_iter());
self
}
- /// Configure the TCP/IP port
- /// Port must range between [`MIN_PORT`] and [`MAX_PORT`]
+ /// Configure the TCP/IP port.
+ ///
+ /// Port must range between [`MIN_PORT`] and [`MAX_PORT`].
pub fn with_tcp(self, tcp_port: Port) -> Self {
if tcp_port > MIN_PORT && tcp_port < MAX_PORT {
BootstrapConfig { tcp_port, ..self }
@@ -66,7 +67,8 @@ impl BootstrapConfig {
}
}
- /// Configure the UDP port
+ /// Configure the UDP port.
+ ///
/// Port must range between [`MIN_PORT`] and [`MAX_PORT`]
pub fn with_udp(self, udp_port: Port) -> Self {
if udp_port > MIN_PORT && udp_port < MAX_PORT {
@@ -161,7 +163,7 @@ impl BootstrapConfig {
}
}
-/// Implement [`Default`] for [`BootstrapConfig`]
+/// [`Default`] implementation for [`BootstrapConfig`].
impl Default for BootstrapConfig {
fn default() -> Self {
Self::new()
From 8c8d120919a3bc425ef696f60d32897adbcbd124 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 17:18:53 +0200
Subject: [PATCH 007/122] docs
---
swarm_nl/doc/core/NetworkBuilder.md | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/swarm_nl/doc/core/NetworkBuilder.md b/swarm_nl/doc/core/NetworkBuilder.md
index 094dc8e9b..c7e46fbe7 100644
--- a/swarm_nl/doc/core/NetworkBuilder.md
+++ b/swarm_nl/doc/core/NetworkBuilder.md
@@ -1,22 +1,23 @@
# Network builder
-To build a swarm you first need to create a [`CoreBuilder`] object (i.e. the bootstrap node) which is then used to build the [`Core`] network.
+To build a swarm you first need to create a [`CoreBuilder`] object using the `with_config` method to build a bootstrap node, then you can simply call `build` to set up the network.
The [`CoreBuilder::with_config`] method takes two parameters:
1. [`BootstrapConfig`] to pass in a bootstrap node configuration
2. [`EventHandler`] to handle network events.
-Here's how you would build a bootstrap node with the default library settings:
+Here's how you would build a bootstrap node with the default library settings and set up the network:
```rust
let config = BootstrapConfig::default();
let handler = DefaultHandler;
-let mut network = swarm_nl::core::CoreBuilder::with_config(config, handler).build().await.unwrap();
+let mut network = CoreBuilder::with_config(config, handler)
+ .build()
+ .await
+ .unwrap()
```
-To add any custom logic around how you want to handle network events, you must implement the methods you want from [`EventHandler`].
-
-For example:
+To add any custom logic around how you want to handle network events, you must implement the methods you want from [`EventHandler`]. For example:
```rust
use swarm_nl::core::EventHandler;
From a5899c374e455fe83eca53a3fb7d9c4de6ca9a1d Mon Sep 17 00:00:00 2001
From: thewoodfish
Date: Fri, 10 May 2024 16:23:23 +0100
Subject: [PATCH 008/122] fix: cleanup generics from ConfigBuilder::with_rpc()
---
swarm_nl/src/core/mod.rs | 393 +++++++++++++++++++--------------------
1 file changed, 194 insertions(+), 199 deletions(-)
diff --git a/swarm_nl/src/core/mod.rs b/swarm_nl/src/core/mod.rs
index 5a44b5227..681a1857f 100644
--- a/swarm_nl/src/core/mod.rs
+++ b/swarm_nl/src/core/mod.rs
@@ -7,11 +7,11 @@
use std::{
collections::{HashMap, HashSet},
+ fs,
net::{IpAddr, Ipv4Addr},
num::NonZeroU32,
sync::Arc,
time::Duration,
- fs,
};
use base58::FromBase58;
@@ -250,10 +250,7 @@ impl CoreBuilder {
}
/// Configure the RPC protocol for the network.
- pub fn with_rpc(self, config: RpcConfig) -> Self
- where
- F: Fn(Vec) -> Vec,
- {
+ pub fn with_rpc(self, config: RpcConfig) -> Self {
// Set the request-response protocol
CoreBuilder {
request_response: Behaviour::new(
@@ -612,7 +609,6 @@ impl CoreBuilder {
}
}
-
/// The core interface for the application layer to interface with the networking layer.
#[derive(Clone)]
pub struct Core {
@@ -843,7 +839,6 @@ impl Core {
mut receiver: Receiver,
mut network_core: Core,
) {
-
let mut exec_queue_1 = ExecQueue::new();
let mut exec_queue_2 = ExecQueue::new();
let mut exec_queue_3 = ExecQueue::new();
@@ -1212,7 +1207,7 @@ impl Core {
if let Some(stream_id) = exec_queue_4.pop().await {
// Send the error back to the application layer
let _ = network_sender.send(StreamData::ToApplication(stream_id, AppResponse::Error(NetworkError::RpcDataFetchError))).await;
- }
+ }
},
_ => {}
}
@@ -1335,221 +1330,221 @@ impl Core {
mod tests {
-use super::*;
-use futures::TryFutureExt;
-use ini::Ini;
-use std::fs::File;
-use std::net::Ipv6Addr;
-use std::fs;
-
-// set up a default node helper
-pub fn setup_core_builder() -> CoreBuilder {
- let config = BootstrapConfig::default();
- let handler = DefaultHandler;
-
- // return default network core builder
- CoreBuilder::with_config(config, handler)
-}
+ use super::*;
+ use futures::TryFutureExt;
+ use ini::Ini;
+ use std::fs;
+ use std::fs::File;
+ use std::net::Ipv6Addr;
-// define custom ports for testing
-const CUSTOM_TCP_PORT: Port = 49666;
-const CUSTOM_UDP_PORT: Port = 49852;
-
-// used to test saving keypair to file
-fn create_test_ini_file(file_path: &str) {
- let mut config = Ini::new();
- config
- .with_section(Some("ports"))
- .set("tcp", CUSTOM_TCP_PORT.to_string())
- .set("udp", CUSTOM_UDP_PORT.to_string());
-
- config.with_section(Some("bootstrap")).set(
- "boot_nodes",
- "[12D3KooWGfbL6ZNGWqS11MoptH2A7DB1DG6u85FhXBUPXPVkVVRq:/ip4/192.168.1.205/tcp/1509]",
- );
- // write config to a new INI file
- config.write_to_file(file_path).unwrap_or_default();
-}
+ // set up a default node helper
+ pub fn setup_core_builder() -> CoreBuilder {
+ let config = BootstrapConfig::default();
+ let handler = DefaultHandler;
-#[test]
-fn default_behavior_works() {
- // build a node with the default network id
- let default_node = setup_core_builder();
+ // return default network core builder
+ CoreBuilder::with_config(config, handler)
+ }
- // assert that the default network id is '/swarmnl/1.0'
- assert_eq!(default_node.network_id, DEFAULT_NETWORK_ID);
+ // define custom ports for testing
+ const CUSTOM_TCP_PORT: Port = 49666;
+ const CUSTOM_UDP_PORT: Port = 49852;
+
+ // used to test saving keypair to file
+ fn create_test_ini_file(file_path: &str) {
+ let mut config = Ini::new();
+ config
+ .with_section(Some("ports"))
+ .set("tcp", CUSTOM_TCP_PORT.to_string())
+ .set("udp", CUSTOM_UDP_PORT.to_string());
+
+ config.with_section(Some("bootstrap")).set(
+ "boot_nodes",
+ "[12D3KooWGfbL6ZNGWqS11MoptH2A7DB1DG6u85FhXBUPXPVkVVRq:/ip4/192.168.1.205/tcp/1509]",
+ );
+ // write config to a new INI file
+ config.write_to_file(file_path).unwrap_or_default();
+ }
- // default transport is TCP/QUIC
- assert_eq!(
- default_node.transport,
- TransportOpts::TcpQuic {
- tcp_config: TcpConfig::Default
- }
- );
+ #[test]
+ fn default_behavior_works() {
+ // build a node with the default network id
+ let default_node = setup_core_builder();
- // default keep alive duration is 60 seconds
- assert_eq!(default_node.keep_alive_duration, 60);
+ // assert that the default network id is '/swarmnl/1.0'
+ assert_eq!(default_node.network_id, DEFAULT_NETWORK_ID);
- // default listen on is 0:0:0:0
- assert_eq!(
- default_node.ip_address,
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
- );
+ // default transport is TCP/QUIC
+ assert_eq!(
+ default_node.transport,
+ TransportOpts::TcpQuic {
+ tcp_config: TcpConfig::Default
+ }
+ );
- // default tcp/udp port is MIN_PORT and MAX_PORT
- assert_eq!(default_node.tcp_udp_port, (MIN_PORT, MAX_PORT));
-}
+ // default keep alive duration is 60 seconds
+ assert_eq!(default_node.keep_alive_duration, 60);
-#[test]
-fn custom_node_setup_works() {
- // build a node with the default network id
- let default_node = setup_core_builder();
-
- // custom node configuration
- let mut custom_network_id = "/custom-protocol/1.0".to_string();
- let mut custom_transport = TransportOpts::TcpQuic {
- tcp_config: TcpConfig::Custom {
- ttl: 10,
- nodelay: true,
- backlog: 10,
- },
- };
- let mut custom_keep_alive_duration = 20;
- let mut custom_ip_address = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
-
- // pass in the custom node configuration and assert it works as expected
- let custom_node = default_node
- .with_network_id(custom_network_id.clone())
- .with_transports(custom_transport.clone())
- .with_idle_connection_timeout(custom_keep_alive_duration.clone())
- .listen_on(custom_ip_address.clone());
-
- // TODO: with_ping
- // e.g. if the node is unreachable after a specific amount of time, it should be
- // disconnected if 10th inteval is configured, if failed 9th time, test decay as each ping
- // comes in
-
- // TODO: with_kademlia
- // e.g. if a record is not found, it should return a specific message
-
- // TODO: configure_network_events
- // test recorded logs. Create a custom handler and test if the logs are recorded.
-
- // assert that the custom network id is '/custom/protocol/1.0'
- assert_eq!(custom_node.network_id(), custom_network_id);
-
- // assert that the custom transport is 'TcpQuic'
- assert_eq!(custom_node.transport, custom_transport);
-
- // assert that the custom keep alive duration is 20
- assert_eq!(custom_node.keep_alive_duration, custom_keep_alive_duration);
-}
+ // default listen on is 0:0:0:0
+ assert_eq!(
+ default_node.ip_address,
+ IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
+ );
-#[test]
-fn network_id_custom_behavior_works_as_expected() {
- // setup a node with the default config builder
- let mut custom_builder = setup_core_builder();
+ // default tcp/udp port is MIN_PORT and MAX_PORT
+ assert_eq!(default_node.tcp_udp_port, (MIN_PORT, MAX_PORT));
+ }
- // configure builder with custom protocol and assert it works as expected
- let custom_protocol: &str = "/custom-protocol/1.0";
- let custom_builder = custom_builder.with_network_id(custom_protocol.to_string());
+ #[test]
+ fn custom_node_setup_works() {
+ // build a node with the default network id
+ let default_node = setup_core_builder();
+
+ // custom node configuration
+ let mut custom_network_id = "/custom-protocol/1.0".to_string();
+ let mut custom_transport = TransportOpts::TcpQuic {
+ tcp_config: TcpConfig::Custom {
+ ttl: 10,
+ nodelay: true,
+ backlog: 10,
+ },
+ };
+ let mut custom_keep_alive_duration = 20;
+ let mut custom_ip_address = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
- // cannot be less than MIN_NETWORK_ID_LENGTH
- assert_eq!(
- custom_builder.network_id().len() >= MIN_NETWORK_ID_LENGTH.into(),
- true
- );
+ // pass in the custom node configuration and assert it works as expected
+ let custom_node = default_node
+ .with_network_id(custom_network_id.clone())
+ .with_transports(custom_transport.clone())
+ .with_idle_connection_timeout(custom_keep_alive_duration.clone())
+ .listen_on(custom_ip_address.clone());
- // must start with a forward slash
- assert!(custom_builder.network_id().starts_with("/"));
+ // TODO: with_ping
+ // e.g. if the node is unreachable after a specific amount of time, it should be
+ // disconnected if 10th inteval is configured, if failed 9th time, test decay as each ping
+ // comes in
- // assert that the custom network id is '/custom/protocol/1.0'
- assert_eq!(custom_builder.network_id(), custom_protocol.to_string());
-}
+ // TODO: with_kademlia
+ // e.g. if a record is not found, it should return a specific message
-#[test]
-#[should_panic("Could not parse provided network id: it must be of the format '/protocol-name/version'")]
-fn network_id_custom_behavior_fails() {
- // build a node with the default network id
- let mut custom_builder = setup_core_builder();
+ // TODO: configure_network_events
+ // test recorded logs. Create a custom handler and test if the logs are recorded.
- // pass in an invalid network ID: network ID length is less than MIN_NETWORK_ID_LENGTH
- let invalid_protocol_1 = "/1.0".to_string();
- assert!(invalid_protocol_1.len() < MIN_NETWORK_ID_LENGTH.into());
- let custom_builder = custom_builder.with_network_id(invalid_protocol_1);
+ // assert that the custom network id is '/custom/protocol/1.0'
+ assert_eq!(custom_node.network_id(), custom_network_id);
- // pass in an invalid network ID: network ID must start with a forward slash
- let invalid_protocol_2 = "1.0".to_string();
- custom_builder.with_network_id(invalid_protocol_2);
-}
+ // assert that the custom transport is 'TcpQuic'
+ assert_eq!(custom_node.transport, custom_transport);
-#[cfg(feature = "tokio-runtime")]
-#[test]
-fn save_keypair_offline_works_tokio() {
- // build a node with the default network id
- let default_node = setup_core_builder();
+ // assert that the custom keep alive duration is 20
+ assert_eq!(custom_node.keep_alive_duration, custom_keep_alive_duration);
+ }
- // use tokio runtime to test async function
- let result = tokio::runtime::Runtime::new().unwrap().block_on(
- default_node
- .build()
- .unwrap_or_else(|_| panic!("Could not build node")),
- );
+ #[test]
+ fn network_id_custom_behavior_works_as_expected() {
+ // setup a node with the default config builder
+ let mut custom_builder = setup_core_builder();
- // create a saved_keys.ini file
- let file_path_1 = "saved_keys.ini";
- create_test_ini_file(file_path_1);
+ // configure builder with custom protocol and assert it works as expected
+ let custom_protocol: &str = "/custom-protocol/1.0";
+ let custom_builder = custom_builder.with_network_id(custom_protocol.to_string());
- // save the keypair to existing file
- let saved_1 = result.save_keypair_offline(&file_path_1);
+ // cannot be less than MIN_NETWORK_ID_LENGTH
+ assert_eq!(
+ custom_builder.network_id().len() >= MIN_NETWORK_ID_LENGTH.into(),
+ true
+ );
+
+ // must start with a forward slash
+ assert!(custom_builder.network_id().starts_with("/"));
- // assert that the keypair was saved successfully
- assert_eq!(saved_1, true);
+ // assert that the custom network id is '/custom/protocol/1.0'
+ assert_eq!(custom_builder.network_id(), custom_protocol.to_string());
+ }
- // test if it works for a file name that does not exist
- let file_path_2 = "test.ini";
- let saved_2 = result.save_keypair_offline(file_path_2);
- assert_eq!(saved_2, true);
+ #[test]
+ #[should_panic(
+ "Could not parse provided network id: it must be of the format '/protocol-name/version'"
+ )]
+ fn network_id_custom_behavior_fails() {
+ // build a node with the default network id
+ let mut custom_builder = setup_core_builder();
+
+ // pass in an invalid network ID: network ID length is less than MIN_NETWORK_ID_LENGTH
+ let invalid_protocol_1 = "/1.0".to_string();
+ assert!(invalid_protocol_1.len() < MIN_NETWORK_ID_LENGTH.into());
+ let custom_builder = custom_builder.with_network_id(invalid_protocol_1);
+
+ // pass in an invalid network ID: network ID must start with a forward slash
+ let invalid_protocol_2 = "1.0".to_string();
+ custom_builder.with_network_id(invalid_protocol_2);
+ }
- // clean up
- fs::remove_file(file_path_1).unwrap_or_default();
- fs::remove_file(file_path_2).unwrap_or_default();
+ #[cfg(feature = "tokio-runtime")]
+ #[test]
+ fn save_keypair_offline_works_tokio() {
+ // build a node with the default network id
+ let default_node = setup_core_builder();
-}
+ // use tokio runtime to test async function
+ let result = tokio::runtime::Runtime::new().unwrap().block_on(
+ default_node
+ .build()
+ .unwrap_or_else(|_| panic!("Could not build node")),
+ );
+ // create a saved_keys.ini file
+ let file_path_1 = "saved_keys.ini";
+ create_test_ini_file(file_path_1);
-#[cfg(feature = "async-std-runtime")]
-#[test]
-fn save_keypair_offline_works_async_std() {
- // build a node with the default network id
- let default_node = setup_core_builder();
-
- // use tokio runtime to test async function
- let result = async_std::task::block_on(
- default_node
- .build()
- .unwrap_or_else(|_| panic!("Could not build node")),
- );
-
- // make a saved_keys.ini file
- let file_path_1 = "saved_keys.ini";
- create_test_ini_file(file_path_1);
-
- // save the keypair to existing file
- let saved_1 = result.save_keypair_offline(file_path_1);
-
- // assert that the keypair was saved successfully
- assert_eq!(saved_1, true);
-
- // now test if it works for a file name that does not exist
- let file_path_2 = "test.txt";
- let saved_2 = result.save_keypair_offline(file_path_2);
-
- // assert that the keypair was saved successfully
- assert_eq!(saved_2, true);
-
- // clean up
- fs::remove_file(file_path_1).unwrap_or_default();
- fs::remove_file(file_path_2).unwrap_or_default();
-}
+ // save the keypair to existing file
+ let saved_1 = result.save_keypair_offline(&file_path_1);
+
+ // assert that the keypair was saved successfully
+ assert_eq!(saved_1, true);
+
+ // test if it works for a file name that does not exist
+ let file_path_2 = "test.ini";
+ let saved_2 = result.save_keypair_offline(file_path_2);
+ assert_eq!(saved_2, true);
+
+ // clean up
+ fs::remove_file(file_path_1).unwrap_or_default();
+ fs::remove_file(file_path_2).unwrap_or_default();
+ }
+
+ #[cfg(feature = "async-std-runtime")]
+ #[test]
+ fn save_keypair_offline_works_async_std() {
+ // build a node with the default network id
+ let default_node = setup_core_builder();
+
+ // use tokio runtime to test async function
+ let result = async_std::task::block_on(
+ default_node
+ .build()
+ .unwrap_or_else(|_| panic!("Could not build node")),
+ );
+
+ // make a saved_keys.ini file
+ let file_path_1 = "saved_keys.ini";
+ create_test_ini_file(file_path_1);
+
+ // save the keypair to existing file
+ let saved_1 = result.save_keypair_offline(file_path_1);
+
+ // assert that the keypair was saved successfully
+ assert_eq!(saved_1, true);
+
+ // now test if it works for a file name that does not exist
+ let file_path_2 = "test.txt";
+ let saved_2 = result.save_keypair_offline(file_path_2);
+
+ // assert that the keypair was saved successfully
+ assert_eq!(saved_2, true);
+
+ // clean up
+ fs::remove_file(file_path_1).unwrap_or_default();
+ fs::remove_file(file_path_2).unwrap_or_default();
+ }
}
From c1f7572be912eceb4c16054b24e2903fac7401e8 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 17:31:06 +0200
Subject: [PATCH 009/122] docs: update nodesetup
---
swarm_nl/doc/setup/NodeSetup.md | 103 +++-----------------------------
1 file changed, 7 insertions(+), 96 deletions(-)
diff --git a/swarm_nl/doc/setup/NodeSetup.md b/swarm_nl/doc/setup/NodeSetup.md
index f48fa74de..7967b1568 100644
--- a/swarm_nl/doc/setup/NodeSetup.md
+++ b/swarm_nl/doc/setup/NodeSetup.md
@@ -1,20 +1,17 @@
# Node setup
-When we say "node setup" we mean the requirements to launch a single or a set of peers that can bootstrap the network. This requires passing in an `.ini` file with bootstrap configuration data such as bootstrap nodes, TCP/UDP ports and cryptographic types for keypair generation. Have a look at the template you can use which outlines the different fields you can include.
+For any node setup, you need a valid `.ini` file to create the bootstrap config object and a [`EventHandler`] object to specify what events you would like to listen to and how you want to handle them.
-If you're setting up a new network for the first time, you don't need to pass in any bootnodes. If you're joining an exisiting network, you need to ask someone for their bootnode addresses to connect to. The [`BootstrapConfig`] object will handle reading the `.ini` file to build a configuration. Then the [`CoreBuilder`] object launches the network with that config.
+When we say "node setup" we mean the requirements to launch a single or a set of peers that can bootstrap the network. This requires passing in an `.ini` file with bootstrap configuration data such as bootstrap nodes, TCP/UDP ports and cryptographic types for keypair generation.
-Once the configuration is setup, a stream can be polled.
+If you're setting up a new network for the first time, you don't need to pass in any bootnodes. If you're joining an exisiting network, you need to ask someone for their bootnode addresses to connect to. The [`BootstrapConfig`] object will handle reading the `.ini` file to build a configuration for setting up the core network.
-## Examples
+## Example
-For any node setup, you need a valid `.ini` file to create the bootstrap config object and a `network_handler` object to specify what events you would like to listen to and how you want to handle them.
-
-1. In this example, we'll setup a new network that uses `Ed25519` for keypair generation and uses the [`DefaultHandler`] from the core library.
-
-Put this `.ini` file at the root of your project:
+An example `.ini` file could look like this:
```ini
+# example .ini file
[ports]
; TCP/IP port to listen on
tcp=3000
@@ -28,97 +25,11 @@ crypto=Ed25519
protobuf_keypair=[]
```
-Run the following:
-
-```rust
-use swarm_nl::core::DefaultHandler;
-
-#[tokio::main]
-async fn main() {
-
- // network handler object
- let handler = DefaultHandler;
-
- // use the default setup, TCP
- let default_config = swarm_nl::setup::BootstrapConfig::default();
-
- // set up network core
- let mut network = swarm_nl::core::CoreBuilder::with_config(default_config, handler)
- .build()
- .await
- .unwrap();
-
- // read first (ready) message
- if let Some(StreamData::Ready) = network.application_receiver.next().await {
- println!("Database is online");
-
- // begin listening
- loop {
- if let Some(data) = network.application_receiver.next().await {
- println!("{:?}", data);
- }
- }
- }
-}
-```
-
-1. In this example, we'll implement our own event handler to override the default handler bahavior and explicitly connect to known bootnodes:
-
-```rust
-use swarm_nl::core::DefaultHandler;
-
-struct ComplexHandler;
-
-impl EventHandler for ComplexHandler {
- fn new_listen_addr(&mut self, _listener_id: ListenerId, addr: Multiaddr) {
- // Log the address we begin listening on
- println!("We're now listening on: {}", addr);
- }
-}
-
-#[tokio::main]
-async fn main() {
- // handler for events happening in the network layer (majorly for technical use)
- // use default handler
- let handler = DefaultHandler;
- let complex_handler = ComplexHandler;
-
- // set up node
- let mut bootnodes: HashMap = HashMap::new();
- bootnodes.insert(
- "12D3KooWBmwXN3rsVfnLsZKbXeBrSLfczHxZHwVjPrbKwpLfYm3t".to_string(),
- "/ip4/127.0.0.1/tcp/63307".to_string(),
- );
-
- // configure default data
- let config = swarm_nl::setup::BootstrapConfig::new().with_bootnodes(bootnodes);
-
- // set up network core
- let mut network = swarm_nl::core::CoreBuilder::with_config(config, complex_handler)
- .build()
- .await
- .unwrap();
-
- // read first (ready) message
- if let Some(StreamData::Ready) = network.application_receiver.next().await {
- println!("Database is online");
-
- // begin listening
- loop {
- if let Some(data) = network.application_receiver.next().await {
- println!("{:?}", data);
- }
- }
- }
-}
-```
-
-
## Fallback behaviour
Node setup will fallback to default behavior if:
* The `ini` file doesn't contain any `[ports]` section, the fallback ports for TCP is [`MIN_PORT`] and for UDP is [`MAX_PORT`].
-* No key type is specified, the default is always `Ed25519`
+* No key type is specified, the default is will fallback to `Ed25519`
* No bootnodes are passed in, an empty hashmap is created
## Panics
From 78e6afb6e24afcdfb6ca8b2643e152c5fe3e72f5 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 17:31:22 +0200
Subject: [PATCH 010/122] docs: add nodesetup
---
swarm_nl/src/setup.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/swarm_nl/src/setup.rs b/swarm_nl/src/setup.rs
index 359757913..c14bf6d85 100644
--- a/swarm_nl/src/setup.rs
+++ b/swarm_nl/src/setup.rs
@@ -3,6 +3,8 @@
//! Data structures and functions to setup a node identity and configure it for networking.
+#![doc = include_str!("../doc/setup/NodeSetup.md")]
+
use std::collections::HashMap;
use libp2p_identity::rsa;
From e6d8d6147733314572e2efc1940d9d11a81bd9dd Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 19:08:41 +0200
Subject: [PATCH 011/122] docs: update network builder docs
---
swarm_nl/doc/core/NetworkBuilder.md | 73 +++++++++++++++++++++++++----
swarm_nl/src/core/mod.rs | 21 ++++++---
2 files changed, 79 insertions(+), 15 deletions(-)
diff --git a/swarm_nl/doc/core/NetworkBuilder.md b/swarm_nl/doc/core/NetworkBuilder.md
index c7e46fbe7..578760d9f 100644
--- a/swarm_nl/doc/core/NetworkBuilder.md
+++ b/swarm_nl/doc/core/NetworkBuilder.md
@@ -1,6 +1,6 @@
# Network builder
-To build a swarm you first need to create a [`CoreBuilder`] object using the `with_config` method to build a bootstrap node, then you can simply call `build` to set up the network.
+To build a network you first need to create a [`CoreBuilder`] object using the [`CoreBuilder::with_config`] method to create a bootstrap node, then you can simply call [`CoreBuilder::build`] to set up the network. This will create a [`Core`] struct with methods you can use to send and receive data to/from the network.
The [`CoreBuilder::with_config`] method takes two parameters:
1. [`BootstrapConfig`] to pass in a bootstrap node configuration
@@ -17,17 +17,74 @@ let mut network = CoreBuilder::with_config(config, handler)
.unwrap()
```
-To add any custom logic around how you want to handle network events, you must implement the methods you want from [`EventHandler`]. For example:
+## Overriding the default network configuration
+
+You can explicitly change the default values of [`CoreBuilder::with_config`] by calling the following methods before building the network:
+
+- [`CoreBuilder::with_transports`]: a custom transport to use, specified in [`TcpConfig::Custom`].
+- [`CoreBuilder::with_network_id`] : the network ID (e.g. `/your-protocol-name/1.0`).
+- [`CoreBuilder::listen_on`] : the IP address to listen on (e.g. 127.0.0.1).
+- [`CoreBuilder::with_network_read_delay`] : a timeout for requests to read from the network.
+- [`CoreBuilder::with_idle_connection_timeout`]: a timeout for keeping a connection alive.
+
+For example: TODO make it docified.
+
+```rust
+ // default node setup
+ let config = BootstrapConfig::default();
+ let handler = DefaultHandler;
+
+ // return default network core builder
+ let default_node = CoreBuilder::with_config(config, handler)
+
+ // custom node configuration
+ let mut custom_network_id = "/custom-protocol/1.0".to_string();
+ let mut custom_transport = TransportOpts::TcpQuic {
+ tcp_config: TcpConfig::Custom {
+ ttl: 10,
+ nodelay: true,
+ backlog: 10,
+ },
+ };
+ let mut custom_keep_alive_duration = 20;
+ let mut custom_ip_address = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+
+ // pass in your custom node configuration
+ let custom_node = default_node
+ .with_network_id(custom_network_id.clone())
+ .with_transports(custom_transport.clone())
+ .with_idle_connection_timeout(custom_keep_alive_duration.clone())
+ .listen_on(custom_ip_address.clone());
+```
+
+### Custom event handler
+
+To add any custom logic around how you want to handle network events, you must implement the methods from [`EventHandler`]. The role of this implementation override is to respond to your custom network's state and instruct the network to behave in a pre-configured way. For example:
```rust
use swarm_nl::core::EventHandler;
-struct ComplexHandler;
+#[derive(Clone)]
+struct ApplicationHandler{
+ name: String,
+ version: u8,
+}
-impl EventHandler for ComplexHandler {
- fn new_listen_addr(&mut self, _listener_id: ListenerId, addr: Multiaddr) {
- // Log the address we begin listening on
- println!("We're now listening on: {}", addr);
+#[async_trait]
+impl EventHandler for ApplicationHandler {
+ async fn new_listen_addr(
+ &mut self,
+ local_peer_id: PeerId,
+ _listener_id: swarm_nl::ListenerId,
+ addr: swarm_nl::Multiaddr,
+ ) {
+ // announce interfaces we're listening on
+ println!("Peer id: {}", local_peer_id);
+ println!("We're listening on the {}", addr);
+ println!(
+ "Connected to {}, current version: {} ",
+ self.name, self.version
+ );
}
}
-```
+```
\ No newline at end of file
diff --git a/swarm_nl/src/core/mod.rs b/swarm_nl/src/core/mod.rs
index 681a1857f..7414cee39 100644
--- a/swarm_nl/src/core/mod.rs
+++ b/swarm_nl/src/core/mod.rs
@@ -183,9 +183,10 @@ impl CoreBuilder {
}
}
- /// Explicitly configure the network (protocol) id e.g /swarmnl/1.0.
- /// Note that it must be of the format "/protocol-name/version" else it will default to
- /// "/swarmnl/1.0"
+ /// Explicitly configure the network (protocol) id.
+ ///
+ /// Note that it must be of the format "/protocol-name/version" otherwise it will default to
+ /// "/swarmnl/1.0". See: [`DEFAULT_NETWORK_ID`].
pub fn with_network_id(self, protocol: String) -> Self {
if protocol.len() > MIN_NETWORK_ID_LENGTH.into() && protocol.starts_with("/") {
CoreBuilder {
@@ -199,15 +200,18 @@ impl CoreBuilder {
}
}
- /// Configure the IP address to listen on
+ /// Configure the IP address to listen on.
+ ///
+ /// If none is specified, the default value is `Ipv4Addr::new(0, 0, 0, 0)`. See: [`DEFAULT_IP_ADDRESS`].
pub fn listen_on(self, ip_address: IpAddr) -> Self {
CoreBuilder { ip_address, ..self }
}
/// Configure the timeout for requests to read from the network layer.
+ ///
/// Reading from the network layer could potentially block if the data corresponding to the
/// [`StreamId`] specified could not be found (or has been read already). This prevents the
- /// future from `await`ing forever. Defaults to 60 seconds
+ /// future from `await`ing forever. The default is 60 seconds. See: [`NETWORK_READ_TIMEOUT`].
pub fn with_network_read_delay(self, network_read_delay: AsyncDuration) -> Self {
CoreBuilder {
network_read_delay,
@@ -216,6 +220,8 @@ impl CoreBuilder {
}
/// Configure how long to keep a connection alive (in seconds) once it is idling.
+ ///
+ /// The default is 60 seconds. See: [`NETWORK_READ_TIMEOUT`].
pub fn with_idle_connection_timeout(self, keep_alive_duration: Seconds) -> Self {
CoreBuilder {
keep_alive_duration,
@@ -279,8 +285,9 @@ impl CoreBuilder {
CoreBuilder { transport, ..self }
}
- /// Configure network event handler
- /// This configures the functions to be called when various network events take place
+ /// Configure network event handler.
+ ///
+ /// This configures the functions to be called when various network events take place.
pub fn configure_network_events(self, handler: T) -> Self {
CoreBuilder { handler, ..self }
}
From fbea1437d8e99f97952d051ab01d83f2b8452e2e Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 19:37:05 +0200
Subject: [PATCH 012/122] docs: add fullstops at the end of each doc line
---
swarm_nl/src/core/prelude.rs | 138 ++++++++++++++++++-----------------
swarm_nl/src/util.rs | 8 +-
2 files changed, 74 insertions(+), 72 deletions(-)
diff --git a/swarm_nl/src/core/prelude.rs b/swarm_nl/src/core/prelude.rs
index 0056904ea..43e3fc848 100644
--- a/swarm_nl/src/core/prelude.rs
+++ b/swarm_nl/src/core/prelude.rs
@@ -9,32 +9,32 @@ use self::ping_config::PingInfo;
use super::*;
/// Type to indicate the duration (in seconds) to wait for data from the network layer before timing
-/// out
+/// out.
pub const NETWORK_READ_TIMEOUT: u64 = 60;
/// The time it takes for the task to sleep before it can recheck if an output has been placed in
-/// the repsonse buffer (7 seconds)
+/// the repsonse buffer (7 seconds).
pub const TASK_SLEEP_DURATION: u64 = 7;
-/// Type that represents the response of the network layer to the application layer's event handler
+/// Type that represents the response of the network layer to the application layer's event handler.
pub type AppResponseResult = Result;
-/// Data exchanged over a stream between the application and network layer
+/// Data exchanged over a stream between the application and network layer.
#[derive(Debug, Clone)]
pub(super) enum StreamData {
- /// Application data sent over the stream
+ /// Application data sent over the stream.
FromApplication(StreamId, AppData),
- /// Network response data sent over the stream to the application layer
+ /// Network response data sent over the stream to the application layer.
ToApplication(StreamId, AppResponse),
}
-/// Data sent from the application layer to the networking layer
+/// Data sent from the application layer to the networking layer.
#[derive(Debug, Clone)]
pub enum AppData {
- /// A simple echo message
+ /// A simple echo message.
Echo(String),
- /// Dail peer
+ /// Dail peer.
DailPeer(MultiaddrString),
- /// Store a value associated with a given key in the Kademlia DHT
+ /// Store a value associated with a given key in the Kademlia DHT.
KademliaStoreRecord {
key: Vec,
value: Vec,
@@ -43,47 +43,47 @@ pub enum AppData {
// store on explicit peers
explicit_peers: Option>,
},
- /// Perform a lookup of a value associated with a given key in the Kademlia DHT
+ /// Perform a lookup of a value associated with a given key in the Kademlia DHT.
KademliaLookupRecord { key: Vec },
- /// Perform a lookup of peers that store a record
+ /// Perform a lookup of peers that store a record.
KademliaGetProviders { key: Vec },
- /// Stop providing a record on the network
+ /// Stop providing a record on the network.
KademliaStopProviding { key: Vec },
- /// Remove record from local store
+ /// Remove record from local store.
KademliaDeleteRecord { key: Vec },
- /// Return important information about the local routing table
+ /// Return important information about the local routing table.
KademliaGetRoutingTableInfo,
- /// Fetch data(s) quickly from a peer over the network
+ /// Fetch data(s) quickly from a peer over the network.
FetchData { keys: Vec>, peer: PeerId },
// Get network information
// Gossip related requests
}
-/// Response to requests sent from the aplication to the network layer
+/// Response to requests sent from the aplication to the network layer.
#[derive(Debug, Clone)]
pub enum AppResponse {
- /// The value written to the network
+ /// The value written to the network.
Echo(String),
- /// The peer we dailed
+ /// The peer we dailed.
DailPeer(String),
- /// Store record success
+ /// Store record success.
KademliaStoreRecordSuccess,
/// DHT lookup result
KademliaLookupRecord(Vec),
- /// Nodes storing a particular record in the DHT
+ /// Nodes storing a particular record in the DHT.
KademliaGetProviders {
key: Vec,
providers: Vec,
},
- /// Routing table information
+ /// Routing table information.
KademliaGetRoutingTableInfo { protocol_id: String },
- /// RPC result
+ /// RPC result.
FetchData(Vec>),
- /// A network error occured while executing the request
+ /// A network error occured while executing the request.
Error(NetworkError),
}
-/// Network error type containing errors encountered during network operations
+/// Network error type containing errors encountered during network operations.
#[derive(Error, Debug, Clone)]
pub enum NetworkError {
#[error("timeout occured waiting for data from network layer")]
@@ -102,7 +102,7 @@ pub enum NetworkError {
DailPeerError,
}
-/// A simple struct used to track requests sent from the application layer to the network layer
+/// A simple struct used to track requests sent from the application layer to the network layer.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct StreamId(u32);
@@ -113,29 +113,30 @@ impl StreamId {
StreamId(0)
}
- /// Generate a new random stream id, using the current as guide
+ /// Generate a new random stream id, using the current as guide.
pub fn next(current_id: StreamId) -> Self {
StreamId(current_id.0.wrapping_add(1))
}
}
-/// Type that specifies the result of querying the network layer
+/// Type that specifies the result of querying the network layer.
pub type NetworkResult = Result;
/// Type that keeps track of the requests from the application layer.
+///
/// This type has a maximum buffer size and will drop subsequent requests when full.
/// It is unlikely to be ever full as the default is usize::MAX except otherwise specified during
/// configuration. It is always good practice to read responses from the internal stream buffer
-/// using `fetch_from_network()` or explicitly using `recv_from_network`
+/// using `fetch_from_network()` or explicitly using `recv_from_network`.
#[derive(Clone, Debug)]
pub(super) struct StreamRequestBuffer {
- /// Max requests we can keep track of
+ /// Max requests we can keep track of.
size: usize,
buffer: HashSet,
}
impl StreamRequestBuffer {
- /// Create a new request buffer
+ /// Create a new request buffer.
pub fn new(buffer_size: usize) -> Self {
Self {
size: buffer_size,
@@ -144,7 +145,8 @@ impl StreamRequestBuffer {
}
/// Push [`StreamId`]s into buffer.
- /// Returns `false` if the buffer is full and request cannot be stored
+ ///
+ /// Returns `false` if the buffer is full and request cannot be stored.
pub fn insert(&mut self, id: StreamId) -> bool {
if self.buffer.len() < self.size {
self.buffer.insert(id);
@@ -156,13 +158,13 @@ impl StreamRequestBuffer {
/// Type that keeps track of the response to the requests from the application layer.
pub(super) struct StreamResponseBuffer {
- /// Max responses we can keep track of
+ /// Max responses we can keep track of.
size: usize,
buffer: HashMap,
}
impl StreamResponseBuffer {
- /// Create a new request buffer
+ /// Create a new request buffer.
pub fn new(buffer_size: usize) -> Self {
Self {
size: buffer_size,
@@ -171,7 +173,8 @@ impl StreamResponseBuffer {
}
/// Push a [`StreamId`] into buffer.
- /// Returns `false` if the buffer is full and request cannot be stored
+ ///
+ /// Returns `false` if the buffer is full and request cannot be stored.
pub fn insert(&mut self, id: StreamId, response: AppResponseResult) -> bool {
if self.buffer.len() < self.size {
self.buffer.insert(id, response);
@@ -180,24 +183,24 @@ impl StreamResponseBuffer {
false
}
- /// Remove a [`StreamId`] from the buffer
+ /// Remove a [`StreamId`] from the buffer.
pub fn remove(&mut self, id: &StreamId) -> Option {
self.buffer.remove(&id)
}
}
-/// Type representing the RPC data structure sent between nodes in the network
+/// Type representing the RPC data structure sent between nodes in the network.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub(super) enum Rpc {
- /// Using request-response
+ /// Using request-response.
ReqResponse { data: Vec> },
}
-/// The configuration for the RPC protocol
+/// The configuration for the RPC protocol.
pub struct RpcConfig {
- /// Timeout for inbound and outbound requests
+ /// Timeout for inbound and outbound requests.
pub timeout: Duration,
- /// Maximum number of concurrent inbound + outbound streams
+ /// Maximum number of concurrent inbound + outbound streams.
pub max_concurrent_streams: usize,
}
@@ -338,7 +341,7 @@ pub trait EventHandler {
// Default implementation
}
- /// Event that announces a `Ping` error
+ /// Event that announces a `Ping` error.
async fn outbound_ping_error(
&mut self,
@@ -348,7 +351,7 @@ pub trait EventHandler {
// Default implementation
}
- /// Event that announces the arrival of a `PeerInfo` via the `Identify` protocol
+ /// Event that announces the arrival of a `PeerInfo` via the `Identify` protocol.
async fn identify_info_recieved(
&mut self,
@@ -358,69 +361,68 @@ pub trait EventHandler {
// Default implementation
}
- /// Event that announces the successful write of a record to the DHT
+ /// Event that announces the successful write of a record to the DHT.
async fn kademlia_put_record_success(&mut self, _key: Vec) {
// Default implementation
}
- /// Event that announces the failure of a node to save a record
+ /// Event that announces the failure of a node to save a record.
async fn kademlia_put_record_error(&mut self) {
// Default implementation
}
- /// Event that announces a node as a provider of a record in the DHT
+ /// Event that announces a node as a provider of a record in the DHT.
async fn kademlia_start_providing_success(&mut self, _key: Vec) {
// Default implementation
}
- /// Event that announces the failure of a node to become a provider of a record in the DHT
+ /// Event that announces the failure of a node to become a provider of a record in the DHT.
async fn kademlia_start_providing_error(&mut self) {
// Default implementation
}
- /// Event that announces the arrival of an RPC message
+ /// Event that announces the arrival of an RPC message.
fn handle_incoming_message(&mut self, data: Vec>) -> Vec>;
}
-/// Default network event handler
+/// Default network event handler.
#[derive(Clone)]
pub struct DefaultHandler;
/// Implement [`EventHandler`] for [`DefaultHandler`]
impl EventHandler for DefaultHandler {
- /// Echo the message back to the sender
+ /// Echo the message back to the sender.
fn handle_incoming_message(&mut self, data: Vec>) -> Vec> {
data
}
}
/// Important information to obtain from the [`CoreBuilder`], to properly handle network
-/// operations
+/// operations.
#[derive(Clone)]
pub(super) struct NetworkInfo {
- /// The name/id of the network
+ /// The name/id of the network.
pub id: StreamProtocol,
- /// Important information to manage `Ping` operations
+ /// Important information to manage `Ping` operations.
pub ping: PingInfo,
}
-/// Module that contains important data structures to manage `Ping` operations on the network
+/// Module that contains important data structures to manage `Ping` operations on the network.
pub mod ping_config {
use libp2p_identity::PeerId;
use std::{collections::HashMap, time::Duration};
- /// Policies to handle a `Ping` error
- /// - All connections to peers are closed during a disconnect operation.
+ /// Policies to handle a `Ping` error. All connections to peers are closed during a disconnect operation.
#[derive(Debug, Clone)]
pub enum PingErrorPolicy {
- /// Do not disconnect under any circumstances
+ /// Do not disconnect under any circumstances.
NoDisconnect,
- /// Disconnect after a number of outbound errors
+ /// Disconnect after a number of outbound errors.
DisconnectAfterMaxErrors(u16),
- /// Disconnect after a certain number of concurrent timeouts
+ /// Disconnect after a certain number of concurrent timeouts.
DisconnectAfterMaxTimeouts(u16),
}
- /// Struct that stores critical information for the execution of the [`PingErrorPolicy`]
+ /// Stores critical information for the execution of the [`PingErrorPolicy`].
#[derive(Debug, Clone)]
pub struct PingManager {
/// The number of timeout errors encountered from a peer
@@ -429,19 +431,19 @@ pub mod ping_config {
pub outbound_errors: HashMap,
}
- /// The configuration for the `Ping` protocol
+ /// The configuration for the `Ping` protocol.
pub struct PingConfig {
/// The interval between successive pings.
- /// Default is 15 seconds
+ /// Default is 15 seconds.
pub interval: Duration,
/// The duration before which the request is considered failure.
- /// Default is 20 seconds
+ /// Default is 20 seconds.
pub timeout: Duration,
- /// Error policy
+ /// Error policy.
pub err_policy: PingErrorPolicy,
}
- /// Critical information to manage `Ping` operations
+ /// Critical information to manage `Ping` operations.
#[derive(Debug, Clone)]
pub struct PingInfo {
pub policy: PingErrorPolicy,
@@ -449,7 +451,7 @@ pub mod ping_config {
}
}
-/// Network queue that tracks the execution of application requests in the network layer
+/// Network queue that tracks the execution of application requests in the network layer.
pub(super) struct ExecQueue {
buffer: Mutex>
}
@@ -462,12 +464,12 @@ impl ExecQueue {
}
}
- // Remove a [`StreamId`] from the top of the queue
+ /// Remove a [`StreamId`] from the top of the queue.
pub async fn pop(&mut self) -> Option {
self.buffer.lock().await.pop_front()
}
- // Append a [`StreamId`] to the queue
+ /// Append a [`StreamId`] to the queue.
pub async fn push(&mut self, stream_id: StreamId) {
self.buffer.lock().await.push_back(stream_id);
}
diff --git a/swarm_nl/src/util.rs b/swarm_nl/src/util.rs
index 197e49d2f..6e56aeb23 100644
--- a/swarm_nl/src/util.rs
+++ b/swarm_nl/src/util.rs
@@ -66,7 +66,7 @@ pub fn read_ini_file(file_path: &str) -> SwarmNlResult {
}
}
-/// write value into config file
+/// Write value into config file.
pub fn write_config(section: &str, key: &str, new_value: &str, file_path: &str) -> bool {
if let Ok(mut conf) = Ini::load_from_file(file_path) {
// Set a value:
@@ -78,7 +78,7 @@ pub fn write_config(section: &str, key: &str, new_value: &str, file_path: &str)
false
}
-/// Parse string into a vector
+/// Parse string into a vector.
fn string_to_vec(input: &str) -> Vec {
input
.trim_matches(|c| c == '[' || c == ']')
@@ -90,7 +90,7 @@ fn string_to_vec(input: &str) -> Vec {
})
}
-/// Parse string into a hashmap
+/// Parse string into a hashmap.
fn string_to_hashmap(input: &str) -> HashMap {
input
.trim_matches(|c| c == '[' || c == ']')
@@ -107,7 +107,7 @@ fn string_to_hashmap(input: &str) -> HashMap {
})
}
-/// Convert PeerId string to peerId
+/// Convert PeerId string to peerId.
pub fn string_to_peer_id(peer_id_string: &str) -> Option {
PeerId::from_bytes(&peer_id_string.from_base58().unwrap_or_default()).ok()
}
From 43e4ef052466d8c1a80c076e23d04d303594cf60 Mon Sep 17 00:00:00 2001
From: sacha <23283108+sacha-l@users.noreply.github.com>
Date: Fri, 10 May 2024 22:11:23 +0200
Subject: [PATCH 013/122] doc fixes
---
README.md | 2 -
swarm_nl/doc/core/ApplicationInteraction.md | 8 +-
swarm_nl/doc/core/NetworkBuilder.md | 63 +++++++-------
swarm_nl/doc/setup/NodeSetup.md | 2 +-
swarm_nl/src/core/mod.rs | 92 +++++++++------------
swarm_nl/src/core/prelude.rs | 2 +-
6 files changed, 79 insertions(+), 90 deletions(-)
diff --git a/README.md b/README.md
index 009c10064..86850e1a5 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,6 @@
# SwarmNL
## a library to build custom networking layers for decentralized web applications
-![swarmnl](/swarm_nl/doc/assets/swarmnl.svg)
-
SwarmNL is a Rust library that provides a highly configurable P2P networking layer to be used in distributed system architectures that require data transfer solutions for off-chain communication.
It is designed to offer developers a lightweight, scalable and configurable networking stack, easy to integrate to any decentralized application.
diff --git a/swarm_nl/doc/core/ApplicationInteraction.md b/swarm_nl/doc/core/ApplicationInteraction.md
index 175f572b9..ee65442cd 100644
--- a/swarm_nl/doc/core/ApplicationInteraction.md
+++ b/swarm_nl/doc/core/ApplicationInteraction.md
@@ -1,7 +1,9 @@
# Application Interaction
-How the application interacts with the swarm (different tasks and stream data).
+The core library provides a request/response protocol that handles sending and receiving data to and from your application. All requests are handled by the [`AppData`] data structure and all responses are handled by the [`AppResponse`] data structure.
-Request / response protocol
+## Request / response protocol
-TODO
\ No newline at end of file
+When request comes in it gets decoded into a `Vec` of strings, then it’s sent to the function configured to answer requests.
+
+TODO
diff --git a/swarm_nl/doc/core/NetworkBuilder.md b/swarm_nl/doc/core/NetworkBuilder.md
index 578760d9f..9d07a2a23 100644
--- a/swarm_nl/doc/core/NetworkBuilder.md
+++ b/swarm_nl/doc/core/NetworkBuilder.md
@@ -17,6 +17,37 @@ let mut network = CoreBuilder::with_config(config, handler)
.unwrap()
```
+### Custom event handler
+
+To add any custom logic around how you want to handle network events, you must implement the methods from [`EventHandler`]. The role of this implementation override is to respond to your custom network's state and instruct the network to behave in a pre-configured way. For example:
+
+```rust
+use swarm_nl::core::EventHandler;
+
+#[derive(Clone)]
+struct ApplicationHandler{
+ name: String,
+ version: u8,
+}
+
+impl EventHandler for ApplicationHandler {
+ async fn new_listen_addr(
+ &mut self,
+ local_peer_id: PeerId,
+ _listener_id: swarm_nl::ListenerId,
+ addr: swarm_nl::Multiaddr,
+ ) {
+ // announce interfaces we're listening on
+ println!("Peer id: {}", local_peer_id);
+ println!("We're listening on the {}", addr);
+ println!(
+ "Connected to {}, current version: {} ",
+ self.name, self.version
+ );
+ }
+}
+```
+
## Overriding the default network configuration
You can explicitly change the default values of [`CoreBuilder::with_config`] by calling the following methods before building the network:
@@ -55,36 +86,4 @@ For example: TODO make it docified.
.with_transports(custom_transport.clone())
.with_idle_connection_timeout(custom_keep_alive_duration.clone())
.listen_on(custom_ip_address.clone());
-```
-
-### Custom event handler
-
-To add any custom logic around how you want to handle network events, you must implement the methods from [`EventHandler`]. The role of this implementation override is to respond to your custom network's state and instruct the network to behave in a pre-configured way. For example:
-
-```rust
-use swarm_nl::core::EventHandler;
-
-#[derive(Clone)]
-struct ApplicationHandler{
- name: String,
- version: u8,
-}
-
-#[async_trait]
-impl EventHandler for ApplicationHandler {
- async fn new_listen_addr(
- &mut self,
- local_peer_id: PeerId,
- _listener_id: swarm_nl::ListenerId,
- addr: swarm_nl::Multiaddr,
- ) {
- // announce interfaces we're listening on
- println!("Peer id: {}", local_peer_id);
- println!("We're listening on the {}", addr);
- println!(
- "Connected to {}, current version: {} ",
- self.name, self.version
- );
- }
-}
```
\ No newline at end of file
diff --git a/swarm_nl/doc/setup/NodeSetup.md b/swarm_nl/doc/setup/NodeSetup.md
index 7967b1568..cee8cf21c 100644
--- a/swarm_nl/doc/setup/NodeSetup.md
+++ b/swarm_nl/doc/setup/NodeSetup.md
@@ -1,6 +1,6 @@
# Node setup
-For any node setup, you need a valid `.ini` file to create the bootstrap config object and a [`EventHandler`] object to specify what events you would like to listen to and how you want to handle them.
+For any node setup, you need a valid `.ini` file to create the bootstrap config object and a [`core::EventHandler`] object to specify what events you would like to listen to and how you want to handle them.
When we say "node setup" we mean the requirements to launch a single or a set of peers that can bootstrap the network. This requires passing in an `.ini` file with bootstrap configuration data such as bootstrap nodes, TCP/UDP ports and cryptographic types for keypair generation.
diff --git a/swarm_nl/src/core/mod.rs b/swarm_nl/src/core/mod.rs
index 7414cee39..885d99485 100644
--- a/swarm_nl/src/core/mod.rs
+++ b/swarm_nl/src/core/mod.rs
@@ -4,6 +4,7 @@
//! Core data structures and protocol implementations for building a swarm.
#![doc = include_str!("../../doc/core/NetworkBuilder.md")]
+#![doc = include_str!("../../doc/core/ApplicationInteraction.md")]
use std::{
collections::{HashMap, HashSet},
@@ -47,7 +48,7 @@ mod prelude;
pub use prelude::*;
/// The Core Behaviour implemented which highlights the various protocols
-/// we'll be adding support for
+/// we'll be adding support for.
#[derive(NetworkBehaviour)]
#[behaviour(to_swarm = "CoreEvent")]
struct CoreBehaviour {
@@ -57,7 +58,7 @@ struct CoreBehaviour {
request_response: request_response::cbor::Behaviour,
}
-/// Network events generated as a result of supported and configured `NetworkBehaviour`'s
+/// Network events generated as a result of supported and configured [`NetworkBehaviour`]'s
#[derive(Debug)]
enum CoreEvent {
Ping(ping::Event),
@@ -66,60 +67,64 @@ enum CoreEvent {
RequestResponse(request_response::Event),
}
-/// Implement ping events for [`CoreEvent`]
+/// Implement ping events for [`CoreEvent`].
impl From for CoreEvent {
fn from(event: ping::Event) -> Self {
CoreEvent::Ping(event)
}
}
-/// Implement kademlia events for [`CoreEvent`]
+/// Implement kademlia events for [`CoreEvent`].
impl From for CoreEvent {
fn from(event: kad::Event) -> Self {
CoreEvent::Kademlia(event)
}
}
-/// Implement identify events for [`CoreEvent`]
+/// Implement identify events for [`CoreEvent`].
impl From for CoreEvent {
fn from(event: identify::Event) -> Self {
CoreEvent::Identify(event)
}
}
-/// Implement request_response events for [`CoreEvent`]
+/// Implement request_response events for [`CoreEvent`].
impl From> for CoreEvent {
fn from(event: request_response::Event) -> Self {
CoreEvent::RequestResponse(event)
}
}
-/// Structure containing necessary data to build [`Core`]
+/// Structure containing necessary data to build [`Core`].
pub struct CoreBuilder {
+ /// The network id for the stream protocol to use.
network_id: StreamProtocol,
+ /// The keypair of the node.
keypair: Keypair,
+ /// The TCP and UDP ports to listen on.
tcp_udp_port: (Port, Port),
+ /// The bootnodes to connect to.
boot_nodes: HashMap,
- /// the network event handler
+ /// The network event handler.
handler: T,
- /// Prevents blocking forever due to absence of expected data from the network layer
+ /// Prevents blocking forever due to absence of expected data from the network layer.
network_read_delay: AsyncDuration,
/// The size of the stream buffers to use to track application requests to the network layer
/// internally.
stream_size: usize,
ip_address: IpAddr,
- /// Connection keep-alive duration while idle
+ /// Connection keep-alive duration while idle.
keep_alive_duration: Seconds,
- transport: TransportOpts, /* Maybe this can be a collection in the future to support
- * additive transports */
- /// The `Behaviour` of the `Ping` protocol
+ /// The transport protocols being used.
+ /// TODO: This can be a collection in the future to support additive transports.
+ transport: TransportOpts,
+ /// The `Behaviour` of the `Ping` protocol.
ping: (ping::Behaviour, PingErrorPolicy),
- /// The `Behaviour` of the `Kademlia` protocol
+ /// The `Behaviour` of the `Kademlia` protocol.
kademlia: kad::Behaviour,
- /// The `Behaviour` of the `Identify` protocol
+ /// The `Behaviour` of the `Identify` protocol.
identify: identify::Behaviour,
- /// The `Behaviour` of the `Request-Response` protocol.
- /// The second field value is the function to handle an incoming request from a peer
+ /// The `Behaviour` of the `Request-Response` protocol. The second field value is the function to handle an incoming request from a peer.
request_response: Behaviour,
}
@@ -131,18 +136,17 @@ impl CoreBuilder {
// The default network id
let network_id = DEFAULT_NETWORK_ID;
- // TCP/IP and QUIC are supported by default
+ // The default transports (TCP/IP and QUIC)
let default_transport = TransportOpts::TcpQuic {
tcp_config: TcpConfig::Default,
};
- // Peer Id
+ // The peer ID of the node
let peer_id = config.keypair().public().to_peer_id();
// Set up default config for Kademlia
let mut cfg = kad::Config::default();
cfg.set_protocol_names(vec![StreamProtocol::new(network_id)]);
-
let store = kad::store::MemoryStore::new(peer_id);
let kademlia = kad::Behaviour::with_config(peer_id, store, cfg);
@@ -167,9 +171,8 @@ impl CoreBuilder {
// Timeout defaults to 60 seconds
network_read_delay: AsyncDuration::from_secs(NETWORK_READ_TIMEOUT),
stream_size: usize::MAX,
- // Default is to listen on all interfaces (ipv4)
- ip_address: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
- // Default to 60 seconds
+ // Default is to listen on all interfaces (ipv4).
+ ip_address: IpAddr::V4(DEFAULT_IP_ADDRESS),
keep_alive_duration: DEFAULT_KEEP_ALIVE_DURATION,
transport: default_transport,
// The peer will be disconnected after 20 successive timeout errors are recorded
@@ -299,22 +302,15 @@ impl CoreBuilder {
/// Build the [`Core`] data structure.
///
- /// Handles the configuration of the libp2p Swarm structure and the selected transport
- /// protocols, behaviours and node identity.
+ /// Handles the configuration of the libp2p Swarm structure and the selected transport protocols, behaviours and node identity for tokio and async-std runtimes. The Swarm is wrapped in the Core
+ /// construct which serves as the interface to interact with the internal networking layer.
pub async fn build(self) -> SwarmNlResult> {
- // Build and configure the libp2p Swarm structure. Thereby configuring the selected
- // transport protocols, behaviours and node identity. The Swarm is wrapped in the Core
- // construct which serves as the interface to interact with the internal networking
- // layer
-
#[cfg(feature = "async-std-runtime")]
let mut swarm = {
- // We're dealing with async-std here
- // Configure transports
+ // Configure transports for default and custom configurations
let swarm_builder: SwarmBuilder<_, _> = match self.transport {
TransportOpts::TcpQuic { tcp_config } => match tcp_config {
TcpConfig::Default => {
- // Use the default config
libp2p::SwarmBuilder::with_existing_identity(self.keypair.clone())
.with_async_std()
.with_tcp(
@@ -332,13 +328,11 @@ impl CoreBuilder {
.await
.map_err(|_| SwarmNlError::DNSConfigError)?
},
-
TcpConfig::Custom {
ttl,
nodelay,
backlog,
} => {
- // Use the provided config
let tcp_config = tcp::Config::default()
.ttl(ttl)
.nodelay(nodelay)
@@ -371,7 +365,6 @@ impl CoreBuilder {
// Configure the selected protocols and their corresponding behaviours
swarm_builder
.with_behaviour(|_|
- // Configure the selected behaviours
CoreBehaviour {
ping: self.ping.0,
kademlia: self.kademlia,
@@ -387,12 +380,9 @@ impl CoreBuilder {
#[cfg(feature = "tokio-runtime")]
let mut swarm = {
- // We're dealing with tokio here
- // Configure transports
let swarm_builder: SwarmBuilder<_, _> = match self.transport {
TransportOpts::TcpQuic { tcp_config } => match tcp_config {
TcpConfig::Default => {
- // Use the default config
libp2p::SwarmBuilder::with_existing_identity(self.keypair.clone())
.with_tokio()
.with_tcp(
@@ -407,13 +397,11 @@ impl CoreBuilder {
})?
.with_quic()
},
-
TcpConfig::Custom {
ttl,
nodelay,
backlog,
} => {
- // Use the provided config
let tcp_config = tcp::Config::default()
.ttl(ttl)
.nodelay(nodelay)
@@ -443,7 +431,6 @@ impl CoreBuilder {
// Configure the selected protocols and their corresponding behaviours
swarm_builder
.with_behaviour(|_|
- // Configure the selected behaviours
CoreBehaviour {
ping: self.ping.0,
kademlia: self.kademlia,
@@ -458,7 +445,7 @@ impl CoreBuilder {
};
// Configure the transport multiaddress and begin listening.
- // It can handle multiple future tranports based on configuration e.g WebRTC
+ // It can handle multiple future tranports based on configuration e.g, in the future, WebRTC.
match self.transport {
// TCP/IP and QUIC
TransportOpts::TcpQuic { tcp_config: _ } => {
@@ -517,11 +504,11 @@ impl CoreBuilder {
let _ = swarm.behaviour_mut().kademlia.bootstrap();
// There must be a way for the application to communicate with the underlying networking
- // core. This will involve acceptiing data and pushing data to the application layer.
+ // core. This will involve accepting and pushing data to the application layer.
// Two streams will be opened: The first mpsc stream will allow SwarmNL push data to the
- // application and the application will comsume it (single consumer) The second stream
+ // application and the application will consume it (single consumer). The second stream
// will have SwarmNl (being the consumer) recieve data and commands from multiple areas
- // in the application;
+ // in the application.
let (application_sender, network_receiver) = mpsc::channel::(100);
let (network_sender, application_receiver) = mpsc::channel::(100);
@@ -650,7 +637,7 @@ impl Core {
/// serialized to protobuf format and only a single keypair can be saved at a time.
pub fn save_keypair_offline(&self, config_file_path: &str) -> bool {
// Check the file exists, and create one if not
- if let Ok(metadata) = fs::metadata(config_file_path) {
+ if let Ok(_metadata) = fs::metadata(config_file_path) {
} else {
fs::File::create(config_file_path).expect("could not create config file");
}
@@ -676,12 +663,13 @@ impl Core {
false
}
- /// Return the node's `PeerId`
+ /// Return the node's `PeerId`.
pub fn peer_id(&self) -> String {
self.keypair.public().to_peer_id().to_string()
}
- /// Send data to the network layer and recieve a unique `StreamId` to track the request
+ /// Send data to the network layer and recieve a unique `StreamId` to track the request.
+ ///
/// If the internal stream buffer is full, `None` will be returned.
pub async fn send_to_network(&mut self, app_request: AppData) -> Option {
// Generate stream id
@@ -721,7 +709,7 @@ impl Core {
/// TODO! Buffer cleanup algorithm
/// Explicitly rectrieve the reponse to a request sent to the network layer.
- /// This function is decoupled from the [`send_to_network()`] function so as to prevent delay
+ /// This function is decoupled from the [`send_to_network`] function so as to prevent delay
/// and read immediately as the response to the request should already be in the stream response
/// buffer.
pub async fn recv_from_network(&mut self, stream_id: StreamId) -> NetworkResult {
@@ -836,9 +824,11 @@ impl Core {
}
/// Handle async operations, which basically involves handling two major data sources:
+ ///
/// - Streams coming from the application layer.
/// - Events generated by (libp2p) network activities.
- /// Important information are sent to the application layer over a (mpsc) stream
+ ///
+ /// Important information are sent to the application layer over a (mpsc) stream.
async fn handle_async_operations(
mut swarm: Swarm,
mut network_info: NetworkInfo,
diff --git a/swarm_nl/src/core/prelude.rs b/swarm_nl/src/core/prelude.rs
index 43e3fc848..557464786 100644
--- a/swarm_nl/src/core/prelude.rs
+++ b/swarm_nl/src/core/prelude.rs
@@ -388,7 +388,7 @@ pub trait EventHandler {
/// Default network event handler.
#[derive(Clone)]
pub struct DefaultHandler;
-/// Implement [`EventHandler`] for [`DefaultHandler`]
+/// Implement [`EventHandler`] for [`DefaultHandler`].
impl EventHandler for DefaultHandler {
/// Echo the message back to the sender.
fn handle_incoming_message(&mut self, data: Vec>) -> Vec> {
From 0e582cffaed3f4c029fcd9c28a5ff3da36392da2 Mon Sep 17 00:00:00 2001
From: thewoodfish
Date: Fri, 10 May 2024 23:06:49 +0100
Subject: [PATCH 014/122] add: demonstrations on library capbilities
---
.DS_Store | Bin 0 -> 6148 bytes
.vscode/settings.json | 3 +-
client/Cargo.toml | 3 +
client/bootstrap_config.ini | 9 +-
client/src/main.rs | 530 +++++++++++++-----
swarm_nl/Cargo.toml | 6 +-
.../bootstrap_config.ini | 0
swarm_nl/src/core/mod.rs | 424 +++++++-------
swarm_nl/src/core/prelude.rs | 8 +-
swarm_nl/src/lib.rs | 2 +
swarm_nl/src/prelude.rs | 4 +-
swarm_nl/src/setup.rs | 4 +-
12 files changed, 642 insertions(+), 351 deletions(-)
create mode 100644 .DS_Store
rename bootstrap_config.ini => swarm_nl/bootstrap_config.ini (100%)
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6
GIT binary patch
literal 6148
zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3
zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ
zLs35+`xjp>T0 Self {
- Empire {
- name,
- soldiers: 100,
- farmers: 100,
- blacksmith: 100,
- land_mass: 100,
- gold_reserve: 100,
+mod age_of_empire {
+ use super::*;
+
+ #[derive(Clone)]
+ pub struct Empire {
+ name: String,
+ soldiers: u8,
+ farmers: u8,
+ blacksmith: u8,
+ land_mass: u8,
+ gold_reserve: u8,
+ }
+
+ impl Empire {
+ /// Create a new empire and assign the assets to begin with
+ pub fn new(name: String) -> Self {
+ Empire {
+ name,
+ soldiers: 100,
+ farmers: 100,
+ blacksmith: 100,
+ land_mass: 100,
+ gold_reserve: 100,
+ }
}
}
-}
-impl EventHandler for Empire {
- fn new_listen_addr(
- &mut self,
- local_peer_id: PeerId,
- _listener_id: swarm_nl::ListenerId,
- addr: swarm_nl::Multiaddr,
- ) {
- // announce interfaces we're listening on
- println!("Peer id: {}", local_peer_id);
- println!("We're listening on the {}", addr);
- println!(
- "There are {} soldiers guarding the {} Empire gate",
- self.soldiers, self.name
- );
+ impl EventHandler for Empire {
+ fn new_listen_addr(
+ &mut self,
+ local_peer_id: PeerId,
+ _listener_id: swarm_nl::ListenerId,
+ addr: swarm_nl::Multiaddr,
+ ) {
+ // announce interfaces we're listening on
+ println!("Peer id: {}", local_peer_id);
+ println!("We're listening on the {}", addr);
+ println!(
+ "There are {} soldiers guarding the {} Empire gate",
+ self.soldiers, self.name
+ );
+ }
+
+ fn connection_established(
+ &mut self,
+ peer_id: PeerId,
+ _connection_id: ConnectionId,
+ _endpoint: &ConnectedPoint,
+ _num_established: NonZeroU32,
+ _established_in: Duration,
+ ) {
+ println!("Connection established with peer: {}", peer_id);
+ }
+
+ /// Handle any incoming RPC from any neighbouring empire
+ fn handle_incoming_message(&mut self, data: Vec>) -> Vec> {
+ // The semantics is left to the application to handle
+ match String::from_utf8_lossy(&data[0]) {
+ // Handle the request to get military status
+ Cow::Borrowed("military_status") => {
+ // Get empire name
+ let empire_name = self.name.as_bytes().to_vec();
+
+ // Get military capacity
+ let military_capacity = self.soldiers;
+
+ // marshall into accepted format andd then return it
+ vec![empire_name, vec![military_capacity]]
+ },
+ _ => Default::default(),
+ }
+ }
}
- fn connection_established(
- &mut self,
- peer_id: PeerId,
- _connection_id: ConnectionId,
- _endpoint: &ConnectedPoint,
- _num_established: NonZeroU32,
- _established_in: Duration,
- ) {
- println!("Connection established with peer: {}", peer_id);
+ /// Setup game (This is for the persian Empire)
+ /// This requires no bootnodes connection
+ // #[cfg(not(feature = "macedonian"))]
+ // pub async fn setup_game() -> Core {
+ // // First, we want to configure our node
+ // let config = BootstrapConfig::default();
+
+ // // State kept by this node
+ // let empire = Empire::new(String::from("Spartan"));
+
+ // // Set up network
+ // CoreBuilder::with_config(config, empire)
+ // .build()
+ // .await
+ // .unwrap()
+ // }
+
+ /// The Macedonian Empire setup.
+ /// These require bootnodes of empires to form alliance.
+ /// We will be providing the location (peer id and multiaddress) of the Spartan Empire as boot
+ /// parameters
+ // #[cfg(feature = "macedonian")]
+ pub async fn setup_game() -> Core {
+ // First, we want to configure our node with the bootstrap config file on disk
+ let config = BootstrapConfig::from_file("bootstrap_config.ini");
+
+ // State kept by this node
+ let empire = Empire::new(String::from("Macedonian"));
+
+ // Set up network
+ CoreBuilder::with_config(config, empire)
+ .build()
+ .await
+ .unwrap()
}
- /// Handle any incoming RPC from any neighbouring empire
- fn handle_incoming_message(&mut self, data: Vec>) -> Vec> {
- // The semantics is left to the application to handle
- match String::from_utf8_lossy(&data[0]) {
- // Handle the request to get military status
- Cow::Borrowed("military_status") => {
- // Get empire name
- let empire_name = self.name.as_bytes().to_vec();
-
- // Get military capacity
- let military_capacity = self.soldiers;
-
- // marshall into accepted format andd then return it
- vec![empire_name, vec![military_capacity]]
- },
- _ => Default::default(),
+ /// Play game
+ pub async fn play_game() {
+ // Setup network
+ let mut core = setup_game().await;
+
+ // TODO: DELAY FOR A WHILE
+
+ // Print game state
+ println!("Empire Information:");
+ println!("Name: {}", core.state.soldiers);
+ println!("Farmers: {}", core.state.farmers);
+ println!("Black smiths: {}", core.state.blacksmith);
+ println!("Land mass: {}", core.state.land_mass);
+ println!("Gold reserve: {}", core.state.gold_reserve);
+
+ // TODO! FUNCTION TO CHECK NODES I'M CONNECTED WITH
+
+ let request = vec!["military_status".as_bytes().to_vec()];
+
+ // Spartan Empire
+ let remote_peer_id = "12D3KooWMD3kvZ7hSngeu1p7HAoCCYusSXqPPYDPvzxsa9T4vz3a";
+
+ // Prepare request
+ let status_request = AppData::FetchData {
+ keys: request,
+ peer: string_to_peer_id(remote_peer_id).unwrap(),
+ };
+
+ // Send request
+ // let stream_id = core.send_to_network(status_request).await.unwrap();
+
+ // Get response
+ // AppData::Fetch returns a Vec>, hence we can parse the response from it
+ if let Ok(status_response) = core.fetch_from_network(status_request).await {
+ if let AppResponse::FetchData(status) = status_response {
+ let empire_name = String::from_utf8_lossy(&status[0]);
+ let military_status = status[1][0];
+
+ // Print the military status of the empire we just contacted
+ println!("Empire Contacted:");
+ println!("Name: {} Empire", empire_name);
+ println!("Military Capacity: {} Soldiers", military_status);
+ }
}
+
+ // Keep looping so we can record network events
+ loop {}
}
}
-/// Setup game (This is for the persian Empire)
-/// This requires no bootnodes connection
-// #[cfg(not(feature = "macedonian"))]
-// pub async fn setup_game() -> Core {
-// // First, we want to configure our node
-// let config = BootstrapConfig::default();
-
-// // State kept by this node
-// let empire = Empire::new(String::from("Spartan"));
-
-// // Set up network
-// CoreBuilder::with_config(config, empire)
-// .build()
-// .await
-// .unwrap()
-// }
-
-/// The Macedonian Empire setup.
-/// These require bootnodes of empires to form alliance.
-/// We will be providing the location (peer id and multiaddress) of the Spartan Empire as boot
-/// parameters
-// #[cfg(feature = "macedonian")]
-pub async fn setup_game() -> Core {
- // First, we want to configure our node with the bootstrap config file on disk
- let config = BootstrapConfig::from_file("bootstrap_config.ini");
-
- // State kept by this node
- let empire = Empire::new(String::from("Macedonian"));
-
- // Set up network
- CoreBuilder::with_config(config, empire)
- .build()
- .await
- .unwrap()
+mod ping_test {
+ use swarm_nl::{
+ core::ping_config::{PingConfig, PingErrorPolicy},
+ Failure,
+ };
+
+ use super::*;
+ /// Sate of the Application
+ #[derive(Clone)]
+ pub struct Ping;
+
+ impl EventHandler for Ping {
+ fn new_listen_addr(
+ &mut self,
+ local_peer_id: PeerId,
+ _listener_id: swarm_nl::ListenerId,
+ addr: swarm_nl::Multiaddr,
+ ) {
+ // announce interfaces we're listening on
+ println!("Peer id: {}", local_peer_id);
+ println!("We're listening on the {}", addr);
+ }
+
+ fn connection_established(
+ &mut self,
+ peer_id: PeerId,
+ _connection_id: ConnectionId,
+ _endpoint: &ConnectedPoint,
+ _num_established: NonZeroU32,
+ _established_in: Duration,
+ ) {
+ println!("Connection established with peer: {}", peer_id);
+ }
+
+ fn outbound_ping_success(&mut self, peer_id: PeerId, duration: Duration) {
+ println!("we just pinged {:?}. RTT = {:?}", peer_id, duration);
+ }
+
+ fn outbound_ping_error(&mut self, peer_id: PeerId, err_type: Failure) {
+ println!("Tried to ping {:?}. Error: {:?}", peer_id, err_type);
+ }
+
+ fn handle_incoming_message(&mut self, data: Vec>) -> Vec> {
+ data
+ }
+ }
+
+ #[cfg(not(feature = "second-node"))]
+ pub async fn setup_node(buffer: &mut [u8], ports: (u16, u16)) -> Core {
+ let app_state = Ping;
+
+ // First, we want to configure our node with the bootstrap config file on disk
+ let config = BootstrapConfig::default()
+ .generate_keypair_from_protobuf("ed25519", buffer)
+ .with_tcp(ports.0)
+ .with_udp(ports.1);
+
+ println!("First node here!");
+
+ // Set up network
+ CoreBuilder::with_config(config, app_state)
+ .build()
+ .await
+ .unwrap()
+ }
+
+ pub async fn run_ping_example() {
+ // Our test keypair for the first node
+ let mut protobuf = vec![
+ 8, 1, 18, 64, 34, 116, 25, 74, 122, 174, 130, 2, 98, 221, 17, 247, 176, 102, 205, 3,
+ 27, 202, 193, 27, 6, 104, 216, 158, 235, 38, 141, 58, 64, 81, 157, 155, 36, 193, 50,
+ 147, 85, 72, 64, 174, 65, 132, 232, 78, 231, 224, 88, 38, 55, 78, 178, 65, 42, 97, 39,
+ 152, 42, 164, 148, 159, 36, 170, 109, 178,
+ ];
+ // Ports for the first node
+ let ports = (49500, 49501);
+
+ // The PeerId of the first node
+ let peer_id = Keypair::from_protobuf_encoding(&protobuf)
+ .unwrap()
+ .public()
+ .to_peer_id();
+
+ #[cfg(not(feature = "second-node"))]
+ let node = ping_test::setup_node(&mut protobuf[..], ports).await;
+
+ #[cfg(feature = "second-node")]
+ let node = ping_test::setup_node(peer_id, ports).await;
+
+ loop {}
+ }
+
+ /// Setup node
+ #[cfg(feature = "second-node")]
+ pub async fn setup_node(peer_id: PeerId, ports: (u16, u16)) -> Core {
+ use std::collections::HashMap;
+ // App state
+ let app_state = Ping;
+
+ // Custom ping configuration
+ let custom_ping = PingConfig {
+ interval: Duration::from_secs(3),
+ timeout: Duration::from_secs(5),
+ err_policy: PingErrorPolicy::DisconnectAfterMaxErrors(3),
+ };
+
+ // Set up bootnode to query node 1
+ let mut bootnode = HashMap::new();
+ bootnode.insert(
+ peer_id.to_base58(),
+ format!("/ip4/127.0.0.1/tcp/{}", ports.0),
+ );
+
+ println!("Second node here!");
+
+ // First, we want to configure our node
+ let config = BootstrapConfig::new().with_bootnodes(bootnode);
+
+ // Set up network by passing in a default handler or application state
+ CoreBuilder::with_config(config, app_state)
+ .with_ping(custom_ping)
+ .build()
+ .await
+ .unwrap()
+ }
}
-/// Play game
-pub async fn play_game() {
- // Setup network
- let mut core = setup_game().await;
+mod layer_communication {
+ use super::*;
- // TODO: DELAY FOR A WHILE
+ /// Sate of the Application
+ #[derive(Clone)]
+ pub struct AppState;
- // Print game state
- println!("Empire Information:");
- println!("Name: {}", core.state.soldiers);
- println!("Farmers: {}", core.state.farmers);
- println!("Black smiths: {}", core.state.blacksmith);
- println!("Land mass: {}", core.state.land_mass);
- println!("Gold reserve: {}", core.state.gold_reserve);
+ impl EventHandler for AppState {
+ fn new_listen_addr(
+ &mut self,
+ local_peer_id: PeerId,
+ _listener_id: swarm_nl::ListenerId,
+ addr: swarm_nl::Multiaddr,
+ ) {
+ // announce interfaces we're listening on
+ println!("Peer id: {}", local_peer_id);
+ println!("We're listening on the {}", addr);
+ }
- // TODO! FUNCTION TO CHECK NODES I'M CONNECTED WITH
+ fn connection_established(
+ &mut self,
+ peer_id: PeerId,
+ _connection_id: ConnectionId,
+ _endpoint: &ConnectedPoint,
+ _num_established: NonZeroU32,
+ _established_in: Duration,
+ ) {
+ println!("Connection established with peer: {}", peer_id);
+ }
- let request = vec!["military_status".as_bytes().to_vec()];
+ fn handle_incoming_message(&mut self, data: Vec>) -> Vec> {
+ data
+ }
+ }
- // Spartan Empire
- let remote_peer_id = "12D3KooWMD3kvZ7hSngeu1p7HAoCCYusSXqPPYDPvzxsa9T4vz3a";
+ pub async fn run_comm_example() {
+ // Our test keypair for the first node
+ let mut protobuf = vec![
+ 8, 1, 18, 64, 34, 116, 25, 74, 122, 174, 130, 2, 98, 221, 17, 247, 176, 102, 205, 3,
+ 27, 202, 193, 27, 6, 104, 216, 158, 235, 38, 141, 58, 64, 81, 157, 155, 36, 193, 50,
+ 147, 85, 72, 64, 174, 65, 132, 232, 78, 231, 224, 88, 38, 55, 78, 178, 65, 42, 97, 39,
+ 152, 42, 164, 148, 159, 36, 170, 109, 178,
+ ];
+ // Ports for the first node
+ let ports = (49500, 49501);
- // Prepare request
- let status_request = AppData::FetchData {
- keys: request,
- peer: string_to_peer_id(remote_peer_id).unwrap(),
- };
+ // The PeerId of the first node
+ let peer_id = Keypair::from_protobuf_encoding(&protobuf)
+ .unwrap()
+ .public()
+ .to_peer_id();
+
+ let node = setup_node(&mut protobuf[..], ports).await;
- // Send request
- // let stream_id = core.send_to_network(status_request).await.unwrap();
-
- // Get response
- // AppData::Fetch returns a Vec>, hence we can parse the response from it
- if let Ok(status_response) = core.fetch_from_network(status_request).await {
- if let AppResponse::FetchData(status) = status_response {
- let empire_name = String::from_utf8_lossy(&status[0]);
- let military_status = status[1][0];
-
- // Print the military status of the empire we just contacted
- println!("Empire Contacted:");
- println!("Name: {} Empire", empire_name);
- println!("Military Capacity: {} Soldiers", military_status);
+ // Test that AppData::Echo works (using fetch)
+ test_echo_atomically(node.clone()).await;
+
+ // Test that AppData::Echo works
+ test_echo(node.clone()).await;
+
+
+ loop {}
+ }
+
+ #[cfg(not(feature = "second-node"))]
+ pub async fn setup_node(buffer: &mut [u8], ports: (u16, u16)) -> Core {
+ let app_state = AppState;
+
+ // First, we want to configure our node with the bootstrap config file on disk
+ let config = BootstrapConfig::default()
+ .generate_keypair_from_protobuf("ed25519", buffer)
+ .with_tcp(ports.0)
+ .with_udp(ports.1);
+
+ println!("First node here!");
+
+ // Set up network
+ CoreBuilder::with_config(config, app_state)
+ .build()
+ .await
+ .unwrap()
+ }
+
+ pub async fn test_echo_atomically(mut node: Core) {
+ // Prepare an echo request
+ let echo_string = "Sacha rocks!".to_string();
+ if let Ok(status_response) = node.fetch_from_network(AppData::Echo(echo_string.clone())).await {
+ if let AppResponse::Echo(echoed_response) = status_response {
+ // Assert that what was sent was gotten back
+ assert_eq!(echo_string, echoed_response);
+
+ println!("{} === {}", echo_string, echoed_response);
+ }
}
}
- // Keep looping so we can record network events
- loop {}
-}
+ pub async fn test_echo(mut node: Core) {
+ // Prepare an echo request
+ let echo_string = "Sacha rocks!".to_string();
+
+ // Get request stream id
+ let stream_id = node.send_to_network(AppData::Echo(echo_string.clone())).await.unwrap();
+
+ println!("This is between the sending and the recieving of the payload. It is stored in an internal buffer, until polled for");
+ if let Ok(status_response) = node.recv_from_network(stream_id).await {
+ if let AppResponse::Echo(echoed_response) = status_response {
+ // Assert that what was sent was gotten back
+ assert_eq!(echo_string, echoed_response);
+
+ println!("{} === {}", echo_string, echoed_response);
+ }
+ }
+ }
+}
// make pr
// merge to main
@@ -185,9 +433,17 @@ pub async fn play_game() {
// appdata
// configure logger
-
// TEST
// Events, dailing, AppData, RPC, Kad, Ping, Gossip
// check for rexeports e.g to initialize gossipsub
-// check if i'm subscribed to topics
\ No newline at end of file
+// check if i'm subscribed to topics
+
+// BootstrapConfig
+// CoreBuilder
+
+// INTEGRATION
+// Core
+// Ping
+// Events
+// App requests i.e kad, rpc, echo
diff --git a/swarm_nl/Cargo.toml b/swarm_nl/Cargo.toml
index d2608d2c2..274e5ab1c 100644
--- a/swarm_nl/Cargo.toml
+++ b/swarm_nl/Cargo.toml
@@ -14,7 +14,6 @@ libp2p-identity = { version="0.2.8", "features"=["secp256k1", "ecdsa", "rsa", "e
futures = "0.3.30"
futures-time = "3.0.0"
serde = "1.0.200"
-async-trait = "0.1.80"
base58 = "0.2.0"
[dependencies.async-std]
@@ -27,4 +26,7 @@ optional = true
[features]
tokio-runtime = ["tokio"]
-async-std-runtime = ["async-std"]
\ No newline at end of file
+async-std-runtime = ["async-std"]
+default = ["tokio-runtime"]
+
+
diff --git a/bootstrap_config.ini b/swarm_nl/bootstrap_config.ini
similarity index 100%
rename from bootstrap_config.ini
rename to swarm_nl/bootstrap_config.ini
diff --git a/swarm_nl/src/core/mod.rs b/swarm_nl/src/core/mod.rs
index d24534523..7007f7bc8 100644
--- a/swarm_nl/src/core/mod.rs
+++ b/swarm_nl/src/core/mod.rs
@@ -2,11 +2,11 @@
/// Core data structures and protocol implementations for building a swarm.
use std::{
collections::{HashMap, HashSet},
+ fs,
net::{IpAddr, Ipv4Addr},
num::NonZeroU32,
sync::Arc,
time::Duration,
- fs,
};
use base58::FromBase58;
@@ -16,14 +16,14 @@ use futures::{
select, SinkExt, StreamExt,
};
use libp2p::{
- gossipsub::{self, IdentTopic, Topic, TopicHash},
+ gossipsub::{self, IdentTopic, TopicHash},
identify::{self, Info},
kad::{self, store::MemoryStore, Record},
multiaddr::Protocol,
noise,
ping::{self, Failure},
request_response::{self, cbor::Behaviour, ProtocolSupport},
- swarm::{ConnectionError, NetworkBehaviour, SwarmEvent},
+ swarm::{dial_opts::DialOpts, ConnectionError, NetworkBehaviour, SwarmEvent},
tcp, tls, yamux, Multiaddr, StreamProtocol, Swarm, SwarmBuilder,
};
@@ -896,9 +896,11 @@ impl