Skip to main content

rings_core/dht/
did.rs

1#![deny(missing_docs)]
2
3//! Distributed identities for the Rings DHT.
4//!
5//! A [`Did`] is a protocol identity located on the Chord identifier circle. It
6//! is also the concrete carrier for the additive cyclic group of `Z / 2^160`
7//! used by Chord routing and placement. The [`crate::algebra::AbelianGroup`]
8//! trait names the operation set; `Did` supplies the representation and
9//! implementation. `Did` does not implement [`crate::algebra::CommutativeRing`]
10//! because Chord never uses identifier multiplication.
11//!
12//! ## Chord identity model
13//!
14//! Chord assigns every node, resource, and placement target a point in a
15//! 160-bit circular identifier space. Clockwise distance is not ordinary
16//! integer distance: it is subtraction in `Z / 2^160`. For an observer `b`, the
17//! relative position of `x` is therefore `x - b`. This translation makes the
18//! observer's position the local zero point and lets a total byte order witness
19//! clockwise ordering from that observer.
20//!
21//! [`BiasId`] is the domain type for that translated view. It is used when a
22//! caller needs to compare identifiers relative to a reference point instead of
23//! comparing their raw encodings. The raw [`Did`] order remains the canonical
24//! representation order; biased order is a separate protocol proposition.
25//!
26//! ## Placement model
27//!
28//! Redundant storage placement uses affine offsets around the identifier ring.
29//! For redundancy `n`, [`Did::rotate_affine`] returns
30//! `self + floor(2^160 * i / n)` for every `i in 0..n`. This is a DHT placement
31//! operation over identities, not a new carrier type. The additive group law is
32//! witnessed by `Did`'s [`crate::algebra::AbelianGroup`] implementation.
33//!
34//! ## Boundary
35//!
36//! `Did` owns parsing, serialization, display, biasing, range checks, fixed
37//! width arithmetic, and DHT placement. Protocol handlers depend on `Did`
38//! operations and do not perform byte-level arithmetic.
39
40use std::cmp::Ordering;
41use std::num::NonZeroU32;
42use std::ops::Add;
43use std::ops::Deref;
44use std::ops::Neg;
45use std::ops::Sub;
46use std::str::FromStr;
47
48use ethereum_types::H160;
49use num_bigint::BigUint;
50use serde::Deserialize;
51use serde::Serialize;
52
53use crate::algebra::AbelianGroup;
54use crate::algebra::Zero;
55use crate::ecc::HashStr;
56use crate::error::Error;
57use crate::error::Result;
58
59/// Non-zero witness for the 360-degree denominator used by [`Rotate`].
60const FULL_ROTATION_DENOMINATOR: NonZeroU32 = NonZeroU32::MIN.saturating_add(359);
61
62/// DHT identity over the `Z / 2^160` identifier ring.
63///
64/// Invariant: the inner [`H160`] is the canonical 20-byte big-endian encoding
65/// of one residue class modulo `2^160`.
66///
67/// Law: `Did` addition, subtraction, and negation are the lifted additive group
68/// operations of the underlying 160-bit ring.
69///
70/// Law: parsing, display, serialization, and conversion through [`H160`]
71/// preserve the same 20-byte canonical encoding.
72#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Debug, Serialize, Deserialize, Hash)]
73pub struct Did(H160);
74
75impl std::fmt::Display for Did {
76    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
77        let inner = &self.0;
78        write!(f, "0x{inner:x}")
79    }
80}
81
82/// DHT identity observed from a reference point.
83///
84/// Chord interval comparisons are relative to an observer. Given raw
85/// identifiers `a` and `b`, there is no single protocol answer to "which is
86/// closer" until a reference identifier `x` is chosen. `BiasId` records that
87/// reference and stores `did - x`, so the reference point becomes zero in the
88/// lifted ring order.
89///
90/// Invariant: `did` is always stored as `raw_did - bias`.
91///
92/// Law: `BiasId::new(x, y).to_did() == y`.
93///
94/// `BiasId` intentionally has no total [`Ord`] implementation. Values with
95/// different observers live in different reference frames, so callers must
96/// either compare same-observer values or name the observer explicitly.
97#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)]
98pub struct BiasId {
99    /// the zero point for determine order of Did.
100    bias: Did,
101    /// did data without bias.
102    did: Did,
103}
104
105/// Affine rotation on the 160-bit Chord circle.
106///
107/// For [`Did`], degrees are mapped to the dyadic ring offset
108/// `floor(2^160 * angle / 360)`.
109pub trait Rotate<Rhs = u16> {
110    /// output type of rotate operation
111    type Output;
112    /// rotate a Did with given angle
113    fn rotate(&self, angle: Rhs) -> Self::Output;
114}
115
116impl Rotate<u16> for Did {
117    type Output = Self;
118    fn rotate(&self, angle: u16) -> Self::Output {
119        *self + Did::dyadic_fraction(angle.into(), FULL_ROTATION_DENOMINATOR)
120    }
121}
122
123impl BiasId {
124    /// Wrap a Did into BiasDid with given bias.
125    pub fn new(bias: Did, did: Did) -> BiasId {
126        BiasId {
127            bias,
128            did: did - bias,
129        }
130    }
131
132    /// Get wrapped biased value from did
133    pub fn to_did(self) -> Did {
134        self.did + self.bias
135    }
136
137    /// Get unwrap value from a BiasDid
138    pub fn pos(&self) -> Did {
139        self.did
140    }
141
142    /// Compare two biased identifiers only when they share the same observer.
143    pub fn cmp_same_observer(&self, other: &Self) -> Option<Ordering> {
144        (self.bias == other.bias).then(|| self.did.cmp(&other.did))
145    }
146
147    /// Compare two raw identifiers from one explicit Chord observer.
148    pub fn cmp_from_observer(observer: Did, left: Did, right: Did) -> Ordering {
149        let (left, right) = (left - observer, right - observer);
150        left.cmp(&right)
151    }
152}
153
154impl PartialOrd for BiasId {
155    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
156        self.cmp_same_observer(other)
157    }
158}
159
160impl PartialEq<Did> for BiasId {
161    fn eq(&self, rhs: &Did) -> bool {
162        let id: Did = self.into();
163        id == *rhs
164    }
165}
166
167impl From<BiasId> for Did {
168    fn from(id: BiasId) -> Did {
169        BiasId::to_did(id)
170    }
171}
172
173impl From<&BiasId> for Did {
174    fn from(id: &BiasId) -> Did {
175        BiasId::to_did(*id)
176    }
177}
178
179impl From<u32> for Did {
180    fn from(id: u32) -> Did {
181        let bytes = id.to_be_bytes();
182        let mut out = [0u8; Self::BYTE_LEN];
183        for (dst, src) in out.iter_mut().rev().zip(bytes.iter().rev()) {
184            *dst = *src;
185        }
186        Self::from_be_bytes(out)
187    }
188}
189
190impl TryFrom<HashStr> for Did {
191    type Error = Error;
192    fn try_from(s: HashStr) -> Result<Self> {
193        Did::from_str(&s.inner())
194    }
195}
196
197impl Did {
198    const BITS: usize = 160;
199
200    const BYTE_LEN: usize = 20;
201
202    const ZERO: Self = Self(H160([0u8; Self::BYTE_LEN]));
203
204    fn from_be_bytes(bytes: [u8; Self::BYTE_LEN]) -> Self {
205        Self(H160::from(bytes))
206    }
207
208    fn to_be_bytes(self) -> [u8; Self::BYTE_LEN] {
209        self.0.to_fixed_bytes()
210    }
211
212    /// Test whether this identity is inside the open clockwise interval
213    /// `(a, b)` observed from `base_id`.
214    ///
215    /// Post: returns `true` exactly when `self - base_id` is strictly after
216    /// `a - base_id` and strictly before `b - base_id` in the canonical ring
217    /// order.
218    pub fn in_range(&self, base_id: Self, a: Self, b: Self) -> bool {
219        // Test x > a && b > x
220        *self - base_id > a - base_id && b - base_id > *self - base_id
221    }
222
223    /// Transform this identity into the view whose zero point is `did`.
224    pub fn bias(&self, did: Self) -> BiasId {
225        BiasId::new(did, *self)
226    }
227
228    /// Rotate this DID into a redundant placement vector.
229    ///
230    /// Pre: `scalar > 0`.
231    ///
232    /// Law: `place(self, n)[i] = self + floor(2^160 * i / n)` for
233    /// `i in 0..n`.
234    ///
235    /// Law: `place(self, n)[0] = self`.
236    ///
237    /// Law: for `i != j`, `place(self, n)[i] != place(self, n)[j]` while
238    /// `n <= 2^160`; the current `u16` domain is therefore injective.
239    pub fn rotate_affine(&self, scalar: u16) -> Result<Vec<Did>> {
240        let Some(denominator) = NonZeroU32::new(u32::from(scalar)) else {
241            return Err(Error::InvalidAffineScalar);
242        };
243
244        Ok((0..scalar)
245            .map(|i| {
246                let offset = Did::dyadic_fraction(i.into(), denominator);
247                *self + offset
248            })
249            .collect())
250    }
251
252    /// Return `2^bit` in `Z / 2^160`.
253    ///
254    /// Law: `bit < 160 => result = 2^bit`.
255    /// Law: `bit >= 160 => result = 0`.
256    pub fn power_of_two(bit: usize) -> Self {
257        if bit >= Self::BITS {
258            return Self::ZERO;
259        }
260
261        let mut bytes = [0u8; Self::BYTE_LEN];
262        set_ring_bit(&mut bytes, bit);
263        Self::from_be_bytes(bytes)
264    }
265
266    // Type: `NonZeroU32` makes a zero denominator unrepresentable.
267    // Post: result is `floor(2^160 * numerator / denominator) mod 2^160`.
268    // Invariant: `remainder < denominator` before and after every bit step.
269    fn dyadic_fraction(numerator: u32, denominator: NonZeroU32) -> Self {
270        let denominator = u64::from(denominator.get());
271        let mut remainder = u64::from(numerator) % denominator;
272        let mut bytes = [0u8; Self::BYTE_LEN];
273
274        for bit in (0..Self::BITS).rev() {
275            remainder *= 2;
276            if remainder >= denominator {
277                set_ring_bit(&mut bytes, bit);
278                remainder -= denominator;
279            }
280        }
281
282        Self::from_be_bytes(bytes)
283    }
284
285    // Post: result = (self + rhs) mod 2^160.
286    // Preservation: carry beyond the most-significant byte is discarded, which
287    // is exactly quotienting by the 160-bit ring modulus.
288    fn add_mod(self, rhs: Self) -> Self {
289        let lhs = self.to_be_bytes();
290        let rhs = rhs.to_be_bytes();
291        let mut out = [0u8; Self::BYTE_LEN];
292        let mut carry = 0u16;
293
294        for ((dst, lhs), rhs) in out
295            .iter_mut()
296            .rev()
297            .zip(lhs.iter().rev())
298            .zip(rhs.iter().rev())
299        {
300            let sum = u16::from(*lhs) + u16::from(*rhs) + carry;
301            let [low, _] = sum.to_le_bytes();
302            *dst = low;
303            carry = sum >> 8;
304        }
305
306        Self::from_be_bytes(out)
307    }
308
309    // Post: result = -self mod 2^160.
310    // Preservation: two's-complement over exactly 20 bytes computes the
311    // additive inverse in `Z / 2^160`; zero maps to zero.
312    fn additive_inverse(self) -> Self {
313        let mut out = self.to_be_bytes();
314        for byte in &mut out {
315            *byte = !*byte;
316        }
317
318        let mut carry = 1u16;
319        for byte in out.iter_mut().rev() {
320            let sum = u16::from(*byte) + carry;
321            let [low, _] = sum.to_le_bytes();
322            *byte = low;
323            carry = sum >> 8;
324        }
325
326        Self::from_be_bytes(out)
327    }
328}
329
330/// Ordering with a did reference
331/// This trait defines necessary method for sorting based on did.
332pub trait SortRing {
333    /// Sort a impl SortRing with given did
334    fn sort(&mut self, did: Did);
335}
336
337impl SortRing for Vec<Did> {
338    fn sort(&mut self, did: Did) {
339        self.sort_by(|a, b| {
340            let (da, db) = (*a - did, *b - did);
341            da.cmp(&db)
342        });
343    }
344}
345
346impl Deref for Did {
347    type Target = H160;
348    fn deref(&self) -> &Self::Target {
349        &self.0
350    }
351}
352
353impl From<Did> for H160 {
354    fn from(a: Did) -> Self {
355        a.0
356    }
357}
358
359impl From<Did> for BigUint {
360    fn from(did: Did) -> BigUint {
361        BigUint::from_bytes_be(did.as_bytes())
362    }
363}
364
365impl From<BigUint> for Did {
366    fn from(a: BigUint) -> Self {
367        let bytes = a.to_bytes_be();
368        let mut out = [0u8; Self::BYTE_LEN];
369
370        // Post: taking the least-significant 20 bytes is reduction modulo
371        // `2^160`; right-aligning keeps the conversion total and panic-free.
372        for (dst, src) in out
373            .iter_mut()
374            .rev()
375            .zip(bytes.iter().rev().take(Self::BYTE_LEN))
376        {
377            *dst = *src;
378        }
379
380        Self::from_be_bytes(out)
381    }
382}
383
384impl From<H160> for Did {
385    fn from(addr: H160) -> Self {
386        Self(addr)
387    }
388}
389
390impl FromStr for Did {
391    type Err = Error;
392    fn from_str(s: &str) -> Result<Self> {
393        Ok(Self(H160::from_str(s).map_err(|_| Error::BadCHexInCache)?))
394    }
395}
396
397impl Default for Did {
398    fn default() -> Self {
399        Self::ZERO
400    }
401}
402
403impl Zero for Did {
404    fn zero() -> Self {
405        Self::ZERO
406    }
407
408    fn is_zero(&self) -> bool {
409        *self == Self::ZERO
410    }
411}
412
413impl AbelianGroup for Did {}
414
415impl Neg for Did {
416    type Output = Self;
417    fn neg(self) -> Self {
418        self.additive_inverse()
419    }
420}
421
422impl Neg for &Did {
423    type Output = Did;
424
425    fn neg(self) -> Self::Output {
426        (*self).neg()
427    }
428}
429
430impl Add for Did {
431    type Output = Self;
432    fn add(self, rhs: Self) -> Self {
433        self.add_mod(rhs)
434    }
435}
436
437impl Sub for Did {
438    type Output = Self;
439    fn sub(self, rhs: Self) -> Self {
440        self + (-rhs)
441    }
442}
443
444// Pre: `bytes` encodes a 160-bit big-endian ring element.
445// Post: if `bit < 160`, the corresponding bit is set; otherwise `bytes` is
446// unchanged.
447fn set_ring_bit(bytes: &mut [u8; Did::BYTE_LEN], bit: usize) {
448    let Some(byte) = (Did::BYTE_LEN - 1).checked_sub(bit / 8) else {
449        return;
450    };
451
452    if let Some(slot) = bytes.get_mut(byte) {
453        *slot |= 1u8 << (bit % 8);
454    }
455}
456
457#[cfg(test)]
458mod tests {
459    use std::cmp::Ordering;
460    use std::collections::BTreeSet;
461    use std::str::FromStr;
462
463    use super::*;
464    use crate::algebra::assert_abelian_group_laws;
465
466    fn ring_size() -> BigUint {
467        BigUint::from(1u8) << 160usize
468    }
469
470    fn samples() -> Vec<Did> {
471        vec![
472            Did::zero(),
473            Did::from(1u32),
474            Did::from(10u32),
475            Did::from(ring_size() - BigUint::from(1u8)),
476            Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap(),
477        ]
478    }
479
480    #[test]
481    fn test_did_abelian_group_laws_hold_on_representative_set() {
482        assert_abelian_group_laws(&samples());
483    }
484
485    #[test]
486    fn test_did_addition_matches_biguint_ring_oracle() {
487        for lhs in samples() {
488            for rhs in samples() {
489                let expected = Did::from((BigUint::from(lhs) + BigUint::from(rhs)) % ring_size());
490                assert_eq!(lhs + rhs, expected);
491            }
492        }
493    }
494
495    #[test]
496    fn test_did_dyadic_fraction_matches_biguint_oracle() {
497        for denominator in [1u32, 2, 3, 7, 17, 360, 361, u16::MAX.into()] {
498            let Some(nonzero_denominator) = NonZeroU32::new(denominator) else {
499                continue;
500            };
501
502            for numerator in [
503                0,
504                1,
505                denominator / 2,
506                denominator.saturating_sub(1),
507                denominator,
508                denominator.saturating_add(1),
509                denominator.saturating_mul(2).saturating_add(1),
510            ] {
511                let expected =
512                    Did::from(ring_size() * BigUint::from(numerator) / BigUint::from(denominator));
513                assert_eq!(
514                    Did::dyadic_fraction(numerator, nonzero_denominator),
515                    expected
516                );
517            }
518        }
519    }
520
521    #[test]
522    fn test_did() {
523        let a = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
524        let b = Did::from_str("0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E").unwrap();
525        let c = Did::from_str("0xc0ffee254729296a45a3885639AC7E10F9d54979").unwrap();
526        assert!(c > b && b > a);
527    }
528
529    #[test]
530    fn test_finate_ring_neg() {
531        let zero = Did::from_str("0x0000000000000000000000000000000000000000").unwrap();
532        let a = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
533        assert_eq!(-a + a, zero);
534        assert_eq!(-(-a), a);
535    }
536
537    #[test]
538    fn test_sort() {
539        let a = Did::from_str("0xaaE807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
540        let b = Did::from_str("0xbb9999cf1046e68e36E1aA2E0E07105eDDD1f08E").unwrap();
541        let c = Did::from_str("0xccffee254729296a45a3885639AC7E10F9d54979").unwrap();
542        let d = Did::from_str("0xdddfee254729296a45a3885639AC7E10F9d54979").unwrap();
543        let mut v = vec![c, b, a, d];
544        v.sort(a);
545        assert_eq!(v, vec![a, b, c, d]);
546        v.sort(b);
547        assert_eq!(v, vec![b, c, d, a]);
548        v.sort(c);
549        assert_eq!(v, vec![c, d, a, b]);
550        v.sort(d);
551        assert_eq!(v, vec![d, a, b, c]);
552    }
553
554    fn former_mixed_observer_cmp(left: BiasId, right: BiasId) -> Ordering {
555        let right_raw = right.to_did();
556        let right_in_left_observer = BiasId::new(left.bias, right_raw);
557        left.pos().cmp(&right_in_left_observer.pos())
558    }
559
560    #[test]
561    fn test_bias_id_orders_same_observer() {
562        let observer = Did::from(10u32);
563        let near = BiasId::new(observer, Did::from(11u32));
564        let far = BiasId::new(observer, Did::from(12u32));
565
566        assert_eq!(near.cmp_same_observer(&far), Some(Ordering::Less));
567        assert_eq!(far.cmp_same_observer(&near), Some(Ordering::Greater));
568        assert_eq!(near.partial_cmp(&far), Some(Ordering::Less));
569        assert_eq!(
570            BiasId::cmp_from_observer(observer, Did::from(11u32), Did::from(12u32)),
571            Ordering::Less
572        );
573    }
574
575    #[test]
576    fn test_bias_id_rejects_mixed_observer_ordering() {
577        let a = BiasId::new(Did::from(0u32), Did::from(1u32));
578        let b = BiasId::new(
579            Did::power_of_two(159),
580            Did::from(ring_size() - BigUint::from(1u8)),
581        );
582
583        assert_eq!(former_mixed_observer_cmp(a, b), Ordering::Less);
584        assert_eq!(former_mixed_observer_cmp(b, a), Ordering::Less);
585        assert_eq!(a.partial_cmp(&b), None);
586        assert_eq!(b.partial_cmp(&a), None);
587    }
588
589    #[test]
590    fn test_rotate_transformation() {
591        assert_eq!(Did::from(0u32), Did::from(BigUint::from(2u16).pow(160)));
592        let did = Did::from(10u32);
593        let result = did.rotate(360);
594        assert_eq!(result, did);
595    }
596
597    #[test]
598    fn test_right_shift() {
599        let did = Did::from(10u32);
600        let ret: Did = did.rotate(180);
601        assert_eq!(ret, did + Did::from(BigUint::from(2u16).pow(159)));
602    }
603
604    #[test]
605    fn test_did_fixed_width_arithmetic_matches_biguint_ring_oracle() -> Result<()> {
606        let zero = Did::from(0u32);
607        let one = Did::from(1u32);
608        let max = Did::from(ring_size() - BigUint::from(1u8));
609        let sample = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0")?;
610
611        assert_eq!(max + one, zero);
612        assert_eq!(zero - one, max);
613        assert_eq!(-zero, zero);
614        assert_eq!(-sample + sample, zero);
615
616        for (lhs, rhs) in [(zero, one), (one, max), (sample, max), (sample, sample)] {
617            let expected = Did::from((BigUint::from(lhs) + BigUint::from(rhs)) % ring_size());
618            assert_eq!(lhs + rhs, expected);
619        }
620        Ok(())
621    }
622
623    #[test]
624    fn test_did_rotate_matches_biguint_dyadic_offset_oracle() {
625        let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
626
627        for angle in [0u16, 1, 90, 180, 359, 360, 361, u16::MAX] {
628            let expected_offset =
629                Did::from(ring_size() * BigUint::from(angle) / BigUint::from(360u32));
630            assert_eq!(did.rotate(angle), did + expected_offset);
631        }
632    }
633
634    #[test]
635    fn test_did_power_of_two_matches_biguint_oracle() {
636        for bit in [0usize, 1, 8, 31, 32, 63, 64, 127, 128, 159, 160, 255] {
637            let expected = Did::from(BigUint::from(1u8) << bit);
638            assert_eq!(Did::power_of_two(bit), expected);
639        }
640    }
641
642    #[test]
643    fn test_did_affine() -> Result<()> {
644        let did = Did::from(10u32);
645        let affine_dids = did.rotate_affine(4)?;
646        assert_eq!(affine_dids.len(), 4);
647        assert_eq!(affine_dids, vec![
648            did.rotate(0),
649            did.rotate(90),
650            did.rotate(180),
651            did.rotate(270)
652        ]);
653        Ok(())
654    }
655
656    #[test]
657    fn test_rotate_affine_rejects_zero_scalar() {
658        let did = Did::from(10u32);
659
660        assert!(matches!(
661            did.rotate_affine(0),
662            Err(Error::InvalidAffineScalar)
663        ));
664    }
665
666    #[test]
667    fn test_rotate_affine_supports_non_degree_divisors() -> Result<()> {
668        let did = Did::from(10u32);
669        let affine_dids = did.rotate_affine(7)?;
670        let unique_dids = affine_dids.iter().copied().collect::<BTreeSet<_>>();
671
672        assert_eq!(affine_dids.len(), 7);
673        assert_eq!(unique_dids.len(), 7);
674        assert_eq!(affine_dids.first(), Some(&did));
675        Ok(())
676    }
677
678    #[test]
679    fn test_rotate_affine_supports_more_than_360_replicas() -> Result<()> {
680        let did = Did::from(10u32);
681        let affine_dids = did.rotate_affine(361)?;
682        let unique_dids = affine_dids.iter().copied().collect::<BTreeSet<_>>();
683
684        assert_eq!(affine_dids.len(), 361);
685        assert_eq!(unique_dids.len(), 361);
686        assert_eq!(affine_dids.first(), Some(&did));
687        Ok(())
688    }
689
690    #[test]
691    fn test_dump_and_load() {
692        // The length must be 40.
693        assert!(Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab").is_err());
694        assert!(Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab00").is_err());
695
696        // Allow omit 0x prefix
697        assert_eq!(
698            Did::from_str("11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap(),
699            Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap(),
700        );
701
702        // from_str then to_string
703        let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
704        assert_eq!(
705            did.to_string(),
706            "0x11e807fcc88dd319270493fb2e822e388fe36ab0"
707        );
708
709        // Serialize
710        let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
711        assert_eq!(
712            serde_json::to_string(&did).unwrap(),
713            "\"0x11e807fcc88dd319270493fb2e822e388fe36ab0\""
714        );
715
716        // Deserialize
717        let did =
718            serde_json::from_str::<Did>("\"0x11e807fcc88dd319270493fb2e822e388fe36ab0\"").unwrap();
719        assert_eq!(
720            did,
721            Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap()
722        );
723
724        // Debug and Display
725        let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
726        assert_eq!(
727            format!("{did}"),
728            "0x11e807fcc88dd319270493fb2e822e388fe36ab0"
729        );
730        assert_eq!(
731            format!("{did:?}"),
732            "Did(0x11e807fcc88dd319270493fb2e822e388fe36ab0)"
733        );
734    }
735}