Skip to main content

rings_core/dht/
chord.rs

1//! Chord algorithm implement.
2#![warn(missing_docs)]
3use std::sync::Arc;
4use std::sync::Mutex;
5use std::sync::MutexGuard;
6
7use async_trait::async_trait;
8use serde::Deserialize;
9use serde::Serialize;
10
11use super::did::BiasId;
12use super::entry::Entry;
13use super::entry::EntryLookupEvidence;
14use super::entry::EntryLookupKey;
15use super::entry::EntryOperation;
16use super::entry::PlacedEntry;
17use super::entry::PlacementMiss;
18use super::finger::DEFAULT_FINGER_TABLE_SIZE;
19use super::successor::SuccessorSeq;
20use super::topology;
21use super::topology::FindSuccessorStep;
22use super::topology::TopologyAction;
23use super::topology::TopologyEvent;
24use super::topology::TopologyState;
25use super::types::Chord;
26use super::types::ChordStorage;
27use super::types::ChordStorageCache;
28use super::types::CorrectChord;
29use super::FingerTable;
30use crate::dht::Did;
31use crate::dht::LiveDid;
32use crate::dht::SuccessorReader;
33use crate::dht::SuccessorWriter;
34use crate::error::Error;
35use crate::error::Result;
36use crate::storage::KvStorageInterface;
37use crate::storage::MemStorage;
38
39/// `EntryStorage` is the type accepted by `PeerRing::new_with_storage`.
40/// It's used to store [Entry]s in a storage media provided by user.
41#[cfg(feature = "wasm")]
42pub type EntryStorage = Box<dyn KvStorageInterface<Entry>>;
43
44/// `EntryStorage` is the type accepted by `PeerRing::new_with_storage`.
45/// It's used to store [Entry]s in a storage media provided by user.
46#[cfg(not(feature = "wasm"))]
47pub type EntryStorage = Box<dyn KvStorageInterface<Entry> + Send + Sync>;
48
49/// PeerRing is used to help a node interact with other nodes.
50/// All nodes in rings network form a clockwise ring in the order of Did.
51/// This struct takes its name from that.
52/// PeerRing implemented [Chord] algorithm.
53/// PeerRing implemented [ChordStorage] protocol.
54pub struct PeerRing {
55    /// The did of current node.
56    pub did: Did,
57    /// [FingerTable] help node to find successor quickly.
58    pub finger: Arc<Mutex<FingerTable>>,
59    /// The next node on the ring.
60    /// The [SuccessorSeq] may contain multiple node dids for fault tolerance.
61    /// The min did should be same as the first element in finger table.
62    pub successor_seq: SuccessorSeq,
63    /// The did of previous node on the ring.
64    pub predecessor: Arc<Mutex<Option<Did>>>,
65    /// Local storage for [ChordStorage].
66    pub storage: EntryStorage,
67    /// Local cache for [ChordStorage].
68    pub cache: EntryStorage,
69}
70
71/// Type alias is just for making the code easy to read.
72type Target = Did;
73
74/// `PeerRing` use this to describe the result of [Chord] algorithm. Sometimes it's a
75/// direct result, sometimes it's an action that is continued externally.
76#[derive(Clone, Debug, PartialEq)]
77pub enum PeerRingAction {
78    /// No result, the whole manipulation is done internally.
79    None,
80    /// Found an entry together with lookup evidence.
81    SomeEntry(EntryLookupEvidence),
82    /// Observed placement misses without a hit.
83    EntryMisses(Vec<PlacementMiss>),
84    /// Found some node.
85    Some(Did),
86    /// Trigger a remote action.
87    RemoteAction(Target, RemoteAction),
88    /// Trigger multiple remote actions.
89    MultiActions(Vec<PeerRingAction>),
90}
91
92/// Some of the process needs to be done remotely. This enum is used to describe that.
93/// Don't worry about leaving the context. There will be callback machinisim externally
94/// that will invoke appropriate methods in `PeerRing` to continue the process.
95///
96/// To avoid ambiguity, in the following comments, `did_a` is the Did declared in
97/// [PeerRingAction]. Other dids are the fields declared in this [RemoteAction].
98#[derive(Clone, Debug, PartialEq, Eq)]
99pub enum RemoteAction {
100    /// Need `did_a` to find `did_b`.
101    FindSuccessor(Did),
102    /// Need `did_a` to find one entry placement.
103    FindEntry(EntryLookupKey),
104    /// Need `did_a` to find Entry for operating.
105    FindEntryForOperate(EntryOperation),
106    /// Send a predecessor notification to `did_a`.
107    ///
108    /// `did_a` is the remote recipient from [`PeerRingAction::RemoteAction`].
109    /// This field is the predecessor DID announced in `NotifyPredecessorSend`.
110    Notify(Did),
111    /// Copy placed entries to the storage destination represented by `did_a`.
112    ///
113    /// In successor hand-off, `did_a` is the new successor node. In repair and
114    /// republish, `did_a` is the placement key and the message layer routes it
115    /// to that key's current successor.
116    SyncEntriesWithSuccessor(Vec<PlacedEntry>),
117
118    /// Need `did_a` to find `did_b` then send back with `for connect` flag.
119    FindSuccessorForConnect(Did),
120
121    /// Need `did_a` to find `did_b` then send back with `for finger table fixing` flag.
122    FindSuccessorForFix {
123        /// DID whose successor should populate the finger slot.
124        did: Did,
125        /// Finger slot that should be updated by the report.
126        index: usize,
127    },
128
129    /// Fetch successor_list from successor
130    QueryForSuccessorList,
131    /// Fetch successor_list and pred from successor
132    QueryForSuccessorListAndPred,
133    /// Try connect to a Node
134    TryConnect,
135}
136
137/// Information about successor and predecessor
138#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone)]
139pub struct TopoInfo {
140    /// Successor list
141    pub successors: Vec<Did>,
142    /// Predecessor
143    pub predecessor: Option<Did>,
144}
145
146impl TryFrom<&PeerRing> for TopoInfo {
147    type Error = Error;
148    fn try_from(dht: &PeerRing) -> Result<TopoInfo> {
149        let successors = dht.successors().list()?;
150        let predecessor = *dht.lock_predecessor()?;
151        Ok(TopoInfo {
152            successors,
153            predecessor,
154        })
155    }
156}
157
158impl PeerRingAction {
159    /// Returns `true` if the action is a [PeerRingAction::None] value.
160    pub fn is_none(&self) -> bool {
161        if let Self::None = self {
162            return true;
163        }
164        false
165    }
166
167    /// Returns `true` if the action is a [PeerRingAction::Some] value.
168    pub fn is_some(&self) -> bool {
169        if let Self::Some(_) = self {
170            return true;
171        }
172        false
173    }
174
175    /// Returns `true` if the action is a [PeerRingAction::SomeEntry] value.
176    pub fn is_some_entry(&self) -> bool {
177        if let Self::SomeEntry(_) = self {
178            return true;
179        }
180        false
181    }
182
183    /// Returns `true` if the action is a [PeerRingAction::RemoteAction] value.
184    pub fn is_remote(&self) -> bool {
185        if let Self::RemoteAction(..) = self {
186            return true;
187        }
188        false
189    }
190
191    /// Returns `true` if the action is a [PeerRingAction::MultiActions] value.
192    pub fn is_multi(&self) -> bool {
193        if let Self::MultiActions(..) = self {
194            return true;
195        }
196        false
197    }
198}
199
200impl From<Vec<PeerRingAction>> for PeerRingAction {
201    fn from(acts: Vec<PeerRingAction>) -> Self {
202        if !acts.is_empty() {
203            Self::MultiActions(acts)
204        } else {
205            Self::None
206        }
207    }
208}
209
210impl PeerRing {
211    /// Same as new with config, but with a given storage.
212    pub fn new_with_storage(did: Did, succ_max: u8, storage: EntryStorage) -> Self {
213        Self::new_with_storage_and_finger_table_size(
214            did,
215            succ_max,
216            storage,
217            DEFAULT_FINGER_TABLE_SIZE,
218        )
219    }
220
221    /// Same as new with config, but with a given storage and finger table size.
222    ///
223    /// `Did` is 160-bit. Sizes above [`DEFAULT_FINGER_TABLE_SIZE`] are clamped
224    /// by [`FingerTable::new`]; zero is allowed to disable finger maintenance.
225    pub fn new_with_storage_and_finger_table_size(
226        did: Did,
227        succ_max: u8,
228        storage: EntryStorage,
229        finger_table_size: usize,
230    ) -> Self {
231        Self {
232            successor_seq: SuccessorSeq::new(did, succ_max),
233            predecessor: Arc::new(Mutex::new(None)),
234            finger: Arc::new(Mutex::new(FingerTable::new(did, finger_table_size))),
235            storage,
236            cache: Box::new(MemStorage::new()),
237            did,
238        }
239    }
240
241    /// Return successor sequence. This function is deprecated, please use [chord.successors] instead.
242    #[deprecated]
243    pub fn lock_successor(&self) -> Result<SuccessorSeq> {
244        Ok(self.successor_seq.clone())
245    }
246
247    /// Return successor sequence
248    pub fn successors(&self) -> SuccessorSeq {
249        self.successor_seq.clone()
250    }
251
252    /// Lock and return MutexGuard of finger table.
253    pub fn lock_finger(&self) -> Result<MutexGuard<FingerTable>> {
254        self.finger.lock().map_err(|_| Error::DHTSyncLockError)
255    }
256
257    /// Lock and return MutexGuard of predecessor.
258    pub fn lock_predecessor(&self) -> Result<MutexGuard<Option<Did>>> {
259        self.predecessor.lock().map_err(|_| Error::DHTSyncLockError)
260    }
261
262    /// Remove a node from finger table.
263    /// Also remove it from successor sequence.
264    /// If successor_seq become empty, try setting the closest node to it.
265    pub fn remove(&self, did: Did) -> Result<()> {
266        let next = topology::step(
267            &self.topology_state()?,
268            TopologyEvent::Remove { peer: did },
269            self.successors().capacity(),
270        );
271        self.interpret_topology_state(&next.state)
272    }
273
274    /// Calculate bias of the Did on the ring.
275    pub fn bias(&self, did: Did) -> BiasId {
276        BiasId::new(self.did, did)
277    }
278
279    fn topology_state(&self) -> Result<TopologyState> {
280        let finger = self.lock_finger()?;
281        Ok(TopologyState::new(
282            self.did,
283            self.successors().list()?,
284            *self.lock_predecessor()?,
285            finger.list().clone(),
286            finger.fix_finger_index(),
287        ))
288    }
289
290    fn interpret_topology_state(&self, next: &TopologyState) -> Result<()> {
291        let successors = self.successors();
292        for did in successors.list()? {
293            successors.remove(did)?;
294        }
295        successors.extend(&next.successors)?;
296        *self.lock_predecessor()? = next.predecessor;
297        self.lock_finger()?
298            .replace_state(&next.fingers, next.fix_finger_index);
299        Ok(())
300    }
301
302    fn topology_action(&self, action: TopologyAction) -> PeerRingAction {
303        match action {
304            TopologyAction::FindSuccessorForConnect { next, did } => {
305                PeerRingAction::RemoteAction(next, RemoteAction::FindSuccessorForConnect(did))
306            }
307            TopologyAction::FindSuccessorForFix { next, did, index } => {
308                PeerRingAction::RemoteAction(next, RemoteAction::FindSuccessorForFix { did, index })
309            }
310            TopologyAction::QuerySuccessorList(did) => {
311                PeerRingAction::RemoteAction(did, RemoteAction::QueryForSuccessorList)
312            }
313            TopologyAction::Notify(did) => {
314                PeerRingAction::RemoteAction(did, RemoteAction::Notify(self.did))
315            }
316        }
317    }
318
319    fn topology_leaf_actions(&self, actions: Vec<TopologyAction>) -> PeerRingAction {
320        let mut actions = actions
321            .into_iter()
322            .map(|action| self.topology_action(action))
323            .collect::<Vec<_>>();
324        match actions.len() {
325            0 => PeerRingAction::None,
326            1 => actions.pop().unwrap_or(PeerRingAction::None),
327            _ => PeerRingAction::MultiActions(actions),
328        }
329    }
330
331    fn topology_multi_actions(&self, actions: Vec<TopologyAction>) -> PeerRingAction {
332        PeerRingAction::MultiActions(
333            actions
334                .into_iter()
335                .map(|action| self.topology_action(action))
336                .collect(),
337        )
338    }
339
340    /// Apply a reported finger successor through the pure topology transition.
341    pub(crate) fn apply_fixed_finger(&self, index: usize, successor: Did) -> Result<()> {
342        let next = topology::step(
343            &self.topology_state()?,
344            TopologyEvent::ApplyFinger { index, successor },
345            self.successors().capacity(),
346        );
347        self.interpret_topology_state(&next.state)
348    }
349
350    /// Join an incoming replicated entry delta into local storage.
351    ///
352    /// Post: the stored value is the least upper bound of the previous local
353    /// value and `incoming` when a previous value exists; otherwise it is
354    /// `incoming` normalized for storage.
355    pub(crate) async fn join_storage_entry(&self, key: Did, incoming: Entry) -> Result<Entry> {
356        let incoming = incoming.try_into_storage_entry()?;
357        let stored = if let Some(local) = self.storage.get(&key.to_string()).await? {
358            local.join(incoming)?
359        } else {
360            incoming
361        }
362        .try_into_storage_entry()?;
363        self.storage.put(&key.to_string(), &stored).await?;
364        Ok(stored)
365    }
366}
367
368impl Chord<PeerRingAction> for PeerRing {
369    /// Join a ring containing a node identified by `did`.
370    /// This method is usually invoked to maintain successor sequence and finger table
371    /// after connect to another node.
372    ///
373    /// This method will return a [RemoteAction::FindSuccessorForConnect] to the caller.
374    /// The caller will send it to the node identified by `did`, and let the node find
375    /// the successor of current node and make current node connect to that successor.
376    fn join(&self, did: Did) -> Result<PeerRingAction> {
377        let next = topology::step(
378            &self.topology_state()?,
379            TopologyEvent::Join { peer: did },
380            self.successors().capacity(),
381        );
382        self.interpret_topology_state(&next.state)?;
383        Ok(self.topology_leaf_actions(next.actions))
384    }
385
386    /// Find the successor of a Did.
387    /// May return a remote action for the successor is recorded in another node.
388    fn find_successor(&self, did: Did) -> Result<PeerRingAction> {
389        let state = self.topology_state()?;
390        let succ = match topology::find_successor(&state, did) {
391            FindSuccessorStep::Local(successor) => {
392                // If the DID is closer to self than the successor head, return
393                // that head as the successor. With an empty successor list, the
394                // pure topology model returns `self.did`, matching
395                // `SuccessorSeq::min`.
396                Ok(PeerRingAction::Some(successor))
397            }
398            FindSuccessorStep::Remote { next, did } => Ok(PeerRingAction::RemoteAction(
399                next,
400                RemoteAction::FindSuccessor(did),
401            )),
402        };
403
404        tracing::debug!(
405            "find_successor: self: {}, did: {}, successor: {:?}, result: {:?}",
406            self.did,
407            did,
408            state.successors,
409            succ
410        );
411
412        succ
413    }
414
415    /// Handle notification from a node that thinks a did is the predecessor of current node.
416    /// The `did` in parameters is the Did of that predecessor.
417    /// If that node is closer to current node or current node has no predecessor, set it to the did.
418    /// This method will return current predecessor after setting.
419    fn notify(&self, did: Did) -> Result<Did> {
420        let next = topology::step(
421            &self.topology_state()?,
422            TopologyEvent::Notify { predecessor: did },
423            self.successors().capacity(),
424        );
425        let Some(predecessor) = next.state.predecessor else {
426            return Err(Error::PeerRingInvalidAction);
427        };
428        self.interpret_topology_state(&next.state)?;
429        Ok(predecessor)
430    }
431
432    /// Fix finger table by finding the successor for each finger.
433    /// According to the paper, this method should be called periodically.
434    /// According to the paper, only one finger should be fixed at a time.
435    fn fix_fingers(&self) -> Result<PeerRingAction> {
436        let next = topology::step(
437            &self.topology_state()?,
438            TopologyEvent::FixFinger,
439            self.successors().capacity(),
440        );
441        self.interpret_topology_state(&next.state)?;
442        Ok(self.topology_leaf_actions(next.actions))
443    }
444}
445
446#[cfg_attr(feature = "wasm", async_trait(?Send))]
447#[cfg_attr(not(feature = "wasm"), async_trait)]
448impl<const REDUNDANT: u16> ChordStorage<PeerRingAction, REDUNDANT> for PeerRing {
449    /// Look up an [`Entry`] by its ring key.
450    /// Always finds resource by finger table, ignoring the local cache.
451    /// If the `entry_key` is between current node and its successor, its resource should be
452    /// stored in current node.
453    async fn entry_lookup(&self, entry_key: Did) -> Result<PeerRingAction> {
454        let mut ret = vec![];
455        let mut misses = vec![];
456        // Pre: REDUNDANT > 0, enforced by rotate_affine.
457        // Post: if result is SomeEntry(e), e.misses contains exactly the
458        // local placement misses observed before the first hit in
459        // place(entry_key, REDUNDANT). Later placements are Unknown.
460        // Post: EntryMisses carries only observed misses; no remote
461        // SearchEntry is emitted solely to classify Unknown as Miss.
462        for placement_key in entry_key.rotate_affine(REDUNDANT)? {
463            let query = EntryLookupKey::new(entry_key, placement_key);
464            let act = match self.find_successor(placement_key) {
465                // Resource should be stored in current node.
466                Ok(PeerRingAction::Some(succ)) => {
467                    match self.storage.get(&placement_key.to_string()).await {
468                        Ok(Some(v)) => {
469                            let observed_misses = std::mem::take(&mut misses);
470                            Ok(PeerRingAction::SomeEntry(EntryLookupEvidence::new(
471                                v,
472                                observed_misses,
473                            )))
474                        }
475                        Ok(None) => {
476                            tracing::debug!(
477                                "Cannot find entry in local storage, try to query from successor"
478                            );
479                            // If cannot find and has successor, try to query it from successor.
480                            // This is useful when the node is just joined and has not stabilized yet.
481                            if succ == self.did {
482                                misses.push(PlacementMiss::new(placement_key, succ));
483                                Ok(PeerRingAction::None)
484                            } else {
485                                Ok(PeerRingAction::RemoteAction(
486                                    succ,
487                                    RemoteAction::FindEntry(query),
488                                ))
489                            }
490                        }
491                        Err(e) => Err(e),
492                    }
493                }
494                // Resource is stored in other nodes.
495                // Return an action to describe how to find it.
496                Ok(PeerRingAction::RemoteAction(n, RemoteAction::FindSuccessor(id))) => {
497                    Ok(PeerRingAction::RemoteAction(
498                        n,
499                        RemoteAction::FindEntry(EntryLookupKey::new(entry_key, id)),
500                    ))
501                }
502                Ok(a) => Err(Error::unexpected_peer_ring_action(a)),
503                Err(e) => Err(e),
504            }?;
505            if act.is_remote() {
506                ret.push(act);
507            } else {
508                // If found entry, break and return directly
509                if act.is_some_entry() {
510                    return Ok(act);
511                }
512            }
513        }
514        if !misses.is_empty() {
515            ret.push(PeerRingAction::EntryMisses(misses));
516        }
517        Ok(ret.into())
518    }
519
520    /// Handle [EntryOperation] if the target entry between current node and the
521    /// successor of current node, otherwise find the responsible node and return
522    /// as Action.
523    async fn entry_operate(&self, op: EntryOperation) -> Result<PeerRingAction> {
524        let op = op.stamped(self.did)?;
525        let entry_key = op.did()?;
526        let mut ret = vec![];
527        // Pre: op.did() is the entry identity id(e), and REDUNDANT > 0 is
528        // checked by rotate_affine.
529        // Post: for every k in place(id(e), REDUNDANT), either sigma_self[k]
530        // is updated with Entry::operate(op) when self is the observed owner,
531        // or exactly one FindEntryForOperate action is emitted toward the
532        // current routing owner for k.
533        // Preservation: no placement outside place(id(e), REDUNDANT) is
534        // written by this transition.
535        for entry_key in entry_key.rotate_affine(REDUNDANT)? {
536            let act = match self.find_successor(entry_key) {
537                // `entry` should be on current node.
538                Ok(PeerRingAction::Some(_)) => {
539                    let this = match self.storage.get(&entry_key.to_string()).await? {
540                        Some(this) => this,
541                        None => op.clone().gen_default_entry()?,
542                    };
543                    let entry = this.operate(op.clone(), self.did)?;
544                    self.join_storage_entry(entry_key, entry).await?;
545                    Ok(PeerRingAction::None)
546                }
547                // `entry` should be on other nodes.
548                // Return an action to describe how to store it.
549                Ok(PeerRingAction::RemoteAction(n, RemoteAction::FindSuccessor(_))) => Ok(
550                    PeerRingAction::RemoteAction(n, RemoteAction::FindEntryForOperate(op.clone())),
551                ),
552                Ok(a) => Err(Error::unexpected_peer_ring_action(a)),
553                Err(e) => Err(e),
554            }?;
555            if act.is_remote() {
556                ret.push(act);
557            }
558        }
559        Ok(ret.into())
560    }
561}
562
563mod storage_repair;
564mod storage_sync;
565
566#[cfg_attr(feature = "wasm", async_trait(?Send))]
567#[cfg_attr(not(feature = "wasm"), async_trait)]
568impl ChordStorageCache<PeerRingAction> for PeerRing {
569    /// Cache fetched `entry` locally.
570    async fn local_cache_put(&self, entry: Entry) -> Result<()> {
571        self.cache.put(&entry.did.to_string(), &entry).await
572    }
573
574    /// Get entry from local cache.
575    async fn local_cache_get(&self, entry_key: Did) -> Result<Option<Entry>> {
576        self.cache.get(&entry_key.to_string()).await
577    }
578}
579
580#[cfg_attr(feature = "wasm", async_trait(?Send))]
581#[cfg_attr(not(feature = "wasm"), async_trait)]
582impl CorrectChord<PeerRingAction> for PeerRing {
583    /// When Chord have a new successor, ask the new successor for successor list
584    async fn update_successor(&self, did: impl LiveDid) -> Result<PeerRingAction> {
585        let is_live = did.live().await;
586        if !is_live {
587            return Ok(PeerRingAction::RemoteAction(
588                did.into(),
589                RemoteAction::TryConnect,
590            ));
591        }
592        let next = topology::step(
593            &self.topology_state()?,
594            TopologyEvent::UpdateSuccessor {
595                successor: did.into(),
596            },
597            self.successors().capacity(),
598        );
599        self.interpret_topology_state(&next.state)?;
600        Ok(self.topology_leaf_actions(next.actions))
601    }
602
603    async fn extend_successor(&self, dids: &[impl LiveDid]) -> Result<PeerRingAction> {
604        let mut ret: Vec<PeerRingAction> = vec![];
605        for did in dids {
606            if let PeerRingAction::RemoteAction(r, act) = self.update_successor(did.clone()).await?
607            {
608                ret.push(PeerRingAction::RemoteAction(r, act))
609            }
610        }
611        Ok(PeerRingAction::MultiActions(ret))
612    }
613
614    /// Join Operation in the paper.
615    /// Zave's work differs from the original Chord paper in that it requires
616    /// a newly joined node to synchronize its successors from remote nodes.
617    async fn join_then_sync(&self, did: impl LiveDid) -> Result<PeerRingAction> {
618        let is_live = did.live().await;
619        if !is_live {
620            return Ok(PeerRingAction::None);
621        }
622        let mut ret: Vec<PeerRingAction> = vec![];
623        let succ_act = self.update_successor(did.clone()).await?;
624        if succ_act.is_remote() {
625            ret.push(succ_act)
626        }
627        let join_act = self.join(did.into())?;
628        ret.push(join_act);
629
630        Ok(PeerRingAction::MultiActions(ret))
631    }
632
633    /// HMCC/Zave Rectify operation.
634    ///
635    /// Rectify is the local predecessor transition run when this node receives
636    /// a predecessor notification from `pred`. It has no remote action: the
637    /// message layer's report path is handled by `NotifyPredecessorSend`.
638    fn rectify(&self, pred: Did) -> Result<()> {
639        // Pre: in protocol traces, pred is the notifier's DID and pred != self.did.
640        // Post: predecessor' = pred iff predecessor = None or
641        // bias(predecessor) < bias(pred); otherwise predecessor is unchanged.
642        // Preservation: successor list, finger table, and storage state are
643        // unchanged. Delegating to Chord::notify is exactly this predecessor
644        // choice rule; Rectify discards the returned predecessor because it
645        // emits no follow-up action.
646        let next = topology::step(
647            &self.topology_state()?,
648            TopologyEvent::Notify { predecessor: pred },
649            self.successors().capacity(),
650        );
651        self.interpret_topology_state(&next.state)
652    }
653
654    /// Pre-Stabilize Operation:
655    /// Before stabilizing, the node should query its first successor for TopoInfo.
656    /// If there are no successors, return PeerRingAction::None.
657    fn pre_stabilize(&self) -> Result<PeerRingAction> {
658        let successor = self.successors();
659        if successor.is_empty()? {
660            return Ok(PeerRingAction::None);
661        }
662        let head = successor.min()?;
663        Ok(PeerRingAction::RemoteAction(
664            head,
665            RemoteAction::QueryForSuccessorListAndPred,
666        ))
667    }
668
669    /// Stabilize Operation:
670    ///
671    /// Mirrors the TLA+-style `CorrectStabilize` operator in
672    /// `tests/default/dht_convergence.rs`.
673    /// The old head is captured before updating successors for the improved-successor
674    /// query check; the remote successor list contributes `but_last`; and notify
675    /// is emitted for the post-update head when that head is not self.
676    fn stabilize(&self, info: TopoInfo) -> Result<PeerRingAction> {
677        let next = topology::step(
678            &self.topology_state()?,
679            TopologyEvent::Stabilize {
680                successors: info.successors,
681                predecessor: info.predecessor,
682            },
683            self.successors().capacity(),
684        );
685        self.interpret_topology_state(&next.state)?;
686        Ok(self.topology_multi_actions(next.actions))
687    }
688
689    /// A function to provide topological information about the chord.
690    fn topo_info(&self) -> Result<TopoInfo> {
691        self.try_into()
692    }
693}
694
695#[cfg(all(not(feature = "wasm"), test))]
696mod storage_tests;
697
698#[cfg(all(not(feature = "wasm"), test))]
699mod tests {
700    //! test module
701    use std::str::FromStr;
702
703    use num_bigint::BigUint;
704
705    use super::*;
706    use crate::ecc::SecretKey;
707    use crate::tests::default::gen_sorted_dht;
708
709    #[tokio::test]
710    async fn test_chord_finger() -> Result<()> {
711        // Setup did a, b, c, d in a clockwise order.
712        let a = Did::from_str("0x00E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
713        let b = Did::from_str("0x119999cf1046e68e36E1aA2E0E07105eDDD1f08E").unwrap();
714        let c = Did::from_str("0xccffee254729296a45a3885639AC7E10F9d54979").unwrap();
715        let d = Did::from_str("0xffffee254729296a45a3885639AC7E10F9d54979").unwrap();
716
717        // This assertion tells you the order of a, b, c, d on the ring.
718        // Note that this vec only describes the order, not the absolute position.
719        // Since they are all on the ring, you cannot say a is the first element or d is
720        // the last. You can only describe their bias based on the same node and a
721        // clockwise order.
722        //
723        // a --> b --> c --> d
724        // ^                 |
725        // |-----------------|
726        //
727        let mut seq = vec![a, b, c, d];
728        seq.sort();
729        assert_eq!(seq, vec![a, b, c, d]);
730
731        // Setup node_a and ensure its successor sequence and finger table is empty.
732        let node_a = PeerRing::new_with_storage(a, 3, Box::new(MemStorage::new()));
733        assert!(node_a.successors().is_empty()?);
734        assert!(node_a.lock_finger()?.is_empty());
735
736        // Test a node won't set itself to successor sequence and finger table.
737        assert_eq!(node_a.join(a)?, PeerRingAction::None);
738        assert!(node_a.successors().is_empty()?);
739        assert!(node_a.lock_finger()?.is_empty());
740
741        // Test join ring with node_b.
742        // We don't need to setup node_b here, we just use its did.
743        let result = node_a.join(b)?;
744
745        // After join, node_a should ask node_b to find its successor on the ring for
746        // connecting.
747        assert_eq!(
748            result,
749            PeerRingAction::RemoteAction(b, RemoteAction::FindSuccessorForConnect(a))
750        );
751
752        // This assertion tells you the position of node_b on the ring.
753        // Hint: The Did type is a 160-bit unsigned integer.
754        assert!(BigUint::from(b) > BigUint::from(2u16).pow(156));
755        assert!(BigUint::from(b) < BigUint::from(2u16).pow(157));
756
757        // After join, the finger table of node_a should be like:
758        // [b] * 157 + [None] * 3
759        let mut expected_finger_list = std::iter::repeat_n(Some(b), 157).collect::<Vec<_>>();
760        expected_finger_list.extend(std::iter::repeat_n(None, 3));
761        assert_eq!(node_a.lock_finger()?.list(), &expected_finger_list);
762
763        // After join, the successor sequence of node_a should be [b].
764        assert_eq!(node_a.successors().list()?, vec![b]);
765
766        // Test repeated join.
767        node_a.join(b)?;
768        assert_eq!(node_a.lock_finger()?.list(), &expected_finger_list);
769        assert_eq!(node_a.successors().list()?, vec![b]);
770        node_a.join(b)?;
771        assert_eq!(node_a.lock_finger()?.list(), &expected_finger_list);
772        assert_eq!(node_a.successors().list()?, vec![b]);
773
774        // Test join ring with node_c.
775        // We don't need to setup node_c here, we just use its did.
776        let result = node_a.join(c)?;
777
778        // Again, after join, node_a should ask node_c to find its successor on the ring
779        // for connecting.
780        assert_eq!(
781            result,
782            PeerRingAction::RemoteAction(c, RemoteAction::FindSuccessorForConnect(a))
783        );
784
785        // This assertion tells you the position of node_c on the ring.
786        // Hint: The Did type is a 160-bit unsigned integer.
787        assert!(BigUint::from(c) > BigUint::from(2u16).pow(159));
788        assert!(BigUint::from(c) < BigUint::from(2u16).pow(160));
789
790        // After join, the finger table of node_a should be like:
791        // [b] * 157 + [c] * 3
792        let mut expected_finger_list = std::iter::repeat_n(Some(b), 157).collect::<Vec<_>>();
793        expected_finger_list.extend(std::iter::repeat_n(Some(c), 3));
794        assert_eq!(node_a.lock_finger()?.list(), &expected_finger_list);
795
796        // After join, the successor sequence of node_a should be [b, c].
797        // Because although node_b is closer to node_a, the sequence is not full.
798        assert_eq!(node_a.successors().list()?, vec![b, c]);
799
800        // When try to find_successor of node_d, node_a will send query to node_c.
801        assert_eq!(
802            node_a.find_successor(d).unwrap(),
803            PeerRingAction::RemoteAction(c, RemoteAction::FindSuccessor(d))
804        );
805        // When try to find_successor of node_c, node_a will send query to node_b.
806        assert_eq!(
807            node_a.find_successor(c).unwrap(),
808            PeerRingAction::RemoteAction(b, RemoteAction::FindSuccessor(c))
809        );
810
811        // Since the test above is clockwise, we need to test anti-clockwise situation.
812        let node_a = PeerRing::new_with_storage(a, 3, Box::new(MemStorage::new()));
813
814        // Test join ring with node_c.
815        assert_eq!(
816            node_a.join(c)?,
817            PeerRingAction::RemoteAction(c, RemoteAction::FindSuccessorForConnect(a))
818        );
819        let expected_finger_list = std::iter::repeat_n(Some(c), 160).collect::<Vec<_>>();
820        assert_eq!(node_a.lock_finger()?.list(), &expected_finger_list);
821        assert_eq!(node_a.successors().list()?, vec![c]);
822
823        // Test join ring with node_b.
824        assert_eq!(
825            node_a.join(b)?,
826            PeerRingAction::RemoteAction(b, RemoteAction::FindSuccessorForConnect(a))
827        );
828        let mut expected_finger_list = std::iter::repeat_n(Some(b), 157).collect::<Vec<_>>();
829        expected_finger_list.extend(std::iter::repeat_n(Some(c), 3));
830        assert_eq!(node_a.lock_finger()?.list(), &expected_finger_list);
831        assert_eq!(node_a.successors().list()?, vec![b, c]);
832
833        // Test join over half ring.
834        let node_d = PeerRing::new_with_storage(d, 1, Box::new(MemStorage::new()));
835        assert_eq!(
836            node_d.join(a)?,
837            PeerRingAction::RemoteAction(a, RemoteAction::FindSuccessorForConnect(d))
838        );
839
840        // This assertion tells you that node_a is over 2^151 far away from node_d.
841        // And node_a is also less than 2^152 far away from node_d.
842        assert!(d + Did::from(BigUint::from(2u16).pow(151)) < a);
843        assert!(d + Did::from(BigUint::from(2u16).pow(152)) > a);
844
845        // After join, the finger table of node_d should be like:
846        // [a] * 152 + [None] * 8
847        let mut expected_finger_list = std::iter::repeat_n(Some(a), 152).collect::<Vec<_>>();
848        expected_finger_list.extend(std::iter::repeat_n(None, 8));
849        assert_eq!(node_d.lock_finger()?.list(), &expected_finger_list);
850
851        // After join, the successor sequence of node_a should be [a].
852        assert_eq!(node_d.successors().list()?, vec![a]);
853
854        // Test join ring with node_b.
855        assert_eq!(
856            node_d.join(b)?,
857            PeerRingAction::RemoteAction(b, RemoteAction::FindSuccessorForConnect(d))
858        );
859
860        // This assertion tells you that node_b is over 2^156 far away from node_d.
861        // And node_b is also less than 2^157 far away from node_d.
862        assert!(d + Did::from(BigUint::from(2u16).pow(156)) < b);
863        assert!(d + Did::from(BigUint::from(2u16).pow(157)) > b);
864
865        // After join, the finger table of node_d should be like:
866        // [a] * 152 + [b] * 5 + [None] * 3
867        let mut expected_finger_list = std::iter::repeat_n(Some(a), 152).collect::<Vec<_>>();
868        expected_finger_list.extend(std::iter::repeat_n(Some(b), 5));
869        expected_finger_list.extend(std::iter::repeat_n(None, 3));
870        assert_eq!(node_d.lock_finger()?.list(), &expected_finger_list);
871
872        // Note the max successor sequence size of node_d is set to 1 when created.
873        // After join, the successor sequence of node_a should still be [a].
874        // Because node_a is closer to node_d, and the sequence is full.
875        assert_eq!(node_d.successors().list()?, vec![a]);
876
877        Ok(())
878    }
879
880    #[tokio::test]
881    async fn test_two_node_finger() -> Result<()> {
882        let mut key1 = SecretKey::random();
883        let mut key2 = SecretKey::random();
884        if key1.address() > key2.address() {
885            (key1, key2) = (key2, key1)
886        }
887        let did1: Did = key1.address().into();
888        let did2: Did = key2.address().into();
889        let node1 = PeerRing::new_with_storage(did1, 3, Box::new(MemStorage::new()));
890        let node2 = PeerRing::new_with_storage(did2, 3, Box::new(MemStorage::new()));
891
892        node1.join(did2)?;
893        node2.join(did1)?;
894        assert!(node1.successors().list()?.contains(&did2));
895        assert!(node2.successors().list()?.contains(&did1));
896
897        assert!(
898            node1.lock_finger()?.contains(Some(did2)),
899            "did1:{did1:?}; did2:{did2:?}"
900        );
901        assert!(
902            node2.lock_finger()?.contains(Some(did1)),
903            "did1:{did1:?}; did2:{did2:?}"
904        );
905
906        Ok(())
907    }
908
909    #[tokio::test]
910    async fn test_two_node_finger_failed_case() -> Result<()> {
911        let did1 = Did::from_str("0x051cf4f8d020cb910474bef3e17f153fface2b5f").unwrap();
912        let did2 = Did::from_str("0x54baa7dc9e28f41da5d71af8fa6f2a302be1c1bf").unwrap();
913        let max = Did::from(BigUint::from(2u16).pow(160) - 1u16);
914        let zero = Did::from(BigUint::from(2u16).pow(160));
915
916        let node1 = PeerRing::new_with_storage(did1, 3, Box::new(MemStorage::new()));
917        let node2 = PeerRing::new_with_storage(did2, 3, Box::new(MemStorage::new()));
918
919        node1.join(did2)?;
920        node2.join(did1)?;
921        assert!(node1.successors().list()?.contains(&did2));
922        assert!(node2.successors().list()?.contains(&did1));
923        let pos_159 = did2 + Did::from(BigUint::from(2u16).pow(159));
924        assert!(pos_159 > did2);
925        assert!(pos_159 < max, "{pos_159:?};{max:?}");
926        let pos_160 = did2 + zero;
927        assert_eq!(pos_160, did2);
928        assert!(pos_160 > did1);
929
930        assert!(
931            node1.lock_finger()?.contains(Some(did2)),
932            "did1:{did1:?}; did2:{did2:?}"
933        );
934        assert!(
935            node2.lock_finger()?.contains(Some(did1)),
936            "did2:{did2:?} dont contains did1:{did1:?}"
937        );
938
939        Ok(())
940    }
941
942    #[test]
943    fn test_correct_chord_stabilize_handles_empty_successor_info() -> Result<()> {
944        let did = Did::from_str("0x051cf4f8d020cb910474bef3e17f153fface2b5f").unwrap();
945        let node = PeerRing::new_with_storage(did, 3, Box::new(MemStorage::new()));
946
947        assert_eq!(
948            node.stabilize(TopoInfo {
949                successors: vec![],
950                predecessor: None,
951            })?,
952            PeerRingAction::MultiActions(vec![])
953        );
954
955        Ok(())
956    }
957
958    /// Test Correct Chord implementation
959    #[tokio::test]
960    async fn test_correct_chord_impl() -> Result<()> {
961        fn assert_successor(dht: &PeerRing, did: &Did) -> bool {
962            let succ_list = dht.successors();
963            succ_list.list().unwrap().contains(did)
964        }
965
966        /// check that two dht is mutual successors
967        fn check_is_mutual_successors(dht1: &PeerRing, dht2: &PeerRing) {
968            let succ_list_1 = dht1.successors();
969            let succ_list_2 = dht2.successors();
970            assert_eq!(succ_list_1.min().unwrap(), dht2.did);
971            assert_eq!(succ_list_2.min().unwrap(), dht1.did);
972        }
973
974        fn check_succ_is_including(dht: &PeerRing, dids: Vec<Did>) {
975            let succ_list = dht.successors();
976            for did in dids {
977                assert!(succ_list.list().unwrap().contains(&did));
978            }
979        }
980
981        let dhts = gen_sorted_dht(5);
982        let [n1, n2, n3, n4, n5] = dhts.as_slice() else {
983            panic!("wrong dhts length");
984        };
985        // we now have:
986        // n1 < n2 < n3 < n4
987
988        // n1 join n2
989        n1.join(n2.did).unwrap();
990        n2.join(n1.did).unwrap();
991        // for now n1, n2 are `mutual successors`.
992        check_is_mutual_successors(n1, n2);
993        // n1 join n3
994
995        n1.join(n3.did).unwrap();
996        n1.join(n4.did).unwrap();
997        // for now n1's successor should include n1 and n3
998        check_succ_is_including(n1, vec![n2.did, n3.did, n4.did]);
999
1000        n1.join(n5.did).unwrap();
1001        // n5 is not in n1's successor list
1002        assert!(!assert_successor(n1, &n5.did));
1003
1004        #[allow(non_local_definitions)]
1005        #[cfg_attr(feature = "wasm", async_trait(?Send))]
1006        #[cfg_attr(not(feature = "wasm"), async_trait)]
1007        impl LiveDid for Did {
1008            async fn live(&self) -> bool {
1009                true
1010            }
1011        }
1012
1013        if let PeerRingAction::MultiActions(rets) = n5.join_then_sync(n1.did).await.unwrap() {
1014            for r in rets {
1015                if let PeerRingAction::RemoteAction(t, _) = r {
1016                    assert_eq!(t, n1.did.clone())
1017                } else {
1018                    panic!("wrong remote");
1019                }
1020            }
1021        } else {
1022            panic!("Wrong ret");
1023        }
1024        Ok(())
1025    }
1026}