Skip to main content

rings_core/swarm/
mod.rs

1#![warn(missing_docs)]
2
3//! This mod is the main entrance of swarm.
4
5mod builder;
6/// Callback interface for swarm
7pub mod callback;
8pub(crate) mod transport;
9
10use std::sync::Arc;
11use std::sync::RwLock;
12
13pub use builder::SwarmBuilder;
14
15use self::callback::InnerSwarmCallback;
16use crate::dht::Did;
17use crate::dht::PeerRing;
18use crate::dht::Stabilizer;
19use crate::ecc::PublicKey;
20use crate::error::Error;
21use crate::error::Result;
22use crate::inspect::ConnectionInspect;
23use crate::inspect::SwarmInspect;
24use crate::message::Message;
25use crate::message::MessagePayload;
26use crate::message::MessageVerificationExt;
27use crate::message::PayloadSender;
28use crate::swarm::callback::SharedSwarmCallback;
29use crate::swarm::transport::SwarmTransport;
30
31/// The transport and dht management.
32pub struct Swarm {
33    /// Reference of DHT.
34    pub(crate) dht: Arc<PeerRing>,
35    /// Swarm transport.
36    pub(crate) transport: Arc<SwarmTransport>,
37    callback: RwLock<SharedSwarmCallback>,
38}
39
40impl Swarm {
41    /// Get did of self.
42    pub fn did(&self) -> Did {
43        self.dht.did
44    }
45
46    /// Get the local account public key used for E2E public-key negotiation.
47    pub fn account_pubkey(&self) -> Result<PublicKey<33>> {
48        self.transport.session_sk().session().account_pubkey()
49    }
50
51    /// Get DHT(Distributed Hash Table) of self.
52    pub fn dht(&self) -> Arc<PeerRing> {
53        self.dht.clone()
54    }
55
56    fn callback(&self) -> Result<SharedSwarmCallback> {
57        Ok(self
58            .callback
59            .read()
60            .map_err(|_| Error::CallbackSyncLockError)?
61            .clone())
62    }
63
64    fn inner_callback(&self) -> Result<InnerSwarmCallback> {
65        Ok(InnerSwarmCallback::new(
66            self.transport.clone(),
67            self.callback()?,
68        ))
69    }
70
71    /// Set callback for swarm.
72    pub fn set_callback(&self, callback: SharedSwarmCallback) -> Result<()> {
73        let mut inner = self
74            .callback
75            .write()
76            .map_err(|_| Error::CallbackSyncLockError)?;
77
78        *inner = callback;
79
80        Ok(())
81    }
82
83    /// Create [Stabilizer] for swarm.
84    pub fn stabilizer(&self) -> Stabilizer {
85        Stabilizer::new(self.transport.clone())
86    }
87
88    /// Disconnect a connection. There are three steps:
89    /// 1) remove from DHT;
90    /// 2) remove from Transport;
91    /// 3) close the connection;
92    pub async fn disconnect(&self, peer: Did) -> Result<()> {
93        self.transport.disconnect(peer).await
94    }
95
96    /// Connect a given Did. If the did is already connected, return directly,
97    /// else try prepare offer and establish connection by dht.
98    /// This function may returns a pending connection or connected connection.
99    pub async fn connect(&self, peer: Did) -> Result<()> {
100        if peer == self.did() {
101            return Err(Error::ShouldNotConnectSelf);
102        }
103        self.transport.connect(peer, self.inner_callback()?).await
104    }
105
106    /// Send [Message] to peer.
107    pub async fn send_message(&self, msg: Message, destination: Did) -> Result<uuid::Uuid> {
108        self.transport.send_message(msg, destination).await
109    }
110
111    /// List peers and their connection status.
112    pub fn peers(&self) -> Vec<ConnectionInspect> {
113        self.transport
114            .get_connections()
115            .iter()
116            .map(|(did, c)| ConnectionInspect {
117                did: did.to_string(),
118                state: format!("{:?}", c.webrtc_connection_state()),
119            })
120            .collect()
121    }
122
123    /// Check the status of swarm
124    pub async fn inspect(&self) -> SwarmInspect {
125        SwarmInspect::inspect(self).await
126    }
127}
128
129impl Swarm {
130    /// Create new connection and its answer. This function will wrap the offer inside a payload
131    /// with verification.
132    pub async fn create_offer(&self, peer: Did) -> Result<MessagePayload> {
133        let offer_msg = self
134            .transport
135            .prepare_connection_offer(peer, self.inner_callback()?)
136            .await?;
137
138        // This payload has fake next_hop.
139        // The invoker should fix it before sending.
140        let payload = MessagePayload::new_send(
141            Message::ConnectNodeSend(offer_msg),
142            self.transport.session_sk(),
143            self.did(),
144            peer,
145        )?;
146
147        Ok(payload)
148    }
149
150    /// Answer the offer of remote connection. This function will verify the answer payload and
151    /// will wrap the answer inside a payload with verification.
152    pub async fn answer_offer(&self, offer_payload: MessagePayload) -> Result<MessagePayload> {
153        if !offer_payload.verify() {
154            return Err(Error::VerifySignatureFailed);
155        }
156
157        let Message::ConnectNodeSend(msg) = offer_payload.transaction.data()? else {
158            return Err(Error::InvalidMessage(
159                "Should be ConnectNodeSend".to_string(),
160            ));
161        };
162
163        let peer = offer_payload.transaction.signer();
164        let answer_msg = self
165            .transport
166            .answer_remote_connection(peer, self.inner_callback()?, &msg)
167            .await?;
168
169        // This payload has fake next_hop.
170        // The invoker should fix it before sending.
171        let answer_payload = MessagePayload::new_send(
172            Message::ConnectNodeReport(answer_msg),
173            self.transport.session_sk(),
174            self.did(),
175            self.did(),
176        )?;
177
178        Ok(answer_payload)
179    }
180
181    /// Accept the answer of remote connection. This function will verify the answer payload and
182    /// will return its did with the connection.
183    pub async fn accept_answer(&self, answer_payload: MessagePayload) -> Result<()> {
184        if !answer_payload.verify() {
185            return Err(Error::VerifySignatureFailed);
186        }
187
188        let Message::ConnectNodeReport(ref msg) = answer_payload.transaction.data()? else {
189            return Err(Error::InvalidMessage(
190                "Should be ConnectNodeReport".to_string(),
191            ));
192        };
193
194        let peer = answer_payload.transaction.signer();
195        self.transport.accept_remote_connection(peer, msg).await
196    }
197}