diff --git a/.changes/2020-12-21T-communication_actor.md b/.changes/2020-12-21T-communication_actor.md new file mode 100644 index 000000000..3791bba88 --- /dev/null +++ b/.changes/2020-12-21T-communication_actor.md @@ -0,0 +1,4 @@ +--- +"stronghold-communication": minor +--- +Refactor the communication actor by adding config and implementing a seperate struct for the swarm task. diff --git a/communication/examples/actor.rs b/communication/examples/actor.rs index 57f9ade8f..418481eca 100644 --- a/communication/examples/actor.rs +++ b/communication/examples/actor.rs @@ -6,7 +6,7 @@ use libp2p::core::identity::Keypair; use riker::actors::*; use serde::{Deserialize, Serialize}; use stronghold_communication::{ - actor::{CommunicationActor, CommunicationEvent}, + actor::{CommsActorConfig, CommunicationActor, CommunicationEvent}, behaviour::message::P2PReqResEvent, }; @@ -64,14 +64,12 @@ impl Actor for TestActor { } fn main() { - let local_keys = Keypair::generate_ed25519(); let sys = ActorSystem::new().unwrap(); + let local_keys = Keypair::generate_ed25519(); let chan: ChannelRef> = channel("p2p", &sys).unwrap(); - sys.actor_of_args::, _>( - "communication-actor", - (local_keys, chan.clone(), None), - ) - .unwrap(); + let config = CommsActorConfig::new(local_keys, None, Some(chan.clone()), None); + sys.actor_of_args::, _>("communication-actor", config) + .unwrap(); sys.actor_of_args::("test-actor", chan).unwrap(); std::thread::sleep(Duration::from_secs(600)); } diff --git a/communication/src/actor.rs b/communication/src/actor.rs index 091f068d5..eb231dc03 100644 --- a/communication/src/actor.rs +++ b/communication/src/actor.rs @@ -10,10 +10,15 @@ use core::{ ops::Deref, task::{Context as TaskContext, Poll}, }; -use futures::{channel::mpsc, future, prelude::*}; +use futures::{ + channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, + future, + prelude::*, + select, +}; use libp2p::{ core::{connection::PendingConnectionError, identity::Keypair, ConnectedPoint, Multiaddr, PeerId}, - swarm::{Swarm, SwarmEvent}, + swarm::{DialError, Swarm, SwarmEvent}, }; use riker::actors::*; @@ -23,6 +28,8 @@ pub enum ConnectPeerError { InvalidPeerId, ConnectionLimit, IO, + Banned, + NoAddresses, } impl From> for ConnectPeerError { @@ -36,6 +43,16 @@ impl From> for ConnectPeerError { } } +impl From for ConnectPeerError { + fn from(error: DialError) -> Self { + match error { + DialError::Banned => ConnectPeerError::Banned, + DialError::ConnectionLimit(_) => ConnectPeerError::ConnectionLimit, + DialError::NoAddresses => ConnectPeerError::NoAddresses, + } + } +} + #[derive(Debug, Clone)] pub enum CommunicationEvent { Message(P2PReqResEvent), @@ -52,6 +69,30 @@ pub enum CommunicationEvent { Shutdown, } +#[derive(Clone)] +pub struct CommsActorConfig { + keypair: Keypair, + listen_addr: Option, + chan: Option>>, + client_ref: Option, +} + +impl CommsActorConfig { + pub fn new( + keypair: Keypair, + listen_addr: Option, + chan: Option>>, + client_ref: Option, + ) -> CommsActorConfig { + CommsActorConfig { + keypair, + listen_addr, + chan, + client_ref, + } + } +} + /// Actor for the communication to a remote actor over the swarm /// /// Publishes incoming request- and response-messages from the swarm in the given channel to the "swarm_inbound" @@ -63,7 +104,7 @@ pub enum CommunicationEvent { /// use libp2p::identity::Keypair; /// use riker::actors::*; /// use serde::{Deserialize, Serialize}; -/// use stronghold_communication::actor::{CommunicationActor, CommunicationEvent}; +/// use stronghold_communication::actor::{CommsActorConfig, CommunicationActor, CommunicationEvent}; /// /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] /// pub enum Request { @@ -78,28 +119,22 @@ pub enum CommunicationEvent { /// let local_keys = Keypair::generate_ed25519(); /// let sys = ActorSystem::new().unwrap(); /// let chan: ChannelRef> = channel("remote-peer", &sys).unwrap(); -/// sys.actor_of_args::, _>("communication-actor", (local_keys, chan, None)); +/// let config = CommsActorConfig::new(local_keys, None, Some(chan), None); +/// sys.actor_of_args::, _>("communication-actor", config); /// ``` pub struct CommunicationActor { - chan: ChannelRef>, - keypair: Keypair, - swarm_tx: Option, Sender)>>, + config: Option>, + swarm_tx: Option>>, poll_swarm_handle: Option>, - listen_addr: Option, } -impl - ActorFactoryArgs<(Keypair, ChannelRef>, Option)> for CommunicationActor -{ - fn create_args( - (keypair, chan, listen_addr): (Keypair, ChannelRef>, Option), - ) -> Self { +impl ActorFactoryArgs> for CommunicationActor { + fn create_args(config: CommsActorConfig) -> Self { + // Channel to communicate from the CommunicationActor with the swarm task. Self { - chan, - keypair, + config: Some(config), swarm_tx: None, poll_swarm_handle: None, - listen_addr, } } } @@ -111,84 +146,99 @@ impl Actor for CommunicationActor { // // The swarm_outbound topic can be used by other actors within the ActorSystem to publish messages for remote peers. fn pre_start(&mut self, ctx: &Context) { - let topic = Topic::from("to_swarm"); - let sub = Box::new(ctx.myself()); - self.chan.tell(Subscribe { actor: sub, topic }, None); + if let Some(chan) = self.config.as_ref().unwrap().chan.clone() { + let topic = Topic::from("to_swarm"); + let sub = Box::new(ctx.myself()); + chan.tell(Subscribe { actor: sub, topic }, None); + } } // Start a seperate task to manage the communication from and to the swarm fn post_start(&mut self, ctx: &Context) { - // Channel to communicate from the CommunicationActor with the swarm task. - let (swarm_tx, mut swarm_rx) = mpsc::channel(16); + let (swarm_tx, swarm_rx) = unbounded(); self.swarm_tx = Some(swarm_tx); - - // Create a P2PNetworkBehaviour for the swarm communication. - let mut swarm = P2PNetworkBehaviour::::init_swarm(self.keypair.clone()).unwrap(); - let listen_addr = self - .listen_addr - .clone() - .unwrap_or_else(|| "/ip4/0.0.0.0/tcp/0".parse().unwrap()); - Swarm::listen_on(&mut swarm, listen_addr).unwrap(); - - let chan = self.chan.clone(); - let self_ref = ctx.myself(); + let self_actor_ref = BasicActorRef::from(ctx.myself()); + let swarm_task = SwarmTask::::new(self.config.take().unwrap(), swarm_rx, self_actor_ref); // Kick off the swarm communication in it's own task. - let handle = ctx.run(future::poll_fn(move |mut tcx: &mut TaskContext<'_>| { - poll_swarm(self_ref.clone(), &mut tcx, &mut swarm, &mut swarm_rx, &chan) - })); - self.poll_swarm_handle = handle.ok(); + self.poll_swarm_handle = ctx.run(swarm_task.poll_swarm()).ok() } // Send shutdown event over tx to swarm task and wait for the swarm to stop listening. fn post_stop(&mut self) { - if let Some(tx) = self.swarm_tx.as_mut() { - task::block_on(future::poll_fn(move |tcx: &mut TaskContext<'_>| { - match tx.poll_ready(tcx) { - Poll::Ready(Ok(())) => Poll::Ready(tx.start_send((CommunicationEvent::Shutdown, None))), - Poll::Ready(err) => Poll::Ready(err), - _ => Poll::Pending, - } - })) - .unwrap(); - } + self.tx_to_swarm_task(CommunicationEvent::Shutdown); if let Some(handle) = self.poll_swarm_handle.as_mut() { task::block_on(handle); } } // Forward the received events to the task that is managing the swarm communication. - fn recv(&mut self, _ctx: &Context, msg: Self::Msg, sender: Sender) { - if let Some(tx) = self.swarm_tx.as_mut() { - task::block_on(future::poll_fn(move |tcx: &mut TaskContext<'_>| { - match tx.poll_ready(tcx) { - Poll::Ready(Ok(())) => Poll::Ready(tx.start_send((msg.clone(), sender.clone()))), - Poll::Ready(err) => Poll::Ready(err), - _ => Poll::Pending, - } - })) - .unwrap(); - } + fn recv(&mut self, _ctx: &Context, msg: Self::Msg, _sender: Sender) { + self.tx_to_swarm_task(msg); } } -// Poll from the swarm for events from remote peers and from the `swarm_tx` channel for events from the local actor, and -// forward them -fn poll_swarm( - self_ref: ActorRef< as Actor>::Msg>, - tcx: &mut TaskContext<'_>, - swarm: &mut Swarm>, - swarm_rx: &mut mpsc::Receiver<(CommunicationEvent, Sender)>, - chan: &ChannelRef>, -) -> Poll<()> { - // Poll for request that are forwarded through the swarm_tx channel and send them over the swarm to remote - // peers. - loop { - let (event, sender_opt) = match swarm_rx.poll_next_unpin(tcx) { - Poll::Ready(Some(e)) => e, - Poll::Ready(None) => return Poll::Ready(()), - Poll::Pending => break, - }; +impl CommunicationActor { + fn tx_to_swarm_task(&mut self, msg: CommunicationEvent) { + let tx = &mut self.swarm_tx.as_mut().unwrap(); + task::block_on(future::poll_fn(move |tcx: &mut TaskContext<'_>| { + match tx.poll_ready(tcx) { + Poll::Ready(Ok(())) => Poll::Ready(tx.start_send(msg.clone())), + Poll::Ready(err) => Poll::Ready(err), + _ => Poll::Pending, + } + })) + .unwrap(); + } +} + +struct SwarmTask { + swarm: Swarm>, + chan: Option>>, + client_ref: Option, + swarm_rx: UnboundedReceiver>, + self_ref: BasicActorRef, +} + +impl SwarmTask { + fn new( + config: CommsActorConfig, + swarm_rx: UnboundedReceiver>, + self_ref: BasicActorRef, + ) -> Self { + // Create a P2PNetworkBehaviour for the swarm communication. + let mut swarm = P2PNetworkBehaviour::::init_swarm(config.keypair.clone()).unwrap(); + let listen_addr = config + .listen_addr + .clone() + .unwrap_or_else(|| "/ip4/0.0.0.0/tcp/0".parse().unwrap()); + Swarm::listen_on(&mut swarm, listen_addr).unwrap(); + SwarmTask { + swarm, + chan: config.chan, + client_ref: config.client_ref, + swarm_rx, + self_ref, + } + } + + // Poll from the swarm for events from remote peers and from the `swarm_tx` channel for events from the local actor, + // and forward them + async fn poll_swarm(mut self) { + loop { + select! { + actor_event = self.swarm_rx.next().fuse()=> { + if actor_event.is_none() || self.handle_actor_event(actor_event.unwrap()).is_none(){ + return + } + }, + swarm_event = self.swarm.next_event().fuse() => self.handle_swarm_event(swarm_event), + }; + } + } + + // Poll for request that are forwarded through the swarm_tx channel and send them over the swarm to remote peers. + fn handle_actor_event(&mut self, event: CommunicationEvent) -> Option<()> { match event { CommunicationEvent::Message(message) => match message { P2PReqResEvent::Req { @@ -196,114 +246,84 @@ fn poll_swarm( request_id: _, request, } => { - swarm.send_request(&peer_id, request); + self.swarm.send_request(&peer_id, request); } P2PReqResEvent::Res { peer_id: _, request_id, response, } => { - let _ = swarm.send_response(response, request_id); + let _ = self.swarm.send_response(response, request_id); } _ => {} }, CommunicationEvent::ConnectPeer(addr) => { - let response_event = connect_remote(self_ref.clone(), swarm, chan, addr); - if let Some(sender) = sender_opt { - let _ = sender.try_tell(response_event, self_ref.clone()); + if Swarm::dial_addr(&mut self.swarm, addr.clone()).is_err() { + let response = CommunicationEvent::ConnectPeerResult { + addr, + result: Err(ConnectPeerError::ConnectionLimit), + }; + self.tell_actor(response); } } CommunicationEvent::GetSwarmInfo => { - if let Some(sender) = sender_opt { - let peer_id = *Swarm::local_peer_id(&swarm); - let listeners = Swarm::listeners(&swarm).cloned().collect(); - let swarm_info = CommunicationEvent::::SwarmInfo { peer_id, listeners }; - let _ = sender.try_tell(swarm_info, self_ref.clone()); - } + let peer_id = *Swarm::local_peer_id(&self.swarm); + let listeners = Swarm::listeners(&self.swarm).cloned().collect(); + let swarm_info = CommunicationEvent::::SwarmInfo { peer_id, listeners }; + self.tell_actor(swarm_info); } - CommunicationEvent::Shutdown => return Poll::Ready(()), + CommunicationEvent::Shutdown => return None, _ => {} } + Some(()) } + // Poll from the swarm for requests and responses from remote peers and publish them in the channel. - loop { - match swarm.poll_next_unpin(tcx) { - Poll::Ready(Some(event)) => { - if let P2PEvent::RequestResponse(boxed_event) = event { - chan.tell( - Publish { - msg: CommunicationEvent::Message(boxed_event.deref().clone()), - topic: Topic::from("from_swarm"), - }, - Option::::from(self_ref.clone()), - ) - } + fn handle_swarm_event(&mut self, event: SwarmEvent, HandleErr>) { + let msg = match event { + SwarmEvent::Behaviour(P2PEvent::RequestResponse(boxed_event)) => { + Some(CommunicationEvent::Message(boxed_event.deref().clone())) } - Poll::Ready(None) => return Poll::Ready(()), - Poll::Pending => break, + SwarmEvent::ConnectionEstablished { + peer_id, + endpoint: ConnectedPoint::Dialer { address }, + num_established: _, + } => Some(CommunicationEvent::ConnectPeerResult { + addr: address, + result: Ok(peer_id), + }), + SwarmEvent::UnreachableAddr { + peer_id: _, + address, + error, + attempts_remaining: 0, + } => Some(CommunicationEvent::ConnectPeerResult { + addr: address, + result: Err(ConnectPeerError::from(error)), + }), + SwarmEvent::UnknownPeerUnreachableAddr { address, error } => Some(CommunicationEvent::ConnectPeerResult { + addr: address, + result: Err(ConnectPeerError::from(error)), + }), + _ => None, + }; + if let Some(msg) = msg { + self.tell_actor(msg); } } - Poll::Pending -} -fn connect_remote( - self_ref: ActorRef< as Actor>::Msg>, - swarm: &mut Swarm>, - chan: &ChannelRef>, - addr: Multiaddr, -) -> CommunicationEvent { - if Swarm::dial_addr(swarm, addr.clone()).is_ok() { - loop { - match task::block_on(swarm.next_event()) { - SwarmEvent::Behaviour(P2PEvent::RequestResponse(boxed_event)) => { - chan.tell( - Publish { - msg: CommunicationEvent::Message(boxed_event.deref().clone()), - topic: Topic::from("from_swarm"), - }, - Option::::from(self_ref.clone()), - ); - } - SwarmEvent::ConnectionEstablished { - peer_id, - endpoint: ConnectedPoint::Dialer { address }, - num_established: _, - } => { - if address == addr { - return CommunicationEvent::ConnectPeerResult { - addr, - result: Ok(peer_id), - }; - } - } - SwarmEvent::UnreachableAddr { - peer_id: _, - address, - error, - attempts_remaining: 0, - } => { - if address == addr { - return CommunicationEvent::ConnectPeerResult { - addr, - result: Err(ConnectPeerError::from(error)), - }; - } - } - SwarmEvent::UnknownPeerUnreachableAddr { address, error } => { - if address == addr { - return CommunicationEvent::ConnectPeerResult { - addr, - result: Err(ConnectPeerError::from(error)), - }; - } - } - _ => {} - } + fn tell_actor(&mut self, msg: CommunicationEvent) { + if let Some(chan) = self.chan.as_ref() { + chan.tell( + Publish { + msg: msg.clone(), + topic: Topic::from("from_swarm"), + }, + Some(self.self_ref.clone()), + ) } - } else { - CommunicationEvent::ConnectPeerResult { - addr, - result: Err(ConnectPeerError::Transport), + if let Some(client) = self.client_ref.as_ref() { + let _ = client.try_tell(msg, self.self_ref.clone()); } } } @@ -326,17 +346,13 @@ mod test { } struct LocalActor { - chan: ChannelRef>, remote_peer_addr: Multiaddr, has_received_response: bool, } - impl ActorFactoryArgs<(ChannelRef>, Multiaddr)> for LocalActor { - fn create_args( - (chan, remote_peer_addr): (ChannelRef>, Multiaddr), - ) -> Self { + impl ActorFactoryArgs for LocalActor { + fn create_args(remote_peer_addr: Multiaddr) -> Self { LocalActor { - chan, remote_peer_addr, has_received_response: false, } @@ -347,15 +363,11 @@ mod test { type Msg = CommunicationEvent; fn pre_start(&mut self, ctx: &Context) { - let topic = Topic::from("from_swarm"); - let sub = Box::new(ctx.myself()); - self.chan.tell(Subscribe { actor: sub, topic }, None); + let self_ref = BasicActorRef::from(ctx.myself()); let local_keys = Keypair::generate_ed25519(); - ctx.actor_of_args::, _>( - "communication", - (local_keys, self.chan.clone(), None), - ) - .unwrap(); + let config = CommsActorConfig::new(local_keys, None, None, Some(self_ref)); + ctx.actor_of_args::, _>("communication", config) + .unwrap(); } fn post_start(&mut self, ctx: &Context) { @@ -394,15 +406,12 @@ mod test { } struct RemoteActor { - chan: ChannelRef>, - local_peer_addr: Multiaddr, + listening_addr: Multiaddr, } - impl ActorFactoryArgs<(ChannelRef>, Multiaddr)> for RemoteActor { - fn create_args( - (chan, local_peer_addr): (ChannelRef>, Multiaddr), - ) -> Self { - RemoteActor { chan, local_peer_addr } + impl ActorFactoryArgs for RemoteActor { + fn create_args(listening_addr: Multiaddr) -> Self { + RemoteActor { listening_addr } } } @@ -410,15 +419,16 @@ mod test { type Msg = CommunicationEvent; fn pre_start(&mut self, ctx: &Context) { - let topic = Topic::from("from_swarm"); - let sub = Box::new(ctx.myself()); - self.chan.tell(Subscribe { actor: sub, topic }, None); + let self_ref = BasicActorRef::from(ctx.myself()); let local_keys = Keypair::generate_ed25519(); - ctx.actor_of_args::, _>( - "communication", - (local_keys, self.chan.clone(), Some(self.local_peer_addr.clone())), - ) - .unwrap(); + let config = CommsActorConfig { + keypair: local_keys, + listen_addr: Some(self.listening_addr.clone()), + chan: None, + client_ref: Some(self_ref), + }; + ctx.actor_of_args::, _>("communication", config) + .unwrap(); } fn supervisor_strategy(&self) -> Strategy { @@ -449,16 +459,14 @@ mod test { // remote actor system let remote_sys = ActorSystem::new().unwrap(); - let chan: ChannelRef> = channel("p2p", &remote_sys).unwrap(); remote_sys - .actor_of_args::("remote-actor", (chan, remote_addr.clone())) + .actor_of_args::("remote-actor", remote_addr.clone()) .unwrap(); // local actor system let local_sys = ActorSystem::new().unwrap(); - let chan: ChannelRef> = channel("p2p", &local_sys).unwrap(); local_sys - .actor_of_args::("local-actor", (chan, remote_addr)) + .actor_of_args::("local-actor", remote_addr) .unwrap(); std::thread::sleep(Duration::new(1, 0)); diff --git a/communication/src/lib.rs b/communication/src/lib.rs index 22cfa283d..c9153e585 100644 --- a/communication/src/lib.rs +++ b/communication/src/lib.rs @@ -4,58 +4,52 @@ //! ## Introduction //! //! This library enables strongholds on different devices and in different networks to communicate with each other. -//! The main basis for its functionality is the rust-libp2p library, which is a system of protocols, specifications, and +//! The main basis for its functionality is the [rust-libp2p](https://github.com/libp2p/rust-libp2p) library, which is a system of protocols, specifications and //! libraries that enable the development of peer-to-peer network applications (https://libp2p.io/). //! //! Libp2p was originally the network protocol of IPFS and has evolved into a modular system with implementations in //! Node.js, Go and Rust. It is important to note that at the current status, the Rust implementation doesn't have all -//! features yet and especially peer discovery in different networks, NAT Traversal and Firewalls pose a problem, that -//! we solved for stronghold by using a mailbox concept that is described later. +//! features yet and especially peer discovery in different networks, NAT Traversal and Firewalls pose a problem that we +//! solved for stronghold by using a mailbox concept that is described later. //! //! ## Transport and the Swarm //! -//! Libp2p uses the term `transport` for their lowest layer that is responsible for sending and receiving data over a -//! network. The current rust implementation supports tcp and websockets, and apart from that provides the option to -//! upgrade a connection with protocols for multiplexing and authentication. -//! This stronghold-communication library uses yamux for multiplexing and the noise-protocol for authentication. +//! Libp2p uses the `transport` as the lowest layer, that is responsible for sending and receiving data over a network. +//! The current rust implementation supports tcp and websockets, and apart from that provides the option to upgrade a +//! connection with protocols for multiplexing and authentication. //! //! The second important concept of libp2p is its `Swarm` (in newer implementations and documents also called `Switch`). //! The swarm is responsible for negotiating protocols, managing transports and sending and receiving messages via //! different protocols. It is possible to combine different protocols into a so called `NetworkBehaviour`, which is -//! what this library is doing. Stronghold-communication uses multicast DNS (mDNS) for peer discovery in a local -//! network, libp2p-kademlia as a distributed hash table for managing known peers in kbuckets and publishing / reading -//! records, and the RequestResponse protocol in order to send / receive custom messages and parse them. +//! what this library is doing. Stronghold-communication uses multicast DNS (mDNS) for peer discovery in a local network +//! and the RequestResponse protocol in order to send / receive custom messages and parse them. //! -//! ## Stronghold-Communication -//! -//! Similar to the swarm in libp2p, the stronghold-communication creates the `P2PNetworkBehaviour` struct that manages -//! sending messages, querying kademlia and reacting upon the outcome of these operation. In order to enable a custom -//! behaviour on events, a `InboundEventCodec` has to be implemented for the `P2PNetworkBehaviour` when creating a new -//! instance. This `InboundEventCodec` has to implement the methods `handle_request_msg`, `handle_response_msg` and -//! `handle_kademlia_event` and can use methods of the `SwarmContext` that is already implemented for -//! `P2PNetworkBehaviour` and which provides a range of outbound operations. +//! ## Multiplexing and Noise-encryption //! -//! The main entry point for all communication with other peers is the `P2PNetwork`. -//! It creates the transport and the swarm for the prior created `P2PNetworkBehaviour` and listens for incoming -//! connections. It has multiple listening addresses due to libp2ps concept of `multiaddresses` that encode different -//! addressing schemes for different protocols. Apart from IPv4 and IPv6 Addresses, these multiaddresses can also be dns -//! addresses, which is relevant if a peer is listening to such an address on a server. The listed multiaddresses are -//! only the ones within the same local network, but if port forwarding was configured, the local /ip4/my-local-address/ -//! tcp/12345 Address can be replaced by the public one or by `/dns/my.public.server.address/tcp/12345`, where the -//! `/tcp/12345` part describes the port. +//! The transport of stronghold-communication is upgraded with yamux for multiplexing and the noise protocol, this noise +//! protocol uses the XX-Handshake and ensures authentification and encryption. //! +//! ## Stronghold-Communication //! -//! It provides methods for dialing and connection other peers, using the swarm behaviour via its `swarm` property (that -//! enables using methods from the `P2PNetworkBehaviour`) and managing mailboxes if necessary. +//! Similar to the swarm in libp2p, the stronghold-communication creates the `P2PNetworkBehaviour` struct that manages +//! sending messages and reacting upon the outcome of the operation. Upon creating a new instance, a transport is +//! created and upgraded, and combined with a the P2PNetworkBehaviour into a ExpandedSwarm. This Swarm is returned to +//! the caller and serves as entrypoint for all communication to other peers. It implements methods for listening to the +//! swarm, sending outbound messages, and manually adding and dialing peers. Incoming `P2PEvent` can be handled by +//! polling from the swarm, e.g. via the `poll_next_unpin` method. Due to libp2ps concept of `multiaddresses`, the swarm +//! has multiple listening addresses that encode different addressing schemes for different protocols. Apart from IPv4 +//! and IPv6 Addresses, these multiaddresses can also be dns addresses, which is relevant if a peer is listening to such +//! an address on a server. The listed multiaddresses are only the ones within the same local network, but if port +//! forwarding was configured, the local /ip4/my-local-address/tcp/12345 Address can be replaced by the public one or by +//! `/dns/my.public.server.address/tcp/12345`, where the `/tcp/12345` part describes the port. //! //! ## Mailbox Concept //! //! Since not all peers can be dialed directly e.g. because they are behind a firewall, stronghold-communication //! includes methods for using a mailbox. The mailbox is a peer running on a server with public IP Address that can be //! reached by all other peers. If can be used to deposit records for unavailable remote peers by sending a -//! `Request::Publish` message with the record to the mailbox, and e.g. implementing a behaviour for the mailbox where -//! it publishes the record in kademlia upon receiving such a message. The remote peer can then connect to the same -//! mailbox and query kademlia for the record. An example for this implementation is provided in /examples/mailbox.rs. +//! `Request::PutRecord` message with the record to the mailbox, and that can then return the Records to remote peers +//! upon receiving a `Request::GetRecord` request. pub mod actor; pub mod behaviour;