Skip to main content

fancy_garbling/wire/
all.rs

1use fancy_traits::HasModulus;
2use rand::CryptoRng;
3use vectoreyes::U8x16;
4
5use crate::{ArithmeticWireLabel, WireLabel, WireMod2, WireMod3, WireModQ};
6
7#[derive(Debug, Clone, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9/// A [`WireLabel`] that supports all possible moduli
10pub enum AllWire {
11    /// A `mod 2` [`WireLabel`].
12    Mod2(WireMod2),
13    /// A `mod 3` [`WireLabel`].
14    Mod3(WireMod3),
15    /// A `mod q` [`WireLabel`], where `3 < q < 2^16`.
16    ModN(WireModQ),
17}
18
19impl HasModulus for AllWire {
20    fn modulus(&self) -> u16 {
21        match &self {
22            AllWire::Mod2(x) => x.modulus(),
23            AllWire::Mod3(x) => x.modulus(),
24            AllWire::ModN(x) => x.modulus(),
25        }
26    }
27}
28
29impl core::default::Default for AllWire {
30    fn default() -> Self {
31        Self::Mod2(Default::default())
32    }
33}
34
35impl core::ops::Add for AllWire {
36    type Output = Self;
37
38    fn add(self, rhs: Self) -> Self::Output {
39        let (p, q) = (self.modulus(), rhs.modulus());
40        match (self, rhs) {
41            (Self::Mod2(x), Self::Mod2(y)) => Self::Mod2(x + y),
42            (Self::Mod3(x), Self::Mod3(y)) => Self::Mod3(x + y),
43            (Self::ModN(x), Self::ModN(y)) => Self::ModN(x + y),
44            _ => panic!("unequal moduli: {p} != {q}"),
45        }
46    }
47}
48
49impl core::ops::AddAssign for AllWire {
50    fn add_assign(&mut self, rhs: Self) {
51        let (p, q) = (self.modulus(), rhs.modulus());
52        match (self, rhs) {
53            (Self::Mod2(x), Self::Mod2(y)) => *x += y,
54            (Self::Mod3(x), Self::Mod3(y)) => *x += y,
55            (Self::ModN(x), Self::ModN(y)) => *x += y,
56            _ => panic!("unequal moduli: {p} != {q}"),
57        }
58    }
59}
60
61impl core::ops::Sub for AllWire {
62    type Output = Self;
63
64    fn sub(self, rhs: Self) -> Self::Output {
65        self + -rhs
66    }
67}
68
69impl core::ops::SubAssign for AllWire {
70    fn sub_assign(&mut self, rhs: Self) {
71        *self = self.clone() - rhs;
72    }
73}
74
75impl core::ops::Neg for AllWire {
76    type Output = Self;
77
78    fn neg(self) -> Self::Output {
79        match self {
80            Self::Mod2(x) => Self::Mod2(-x),
81            Self::Mod3(x) => Self::Mod3(-x),
82            Self::ModN(x) => Self::ModN(-x),
83        }
84    }
85}
86
87impl core::ops::Mul<u16> for AllWire {
88    type Output = Self;
89
90    fn mul(self, rhs: u16) -> Self::Output {
91        match self {
92            Self::Mod2(x) => Self::Mod2(x * rhs),
93            Self::Mod3(x) => Self::Mod3(x * rhs),
94            Self::ModN(x) => Self::ModN(x * rhs),
95        }
96    }
97}
98
99impl core::ops::MulAssign<u16> for AllWire {
100    fn mul_assign(&mut self, rhs: u16) {
101        match self {
102            Self::Mod2(x) => {
103                *x *= rhs;
104            }
105            Self::Mod3(x) => {
106                *x *= rhs;
107            }
108            Self::ModN(x) => {
109                *x *= rhs;
110            }
111        };
112    }
113}
114
115impl WireLabel for AllWire {
116    fn rand_delta<R: CryptoRng>(rng: &mut R, q: u16) -> Self {
117        match q {
118            2 => AllWire::Mod2(WireMod2::rand_delta(rng, q)),
119            3 => AllWire::Mod3(WireMod3::rand_delta(rng, q)),
120            _ => AllWire::ModN(WireModQ::rand_delta(rng, q)),
121        }
122    }
123
124    fn to_repr(&self) -> U8x16 {
125        match &self {
126            AllWire::Mod2(x) => x.to_repr(),
127            AllWire::Mod3(x) => x.to_repr(),
128            AllWire::ModN(x) => x.to_repr(),
129        }
130    }
131    fn color(&self) -> u16 {
132        match &self {
133            AllWire::Mod2(x) => x.color(),
134            AllWire::Mod3(x) => x.color(),
135            AllWire::ModN(x) => x.color(),
136        }
137    }
138    fn from_repr(inp: U8x16, q: u16) -> Self {
139        match q {
140            2 => AllWire::Mod2(WireMod2::from_repr(inp, q)),
141            3 => AllWire::Mod3(WireMod3::from_repr(inp, q)),
142            _ => AllWire::ModN(WireModQ::from_repr(inp, q)),
143        }
144    }
145
146    fn rand<R: CryptoRng>(rng: &mut R, q: u16) -> Self {
147        match q {
148            2 => AllWire::Mod2(WireMod2::rand(rng, q)),
149            3 => AllWire::Mod3(WireMod3::rand(rng, q)),
150            _ => AllWire::ModN(WireModQ::rand(rng, q)),
151        }
152    }
153
154    fn hash_to_mod(hash: U8x16, q: u16) -> Self {
155        if q == 3 {
156            AllWire::Mod3(WireMod3::encode_block_mod3(hash))
157        } else {
158            Self::from_repr(hash, q)
159        }
160    }
161}
162
163impl ArithmeticWireLabel for AllWire {}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use crate::util::as_base_q_u128;
169    use fancy_circuits::util::RngExt;
170    use itertools::Itertools;
171    use rand::{RngExt as _, rng};
172
173    #[test]
174    fn packing() {
175        let rng = &mut rng();
176        for q in 2..256 {
177            for _ in 0..1000 {
178                let w = AllWire::rand(rng, q);
179                assert_eq!(w, AllWire::from_repr(w.to_repr(), q));
180            }
181        }
182    }
183
184    #[test]
185    fn base_conversion_lookup_method() {
186        let rng = &mut rng();
187        for _ in 0..1000 {
188            let q = 5 + (rng.random::<u16>() % 110);
189            let x = rng.random::<u128>();
190            let w = WireModQ::from_repr(U8x16::from(x), q);
191            let should_be = as_base_q_u128(x, q);
192            assert_eq!(w.ds, should_be, "x={} q={}", x, q);
193        }
194    }
195
196    #[test]
197    fn hash() {
198        let mut rng = rng();
199        for _ in 0..100 {
200            let q = 2 + (rng.random::<u16>() % 110);
201            let x = AllWire::rand(&mut rng, q);
202            let y = AllWire::hash_to_mod(x.hash(1u128), q);
203            assert!(x != y);
204            match y {
205                AllWire::Mod2(WireMod2 { val }) => assert!(u128::from(val) > 0),
206                AllWire::Mod3(WireMod3 { lsb, msb }) => assert!(lsb > 0 && msb > 0),
207                AllWire::ModN(WireModQ { ds, .. }) => assert!(!ds.iter().all(|&y| y == 0)),
208            }
209        }
210    }
211
212    #[test]
213    fn negation() {
214        let rng = &mut rng();
215        for _ in 0..1000 {
216            let q = rng.gen_modulus();
217            let x = AllWire::rand(rng, q);
218            let xneg = -x.clone();
219            if q != 2 {
220                assert!(x != xneg);
221            }
222            let y = -xneg;
223            assert_eq!(x, y);
224        }
225    }
226
227    #[test]
228    #[allow(clippy::erasing_op)]
229    fn arithmetic() {
230        let mut rng = rng();
231        for _ in 0..1024 {
232            let q = rng.gen_modulus();
233            let x = AllWire::rand(&mut rng, q);
234            let y = AllWire::rand(&mut rng, q);
235            assert_eq!(x.clone() * 0, x.clone() - x.clone());
236            assert_eq!(x.clone() * q, x.clone() - x.clone());
237            assert_eq!(x.clone() + x.clone(), x.clone() * 2);
238            assert_eq!(x.clone() + x.clone() + x.clone(), x.clone() * 3);
239            assert_eq!(-(-x.clone()), x);
240            if q == 2 {
241                assert_eq!(x.clone() + y.clone(), x.clone() - y.clone());
242            } else {
243                assert_eq!(x.clone() + -x.clone(), x.clone() - x.clone());
244                assert_eq!(x.clone() + -y.clone(), x.clone() - y.clone());
245            }
246            let mut w = x.clone();
247            let z = w.clone() + y.clone();
248            w += y;
249            assert_eq!(w, z);
250
251            w = x.clone();
252            w *= 2;
253            assert_eq!(x.clone() + x.clone(), w);
254
255            w = x.clone();
256            w = -w;
257            assert_eq!(-x, w);
258        }
259    }
260
261    #[test]
262    fn ndigits_correct() {
263        let mut rng = rng();
264        for _ in 0..1024 {
265            let q = rng.gen_modulus();
266            let x = WireModQ::rand(&mut rng, q);
267            assert_eq!(x.ds.len(), crate::util::digits_per_u128(q));
268        }
269    }
270
271    #[test]
272    fn parallel_hash() {
273        let n = 1000;
274        let mut rng = rng();
275        let q = rng.gen_modulus();
276        let ws = (0..n).map(|_| AllWire::rand(&mut rng, q)).collect_vec();
277
278        let mut handles = Vec::new();
279        for w in ws.iter() {
280            let w_ = w.clone();
281            let h = std::thread::spawn(move || w_.hash(0u128));
282            handles.push(h);
283        }
284        let hashes = handles.into_iter().map(|h| h.join().unwrap()).collect_vec();
285
286        let should_be = ws.iter().map(|w| w.hash(0u128)).collect_vec();
287
288        assert_eq!(hashes, should_be);
289    }
290
291    #[cfg(feature = "serde")]
292    #[test]
293    fn test_serialize_allwire() {
294        let mut rng = rng();
295        for q in 2..16 {
296            let w = AllWire::rand(&mut rng, q);
297            let serialized = serde_json::to_string(&w).unwrap();
298
299            let deserialized: AllWire = serde_json::from_str(&serialized).unwrap();
300
301            assert_eq!(w, deserialized);
302        }
303    }
304}