1use std::iter::repeat_n;
2
3use ff::Field;
4use num_traits::{One, Zero};
5use primitives::{
6 algebra::{
7 elliptic_curve::{BaseFieldElement, Curve, Point, Scalar},
8 field::{Bit, FieldExtension, SubfieldElement},
9 BoxedUint,
10 },
11 types::PeerNumber,
12};
13use serde::{Deserialize, Serialize};
14use typenum::Unsigned;
15
16use crate::{
17 circuit::{errors::BatchSizeError, AlgebraicType, BatchSize, GateIndex, ShareOrPlaintext},
18 config::{MpcConfig, MpcFieldElement},
19 errors::{AbortError, FaultyPeer},
20};
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
24#[repr(C)]
25pub enum FieldPlaintextUnaryOp {
26 Neg,
27 MulInverse,
29 BitExtract {
31 little_endian_bit_idx: u16,
32 signed: bool,
33 },
34 Sqrt,
35 Pow {
36 exp: BoxedUint,
37 },
38}
39
40impl FieldPlaintextUnaryOp {
41 pub fn eval<F: FieldExtension>(
43 &self,
44 label: GateIndex,
45 x: &SubfieldElement<F>,
46 ) -> Result<SubfieldElement<F>, AbortError> {
47 match self {
48 FieldPlaintextUnaryOp::Neg => Ok(-x),
49 FieldPlaintextUnaryOp::MulInverse => {
50 Ok(x.invert().unwrap_or(SubfieldElement::<F>::zero()))
51 }
52 FieldPlaintextUnaryOp::BitExtract {
53 little_endian_bit_idx: idx,
54 signed,
55 } => {
56 let bit = if *signed && *x > -x {
57 !(-SubfieldElement::<F>::one() - x)
58 .to_biguint()
59 .bit(*idx as u64)
60 } else {
61 x.to_biguint().bit(*idx as u64)
62 };
63 Ok(SubfieldElement::<F>::from(bit))
64 }
65 FieldPlaintextUnaryOp::Sqrt => {
66 let (choice, sqrt) =
67 SubfieldElement::<F>::sqrt_ratio(x, &SubfieldElement::<F>::one());
68 if !bool::from(choice) {
69 return Err(AbortError::quadratic_non_residue(label, FaultyPeer::Local));
70 }
71 Ok(sqrt)
72 }
73 FieldPlaintextUnaryOp::Pow { exp } => Ok(x.pow(exp)),
74 }
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
80#[repr(C)]
81pub enum FieldPlaintextBinaryOp {
82 Add,
83 Mul,
84 EuclDiv,
85 Mod,
86 Gt,
87 Ge,
88 Eq,
89 Xor,
90 Or,
91}
92
93impl FieldPlaintextBinaryOp {
94 pub fn eval<F: FieldExtension>(
95 &self,
96 x: &SubfieldElement<F>,
97 y: &SubfieldElement<F>,
98 label: GateIndex,
99 ) -> Result<SubfieldElement<F>, AbortError> {
100 match self {
101 FieldPlaintextBinaryOp::Add => Ok(x + y),
102 FieldPlaintextBinaryOp::Mul => Ok(x * y),
103 FieldPlaintextBinaryOp::EuclDiv => euclidean_division::<F>(x, y, label),
104 FieldPlaintextBinaryOp::Mod => modulo::<F>(x, y, label),
105 FieldPlaintextBinaryOp::Gt => Ok(SubfieldElement::<F>::from(x > y)),
106 FieldPlaintextBinaryOp::Ge => Ok(SubfieldElement::<F>::from(x >= y)),
107 FieldPlaintextBinaryOp::Eq => Ok(SubfieldElement::<F>::from(x == y)),
108 FieldPlaintextBinaryOp::Xor => Ok(x + y - SubfieldElement::<F>::from(2u32) * x * y),
109 FieldPlaintextBinaryOp::Or => Ok(x + y - x * y),
110 }
111 }
112}
113
114pub(crate) fn euclidean_division<F: FieldExtension>(
115 x: &SubfieldElement<F>,
116 y: &SubfieldElement<F>,
117 label: GateIndex,
118) -> Result<SubfieldElement<F>, AbortError> {
119 if *y == SubfieldElement::<F>::zero() {
120 return Err(AbortError::division_by_zero(label, FaultyPeer::Local));
121 }
122
123 let x = x.to_biguint();
125 let y = y.to_biguint();
126
127 let div = (x / y).to_bytes_be();
128 let div = repeat_n(0, F::FieldBytesSize::USIZE - div.len())
130 .chain(div)
131 .collect::<Vec<_>>();
132
133 Ok(SubfieldElement::<F>::from_be_bytes(&div)?)
134}
135
136fn modulo<F: FieldExtension>(
137 x: &SubfieldElement<F>,
138 y: &SubfieldElement<F>,
139 label: GateIndex,
140) -> Result<SubfieldElement<F>, AbortError> {
141 if *y == SubfieldElement::<F>::zero() {
142 return Err(AbortError::division_by_zero(label, FaultyPeer::Local));
143 }
144
145 let x = x.to_biguint();
147 let y = y.to_biguint();
148
149 let modulo = x.modpow(&num_bigint::BigUint::from(1u32), &y).to_bytes_be();
150 let modulo = repeat_n(0, F::FieldBytesSize::USIZE - modulo.len())
152 .chain(modulo)
153 .collect::<Vec<_>>();
154
155 Ok(SubfieldElement::<F>::from_be_bytes(&modulo)?)
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
160#[repr(C)]
161pub enum FieldShareUnaryOp {
162 Neg,
164 MulInverse,
166 Open,
168 IsZero,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
175#[repr(C)]
176pub enum FieldShareBinaryOp {
177 Add,
179 Mul,
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
185#[repr(C)]
186pub enum BitShareUnaryOp {
187 Not,
189 Open,
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
195#[repr(C)]
196pub enum BitShareBinaryOp {
197 Xor,
199 Or,
201 And,
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
207#[repr(C)]
208pub enum BitPlaintextUnaryOp {
209 Not,
211}
212
213impl BitPlaintextUnaryOp {
214 pub fn eval(&self, x: Bit) -> Bit {
215 match self {
216 BitPlaintextUnaryOp::Not => Bit::ONE - x,
217 }
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
223#[repr(C)]
224pub enum BitPlaintextBinaryOp {
225 Xor,
227 Or,
229 And,
231}
232
233impl BitPlaintextBinaryOp {
234 pub fn eval(&self, x: Bit, y: Bit) -> Bit {
235 match self {
236 BitPlaintextBinaryOp::Xor => x + y,
237 BitPlaintextBinaryOp::Or => x + y - x * y,
238 BitPlaintextBinaryOp::And => x * y,
239 }
240 }
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
245#[repr(C)]
246pub enum PointPlaintextUnaryOp {
247 Neg,
249}
250
251impl PointPlaintextUnaryOp {
252 pub fn eval<C: Curve>(&self, x: &Point<C>) -> Result<Point<C>, AbortError> {
253 match self {
254 PointPlaintextUnaryOp::Neg => Ok(-x),
255 }
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
261#[repr(C)]
262pub enum PointPlaintextBinaryOp {
263 Add,
265 ScalarMul,
267}
268
269impl PointPlaintextBinaryOp {
270 pub fn eval<C: Curve>(&self, x: &Point<C>, y: &Point<C>) -> Result<Point<C>, AbortError> {
271 match self {
272 PointPlaintextBinaryOp::Add => Ok(x + y),
273 PointPlaintextBinaryOp::ScalarMul => Err(AbortError::internal_error(
274 "PointPlaintextBinaryOp::eval not supported for PointPlaintextBinaryOp::ScalarMul.",
275 )),
276 }
277 }
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
282#[repr(C)]
283pub enum PointShareUnaryOp {
284 Neg,
286 Open,
288 IsZero,
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
294#[repr(C)]
295pub enum PointShareBinaryOp {
296 Add,
298 ScalarMul,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
305#[repr(C)]
306pub enum Input {
307 Plaintext {
308 algebraic_type: AlgebraicType,
309 batch_size: BatchSize,
310 },
311 SecretPlaintext {
312 inputer: PeerNumber,
313 algebraic_type: AlgebraicType,
314 batch_size: BatchSize,
315 },
316 Share {
317 algebraic_type: AlgebraicType,
318 batch_size: BatchSize,
319 },
320}
321
322impl Input {
323 pub fn batch_size(&self) -> u32 {
324 match self {
325 Input::Plaintext { batch_size, .. }
326 | Input::SecretPlaintext { batch_size, .. }
327 | Input::Share { batch_size, .. } => *batch_size,
328 }
329 }
330
331 pub fn algebraic_type(&self) -> AlgebraicType {
332 match self {
333 Input::Plaintext { algebraic_type, .. }
334 | Input::Share { algebraic_type, .. }
335 | Input::SecretPlaintext { algebraic_type, .. } => *algebraic_type,
336 }
337 }
338
339 pub fn share_or_plaintext(&self) -> ShareOrPlaintext {
340 match self {
341 Input::SecretPlaintext { .. } | Input::Share { .. } => ShareOrPlaintext::Share,
342 Input::Plaintext { .. } => ShareOrPlaintext::Plaintext,
343 }
344 }
345}
346
347#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
348#[serde(bound(
349 serialize = "Scalar<C::Curve>: Serialize, Point<C::Curve>: Serialize",
350 deserialize = "Scalar<C::Curve>: Deserialize<'de>, Point<C::Curve>: Deserialize<'de>"
351))]
352#[repr(C)]
353pub enum Constant<C: MpcConfig> {
354 Scalar(Scalar<C::Curve>),
355 ScalarBatch(Vec<Scalar<C::Curve>>),
356 BaseField(BaseFieldElement<C::Curve>),
357 BaseFieldBatch(Vec<BaseFieldElement<C::Curve>>),
358 MpcField(MpcFieldElement<C>),
359 MpcFieldBatch(Vec<MpcFieldElement<C>>),
360 Bit(Bit),
361 BitBatch(Vec<Bit>),
362 Point(Point<C::Curve>),
363 PointBatch(Vec<Point<C::Curve>>),
364}
365
366impl<C: MpcConfig> Constant<C> {
367 pub fn batch_size(&self) -> Result<u32, BatchSizeError> {
368 let n = match self {
369 Constant::ScalarBatch(v) => v.len(),
370 Constant::BaseFieldBatch(v) => v.len(),
371 Constant::MpcFieldBatch(v) => v.len(),
372 Constant::BitBatch(v) => v.len(),
373 Constant::PointBatch(v) => v.len(),
374 Constant::Scalar(_)
375 | Constant::BaseField(_)
376 | Constant::MpcField(_)
377 | Constant::Bit(_)
378 | Constant::Point(_) => 1,
379 };
380 if let Ok(n) = u32::try_from(n) {
381 Ok(n)
382 } else {
383 Err(BatchSizeError(n))
384 }
385 }
386
387 pub fn algebraic_type(&self) -> AlgebraicType {
388 match self {
389 Constant::Scalar(_) | Constant::ScalarBatch(_) => AlgebraicType::ScalarField,
390 Constant::BaseField(_) | Constant::BaseFieldBatch(_) => AlgebraicType::BaseField,
391 Constant::MpcField(_) | Constant::MpcFieldBatch(_) => AlgebraicType::MpcField,
392 Constant::Bit(_) | Constant::BitBatch(_) => AlgebraicType::Bit,
393 Constant::Point(_) | Constant::PointBatch(_) => AlgebraicType::Point,
394 }
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use primitives::algebra::{
401 elliptic_curve::{BaseField, Curve25519Ristretto as C, ScalarField},
402 field::SubfieldElement,
403 };
404
405 use super::*;
406
407 #[test]
408 fn test_scalar_unary_op() {
409 let mut rng = rand::thread_rng();
410 let x = SubfieldElement::<ScalarField<C>>::random(&mut rng);
411 let label = 0;
412 let neg = FieldPlaintextUnaryOp::Neg;
413 let mul_inverse = FieldPlaintextUnaryOp::MulInverse;
414
415 assert_eq!(neg.eval::<ScalarField<C>>(label, &x), Ok(-x));
416 assert_eq!(
417 mul_inverse.eval::<ScalarField<C>>(label, &x),
418 Ok(x.invert().unwrap())
419 );
420 }
421
422 #[test]
423 fn test_scalar_binary_op() {
424 let mut rng = rand::thread_rng();
425 let x = SubfieldElement::<ScalarField<C>>::random(&mut rng);
426 let y = SubfieldElement::<ScalarField<C>>::random(&mut rng);
427 let label = 0;
428
429 let add = FieldPlaintextBinaryOp::Add;
430 let mul = FieldPlaintextBinaryOp::Mul;
431 let eucl_div = FieldPlaintextBinaryOp::EuclDiv;
432 let modulo_op = FieldPlaintextBinaryOp::Mod;
433 let gt = FieldPlaintextBinaryOp::Gt;
434 let ge = FieldPlaintextBinaryOp::Ge;
435 let eq = FieldPlaintextBinaryOp::Eq;
436
437 assert_eq!(add.eval::<ScalarField<C>>(&x, &y, label), Ok(x + y));
438 assert_eq!(mul.eval::<ScalarField<C>>(&x, &y, label), Ok(x * y));
439 assert_eq!(
440 eucl_div.eval::<ScalarField<C>>(&x, &y, label),
441 euclidean_division::<ScalarField<C>>(&x, &y, label)
442 );
443 assert_eq!(
444 modulo_op.eval::<ScalarField<C>>(&x, &y, label),
445 modulo::<ScalarField<C>>(&x, &y, label)
446 );
447 assert_eq!(
448 gt.eval::<ScalarField<C>>(&x, &y, label),
449 Ok(SubfieldElement::<ScalarField<C>>::from(x > y))
450 );
451 assert_eq!(
452 ge.eval::<ScalarField<C>>(&x, &y, label),
453 Ok(SubfieldElement::<ScalarField<C>>::from(x >= y))
454 );
455 assert_eq!(
456 eq.eval::<ScalarField<C>>(&x, &y, label),
457 Ok(SubfieldElement::<ScalarField<C>>::from(x == y))
458 );
459 }
460
461 #[test]
462 fn test_scalar_boolean_binary_op() {
463 let and = FieldPlaintextBinaryOp::Mul;
464 let or = FieldPlaintextBinaryOp::Or;
465 let xor = FieldPlaintextBinaryOp::Xor;
466 let label = 0;
467 for bool_x in [false, true] {
468 for bool_y in [false, true] {
469 let scalar_x = SubfieldElement::<ScalarField<C>>::from(bool_x);
470 let scalar_y = SubfieldElement::<ScalarField<C>>::from(bool_y);
471 assert_eq!(
472 and.eval::<ScalarField<C>>(&scalar_x, &scalar_y, label),
473 Ok((bool_x && bool_y).into())
474 );
475 assert_eq!(
476 or.eval::<ScalarField<C>>(&scalar_x, &scalar_y, label),
477 Ok((bool_x || bool_y).into())
478 );
479 assert_eq!(
480 xor.eval::<ScalarField<C>>(&scalar_x, &scalar_y, label),
481 Ok((bool_x ^ bool_y).into())
482 );
483 }
484 }
485 }
486
487 #[test]
488 fn test_bit_ops() {
489 let not = BitPlaintextUnaryOp::Not;
490 for bool_x in [false, true] {
491 let x = Bit::from(bool_x);
492 assert_eq!(not.eval(x), (!bool_x).into());
493 }
494
495 let and = BitPlaintextBinaryOp::And;
496 let or = BitPlaintextBinaryOp::Or;
497 let xor = BitPlaintextBinaryOp::Xor;
498 for bool_x in [false, true] {
499 for bool_y in [false, true] {
500 let x = Bit::from(bool_x);
501 let y = Bit::from(bool_y);
502 assert_eq!(and.eval(x, y), (bool_x && bool_y).into());
503 assert_eq!(or.eval(x, y), (bool_x || bool_y).into());
504 assert_eq!(xor.eval(x, y), (bool_x ^ bool_y).into());
505 }
506 }
507 }
508
509 #[test]
510 fn test_euclidian_division() {
511 let x = SubfieldElement::<ScalarField<C>>::from(37u32);
512 let y = SubfieldElement::<ScalarField<C>>::from(12u32);
513 let label = 0;
514
515 let result = euclidean_division::<ScalarField<C>>(&x, &y, label).unwrap();
516 assert_eq!(result, SubfieldElement::<ScalarField<C>>::from(37u32 / 12));
517 }
518
519 #[test]
520 fn test_modulo() {
521 let x = SubfieldElement::<ScalarField<C>>::from(37u32);
522 let y = SubfieldElement::<ScalarField<C>>::from(12u32);
523 let label = 0;
524
525 let result = modulo::<ScalarField<C>>(&x, &y, label).unwrap();
526 assert_eq!(result, SubfieldElement::<ScalarField<C>>::from(37u32 % 12));
527 }
528
529 #[test]
530 fn test_signed_bit_extract() {
531 let x = -Scalar::<C>::from(9u32);
532 let label = 0;
533 for i in 0..5 {
534 let op = FieldPlaintextUnaryOp::BitExtract {
535 little_endian_bit_idx: i,
536 signed: true,
537 };
538 let result = op.eval::<ScalarField<C>>(label, &x);
539 assert_eq!(result.unwrap(), ((-9i32 >> i) & 1 == 1).into())
540 }
541 }
542
543 #[test]
544 fn test_sqrt() {
545 let mut rng = rand::thread_rng();
546 let x = SubfieldElement::<ScalarField<C>>::random(&mut rng);
547 let label = 0;
548 let result = FieldPlaintextUnaryOp::Sqrt
549 .eval::<ScalarField<C>>(label, &(x * x))
550 .unwrap();
551
552 assert_eq!(result * result, x * x)
553 }
554
555 #[test]
556 fn test_pow() {
557 let mut rng = rand::thread_rng();
558 let x = SubfieldElement::<BaseField<C>>::random(&mut rng);
559 let label = 0;
560 let five = BoxedUint::from(vec![5u64]);
561 let five_inv = BoxedUint::from(vec![
562 14757395258967641281,
563 14757395258967641292,
564 14757395258967641292,
565 5534023222112865484,
566 ]);
567 let x_pow_5 = FieldPlaintextUnaryOp::Pow { exp: five }
568 .eval::<BaseField<C>>(label, &x)
569 .unwrap();
570 let x_again = FieldPlaintextUnaryOp::Pow { exp: five_inv }
571 .eval::<BaseField<C>>(label, &x_pow_5)
572 .unwrap();
573
574 assert_eq!(x_again, x)
575 }
576}