1use primitives::algebra::elliptic_curve::{Curve, NumCoordinates};
2use typenum::Unsigned;
3
4use crate::{
5 circuit::{
6 errors::{CircuitError, ConversionError},
7 gate::Gate,
8 preprocessing::AES_S_BOX_N_NETWORK_ROUNDS,
9 AlgebraicType,
10 BitShareBinaryOp,
11 BitShareUnaryOp,
12 FieldShareBinaryOp,
13 FieldShareUnaryOp,
14 FieldType,
15 GateIndex,
16 Input,
17 PointPlaintextBinaryOp,
18 PointShareBinaryOp,
19 PointShareUnaryOp,
20 ShareOrPlaintext,
21 },
22 key_recovery::{MXE_KEY_RECOVERY_D, MXE_KEY_RECOVERY_N},
23};
24
25#[derive(Default, PartialEq, Debug, Clone)]
32pub struct Circuit<C: Curve> {
33 pub(super) gates: Vec<GateExt<C>>,
35 pub(super) inputs: Vec<GateIndex>,
37 pub(super) outputs: Vec<GateIndex>,
39}
40
41#[derive(Clone, Debug, PartialEq)]
44pub struct GateExt<C: Curve> {
45 pub gate: Gate<C>,
46 pub output: GateOutput,
47 pub level: GateLevel,
48}
49
50#[derive(PartialEq, Copy, Clone, Debug)]
52pub struct GateOutput {
53 pub(super) algebraic_type: AlgebraicType,
54 pub(super) form: ShareOrPlaintext,
55 pub(super) batch_size: u32,
56}
57
58#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Default)]
65pub struct GateLevel {
66 comm_level: usize,
67 level: usize, }
69
70impl<C: Curve> GateExt<C> {
71 pub fn new(gate: Gate<C>, output: GateOutput, level: GateLevel) -> Self {
72 Self {
73 gate,
74 output,
75 level,
76 }
77 }
78}
79
80impl GateOutput {
81 pub fn get_type(&self) -> AlgebraicType {
82 self.algebraic_type
83 }
84
85 pub fn get_field_type(&self) -> Result<FieldType, ConversionError> {
86 FieldType::try_from(self.algebraic_type)
87 }
88
89 pub fn get_field_type_unchecked(&self) -> FieldType {
90 self.get_field_type().unwrap()
91 }
92
93 pub fn get_form(&self) -> ShareOrPlaintext {
94 self.form
95 }
96
97 pub fn get_batch_size(&self) -> u32 {
98 self.batch_size
99 }
100
101 fn is_field(&self) -> bool {
102 FieldType::try_from(self.algebraic_type).is_ok()
103 }
104
105 fn is_bit(&self) -> bool {
106 self.algebraic_type == AlgebraicType::Bit
107 }
108
109 fn is_point(&self) -> bool {
110 self.algebraic_type == AlgebraicType::Point
111 }
112
113 fn is_base_field(&self) -> bool {
114 self.algebraic_type == AlgebraicType::BaseField
115 }
116
117 fn is_scalar_field(&self) -> bool {
118 self.algebraic_type == AlgebraicType::ScalarField
119 }
120
121 fn is_share(&self) -> bool {
122 self.form == ShareOrPlaintext::Share
123 }
124
125 pub fn is_plaintext(&self) -> bool {
126 self.form == ShareOrPlaintext::Plaintext
127 }
128
129 fn with_new_form(self, form: ShareOrPlaintext) -> Self {
130 let mut res = self;
131 res.form = form;
132 res
133 }
134
135 fn with_new_type(self, algebraic_type: AlgebraicType) -> Self {
136 let mut res = self;
137 res.algebraic_type = algebraic_type;
138 res
139 }
140
141 fn with_new_batch_size(self, batch_size: u32) -> Self {
142 let mut res = self;
143 res.batch_size = batch_size;
144 res
145 }
146}
147
148impl GateLevel {
149 fn next(&self, comm_rounds: usize) -> GateLevel {
150 if comm_rounds > 0 {
151 GateLevel {
152 comm_level: self.comm_level + comm_rounds,
153 level: 0,
154 }
155 } else {
156 GateLevel {
157 comm_level: self.comm_level,
158 level: self.level + 1,
159 }
160 }
161 }
162
163 pub fn comm_level(&self) -> usize {
164 self.comm_level
165 }
166}
167
168impl<C: Curve> Circuit<C> {
169 pub fn new() -> Self {
170 Self::default()
171 }
172
173 pub fn add_gate(&mut self, gate: Gate<C>) -> Result<GateIndex, CircuitError<C>> {
177 self.validate_gate(&gate)?;
178
179 let index = self.nb_gates();
180 if index == GateIndex::MAX {
181 return Err(CircuitError::CircuitTooBig);
182 }
183
184 let gate_output = self.comp_gate_output(&gate);
185 let level = self.comp_gate_level(&gate);
186
187 if gate.is_input() {
188 self.inputs.push(index);
189 }
190 self.gates.push(GateExt::new(gate, gate_output?, level));
191
192 Ok(index)
193 }
194
195 pub fn add_output(&mut self, index: GateIndex) -> Result<(), CircuitError<C>> {
199 if index < self.nb_gates() {
200 self.outputs.push(index);
201 Ok(())
202 } else {
203 Err(CircuitError::GateIndexOutOfBounds(index, self.nb_gates()))
204 }
205 }
206
207 pub fn nb_gates(&self) -> GateIndex {
208 self.gates.len() as GateIndex
209 }
210
211 pub fn nb_inputs(&self) -> GateIndex {
212 self.inputs.len() as GateIndex
213 }
214
215 pub fn nb_outputs(&self) -> GateIndex {
216 self.outputs.len() as GateIndex
217 }
218
219 pub fn into_gates(self) -> Vec<GateExt<C>> {
221 self.gates
222 }
223
224 pub fn iter_gates_ext(
225 &self,
226 ) -> impl ExactSizeIterator<Item = &GateExt<C>> + DoubleEndedIterator {
227 self.gates.iter()
228 }
229
230 pub fn iter_gates(&self) -> impl ExactSizeIterator<Item = &Gate<C>> + DoubleEndedIterator {
231 self.gates.iter().map(|g| &g.gate)
232 }
233
234 pub fn iter_output_indices(&self) -> impl ExactSizeIterator<Item = &GateIndex> {
235 self.outputs.iter()
236 }
237
238 pub fn iter_input_indices(&self) -> impl ExactSizeIterator<Item = &GateIndex> {
239 self.inputs.iter()
240 }
241
242 pub fn gate_ext(&self, index: GateIndex) -> Result<&GateExt<C>, CircuitError<C>> {
243 if index < self.nb_gates() {
244 Ok(&self.gates[index as usize])
245 } else {
246 Err(CircuitError::GateIndexOutOfBounds(index, self.nb_gates()))
247 }
248 }
249
250 pub fn gate_ext_unchecked(&self, index: GateIndex) -> &GateExt<C> {
251 &self.gates[index as usize]
252 }
253
254 pub fn gate(&self, index: GateIndex) -> Result<&Gate<C>, CircuitError<C>> {
255 self.gate_ext(index).map(|g| &g.gate)
256 }
257
258 pub fn gate_unchecked(&self, index: GateIndex) -> &Gate<C> {
259 &self.gate_ext_unchecked(index).gate
260 }
261
262 pub fn gate_output(&self, index: GateIndex) -> Result<GateOutput, CircuitError<C>> {
263 self.gate_ext(index).map(|g| g.output)
264 }
265
266 pub fn gate_output_unchecked(&self, index: GateIndex) -> GateOutput {
267 self.gate_ext_unchecked(index).output
268 }
269
270 pub fn gate_level(&self, index: GateIndex) -> Result<GateLevel, CircuitError<C>> {
271 self.gate_ext(index).map(|g| g.level)
272 }
273
274 pub fn gate_level_unchecked(&self, index: GateIndex) -> GateLevel {
275 self.gate_ext_unchecked(index).level
276 }
277}
278
279macro_rules! check_algebraic_type {
280 ($exp_type:expr, $found_type:expr) => {
281 if $exp_type != $found_type {
282 return Err(CircuitError::InvalidGateAlgebraicType {
283 expected: $exp_type,
284 found: $found_type,
285 });
286 }
287 };
288}
289
290impl<C: Curve> Circuit<C> {
291 pub fn open_and_output_scalar(&mut self, x: GateIndex) -> Result<(), CircuitError<C>> {
293 check_algebraic_type!(AlgebraicType::ScalarField, self.gate_output(x)?.get_type());
294 let opening_index = self.add_gate(Gate::FieldShareUnaryOp {
295 x,
296 op: FieldShareUnaryOp::Open,
297 })?;
298 self.add_output(opening_index)
299 }
300
301 pub fn open_and_output_base_field(&mut self, x: GateIndex) -> Result<(), CircuitError<C>> {
303 check_algebraic_type!(AlgebraicType::BaseField, self.gate_output(x)?.get_type());
304 let opening_index = self.add_gate(Gate::FieldShareUnaryOp {
305 x,
306 op: FieldShareUnaryOp::Open,
307 })?;
308 self.add_output(opening_index)
309 }
310
311 pub fn open_and_output_mersenne107(&mut self, x: GateIndex) -> Result<(), CircuitError<C>> {
313 check_algebraic_type!(AlgebraicType::Mersenne107, self.gate_output(x)?.get_type());
314 let opening_index = self.add_gate(Gate::FieldShareUnaryOp {
315 x,
316 op: FieldShareUnaryOp::Open,
317 })?;
318 self.add_output(opening_index)
319 }
320
321 pub fn open_and_output_point(&mut self, p: GateIndex) -> Result<(), CircuitError<C>> {
323 check_algebraic_type!(AlgebraicType::Point, self.gate_output(p)?.get_type());
324 let opening_index = self.add_gate(Gate::PointShareUnaryOp {
325 p,
326 op: PointShareUnaryOp::Open,
327 })?;
328 self.add_output(opening_index)
329 }
330
331 pub fn open_and_output_bit(&mut self, x: GateIndex) -> Result<(), CircuitError<C>> {
333 check_algebraic_type!(AlgebraicType::Bit, self.gate_output(x)?.get_type());
334 let opening_index = self.add_gate(Gate::BitShareUnaryOp {
335 x,
336 op: BitShareUnaryOp::Open,
337 })?;
338 self.add_output(opening_index)
339 }
340}
341
342impl<C: Curve> Circuit<C> {
343 fn validate_gate(&self, gate: &Gate<C>) -> Result<(), CircuitError<C>> {
351 macro_rules! check_op {
352 ($msg:expr, $gate:expr => $($val1:expr, $op:tt, $val2:expr);+$(;)?) => {
353 $(if !($val1 $op $val2) {
354 return Err(CircuitError::InvalidGate(
355 $gate.clone(),
356 format!("{}: {:?} {:?} {:?}", $msg, $val1, stringify!($op), $val2),
357 ));
358 })+
359 };
360 }
361
362 macro_rules! check_gate_properties {
363 ($gate:expr, $($func:ident),* $(,)?) => {
364 $(if !($gate.output.$func()) {
365 return Err(CircuitError::InvalidGate(
366 $gate.gate.clone(),
367 format!("{:?} fails - {}", $gate.output, stringify!($func)))); }
368 )*
369 };
370 }
371
372 match gate {
373 Gate::Input(input) => {
374 check_op!(
375 "input batch size must be non-zero",
376 gate =>
377 0, <, input.batch_size();
378 );
379 }
380 Gate::Constant(constant) => {
381 check_op!(
382 "constant batch size must be non-zero",
383 gate =>
384 0, <, constant.batch_size()?;
385 );
386 }
387 Gate::Random { batch_size, .. } => {
388 check_op!(
389 "random batch size must be non-zero",
390 gate =>
391 0, <, *batch_size;
392 );
393 }
394 Gate::FieldShareUnaryOp { x, .. } => {
395 check_gate_properties!(self.gate_ext(*x)?, is_field, is_share);
396 }
397 Gate::FieldShareBinaryOp { x, y, .. } => {
398 let (gx, gy) = (self.gate_ext(*x)?, self.gate_ext(*y)?);
399 check_gate_properties!(gx, is_field, is_share);
400 check_gate_properties!(gy, is_field);
401 check_op!(
402 "inputs must have same batch-size and field type",
403 gate =>
404 gx.output.batch_size, ==, gy.output.batch_size;
405 gx.output.algebraic_type, ==, gy.output.algebraic_type
406 );
407 }
408 Gate::BatchSummation { x } => {
409 self.gate_ext(*x)?;
410 }
411 Gate::BitShareUnaryOp { x, .. } => {
412 check_gate_properties!(self.gate_ext(*x)?, is_bit, is_share);
413 }
414 Gate::BitShareBinaryOp { x, y, .. } => {
415 let (gx, gy) = (self.gate_ext(*x)?, self.gate_ext(*y)?);
416 check_gate_properties!(gx, is_bit, is_share);
417 check_gate_properties!(gy, is_bit);
418 check_op!(
419 "inputs must have same batch-size",
420 gate =>
421 gx.output.batch_size, ==, gy.output.batch_size
422 );
423 }
424 Gate::PointShareUnaryOp { p: x, .. } => {
425 check_gate_properties!(self.gate_ext(*x)?, is_point, is_share);
426 }
427 Gate::PointShareBinaryOp { p: x, y, op } => {
428 let (gx, gy) = (self.gate_ext(*x)?, self.gate_ext(*y)?);
429 if gx.output.is_plaintext() && gy.output.is_plaintext() {
430 return Err(CircuitError::InvalidGate(
431 gate.clone(),
432 "at least one input must be share".to_string(),
433 ));
434 }
435 check_gate_properties!(gx, is_point);
436 match op {
437 PointShareBinaryOp::Add => {
438 check_gate_properties!(gy, is_point);
439 }
440 PointShareBinaryOp::ScalarMul => {
441 check_gate_properties!(gy, is_scalar_field);
442 }
443 };
444 check_op!(
445 "inputs must have same batch-size",
446 gate =>
447 gx.output.batch_size, ==, gy.output.batch_size
448 );
449 }
450 Gate::FieldPlaintextUnaryOp { x, .. } => {
451 check_gate_properties!(self.gate_ext(*x)?, is_field, is_plaintext);
452 }
453 Gate::FieldPlaintextBinaryOp { x, y, .. } => {
454 let (gx, gy) = (self.gate_ext(*x)?, self.gate_ext(*y)?);
455 check_gate_properties!(gx, is_field, is_plaintext);
456 check_gate_properties!(gy, is_field, is_plaintext);
457 check_op!(
458 "inputs must have same field type",
459 gate =>
460 gx.output.algebraic_type, ==, gy.output.algebraic_type;
461 gx.output.batch_size, ==, gy.output.batch_size
462 );
463 }
464 Gate::BitPlaintextUnaryOp { x, .. } => {
465 check_gate_properties!(self.gate_ext(*x)?, is_bit, is_plaintext);
466 }
467 Gate::BitPlaintextBinaryOp { x, y, .. } => {
468 let (gx, gy) = (self.gate_ext(*x)?, self.gate_ext(*y)?);
469 check_gate_properties!(gx, is_bit, is_plaintext);
470 check_gate_properties!(gy, is_bit, is_plaintext);
471 check_op!(
472 "inputs must have same batch-size",
473 gate =>
474 gx.output.batch_size, ==, gy.output.batch_size
475 );
476 }
477 Gate::PointPlaintextUnaryOp { p: x, .. } => {
478 check_gate_properties!(self.gate_ext(*x)?, is_point, is_plaintext);
479 }
480 Gate::PointPlaintextBinaryOp { p: x, y, op } => {
481 let (gx, gy) = (self.gate_ext(*x)?, self.gate_ext(*y)?);
482 check_gate_properties!(gx, is_point, is_plaintext);
483 match op {
484 PointPlaintextBinaryOp::Add => {
485 check_gate_properties!(gy, is_point, is_plaintext);
486 }
487 PointPlaintextBinaryOp::ScalarMul => {
488 check_gate_properties!(gy, is_scalar_field, is_plaintext);
489 }
490 }
491 check_op!(
492 "inputs must have same batch-size",
493 gate =>
494 gx.output.batch_size, ==, gy.output.batch_size
495 );
496 }
497 Gate::DaBit { batch_size, .. } => {
498 check_op!(
499 "input batch size must be non-zero",
500 gate =>
501 0, <, *batch_size
502 );
503 }
504 Gate::GetDaBitFieldShare { x, .. } => {
505 check_gate_properties!(self.gate_ext(*x)?, is_field, is_share);
507 }
508 Gate::GetDaBitSharedBit { x, .. } => {
509 check_gate_properties!(self.gate_ext(*x)?, is_field, is_share);
511 }
512 Gate::BaseFieldPow { x, .. } => {
513 check_gate_properties!(self.gate_ext(*x)?, is_base_field, is_share);
514 }
515 Gate::BitPlaintextToField { x, .. } => {
516 check_gate_properties!(self.gate_ext(*x)?, is_bit, is_plaintext);
517 }
518 Gate::FieldPlaintextToBit { x, .. } => {
519 check_gate_properties!(self.gate_ext(*x)?, is_field, is_plaintext);
520 }
521 Gate::ExtractFromBatch { x, slice, .. } => {
522 let gx = self.gate_ext(*x)?;
523 if slice.is_empty() {
524 return Err(CircuitError::InvalidGate(
525 gate.clone(),
526 format!("slice must be non-empty: {slice:?}"),
527 ));
528 }
529 if slice.get_indices()
530 .into_iter()
531 .max()
532 .expect("non-empty slice expected") >= gx.output.batch_size
534 {
535 return Err(CircuitError::InvalidGate(
536 gate.clone(),
537 format!("slice indices out-of-range: {slice:?}"),
538 ));
539 }
540 }
541 Gate::CollectToBatch { wires } => {
542 check_op!("expected at least one input", gate => 0, <, wires.len());
543 let first = self.gate_ext(wires[0])?.output;
544 for x in wires.iter().skip(1) {
545 let gx = self.gate_ext(*x)?.output;
546 check_op!(
547 "all inputs must have the same type",
548 gate =>
549 first.algebraic_type, ==, gx.algebraic_type;
550 first.form, ==, gx.form
551 );
552 }
553 }
554 Gate::PointFromPlaintextCoordinates { wires } => {
555 check_op!(
556 "expected one input per point coordinate",
557 gate => wires.len(), ==, NumCoordinates::<C>::USIZE
558 );
559 for x in wires {
560 let gx = self.gate_ext(*x)?;
561 check_gate_properties!(gx, is_base_field, is_plaintext);
562 check_op!("expected batch-size 1", gate => gx.output.batch_size, ==, 1);
563 }
564 }
565 Gate::PlaintextPointToCoordinates { point: x, .. } => {
566 let gx = self.gate_ext(*x)?;
567 check_gate_properties!(gx, is_point, is_plaintext);
568 check_op!("expected batch-size 1", gate => gx.output.batch_size, ==, 1);
569 }
570 Gate::PlaintextKeccakF1600 { x } => {
571 let gx = self.gate_ext(*x)?;
572 check_gate_properties!(gx, is_bit, is_plaintext);
573 check_op!("expected batch-size 1600", gate => gx.output.batch_size, ==, 1600);
574 }
575 Gate::CompressPlaintextPoint { point: x, .. } => {
576 let gx = self.gate_ext(*x)?;
577 check_gate_properties!(gx, is_point, is_plaintext);
578 check_op!("expected batch-size 1", gate => gx.output.batch_size, ==, 1);
579 }
580 Gate::KeyRecoveryPlaintextComputeErrors {
581 d_minus_one,
582 syndromes,
583 } => {
584 let g1 = self.gate_ext(*d_minus_one)?;
585 let g2 = self.gate_ext(*syndromes)?;
586 check_gate_properties!(g1, is_base_field, is_plaintext);
587 check_gate_properties!(g2, is_base_field, is_plaintext);
588
589 check_op!("expected batch-size 1", gate => g1.output.batch_size, ==, 1);
590 check_op!(format!("expected batch-size {}", MXE_KEY_RECOVERY_D - 1),
592 gate => g2.output.batch_size, ==, MXE_KEY_RECOVERY_D as u32 - 1);
593 }
594 #[cfg(any(test, feature = "dev"))]
595 Gate::AesKeySchedule { key } => {
596 let g = self.gate_ext(*key)?;
597 check_gate_properties!(g, is_bit, is_share);
598 let key_length = g.output.batch_size;
599 check_op!("key_length: expected 128, 192 or 256", gate => matches!(key_length, 128 | 192 | 256), ==, true);
600 }
601 Gate::AesGcmKeyStream {
602 round_keys,
603 iv,
604 n_ciphertext_blocks,
605 } => {
606 let g1 = self.gate_ext(*round_keys)?;
607 let g2 = self.gate_ext(*iv)?;
608 check_gate_properties!(g1, is_bit, is_share);
609 check_gate_properties!(g2, is_bit, is_plaintext);
610 let is_multiple = g1.output.batch_size.is_multiple_of(128);
611 check_op!("round_keys: expected batch-size multiple of 128", gate => is_multiple, ==, true);
612 let n_round_key_blocks = g1.output.batch_size / 128;
613 check_op!("round_keys: expected 11, 13 or 15 128-bit round_keys", gate => matches!(n_round_key_blocks, 11 | 13 | 15), ==, true);
615 check_op!("IV: expected batch-size 96", gate => g2.output.batch_size, ==, 96);
616 check_op!("n_ciphertext_blocks: expected at most 2^24 - 1", gate => *n_ciphertext_blocks, <, 1u32 << 24);
620 }
621 }
622
623 Ok(())
624 }
625
626 fn comp_gate_output(&self, gate: &Gate<C>) -> Result<GateOutput, CircuitError<C>> {
630 let r = match gate {
631 Gate::Input(input_type) => GateOutput {
632 batch_size: input_type.batch_size(),
633 algebraic_type: input_type.algebraic_type(),
634 form: input_type.share_or_plaintext(),
635 },
636
637 Gate::Constant(const_type) => GateOutput {
638 batch_size: const_type.batch_size()?,
639 algebraic_type: const_type.algebraic_type(),
640 form: ShareOrPlaintext::Plaintext,
641 },
642
643 Gate::Random {
644 algebraic_type,
645 batch_size,
646 } => GateOutput {
647 batch_size: *batch_size,
648 algebraic_type: *algebraic_type,
649 form: ShareOrPlaintext::Share,
650 },
651
652 Gate::FieldShareUnaryOp { x, op } => match op {
653 FieldShareUnaryOp::Neg | FieldShareUnaryOp::MulInverse => {
654 self.gate_output_unchecked(*x)
655 }
656 FieldShareUnaryOp::Open | FieldShareUnaryOp::IsZero => self
657 .gate_output_unchecked(*x)
658 .with_new_form(ShareOrPlaintext::Plaintext),
659 },
660
661 Gate::FieldShareBinaryOp { x, .. }
662 | Gate::BitShareBinaryOp { x, .. }
663 | Gate::FieldPlaintextUnaryOp { x, .. }
664 | Gate::FieldPlaintextBinaryOp { x, .. }
665 | Gate::BitPlaintextUnaryOp { x, .. }
666 | Gate::BitPlaintextBinaryOp { x, .. }
667 | Gate::PointPlaintextUnaryOp { p: x, .. }
668 | Gate::PointPlaintextBinaryOp { p: x, .. }
669 | Gate::GetDaBitFieldShare { x, .. }
670 | Gate::BaseFieldPow { x, .. } => self.gate_output_unchecked(*x),
671
672 Gate::BatchSummation { x, .. } => self.gate_output_unchecked(*x).with_new_batch_size(1),
673
674 Gate::PointShareBinaryOp { p: x, .. } => self
675 .gate_output_unchecked(*x)
676 .with_new_form(ShareOrPlaintext::Share),
677
678 Gate::BitShareUnaryOp { x, op } => match op {
679 BitShareUnaryOp::Not => self.gate_output_unchecked(*x),
680 BitShareUnaryOp::Open => self
681 .gate_output_unchecked(*x)
682 .with_new_form(ShareOrPlaintext::Plaintext),
683 },
684
685 Gate::PointShareUnaryOp { p: x, op } => match op {
686 PointShareUnaryOp::Neg => self.gate_output_unchecked(*x),
687 PointShareUnaryOp::Open => self
688 .gate_output_unchecked(*x)
689 .with_new_form(ShareOrPlaintext::Plaintext),
690 PointShareUnaryOp::IsZero => self
691 .gate_output_unchecked(*x)
692 .with_new_form(ShareOrPlaintext::Plaintext)
693 .with_new_type(AlgebraicType::ScalarField),
694 },
695
696 Gate::DaBit {
697 field_type,
698 batch_size,
699 } => GateOutput {
700 batch_size: *batch_size,
701 algebraic_type: AlgebraicType::from(*field_type),
702 form: ShareOrPlaintext::Share,
703 },
704
705 Gate::GetDaBitSharedBit { x, .. } => self
706 .gate_output_unchecked(*x)
707 .with_new_type(AlgebraicType::Bit),
708
709 Gate::BitPlaintextToField { x, field_type } => self
710 .gate_output_unchecked(*x)
711 .with_new_type(AlgebraicType::from(*field_type)),
712
713 Gate::FieldPlaintextToBit { x } => self
714 .gate_output_unchecked(*x)
715 .with_new_type(AlgebraicType::Bit),
716
717 Gate::ExtractFromBatch { x, slice } => self
718 .gate_output_unchecked(*x)
719 .with_new_batch_size(slice.len()),
720
721 Gate::CollectToBatch { wires, .. } => {
722 let batch_size = wires
723 .iter()
724 .map(|x| self.gate_output_unchecked(*x).batch_size)
725 .sum();
726 self.gate_output_unchecked(wires[0])
727 .with_new_batch_size(batch_size)
728 }
729
730 Gate::PointFromPlaintextCoordinates { .. } => GateOutput {
731 algebraic_type: AlgebraicType::Point,
732 form: ShareOrPlaintext::Plaintext,
733 batch_size: 1,
734 },
735 Gate::PlaintextPointToCoordinates { .. } => GateOutput {
736 algebraic_type: AlgebraicType::BaseField,
737 form: ShareOrPlaintext::Plaintext,
738 batch_size: NumCoordinates::<C>::U32,
739 },
740 Gate::PlaintextKeccakF1600 { .. } => GateOutput {
741 algebraic_type: AlgebraicType::Bit,
742 form: ShareOrPlaintext::Plaintext,
743 batch_size: 1600,
744 },
745 Gate::CompressPlaintextPoint { .. } => GateOutput {
746 algebraic_type: AlgebraicType::Bit,
747 form: ShareOrPlaintext::Plaintext,
748 batch_size: 256,
749 },
750 Gate::KeyRecoveryPlaintextComputeErrors { .. } => GateOutput {
751 algebraic_type: AlgebraicType::BaseField,
752 form: ShareOrPlaintext::Plaintext,
753 batch_size: MXE_KEY_RECOVERY_N as u32,
754 },
755 #[cfg(any(test, feature = "dev"))]
756 Gate::AesKeySchedule { key } => {
757 let key_length = self.gate_output_unchecked(*key).batch_size;
758 let n_round_key_bits = match key_length {
759 128 => 11 * 128,
760 192 => 13 * 128,
761 256 => 15 * 128,
762 _ => {
763 panic!("key length {key_length} does not match any of 128, 192 or 256");
765 }
766 };
767 GateOutput {
768 algebraic_type: AlgebraicType::Bit,
769 form: ShareOrPlaintext::Share,
770 batch_size: n_round_key_bits,
771 }
772 }
773 Gate::AesGcmKeyStream {
774 n_ciphertext_blocks,
775 ..
776 } => GateOutput {
777 algebraic_type: AlgebraicType::Bit,
778 form: ShareOrPlaintext::Share,
779 batch_size: (1 + n_ciphertext_blocks) * 128,
780 },
781 };
782
783 Ok(r)
784 }
785
786 fn comp_gate_comm_rounds(&self, gate: &Gate<C>) -> usize {
790 match gate {
791 Gate::Input(input_type) => match input_type {
792 Input::SecretPlaintext { .. } => 1,
793 _ => 0,
794 },
795
796 Gate::Constant(_) | Gate::Random { .. } => 0,
797
798 Gate::FieldShareUnaryOp { op, .. } => match op {
799 FieldShareUnaryOp::Neg => 0,
800 FieldShareUnaryOp::MulInverse => 2,
801 FieldShareUnaryOp::Open => 1,
802 FieldShareUnaryOp::IsZero => 2,
803 },
804 Gate::FieldShareBinaryOp { op, y, .. } => {
805 match (op, self.gate_output_unchecked(*y).form) {
806 (FieldShareBinaryOp::Mul, ShareOrPlaintext::Share) => 1,
807 (FieldShareBinaryOp::Mul, ShareOrPlaintext::Plaintext)
808 | (FieldShareBinaryOp::Add, _) => 0,
809 }
810 }
811 Gate::BatchSummation { .. } => 0,
812 Gate::BitShareUnaryOp { op, .. } => match op {
813 BitShareUnaryOp::Not => 0,
814 BitShareUnaryOp::Open => 1,
815 },
816 Gate::BitShareBinaryOp { op, y, .. } => {
817 match (op, self.gate_output_unchecked(*y).form) {
818 (BitShareBinaryOp::Xor, _) => 0,
819 (_, ShareOrPlaintext::Share) => 1,
820 (_, ShareOrPlaintext::Plaintext) => 0,
821 }
822 }
823 Gate::PointShareUnaryOp { op, .. } => match op {
824 PointShareUnaryOp::Neg => 0,
825 PointShareUnaryOp::Open => 1,
826 PointShareUnaryOp::IsZero => 2,
827 },
828 Gate::PointShareBinaryOp { y, op, .. } => {
829 match (op, self.gate_output_unchecked(*y).form) {
830 (PointShareBinaryOp::Add, _) => 0,
831 (PointShareBinaryOp::ScalarMul, ShareOrPlaintext::Share) => 1,
832 (PointShareBinaryOp::ScalarMul, ShareOrPlaintext::Plaintext) => 0,
833 }
834 }
835
836 Gate::BaseFieldPow { .. } => 2,
837
838 Gate::FieldPlaintextUnaryOp { .. }
839 | Gate::FieldPlaintextBinaryOp { .. }
840 | Gate::BitPlaintextUnaryOp { .. }
841 | Gate::BitPlaintextBinaryOp { .. }
842 | Gate::PointPlaintextUnaryOp { .. }
843 | Gate::PointPlaintextBinaryOp { .. }
844 | Gate::DaBit { .. }
845 | Gate::GetDaBitFieldShare { .. }
846 | Gate::GetDaBitSharedBit { .. }
847 | Gate::BitPlaintextToField { .. }
848 | Gate::FieldPlaintextToBit { .. }
849 | Gate::ExtractFromBatch { .. }
850 | Gate::CollectToBatch { .. }
851 | Gate::PointFromPlaintextCoordinates { .. }
852 | Gate::PlaintextPointToCoordinates { .. }
853 | Gate::PlaintextKeccakF1600 { .. }
854 | Gate::CompressPlaintextPoint { .. }
855 | Gate::KeyRecoveryPlaintextComputeErrors { .. } => 0,
856 #[cfg(any(test, feature = "dev"))]
857 Gate::AesKeySchedule { key, .. } => {
858 let key_length = self.gate_ext(*key).map(|g| g.output.batch_size);
859 let n_sub_bytes_calls = match key_length {
860 Ok(128) => 10,
861 Ok(192) => 8,
862 Ok(256) => 13,
863 _ => panic!("Invalid key length"),
864 };
865 n_sub_bytes_calls * AES_S_BOX_N_NETWORK_ROUNDS
866 }
867
868 Gate::AesGcmKeyStream { round_keys, .. } => {
869 let round_keys_length = self.gate_ext(*round_keys).map(|g| g.output.batch_size);
870 let n_rounds = match round_keys_length {
873 Ok(1408) => 10,
874 Ok(1664) => 12,
875 Ok(1920) => 14,
876 _ => panic!("Invalid round keys length"),
877 };
878 (1 + n_rounds) * AES_S_BOX_N_NETWORK_ROUNDS
881 }
882 }
883 }
884
885 fn comp_gate_level(&self, gate: &Gate<C>) -> GateLevel {
889 let comm_rounds = self.comp_gate_comm_rounds(gate);
890 match gate
891 .get_inputs()
892 .iter()
893 .map(|pred| self.gate_level_unchecked(*pred))
894 .max()
895 {
896 None => GateLevel::default(),
897 Some(preds_level) => preds_level.next(comm_rounds),
898 }
899 }
900
901 #[cfg(feature = "dev")]
902 pub fn remove_secret_shared_inputs(&mut self) {
903 let mut inputs_to_remove = std::collections::HashSet::new();
904 for (idx, gate) in self.gates.iter_mut().enumerate() {
905 if let &Gate::Input(Input::Share {
906 algebraic_type,
907 batch_size,
908 }) = &gate.gate
909 {
910 gate.gate = Gate::Random {
911 algebraic_type,
912 batch_size,
913 };
914 inputs_to_remove.insert(idx as GateIndex);
915 }
916 }
917 self.inputs.retain(|gate| !inputs_to_remove.contains(gate));
918 }
919}
920
921#[cfg(test)]
922mod tests {
923 use primitives::algebra::elliptic_curve::Curve25519Ristretto as C;
924
925 use crate::circuit::{AlgebraicType, Circuit, FieldShareBinaryOp, Gate, Input};
926
927 #[test]
928 fn test_circuit_new() {
929 let mut circuit = Circuit::<C>::new();
930
931 let x = circuit
932 .add_gate(Gate::Input(Input::SecretPlaintext {
933 inputer: 0,
934 algebraic_type: AlgebraicType::Mersenne107,
935 batch_size: 3,
936 }))
937 .unwrap();
938
939 let y = circuit
940 .add_gate(Gate::Input(Input::SecretPlaintext {
941 inputer: 0,
942 algebraic_type: AlgebraicType::Mersenne107,
943 batch_size: 3,
944 }))
945 .unwrap();
946
947 let z = circuit
948 .add_gate(Gate::FieldShareBinaryOp {
949 x,
950 y,
951 op: FieldShareBinaryOp::Mul,
952 })
953 .unwrap();
954
955 circuit.add_output(z).unwrap();
956
957 assert_eq!(circuit.nb_inputs(), 2);
958 assert_eq!(circuit.nb_gates(), 2 + 1);
959 assert_eq!(circuit.nb_outputs(), 1);
960 }
961}