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
connectis amultiaddrstring, indicates that the network stack shall create/reuse aQUICtransport 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
traitsthat 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
Connectordriver 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
PeerBookdriver 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
Connectorimplementation forSwitch. - A type wrapper of
DriverConnector - A type wrapper of
EventMediator - A
streamthat accept a kined of eventE - A type wrapper of
EventStream - A type wrapper of
DriverKeyStore - In-memory
KeyStoreimplementation with random key generation on startup. - An in memory
PeerBookimplementation - The default implementation of
StreamDispatcher - A type wrapper of
DriverConnection - A type wrapper of
DriverPeerBook - A
PeerInfocombines 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 Switchis the entry point of the libp2p network.- A builder to create the
Switchinstance. - The configuration of
Switch. - The default
EventMediatorimplementation, 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
connectfunction. - Variant returns by
Connector::connectfunction - 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
Switchevent must implement this trait. - An extension trait for libp2p rpc calls.
Functions§
- global_
switch global_registerReturns the registered globalSwitchinstance. - register_
switch global_registerRegister oneSwitchas a global instance.
Type Aliases§
- The result type for this module.