Expand description
Core data structures and protocol implementations for building a swarm.
§Network builder
To build a network or a node, 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:
BootstrapConfig
to pass in a bootstrap node configuration.EventHandler
to respond to network events.
§Default setup
Here’s how you would build a bootstrap node with the default library settings, using a DefaultHandler
struct to respond to network events:
// Default config
let config = BootstrapConfig::default();
// Default Handler
let handler = DefaultHandler;
let mut network = CoreBuilder::with_config(config, handler)
.build()
.await
.unwrap();
§Custom event handler
To customize how your application handles network events, you’ll need to implement the methods from EventHandler
. It’s best to implement EventHandler
on your application’s state. This allows you to:
- make critical state changes in response to network events.
- log state data at different point during network event changes.
use swarm_nl::core::EventHandler;
#[derive(Clone)]
struct ApplicationState {
name: String,
version: f32,
}
impl EventHandler for ApplicationState {
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
);
}
// Echo data recieved from a RPC
fn rpc_handle_incoming_message(&mut self, data: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
println!("Recvd incoming RPC: {:?}", data);
data
}
// Handle the incoming gossip message
fn gossipsub_handle_incoming_message(&mut self, source: PeerId, data: Vec<String>) {
println!("Recvd incoming gossip: {:?}", data);
}
}
§Overriding the default network configuration
You can explicitly overrride the default values of CoreBuilder::with_config
by calling the methods like the following before building the network:
CoreBuilder::with_transports
: Configures a custom transport to use, specified inTransportOpts
.CoreBuilder::with_network_id
: Configures the network ID or name e.g./your-protocol-name/1.0
.CoreBuilder::listen_on
: Configures the IP address to listen on e.g. IPv4(127.0.0.1).CoreBuilder::with_idle_connection_timeout
: Configures a timeout for keeping a connection alive.- etc.
For example:
// Default config
let config = BootstrapConfig::default();
// Default handler
let handler = DefaultHandler;
// Create a default network core builder
let default_node = CoreBuilder::with_config(config, handler);
// Override default with custom configurations
// Network Id
let mut custom_network_id = "/custom-protocol/1.0".to_string();
// Transport
let mut custom_transport = TransportOpts::TcpQuic {
tcp_config: TcpConfig::Custom {
ttl: 10,
nodelay: true,
backlog: 10,
},
};
// Keep-alive
let mut custom_keep_alive_duration = 20;
// IP address
let mut custom_ip_address = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
// Build a custom configured node
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())
.build()
.await.
.unwrap();
§Application Interaction
The core library provides very simple interfaces to communicate with the network layer to trigger and drive network behaviour or to make enquiries about internal network state.
This is achieved by constructing a request with the AppData
struct and passing it to the network. An AppResponse
structure is returned, containing the network’s response to the applications request.
There are two ways of querying the network layer with each having its own peculiarities and use-cases:
- Using the
Core::query_network()
method: This method aims to complete its operations atomically and blocks until the network returns a reponse or if the request times out. It is useful when the response from the network is important for the application logic to continue.
// Default config
let config = BootstrapConfig::default();
// Default handler
let handler = DefaultHandler;
// Create a default network core builder
let node = CoreBuilder::with_config(config, handler);
// Join a network (subscribe to a topic)
let gossip_request = AppData::GossipsubJoinNetwork(MY_NETWORK.to_string());
// Blocks until response is returned from the network layer
if let Ok(_) = node.query_network(gossip_request).await {
println!("Subscription successfull");
}
- Using the
Core::send_to_network()
andCore::recv_from_network()
method: This method does not block and is split into two parts - sending and recieving. When the request is recieved by the network layer through theCore::send_to_network()
method, aStreamId
is immediately returned and the request is handled. When there is a response (or a timeout), it is stored internally in a response buffer until it is returned by explicitly polling the network through theCore::recv_from_network()
method which takes in theStreamId
returned earlier. TheStreamId
helps to track the requests and their corresponding responses internally in the network layer.
// Default config
let config = BootstrapConfig::default();
// Default handler
let handler = DefaultHandler;
// Create a default network core builder
let node = CoreBuilder::with_config(config, handler);
// Join a network (subscribe to a topic)
let gossip_request = AppData::GossipsubJoinNetwork(MY_NETWORK.to_string());
// Send request to the application layer
let stream_id = node.send_to_network(gossip_request).await.unwrap();
// ...
// Run other application logic
//...
// Explicitly retrieve the response of our request
if let Ok(result) = node.recv_from_network(stream_id).await {
println!("Subscription successfull");
assert_eq!(AppResponse::GossipsubJoinSuccess, result);
}
Note: The internal buffer is limited in capacity and pending responses should be removed as soon as possibe. A full buffer will prevent the network from recieving more requests.
Modules§
- Module that contains important data structures to manage
Ping
operations on the network.
Structs§
- The core interface for the application layer to interface with the networking layer.
- Structure containing necessary data to build
Core
. - Default network event handler.
- The configuration for the RPC protocol.
- A simple struct used to track requests sent from the application layer to the network layer.
Enums§
- Request sent from the application layer to the networking layer.
- Response to requests sent from the application to the network layer.
- Network error type containing errors encountered during network operations.
Constants§
- The duration (in seconds) to wait for response from the network layer before timing out.
- The time it takes for the task to sleep before it can recheck if an output has been placed in the repsonse buffer;
Traits§
- The high level trait that provides an interface for the application layer to respond to network events.
Type Aliases§
- Type that contains the result of querying the network layer.