Expand description
XSTACK is developed with the primary goal of making it less difficult to use the libp2p
network stack in rust.
For this purpose, the API of XSTACK has been designed to closely resemble the std::net
library.
§Connect to peer and negotiate the application layer protocol
use xstack::ProtocolStream;
let (stream, negotiated_proto_id) =
ProtocolStream::connect(
"/ip4/104.131.131.82/udp/4001/quic-v1/p2p/QmaCpDMGv...",
["/ipfs/kad/1.0.0"]
)
.await
.unwrap();
-
The first parameter of
connect
is amultiaddr
string, indicates that the network stack shall create/reuse aQUIC
transport connection to peerQmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
; In order to conserve resources, the protocol stack may not follow the multiaddr recommendation, but instead directly reuse a pre-existing connection for a different transport layer protocol; -
The second parameter is a list of candidate protocols, the network protocol stack will use this list to negotiate with peer to select a protocol to communicate with.
§Server-side listeners.
There are two types of inbound communication channel listeners
§one for the transport layer
If the node needs to be able to receive inbound transport connections, developers need to manually create transport listeners; the transport listener is more like a traditional socket listener, but developers can’t handle inbound connections manually, that part is handled automatically by the stack.
the following codes creates an instance of the Switch
(network stack) and binds a tcp transport listener to it:
use xstack::Switch;
Switch::new("test")
// .transport(TcpTransport)
.transport_bind(["/ip4/127.0.0.1/tcp/0"])
.create()
.await
.unwrap()
// register to global context.
.into_global();
§one for the application layer
If a node wishes to process a specific rpc request from a peer node, it should create a corresponding protocol listener;
use a list of candidate protocols as the parameter to create one:
use xstack::ProtocolListener;
use futures::TryStreamExt;
let mut incoming = ProtocolListener::bind(["/ipfs/kad/1.0.0"]).await.unwrap().into_incoming();
while let Some((stream,_)) = incoming.try_next().await.unwrap() {
// handle rpc request.
}
§Modularity
XSTACK is designed to be modular, allowing developers to mix and match different components to meet the needs of their particular application. This makes it easy to customize the networking stack to fit the specific requirements of any P2P application.
There are two types of components to customize the networking stack
-
Transport: xstack provides a set of
traits
that can be adapted to support various transport protocols, allowing xstack applications to operate in various networking environments as the wealth of transport protocol choices makes it possible to use xstack in a variety of scenarios. -
Protocol: based on
ProtocolStream
/ProtocolListener
, developers can customise the application layer/business logic. implement utilities such as: node routing, data storage, overlay networks, etc.
§Customize networking stack
When create a new switch, the framework forces you to specify the transport layer protocols supported
by the stack as well as the listening ports.
see transport
and transport_bind
for
more informations.
use xstack::Switch;
Switch::new("test")
// .transport(TcpTransport)
.transport_bind(["/ip4/127.0.0.1/tcp/0"])
.create()
.await
.unwrap()
// register to global context.
.into_global();
Also, the framework does not have any restrictions on the protocol layer; developers can use XSTACK directly to develop specific application layer protocols or build their own frameworks further on top of it.
§Usability
Since XSTACK is a user-mode network stack, any call
(e.g., connect_with
, bind_with
, etc.)
requires the switch instance as the first argument;
We call this instance the XSTACK context
, which you can think of as a transport
layer virtual switch for libp2p networks, all of our incoming and outgoing connections
are established through it.
To make framework even easier to use, we also provide a global XSTACK context
option(available on crate feature global_register only).
before calling those functions(e.g., connect
, bind
, etc.),
the developers should first register the global XSTACK context
:
use xstack::register_switch;
fn main() {
// register_switch(switch);
}
§Inside Switch
see transport_syscall
for details
§Asynchronous system interface.
Due to the chaos of the rust asynchronous programming, XSTACK develop the RASI
crate
to access various rust asynchronous runtimes (such as tokio, async-std, etc.).
Modules§
- A
Connector
driver must implement theDriver-*
traits in this module. - A xstack event mediator driver must implement the
Driver-*
traits in this module. - events of switch.
- A node’s network identity keys.
- A libp2p keystore driver must implement the
Driver-*
traits in this module. - Implementation of multiaddr in Rust.
- The
PeerBook
driver must implement theDriver-*
traits in this module. - A libp2p protocol dispather driver must implement the
Driver-*
traits in this module. - A libp2p transport driver must implement the
Driver-*
traits in this module.
Macros§
- Create a Wrapper type for Driver-* traits.
Structs§
- The default
Connector
implementation forSwitch
. - A type wrapper of
DriverConnector
- A type wrapper of
EventMediator
- A
stream
that accept a kined of eventE
- A type wrapper of
EventStream
- A type wrapper of
DriverKeyStore
- In-memory
KeyStore
implementation with random key generation on startup. - An in memory
PeerBook
implementation - The default implementation of
StreamDispatcher
- A type wrapper of
DriverConnection
- A type wrapper of
DriverPeerBook
- A
PeerInfo
combines a Peer ID with a set of multiaddrs that the peer is listening on. - A listener of protocol streams.
- A type wrapper of
DriverStream
- A type wrapper of
DriverStreamDispatcher
Switch
is the entry point of the libp2p network.- A builder to create the
Switch
instance. - The configuration of
Switch
. - The default
EventMediator
implementation, it guarantees that event messages will not be lost. - A type wrapper of
DriverTransport
- A type wrapper of
DriverListener
- Monotonically increasing id
Enums§
- A variant for autonat protocol state.
- Variant type used by
connect
function. - Variant returns by
Connector::connect
function - A error variant, returns by switch apis.
- Variant of switch events.
Constants§
- protocol name of libp2p identity
- protocol name of libp2p ping
- protocol name of libp2p identity push
Traits§
- A
Switch
event must implement this trait. - An extension trait for libp2p rpc calls.
Functions§
- global_
switch global_register
Returns the registered globalSwitch
instance. - register_
switch global_register
Register oneSwitch
as a global instance.
Type Aliases§
- The result type for this module.