Skip to main content

rings_core/dht/
did.rs

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