Skip to main content

rings_core/message/handlers/
mod.rs

1#![warn(missing_docs)]
2//! This module implemented message handler of rings network.
3
4use std::sync::Arc;
5
6use async_recursion::async_recursion;
7use async_trait::async_trait;
8
9use super::effects::lower_dht_action;
10use super::effects::ConnectionFunctor;
11use super::effects::CoreEffect;
12use super::effects::CoreEffectInterpreter;
13use super::MessagePayload;
14use crate::dht::ChordStorageRepair;
15use crate::dht::CorrectChord;
16use crate::dht::Did;
17use crate::dht::PeerRing;
18use crate::dht::PeerRingAction;
19use crate::error::Error;
20use crate::error::Result;
21use crate::swarm::callback::InnerSwarmCallback;
22use crate::swarm::callback::SharedSwarmCallback;
23use crate::swarm::transport::SwarmTransport;
24
25/// Operator and Handler for Connection
26pub mod connection;
27/// Operator and Handler for CustomMessage
28pub mod custom;
29/// Operator and Handler for E2E encrypted messages
30pub mod e2e;
31/// Operator and handler for DHT stabilization
32pub mod stabilization;
33/// Operator and Handler for Storage
34pub mod storage;
35/// Operator and Handler for Subring
36pub mod subring;
37
38/// Shared message-handler handle.
39///
40/// Clone law: cloning duplicates `Arc` handles to the same transport, DHT
41/// state, and callback. It never forks protocol state or transfers ownership.
42#[derive(Clone)]
43pub struct MessageHandler {
44    transport: Arc<SwarmTransport>,
45    dht: Arc<PeerRing>,
46    swarm_callback: SharedSwarmCallback,
47}
48
49/// Generic trait for handle message ,inspired by Actor-Model.
50#[cfg_attr(feature = "wasm", async_trait(?Send))]
51#[cfg_attr(not(feature = "wasm"), async_trait)]
52pub trait HandleMsg<T> {
53    /// Message handler.
54    async fn handle(&self, ctx: &MessagePayload, msg: &T) -> Result<()>;
55}
56
57impl MessageHandler {
58    /// Create a new MessageHandler instance.
59    pub fn new(transport: Arc<SwarmTransport>, swarm_callback: SharedSwarmCallback) -> Self {
60        let dht = transport.dht.clone();
61        Self {
62            transport,
63            dht,
64            swarm_callback,
65        }
66    }
67
68    fn inner_callback(&self) -> InnerSwarmCallback {
69        InnerSwarmCallback::new(self.transport.clone(), self.swarm_callback.clone())
70    }
71
72    pub(crate) async fn run_effects<'payload>(
73        &self,
74        effects: impl IntoIterator<Item = CoreEffect<'payload>>,
75    ) -> Result<()> {
76        CoreEffectInterpreter::new(&self.transport, &self.swarm_callback)
77            .run_all(effects)
78            .await
79    }
80
81    /// Idempotently establish a DHT-driven transport connection.
82    ///
83    /// Self and already-connected peers are no-ops. `AlreadyConnected` is treated
84    /// as success so concurrent DHT actions racing through `MultiActions` do not
85    /// fail the whole handler.
86    pub(crate) async fn connect_dht_peer(&self, peer: Did) -> Result<()> {
87        self.run_effects([ConnectionFunctor::connect_dht_peer(peer).into()])
88            .await
89    }
90
91    /// Idempotently establish DHT-driven transport connections in local quality order.
92    pub(crate) async fn connect_dht_peers(
93        &self,
94        peers: impl IntoIterator<Item = Did>,
95    ) -> Result<()> {
96        for peer in self.transport.order_dht_candidates_by_quality(peers).await {
97            self.connect_dht_peer(peer).await?;
98        }
99        Ok(())
100    }
101
102    pub(crate) async fn join_dht(&self, peer: Did) -> Result<()> {
103        // Default HMCC/Zave join path: maps to the JoinThenSync operation in
104        // the CorrectChord spec (see tests/default/dht_convergence.rs).
105        let conn = self
106            .transport
107            .get_connection(peer)
108            .ok_or(Error::SwarmMissDidInTable(peer))?;
109        let dht_ev = self.dht.join_then_sync(conn).await?;
110        // The local join has completed. Follow-up convergence messages are
111        // best-effort: a peer can churn before these sends complete, and that
112        // must not suppress the application-level Connected event.
113        if let Err(e) = self.handle_dht_events(&dht_ev).await {
114            tracing::warn!("Failed to handle dht events while joining {peer}: {e:?}");
115        }
116        Ok(())
117    }
118
119    pub(crate) async fn leave_dht(&self, peer: Did) -> Result<()> {
120        if self
121            .transport
122            .get_and_check_connection(peer)
123            .await
124            .is_none()
125        {
126            let should_repair = self
127                .dht
128                .peer_may_share_storage_responsibility(peer, self.transport.storage_redundancy())
129                .await?;
130            self.dht.remove(peer)?;
131            if should_repair {
132                let repair = self
133                    .dht
134                    .republish_local_entries(self.transport.storage_redundancy())
135                    .await?;
136                storage::handle_storage_repair_act(self.transport.clone(), repair).await?;
137            }
138        };
139        Ok(())
140    }
141
142    fn collect_dht_effects(
143        &self,
144        act: &PeerRingAction,
145        effects: &mut Vec<CoreEffect<'static>>,
146    ) -> Result<()> {
147        match act {
148            PeerRingAction::MultiActions(acts) => {
149                for act in acts {
150                    self.collect_dht_effects(act, effects)?;
151                }
152                Ok(())
153            }
154            act => {
155                if let Some(effect) =
156                    lower_dht_action(act, |did| self.transport.get_connection(did).is_some())?
157                {
158                    effects.push(effect);
159                }
160                Ok(())
161            }
162        }
163    }
164
165    async fn run_prioritized_dht_effects(&self, effects: Vec<CoreEffect<'static>>) -> Result<()> {
166        let mut connection_peers = Vec::new();
167        let mut other_effects = Vec::new();
168        for effect in effects {
169            match effect {
170                CoreEffect::Connection(ConnectionFunctor::ConnectDhtPeer { peer }) => {
171                    connection_peers.push(peer);
172                }
173                effect => other_effects.push(effect),
174            }
175        }
176
177        for peer in self
178            .transport
179            .order_dht_candidates_by_quality(connection_peers)
180            .await
181        {
182            if let Err(e) = self.connect_dht_peer(peer).await {
183                tracing::error!("Failed on handle multi connection action: {e:?}");
184            }
185        }
186
187        for effect in other_effects {
188            if let Err(e) = self.run_effects([effect]).await {
189                tracing::error!("Failed on handle multi action: {e:?}");
190            }
191        }
192
193        Ok(())
194    }
195
196    #[cfg_attr(feature = "wasm", async_recursion(?Send))]
197    #[cfg_attr(not(feature = "wasm"), async_recursion)]
198    pub(crate) async fn handle_dht_events(&self, act: &PeerRingAction) -> Result<()> {
199        if matches!(act, PeerRingAction::MultiActions(_)) {
200            let mut effects = Vec::new();
201            self.collect_dht_effects(act, &mut effects)?;
202            self.run_prioritized_dht_effects(effects).await
203        } else {
204            let effects =
205                lower_dht_action(act, |did| self.transport.get_connection(did).is_some())?;
206            self.run_effects(effects).await
207        }
208    }
209}