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}