1use crate::util;
9use fancy_traits::HasModulus;
10use rand::{CryptoRng, Rng};
11use swanky_cr_hash::TweakableCircularCorrelationRobustHash;
12use vectoreyes::{
13 U8x16,
14 array_utils::{ArrayUnrolledExt, ArrayUnrolledOps, UnrollableArraySize},
15};
16
17mod mod2;
18pub use mod2::WireMod2;
19mod mod3;
20pub use mod3::WireMod3;
21mod modq;
22pub use modq::WireModQ;
23mod npaths_tab;
24
25pub fn hash_wires<const Q: usize, W: WireLabel>(wires: [&W; Q], tweak: u128) -> [U8x16; Q]
27where
28 ArrayUnrolledOps: UnrollableArraySize<Q>,
29{
30 let batch = wires.array_map(|x| x.to_repr());
31 TweakableCircularCorrelationRobustHash::fixed_key().hash_many(batch, tweak)
32}
33
34pub trait ArithmeticWire: Clone {}
37
38pub trait WireLabel:
43 Clone
44 + core::fmt::Debug
45 + core::default::Default
46 + HasModulus
47 + core::ops::Add<Output = Self>
48 + core::ops::AddAssign
49 + core::ops::Sub<Output = Self>
50 + core::ops::SubAssign
51 + core::ops::Neg<Output = Self>
52 + core::ops::Mul<u16, Output = Self>
53 + core::ops::MulAssign<u16>
54{
55 fn to_repr(&self) -> U8x16;
57
58 fn color(&self) -> u16;
60
61 fn from_repr(inp: U8x16, q: u16) -> Self;
68
69 fn rand_delta<R: CryptoRng + Rng>(rng: &mut R, q: u16) -> Self;
75
76 fn rand<R: CryptoRng + Rng>(rng: &mut R, q: u16) -> Self;
82
83 fn hash_to_mod(hash: U8x16, q: u16) -> Self;
92
93 fn hashback(&self, tweak: u128, q: u16) -> Self {
104 let hash = self.hash(tweak);
105 Self::hash_to_mod(hash, q)
106 }
107
108 fn hash(&self, tweak: u128) -> U8x16 {
110 TweakableCircularCorrelationRobustHash::fixed_key().hash(self.to_repr(), tweak)
111 }
112
113 fn constant<RNG: CryptoRng + Rng>(x: u16, q: u16, delta: &Self, rng: &mut RNG) -> (Self, Self) {
116 let zero = Self::rand(rng, q);
117 let wire = zero.clone() + delta.clone() * x;
118 (zero, wire)
119 }
120}
121
122#[derive(Debug, Clone, PartialEq)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124pub enum AllWire {
126 Mod2(WireMod2),
128 Mod3(WireMod3),
130 ModN(WireModQ),
132}
133
134impl HasModulus for AllWire {
135 fn modulus(&self) -> u16 {
136 match &self {
137 AllWire::Mod2(x) => x.modulus(),
138 AllWire::Mod3(x) => x.modulus(),
139 AllWire::ModN(x) => x.modulus(),
140 }
141 }
142}
143
144impl core::default::Default for AllWire {
145 fn default() -> Self {
146 Self::Mod2(Default::default())
147 }
148}
149
150impl core::ops::Add for AllWire {
151 type Output = Self;
152
153 fn add(self, rhs: Self) -> Self::Output {
154 let (p, q) = (self.modulus(), rhs.modulus());
155 match (self, rhs) {
156 (Self::Mod2(x), Self::Mod2(y)) => Self::Mod2(x + y),
157 (Self::Mod3(x), Self::Mod3(y)) => Self::Mod3(x + y),
158 (Self::ModN(x), Self::ModN(y)) => Self::ModN(x + y),
159 _ => panic!("unequal moduli: {p} != {q}"),
160 }
161 }
162}
163
164impl core::ops::AddAssign for AllWire {
165 fn add_assign(&mut self, rhs: Self) {
166 let (p, q) = (self.modulus(), rhs.modulus());
167 match (self, rhs) {
168 (Self::Mod2(x), Self::Mod2(y)) => *x += y,
169 (Self::Mod3(x), Self::Mod3(y)) => *x += y,
170 (Self::ModN(x), Self::ModN(y)) => *x += y,
171 _ => panic!("unequal moduli: {p} != {q}"),
172 }
173 }
174}
175
176impl core::ops::Sub for AllWire {
177 type Output = Self;
178
179 fn sub(self, rhs: Self) -> Self::Output {
180 self + -rhs
181 }
182}
183
184impl core::ops::SubAssign for AllWire {
185 fn sub_assign(&mut self, rhs: Self) {
186 *self = self.clone() - rhs;
187 }
188}
189
190impl core::ops::Neg for AllWire {
191 type Output = Self;
192
193 fn neg(self) -> Self::Output {
194 match self {
195 Self::Mod2(x) => Self::Mod2(-x),
196 Self::Mod3(x) => Self::Mod3(-x),
197 Self::ModN(x) => Self::ModN(-x),
198 }
199 }
200}
201
202impl core::ops::Mul<u16> for AllWire {
203 type Output = Self;
204
205 fn mul(self, rhs: u16) -> Self::Output {
206 match self {
207 Self::Mod2(x) => Self::Mod2(x * rhs),
208 Self::Mod3(x) => Self::Mod3(x * rhs),
209 Self::ModN(x) => Self::ModN(x * rhs),
210 }
211 }
212}
213
214impl core::ops::MulAssign<u16> for AllWire {
215 fn mul_assign(&mut self, rhs: u16) {
216 match self {
217 Self::Mod2(x) => {
218 *x *= rhs;
219 }
220 Self::Mod3(x) => {
221 *x *= rhs;
222 }
223 Self::ModN(x) => {
224 *x *= rhs;
225 }
226 };
227 }
228}
229
230impl WireLabel for AllWire {
231 fn rand_delta<R: CryptoRng + Rng>(rng: &mut R, q: u16) -> Self {
232 match q {
233 2 => AllWire::Mod2(WireMod2::rand_delta(rng, q)),
234 3 => AllWire::Mod3(WireMod3::rand_delta(rng, q)),
235 _ => AllWire::ModN(WireModQ::rand_delta(rng, q)),
236 }
237 }
238
239 fn to_repr(&self) -> U8x16 {
240 match &self {
241 AllWire::Mod2(x) => x.to_repr(),
242 AllWire::Mod3(x) => x.to_repr(),
243 AllWire::ModN(x) => x.to_repr(),
244 }
245 }
246 fn color(&self) -> u16 {
247 match &self {
248 AllWire::Mod2(x) => x.color(),
249 AllWire::Mod3(x) => x.color(),
250 AllWire::ModN(x) => x.color(),
251 }
252 }
253 fn from_repr(inp: U8x16, q: u16) -> Self {
254 match q {
255 2 => AllWire::Mod2(WireMod2::from_repr(inp, q)),
256 3 => AllWire::Mod3(WireMod3::from_repr(inp, q)),
257 _ => AllWire::ModN(WireModQ::from_repr(inp, q)),
258 }
259 }
260
261 fn rand<R: CryptoRng + Rng>(rng: &mut R, q: u16) -> Self {
262 match q {
263 2 => AllWire::Mod2(WireMod2::rand(rng, q)),
264 3 => AllWire::Mod3(WireMod3::rand(rng, q)),
265 _ => AllWire::ModN(WireModQ::rand(rng, q)),
266 }
267 }
268
269 fn hash_to_mod(hash: U8x16, q: u16) -> Self {
270 if q == 3 {
271 AllWire::Mod3(WireMod3::encode_block_mod3(hash))
272 } else {
273 Self::from_repr(hash, q)
274 }
275 }
276}
277fn _unrank(inp: u128, q: u16) -> Vec<u16> {
278 let mut x = inp;
279 let ndigits = util::digits_per_u128(q);
280 let npaths_tab = npaths_tab::lookup(q);
281 x %= npaths_tab[ndigits - 1] * q as u128;
282
283 let mut ds = vec![0; ndigits];
284 for i in (0..ndigits).rev() {
285 let npaths = npaths_tab[i];
286
287 if q <= 23 {
288 let mut acc = 0;
290 for j in 0..q {
291 acc += npaths;
292 if acc > x {
293 x -= acc - npaths;
294 ds[i] = j;
295 break;
296 }
297 }
298 } else {
299 let d = x / npaths;
301 ds[i] = d as u16;
302 x -= d * npaths;
303 }
304 }
326 ds
327}
328
329impl ArithmeticWire for AllWire {}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334 use crate::util::as_base_q_u128;
335 use fancy_circuits::util::RngExt;
336 use itertools::Itertools;
337 use rand::{RngExt as _, rng};
338
339 #[test]
340 fn packing() {
341 let rng = &mut rng();
342 for q in 2..256 {
343 for _ in 0..1000 {
344 let w = AllWire::rand(rng, q);
345 assert_eq!(w, AllWire::from_repr(w.to_repr(), q));
346 }
347 }
348 }
349
350 #[test]
351 fn base_conversion_lookup_method() {
352 let rng = &mut rng();
353 for _ in 0..1000 {
354 let q = 5 + (rng.random::<u16>() % 110);
355 let x = rng.random::<u128>();
356 let w = WireModQ::from_repr(U8x16::from(x), q);
357 let should_be = as_base_q_u128(x, q);
358 assert_eq!(w.ds, should_be, "x={} q={}", x, q);
359 }
360 }
361
362 #[test]
363 fn hash() {
364 let mut rng = rng();
365 for _ in 0..100 {
366 let q = 2 + (rng.random::<u16>() % 110);
367 let x = AllWire::rand(&mut rng, q);
368 let y = x.hashback(1u128, q);
369 assert!(x != y);
370 match y {
371 AllWire::Mod2(WireMod2 { val }) => assert!(u128::from(val) > 0),
372 AllWire::Mod3(WireMod3 { lsb, msb }) => assert!(lsb > 0 && msb > 0),
373 AllWire::ModN(WireModQ { ds, .. }) => assert!(!ds.iter().all(|&y| y == 0)),
374 }
375 }
376 }
377
378 #[test]
379 fn negation() {
380 let rng = &mut rng();
381 for _ in 0..1000 {
382 let q = rng.gen_modulus();
383 let x = AllWire::rand(rng, q);
384 let xneg = -x.clone();
385 if q != 2 {
386 assert!(x != xneg);
387 }
388 let y = -xneg;
389 assert_eq!(x, y);
390 }
391 }
392
393 #[test]
394 #[allow(clippy::erasing_op)]
395 fn arithmetic() {
396 let mut rng = rng();
397 for _ in 0..1024 {
398 let q = rng.gen_modulus();
399 let x = AllWire::rand(&mut rng, q);
400 let y = AllWire::rand(&mut rng, q);
401 assert_eq!(x.clone() * 0, x.clone() - x.clone());
402 assert_eq!(x.clone() * q, x.clone() - x.clone());
403 assert_eq!(x.clone() + x.clone(), x.clone() * 2);
404 assert_eq!(x.clone() + x.clone() + x.clone(), x.clone() * 3);
405 assert_eq!(-(-x.clone()), x);
406 if q == 2 {
407 assert_eq!(x.clone() + y.clone(), x.clone() - y.clone());
408 } else {
409 assert_eq!(x.clone() + -x.clone(), x.clone() - x.clone());
410 assert_eq!(x.clone() + -y.clone(), x.clone() - y.clone());
411 }
412 let mut w = x.clone();
413 let z = w.clone() + y.clone();
414 w += y;
415 assert_eq!(w, z);
416
417 w = x.clone();
418 w *= 2;
419 assert_eq!(x.clone() + x.clone(), w);
420
421 w = x.clone();
422 w = -w;
423 assert_eq!(-x, w);
424 }
425 }
426
427 #[test]
428 fn ndigits_correct() {
429 let mut rng = rng();
430 for _ in 0..1024 {
431 let q = rng.gen_modulus();
432 let x = WireModQ::rand(&mut rng, q);
433 assert_eq!(x.ds.len(), util::digits_per_u128(q));
434 }
435 }
436
437 #[test]
438 fn parallel_hash() {
439 let n = 1000;
440 let mut rng = rng();
441 let q = rng.gen_modulus();
442 let ws = (0..n).map(|_| AllWire::rand(&mut rng, q)).collect_vec();
443
444 let mut handles = Vec::new();
445 for w in ws.iter() {
446 let w_ = w.clone();
447 let h = std::thread::spawn(move || w_.hash(0u128));
448 handles.push(h);
449 }
450 let hashes = handles.into_iter().map(|h| h.join().unwrap()).collect_vec();
451
452 let should_be = ws.iter().map(|w| w.hash(0u128)).collect_vec();
453
454 assert_eq!(hashes, should_be);
455 }
456
457 #[cfg(feature = "serde")]
458 #[test]
459 fn test_serialize_allwire() {
460 let mut rng = rng();
461 for q in 2..16 {
462 let w = AllWire::rand(&mut rng, q);
463 let serialized = serde_json::to_string(&w).unwrap();
464
465 let deserialized: AllWire = serde_json::from_str(&serialized).unwrap();
466
467 assert_eq!(w, deserialized);
468 }
469 }
470}