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