Skip to main content

rings_core/
algebra.rs

1#![warn(missing_docs)]
2
3//! Algebraic structure traits shared by DHT identifiers and elliptic-curve
4//! groups.
5//!
6//! This module names the algebraic structure carried by a domain type. It is
7//! deliberately about carriers and operations, not about object hierarchies:
8//! the implementing type is the carrier set, and each trait states which
9//! operations and laws are part of that type's public model.
10//!
11//! ## Model Boundary
12//!
13//! Rust trait bounds can require operation shapes such as
14//! [`Add<Output = Self>`], [`Neg<Output = Self>`], or [`Mul<Output = Self>`].
15//! They cannot prove associativity, commutativity, distributivity, identity, or
16//! inverse laws. Implementing one of these traits is therefore a proof
17//! obligation: the implementation asserts the law, and law tests witness the
18//! assertion on representative samples.
19//!
20//! The public surface is intentionally small:
21//!
22//! - [`Zero`] and [`One`] name additive and multiplicative identities together
23//!   with the operations whose identities they are.
24//! - [`AbelianGroup`] is the additive structure used by Chord identifiers and
25//!   elliptic-curve points.
26//! - [`CommutativeRing`] combines an additive abelian group with commutative
27//!   multiplication and a multiplicative identity.
28//! - [`Field`] is a commutative ring whose non-zero elements have inverses.
29//! - [`Module`] is a right scalar action of a commutative ring on an abelian
30//!   group.
31//! - [`JoinSemilattice`] is the CRDT merge structure used by replicated DHT
32//!   state.
33//!
34//! ## Rings DHT
35//!
36//! [`crate::dht::Did`] is the carrier for Chord identifier arithmetic. It is an
37//! [`AbelianGroup`] under addition in `Z / 2^160`, which is exactly the
38//! operation used for clockwise offsets, biased ordering, finger targets, and
39//! affine replica placement. It intentionally does not implement
40//! [`CommutativeRing`]: Chord does not use identifier multiplication as a
41//! protocol operation, so the public model should not expose it.
42//!
43//! ## Elliptic Curves
44//!
45//! Curve points are additive abelian groups. Curve scalars are finite fields.
46//! A point group with scalar multiplication is modeled as
47//! `Point<C>: Module<Scalar<C>>`. The module action is a right action because
48//! Rust's operator implementation in this crate is `Point<C> * Scalar<C>`.
49//! This keeps cryptographic algorithms phrased in algebraic terms while curve
50//! libraries remain adapters behind [`crate::ecc::group::CurveGroup`].
51//!
52//! ## Law Witnesses
53//!
54//! The `assert_*_laws` functions are test helpers, not proofs in the type
55//! system. They are useful because every implementation can be checked through
56//! the same vocabulary, but they remain finite-sample witnesses. A new
57//! implementation must still explain why its carrier and operations satisfy the
58//! stated laws for all values, usually by delegating to a native finite-field or
59//! group implementation with documented semantics.
60//!
61//! Identities are functions rather than associated constants because several
62//! curve adapters obtain identity values through their native libraries.
63
64#[cfg(test)]
65use std::fmt::Debug;
66use std::ops::Add;
67use std::ops::Mul;
68use std::ops::Neg;
69use std::ops::Sub;
70
71/// Join-semilattice for state-based CRDT merge.
72///
73/// `join` returns the least upper bound of two states from the same carrier.
74/// Implementors must make this operation inflationary with respect to the
75/// carrier's induced partial order `a <= b iff a.join(b) == b`; because Rust
76/// does not expose that order here, the obligation is witnessed through the
77/// idempotence, commutativity, and associativity laws below.
78///
79/// The operation takes both arguments by value to model a pure state transition
80/// into a canonical least upper bound. Implementations that need an in-place
81/// merge can provide that adapter separately and keep this law-facing
82/// signature as the common algebraic surface.
83///
84/// Law: `a.join(a) == a`.
85///
86/// Law: `a.join(b) == b.join(a)`.
87///
88/// Law: `a.join(b).join(c) == a.join(b.join(c))`.
89pub trait JoinSemilattice: Sized {
90    /// Return the least upper bound of `self` and `other`.
91    fn join(self, other: Self) -> Self;
92}
93
94/// Additive identity for an additive carrier.
95///
96/// Implement this only for a type whose [`Add`] operation has an identity
97/// element. `is_zero` must recognize exactly that same value; algorithms use it
98/// as a semantic predicate, not as an encoding shortcut.
99///
100/// Law: `a + zero() == a` and `zero() + a == a`.
101pub trait Zero: Sized + Add<Self, Output = Self> {
102    /// Return the additive identity.
103    fn zero() -> Self;
104
105    /// Return whether this value is the additive identity.
106    fn is_zero(&self) -> bool;
107}
108
109/// Multiplicative identity for a multiplicative carrier.
110///
111/// Implement this only for a type whose [`Mul`] operation has an identity
112/// element.
113///
114/// Law: `a * one() == a` and `one() * a == a`.
115pub trait One: Sized + Mul<Self, Output = Self> {
116    /// Return the multiplicative identity.
117    fn one() -> Self;
118}
119
120/// Abelian group under addition.
121///
122/// This is the additive structure used by Chord identifiers and elliptic-curve
123/// points. Subtraction must be the derived operation `a - b == a + (-b)`, not an
124/// unrelated primitive.
125///
126/// Law: addition is associative and commutative.
127///
128/// Law: [`Zero::zero`] is the additive identity.
129///
130/// Law: [`Neg`] returns the additive inverse.
131///
132/// Law: [`Sub`] is addition with the additive inverse.
133pub trait AbelianGroup:
134    Sized + Add<Self, Output = Self> + Sub<Self, Output = Self> + Neg<Output = Self> + Zero
135{
136}
137
138/// Unital commutative ring.
139///
140/// `CommutativeRing` is a capability boundary: implement it only when both
141/// addition and multiplication are semantic operations of the domain type. A
142/// type may have a mathematically possible multiplication and still not
143/// implement `CommutativeRing` when that operation is outside the protocol
144/// model.
145///
146/// Law: the implementor is an [`AbelianGroup`] under addition.
147///
148/// Law: multiplication is associative and commutative.
149///
150/// Law: [`One::one`] is the multiplicative identity.
151///
152/// Law: multiplication distributes over addition.
153pub trait CommutativeRing: AbelianGroup + Mul<Self, Output = Self> + One {}
154
155/// Field.
156///
157/// A field is a commutative ring whose non-zero values form a multiplicative
158/// group. `try_inverse` is fallible only because zero has no multiplicative
159/// inverse; returning `None` for a non-zero value violates the trait law.
160///
161/// Law: [`Zero::zero`] is distinct from [`One::one`].
162///
163/// Law: non-zero values have a multiplicative inverse.
164pub trait Field: CommutativeRing {
165    /// Return the multiplicative inverse.
166    ///
167    /// Post: if this returns `Some(inv)`, then `self * inv == one()` and
168    /// `inv * self == one()`.
169    fn try_inverse(&self) -> Option<Self>;
170}
171
172/// Right scalar action of a commutative ring on an abelian group.
173///
174/// `Module<Scalar>` is parameterized by the scalar carrier. The element carrier
175/// is `Self`, and the scalar action is expressed by `Self: Mul<Scalar>`. In this
176/// crate that matches elliptic-curve notation as `point * scalar`.
177///
178/// A left action would be a different Rust operation shape,
179/// `Scalar: Mul<Self>`. Do not implement this trait for a left-only action by
180/// swapping argument meaning in the implementation.
181///
182/// Law: `a * (s + t) == a * s + a * t`.
183///
184/// Law: `(a + b) * s == a * s + b * s`.
185///
186/// Law: `a * (s * t) == (a * s) * t`.
187///
188/// Law: `a * Scalar::one() == a`.
189pub trait Module<Scalar>: AbelianGroup + Mul<Scalar, Output = Self>
190where Scalar: CommutativeRing
191{
192}
193
194/// Assert the abelian-group laws for a representative finite sample.
195///
196/// This helper checks identity, inverse, involutive negation, commutativity, and
197/// associativity over `values`. It is a shared test witness for implementations
198/// of [`AbelianGroup`]; it does not replace the implementor's obligation to
199/// explain why the laws hold for the whole carrier.
200#[cfg(test)]
201pub fn assert_abelian_group_laws<T>(values: &[T])
202where T: AbelianGroup + Clone + Eq + Debug {
203    for a in values {
204        assert_eq!(a.clone() + T::zero(), *a);
205        assert_eq!(T::zero() + a.clone(), *a);
206        assert_eq!(a.clone() + (-a.clone()), T::zero());
207        assert_eq!((-a.clone()) + a.clone(), T::zero());
208        assert_eq!(-(-a.clone()), *a);
209
210        for b in values {
211            let lhs = a.clone() + b.clone();
212            let rhs = b.clone() + a.clone();
213            assert_eq!(lhs, rhs);
214
215            for c in values {
216                let lhs = (a.clone() + b.clone()) + c.clone();
217                let rhs = a.clone() + (b.clone() + c.clone());
218                assert_eq!(lhs, rhs);
219            }
220        }
221    }
222}
223
224/// Assert join-semilattice laws for a representative finite sample.
225///
226/// This helper checks the state-based CRDT merge laws that imply strong
227/// eventual consistency for replicas that observe the same set of deltas in any
228/// order, with any duplication.
229#[cfg(test)]
230pub fn assert_join_semilattice_laws<T>(values: &[T])
231where T: JoinSemilattice + Clone + Eq + Debug {
232    for a in values {
233        assert_eq!(a.clone().join(a.clone()), *a);
234
235        for b in values {
236            assert_eq!(a.clone().join(b.clone()), b.clone().join(a.clone()));
237
238            for c in values {
239                let lhs = a.clone().join(b.clone()).join(c.clone());
240                let rhs = a.clone().join(b.clone().join(c.clone()));
241                assert_eq!(lhs, rhs);
242            }
243        }
244    }
245}
246
247/// Assert strong eventual consistency for one base state and a finite delta set.
248///
249/// The witness applies the same deltas in forward, reverse, and duplicated
250/// schedules. A lawful join-semilattice must materialize the same least upper
251/// bound for each schedule.
252#[cfg(test)]
253pub fn assert_strong_eventual_consistency<T>(base: T, deltas: &[T])
254where T: JoinSemilattice + Clone + Eq + Debug {
255    let forward = deltas
256        .iter()
257        .cloned()
258        .fold(base.clone(), JoinSemilattice::join);
259    let reverse = deltas
260        .iter()
261        .rev()
262        .cloned()
263        .fold(base.clone(), JoinSemilattice::join);
264    let duplicated = deltas
265        .iter()
266        .cloned()
267        .chain(deltas.iter().cloned())
268        .fold(base, JoinSemilattice::join);
269
270    assert_eq!(forward, reverse);
271    assert_eq!(forward, duplicated);
272}
273
274/// Assert the commutative-ring laws for a representative finite sample.
275///
276/// This helper first checks the additive abelian-group laws, then checks
277/// multiplicative identity, multiplicative commutativity, multiplicative
278/// associativity, and left distributivity over `values`. Because multiplication
279/// is required to be commutative, left distributivity witnesses right
280/// distributivity on the same sample.
281#[cfg(test)]
282pub fn assert_commutative_ring_laws<T>(values: &[T])
283where T: CommutativeRing + Clone + Eq + Debug {
284    assert_abelian_group_laws(values);
285
286    for a in values {
287        assert_eq!(a.clone() * T::one(), *a);
288        assert_eq!(T::one() * a.clone(), *a);
289
290        for b in values {
291            let lhs = a.clone() * b.clone();
292            let rhs = b.clone() * a.clone();
293            assert_eq!(lhs, rhs);
294
295            for c in values {
296                let lhs = (a.clone() * b.clone()) * c.clone();
297                let rhs = a.clone() * (b.clone() * c.clone());
298                assert_eq!(lhs, rhs);
299
300                let lhs = a.clone() * (b.clone() + c.clone());
301                let rhs = (a.clone() * b.clone()) + (a.clone() * c.clone());
302                assert_eq!(lhs, rhs);
303            }
304        }
305    }
306}
307
308/// Assert the field inverse laws for a representative finite sample.
309///
310/// This helper first checks the commutative-ring laws and the field
311/// non-degeneracy law `zero() != one()`. It then checks that zero has no inverse
312/// and every sampled non-zero value has a two-sided inverse.
313#[cfg(test)]
314pub fn assert_field_laws<T>(values: &[T])
315where T: Field + Clone + Eq + Debug {
316    assert_commutative_ring_laws(values);
317    assert_ne!(T::zero(), T::one());
318
319    for a in values {
320        if a.is_zero() {
321            assert_eq!(a.try_inverse(), None);
322            continue;
323        }
324
325        let Some(inverse) = a.try_inverse() else {
326            panic!("non-zero field element has no inverse");
327        };
328        assert_eq!(a.clone() * inverse.clone(), T::one());
329        assert_eq!(inverse * a.clone(), T::one());
330    }
331}
332
333/// Assert right scalar-action laws for representative samples.
334///
335/// This helper assumes the caller has already checked the scalar carrier laws.
336/// It still checks the element abelian-group laws because module elements carry
337/// their own additive group structure. Use it when a test has already run a
338/// stricter scalar law helper, such as [`assert_field_laws`], and only needs the
339/// module action witness afterward.
340#[cfg(test)]
341pub fn assert_module_action_laws<Scalar, Element>(scalars: &[Scalar], elements: &[Element])
342where
343    Scalar: CommutativeRing + Clone + Eq + Debug,
344    Element: Module<Scalar> + Clone + Eq + Debug,
345{
346    assert_abelian_group_laws(elements);
347
348    for s in scalars {
349        for t in scalars {
350            for a in elements {
351                let lhs = a.clone() * (s.clone() + t.clone());
352                let rhs = (a.clone() * s.clone()) + (a.clone() * t.clone());
353                assert_eq!(lhs, rhs);
354
355                let lhs = a.clone() * (s.clone() * t.clone());
356                let rhs = (a.clone() * s.clone()) * t.clone();
357                assert_eq!(lhs, rhs);
358
359                for b in elements {
360                    let lhs = (a.clone() + b.clone()) * s.clone();
361                    let rhs = (a.clone() * s.clone()) + (b.clone() * s.clone());
362                    assert_eq!(lhs, rhs);
363                }
364            }
365        }
366
367        for a in elements {
368            assert_eq!(a.clone() * Scalar::one(), *a);
369        }
370    }
371}