1#![deny(missing_docs)]
2
3use std::sync::Arc;
4
5use async_recursion::async_recursion;
6use async_trait::async_trait;
7
8use crate::dht::entry::Entry;
9use crate::dht::entry::EntryKind;
10use crate::dht::entry::EntryOperation;
11use crate::dht::entry::PlacedEntryOperation;
12use crate::dht::entry::SyncedEntryAck;
13use crate::dht::ChordStorage;
14use crate::dht::ChordStorageCache;
15use crate::dht::ChordStorageRepair;
16use crate::dht::ChordStorageSync;
17use crate::dht::Did;
18use crate::dht::PeerRing;
19use crate::dht::PeerRingAction;
20use crate::dht::PeerRingRemoteAction;
21use crate::dht::StorageSyncDestination;
22use crate::dht::StorageSyncPurpose;
23use crate::error::Error;
24use crate::error::Result;
25use crate::message::effects::core_actor_steps;
26use crate::message::effects::yield_core_actor_step;
27use crate::message::effects::CoreEffect;
28use crate::message::types::FoundEntry;
29use crate::message::types::Message;
30use crate::message::types::SearchEntry;
31use crate::message::types::SyncEntriesWithSuccessor;
32use crate::message::types::SyncEntriesWithSuccessorReport;
33use crate::message::Encoded;
34use crate::message::HandleMsg;
35use crate::message::MessageHandler;
36use crate::message::MessagePayload;
37use crate::message::MessageVerificationExt;
38use crate::message::PayloadSender;
39use crate::swarm::transport::SwarmTransport;
40use crate::swarm::Swarm;
41
42#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
44#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
45pub trait ChordStorageInterface<const REDUNDANT: u16> {
46 async fn storage_fetch(&self, entry_key: Did) -> Result<()>;
48 async fn storage_store(&self, entry: Entry) -> Result<()>;
50 async fn storage_append_data(&self, topic: &str, data: Encoded) -> Result<()>;
52 async fn storage_touch_data(&self, topic: &str, data: Encoded) -> Result<()>;
54 async fn storage_tombstone_data(&self, topic: &str, data: Encoded) -> Result<()>;
56 async fn storage_compact_data(&self, topic: &str, removals: Vec<Encoded>) -> Result<()>;
58}
59
60#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
62#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
63pub trait ChordStorageInterfaceCacheChecker {
64 async fn storage_check_cache(&self, entry_key: Did) -> Option<Entry>;
68}
69
70fn finish_storage_action(act: PeerRingAction) -> Result<()> {
71 match act {
72 PeerRingAction::None => Ok(()),
73 act => Err(Error::unexpected_peer_ring_action(act)),
74 }
75}
76
77async fn reset_storage_relay_destination(
78 handler: &MessageHandler,
79 ctx: &MessagePayload,
80 next: Did,
81) -> Result<()> {
82 handler
83 .run_effects([CoreEffect::reset_destination(ctx, next)])
84 .await
85}
86
87async fn repair_observed_storage_misses(
88 transport: Arc<SwarmTransport>,
89 entry: Entry,
90 redundancy: u16,
91) -> Result<()> {
92 let misses = transport.take_storage_misses(entry.did, redundancy)?;
93 let repair = transport
94 .dht
95 .read_repair_entry(entry, &misses, redundancy)
96 .await?;
97 run_storage_repair_transport_effects(transport, repair).await
98}
99
100#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_recursion(?Send))]
102#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_recursion)]
103async fn handle_storage_fetch_act<const REDUNDANT: u16>(
104 transport: Arc<SwarmTransport>,
105 resource: Did,
106 act: PeerRingAction,
107) -> Result<()> {
108 match act {
109 PeerRingAction::SomeEntry(evidence) => {
110 transport
111 .dht
112 .local_cache_put(evidence.entry.clone())
113 .await?;
114 let misses = evidence.misses;
115 let repair = transport
116 .dht
117 .read_repair_entry(evidence.entry, &misses, REDUNDANT)
118 .await?;
119 run_storage_repair_transport_effects(transport.clone(), repair).await?;
120 }
121 PeerRingAction::RemoteAction(next, dht_act) => {
122 if let PeerRingRemoteAction::FindEntry(query) = dht_act {
123 tracing::debug!(
124 "storage_fetch send_message: SearchEntry({:?}) to {:?}",
125 query,
126 next
127 );
128 transport
129 .send_message(
130 Message::SearchEntry(SearchEntry {
131 resource: query.resource,
132 placement: query.placement,
133 redundancy: REDUNDANT,
134 }),
135 next,
136 )
137 .await?;
138 }
139 }
140 PeerRingAction::MultiActions(acts) => {
141 for (act, has_next) in core_actor_steps(acts) {
142 handle_storage_fetch_act::<REDUNDANT>(transport.clone(), resource, act).await?;
143 if has_next {
144 yield_core_actor_step().await;
145 }
146 }
147 }
148 PeerRingAction::EntryMisses(misses) => {
149 transport.observe_storage_misses(resource, REDUNDANT, misses)?;
150 }
151 act => finish_storage_action(act)?,
152 }
153 Ok(())
154}
155
156#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_recursion(?Send))]
158#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_recursion)]
159pub(super) async fn handle_storage_store_act(
160 transport: Arc<SwarmTransport>,
161 act: PeerRingAction,
162) -> Result<()> {
163 match act {
164 PeerRingAction::RemoteAction(target, PeerRingRemoteAction::FindEntryForOperate(op)) => {
165 transport
166 .send_message(Message::OperateEntry(op), target)
167 .await?;
168 }
169 PeerRingAction::MultiActions(acts) => {
170 for (act, has_next) in core_actor_steps(acts) {
171 handle_storage_store_act(transport.clone(), act).await?;
172 if has_next {
173 yield_core_actor_step().await;
174 }
175 }
176 }
177 act => finish_storage_action(act)?,
178 }
179 Ok(())
180}
181
182async fn operate_entry_at_placement(
183 dht: &PeerRing,
184 placement: Did,
185 op: EntryOperation,
186) -> Result<()> {
187 let op = op.stamped(dht.did)?;
188 let this = match dht.storage.get(&placement.to_string()).await? {
189 Some(this) => this,
190 None => op.clone().gen_default_entry()?,
191 };
192 let entry = this.operate(op, dht.did)?;
193 dht.join_storage_entry(placement, entry).await?;
194 Ok(())
195}
196
197async fn handle_placed_entry_operation(
198 handler: &MessageHandler,
199 ctx: &MessagePayload,
200 msg: &PlacedEntryOperation,
201) -> Result<()> {
202 msg.validate_placement(handler.transport.storage_redundancy())?;
203
204 match handler.dht.find_storage_owner(msg.placement)? {
205 PeerRingAction::Some(_) => {
206 operate_entry_at_placement(&handler.dht, msg.placement, msg.op.clone()).await
207 }
208 PeerRingAction::RemoteAction(next, PeerRingRemoteAction::FindSuccessor(_)) => {
209 reset_storage_relay_destination(handler, ctx, next).await
210 }
211 action => Err(Error::unexpected_peer_ring_action(action)),
212 }
213}
214
215async fn run_storage_repair_transport_effects(
217 transport: Arc<SwarmTransport>,
218 act: PeerRingAction,
219) -> Result<()> {
220 for (delivery, has_next) in core_actor_steps(act.coalesced_storage_sync_deliveries()?) {
221 let msg = SyncEntriesWithSuccessor::from_delivery(delivery);
222 transport
223 .send_storage_sync_or_defer(msg, "storage_repair")
224 .await?;
225 if has_next {
226 yield_core_actor_step().await;
227 }
228 }
229 Ok(())
230}
231
232#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_recursion(?Send))]
234#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_recursion)]
235async fn handle_storage_search_act(
236 handler: &MessageHandler,
237 ctx: &MessagePayload,
238 act: PeerRingAction,
239 resource: Did,
240 redundancy: u16,
241) -> Result<()> {
242 match act {
243 PeerRingAction::SomeEntry(evidence) => {
244 handler
245 .run_effects([CoreEffect::send_report_message(
246 ctx,
247 Message::FoundEntry(FoundEntry {
248 data: vec![evidence.entry],
249 misses: evidence.misses,
250 resource,
251 redundancy,
252 }),
253 )])
254 .await
255 }
256 PeerRingAction::EntryMisses(misses) => {
257 handler
258 .run_effects([CoreEffect::send_report_message(
259 ctx,
260 Message::FoundEntry(FoundEntry {
261 data: vec![],
262 misses,
263 resource,
264 redundancy,
265 }),
266 )])
267 .await
268 }
269 PeerRingAction::RemoteAction(next, _) => {
270 reset_storage_relay_destination(handler, ctx, next).await
271 }
272 PeerRingAction::MultiActions(acts) => {
273 for (act, has_next) in core_actor_steps(acts) {
274 handle_storage_search_act(handler, ctx, act, resource, redundancy).await?;
275 if has_next {
276 yield_core_actor_step().await;
277 }
278 }
279
280 Ok(())
281 }
282 act => finish_storage_action(act),
283 }
284}
285
286async fn operate_storage_entry<const REDUNDANT: u16>(
287 swarm: &Swarm,
288 operation: EntryOperation,
289) -> Result<()> {
290 swarm.transport.ensure_storage_redundancy::<REDUNDANT>()?;
291 let action =
292 <PeerRing as ChordStorage<_, REDUNDANT>>::entry_operate(&swarm.dht, operation).await?;
293 handle_storage_store_act(swarm.transport.clone(), action).await
294}
295
296fn next_hop_for_sync_entries(
297 handler: &MessageHandler,
298 ctx: &MessagePayload,
299 msg: &SyncEntriesWithSuccessor,
300) -> Result<Option<Did>> {
301 if msg.destination.did() != ctx.relay.destination {
302 return Err(Error::InvalidMessage(format!(
303 "sync destination {:?} does not match relay destination {}",
304 msg.destination, ctx.relay.destination
305 )));
306 }
307
308 if ctx.is_relay_destination_for(handler.dht.did) {
309 return Ok(None);
310 }
311
312 handler.dht.next_hop_for_storage_sync(msg.destination)
313}
314
315async fn report_synced_entries(
316 handler: &MessageHandler,
317 ctx: &MessagePayload,
318 purpose: StorageSyncPurpose,
319 destination: StorageSyncDestination,
320 acks: Vec<SyncedEntryAck>,
321) -> Result<()> {
322 handler
323 .run_effects([CoreEffect::send_report_message(
324 ctx,
325 Message::SyncEntriesWithSuccessorReport(SyncEntriesWithSuccessorReport::new(
326 purpose,
327 destination,
328 handler.dht.did,
329 acks,
330 )),
331 )])
332 .await
333}
334
335#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
336#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
337impl ChordStorageInterfaceCacheChecker for Swarm {
338 async fn storage_check_cache(&self, entry_key: Did) -> Option<Entry> {
340 self.dht.local_cache_get(entry_key).await.ok().flatten()
341 }
342}
343
344#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
345#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
346impl<const REDUNDANT: u16> ChordStorageInterface<REDUNDANT> for Swarm {
347 async fn storage_fetch(&self, entry_key: Did) -> Result<()> {
350 self.transport.ensure_storage_redundancy::<REDUNDANT>()?;
351 self.transport.start_storage_lookup(entry_key, REDUNDANT)?;
352 let act = self
354 .dht
355 .entry_lookup_for_fetch::<REDUNDANT>(entry_key)
356 .await?;
357 handle_storage_fetch_act::<REDUNDANT>(self.transport.clone(), entry_key, act).await?;
358 Ok(())
359 }
360
361 async fn storage_store(&self, entry: Entry) -> Result<()> {
363 operate_storage_entry::<REDUNDANT>(self, EntryOperation::Overwrite(entry)).await
364 }
365
366 async fn storage_append_data(&self, topic: &str, data: Encoded) -> Result<()> {
367 let entry: Entry = (topic.to_string(), data).try_into()?;
368 operate_storage_entry::<REDUNDANT>(self, EntryOperation::Extend(entry)).await
369 }
370
371 async fn storage_touch_data(&self, topic: &str, data: Encoded) -> Result<()> {
372 let entry: Entry = (topic.to_string(), data).try_into()?;
373 operate_storage_entry::<REDUNDANT>(self, EntryOperation::Touch(entry)).await
374 }
375
376 async fn storage_tombstone_data(&self, topic: &str, data: Encoded) -> Result<()> {
377 let entry: Entry = (topic.to_string(), data).try_into()?;
378 operate_storage_entry::<REDUNDANT>(self, EntryOperation::Tombstone(entry)).await
379 }
380
381 async fn storage_compact_data(&self, topic: &str, removals: Vec<Encoded>) -> Result<()> {
382 let entry = Entry::new(Entry::gen_did(topic)?, removals, EntryKind::Data);
383 operate_storage_entry::<REDUNDANT>(self, EntryOperation::CompactData(entry)).await
384 }
385}
386
387#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
388#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
389impl HandleMsg<SearchEntry> for MessageHandler {
390 async fn handle(&self, ctx: &MessagePayload, msg: &SearchEntry) -> Result<()> {
393 match <PeerRing as ChordStorage<_, 1>>::entry_lookup(&self.dht, msg.placement).await {
395 Ok(action) => {
396 handle_storage_search_act(self, ctx, action, msg.resource, msg.redundancy).await
397 }
398 Err(e) => Err(e),
399 }
400 }
401}
402
403#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
404#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
405impl HandleMsg<FoundEntry> for MessageHandler {
406 async fn handle(&self, ctx: &MessagePayload, msg: &FoundEntry) -> Result<()> {
407 if ctx.should_forward_from(self.dht.did) {
408 return self
409 .run_effects([CoreEffect::forward_payload(ctx, None)])
410 .await;
411 }
412 let found_entry = msg.single_entry()?;
416 self.transport
417 .ensure_storage_lookup_active(msg.resource, msg.redundancy)?;
418 self.transport.observe_storage_misses(
419 msg.resource,
420 msg.redundancy,
421 msg.misses.iter().copied(),
422 )?;
423 if let Some(data) = found_entry {
424 self.dht.local_cache_put(data.clone()).await?;
425 repair_observed_storage_misses(self.transport.clone(), data.clone(), msg.redundancy)
426 .await?;
427 } else if !msg.misses.is_empty() {
428 if let Some(entry) = self.dht.local_cache_get(msg.resource).await? {
429 repair_observed_storage_misses(self.transport.clone(), entry, msg.redundancy)
430 .await?;
431 }
432 }
433 Ok(())
434 }
435}
436
437#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
438#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
439impl HandleMsg<PlacedEntryOperation> for MessageHandler {
440 async fn handle(&self, ctx: &MessagePayload, msg: &PlacedEntryOperation) -> Result<()> {
441 handle_placed_entry_operation(self, ctx, msg).await
442 }
443}
444
445#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
446#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
447impl HandleMsg<SyncEntriesWithSuccessor> for MessageHandler {
448 async fn handle(&self, ctx: &MessagePayload, msg: &SyncEntriesWithSuccessor) -> Result<()> {
450 if let Some(next) = next_hop_for_sync_entries(self, ctx, msg)? {
451 return self
452 .run_effects([CoreEffect::forward_payload(ctx, Some(next))])
453 .await;
454 }
455
456 let acks = self.transport.persist_storage_sync_entries(msg).await?;
457 if msg.purpose.permits_source_cleanup() {
458 if let Err(e) =
459 report_synced_entries(self, ctx, msg.purpose, msg.destination, acks).await
460 {
461 tracing::warn!("Failed to report synced entries: {e:?}");
462 }
463 }
464 Ok(())
465 }
466}
467
468#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
469#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
470impl HandleMsg<SyncEntriesWithSuccessorReport> for MessageHandler {
471 async fn handle(
472 &self,
473 ctx: &MessagePayload,
474 msg: &SyncEntriesWithSuccessorReport,
475 ) -> Result<()> {
476 if ctx.should_forward_from(self.dht.did) {
477 return self
478 .run_effects([CoreEffect::forward_payload(ctx, None)])
479 .await;
480 }
481
482 let signer = ctx.transaction.signer();
483 let origin = ctx.relay.try_origin_sender()?;
484 if signer != msg.receiver || origin != msg.receiver {
485 return Err(Error::InvalidMessage(
486 "storage sync report receiver does not match signed report origin".to_string(),
487 ));
488 }
489 let acks =
490 self.transport
491 .take_pending_storage_sync_ack(ctx.transaction.tx_id, signer, msg)?;
492 let action = self.dht.acknowledge_synced_entries(&acks).await?;
493 finish_storage_action(action)
494 }
495}
496
497#[cfg(not(all(feature = "wasm", target_family = "wasm")))]
498#[cfg(test)]
499mod tests;