1use std::hash::Hash;
2
3use primitives::algebra::{
4 elliptic_curve::{Point, Scalar},
5 BoxedUint,
6};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 circuit::{
11 errors::CircuitError,
12 AlgebraicType,
13 BatchSize,
14 BitPlaintextBinaryOp,
15 BitPlaintextUnaryOp,
16 BitShareBinaryOp,
17 BitShareUnaryOp,
18 Constant,
19 ConstraintClause,
20 FieldPlaintextBinaryOp,
21 FieldPlaintextUnaryOp,
22 FieldShareBinaryOp,
23 FieldShareUnaryOp,
24 FieldType,
25 GateIndex,
26 Input,
27 OnAmbiguity,
28 PointPlaintextBinaryOp,
29 PointPlaintextUnaryOp,
30 PointShareBinaryOp,
31 PointShareUnaryOp,
32 Slice,
33 },
34 config::MpcConfig,
35};
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
39#[serde(bound(
40 serialize = "Scalar<C::Curve>: Serialize, Point<C::Curve>: Serialize",
41 deserialize = "Scalar<C::Curve>: Deserialize<'de>, Point<C::Curve>: Deserialize<'de>"
42))]
43#[repr(C)]
44pub enum Gate<C: MpcConfig> {
45 Input(Input),
47 Constant(Constant<C>),
49 Random {
51 algebraic_type: AlgebraicType,
52 batch_size: BatchSize,
53 },
54 FieldShareUnaryOp {
56 x: GateIndex,
57 op: FieldShareUnaryOp,
58 },
59 FieldShareBinaryOp {
61 x: GateIndex,
62 y: GateIndex,
63 op: FieldShareBinaryOp,
64 },
65 BatchSummation {
66 x: GateIndex,
67 },
68 BitShareUnaryOp {
69 x: GateIndex,
70 op: BitShareUnaryOp,
71 },
72 BitShareBinaryOp {
73 x: GateIndex,
74 y: GateIndex,
75 op: BitShareBinaryOp,
76 },
77 PointShareUnaryOp {
79 p: GateIndex,
80 op: PointShareUnaryOp,
81 },
82 PointShareBinaryOp {
83 p: GateIndex,
84 y: GateIndex,
85 op: PointShareBinaryOp,
86 },
87 FieldPlaintextUnaryOp {
89 x: GateIndex,
90 op: FieldPlaintextUnaryOp,
91 },
92 FieldPlaintextBinaryOp {
94 x: GateIndex,
95 y: GateIndex,
96 op: FieldPlaintextBinaryOp,
97 },
98 BitPlaintextUnaryOp {
99 x: GateIndex,
100 op: BitPlaintextUnaryOp,
101 },
102 BitPlaintextBinaryOp {
103 x: GateIndex,
104 y: GateIndex,
105 op: BitPlaintextBinaryOp,
106 },
107 PointPlaintextUnaryOp {
108 p: GateIndex,
109 op: PointPlaintextUnaryOp,
110 },
111 PointPlaintextBinaryOp {
112 p: GateIndex,
113 y: GateIndex,
114 op: PointPlaintextBinaryOp,
115 },
116 DaBit {
118 field_type: FieldType,
119 batch_size: BatchSize,
120 },
121 GetDaBitFieldShare {
122 x: GateIndex,
123 },
124 GetDaBitSharedBit {
125 x: GateIndex,
126 },
127 BaseFieldPow {
129 x: GateIndex,
130 exp: BoxedUint,
131 },
132 BitPlaintextToField {
134 x: GateIndex,
135 field_type: FieldType,
136 },
137 FieldPlaintextToBit {
138 x: GateIndex,
139 },
140 ExtractFromBatch {
142 x: GateIndex,
143 slice: Slice,
144 },
145 CollectToBatch {
146 wires: Vec<GateIndex>,
147 },
148 PointFromPlaintextCoordinates {
149 wires: Vec<GateIndex>,
150 },
151 PlaintextPointToCoordinates {
152 point: GateIndex,
153 },
154 PlaintextKeccakF1600 {
155 x: GateIndex,
156 },
157 CompressPlaintextPoint {
158 point: GateIndex,
159 },
160 KeyRecoveryPlaintextComputeErrors {
161 d_minus_one: GateIndex,
162 syndromes: GateIndex,
163 },
164 AesGcmKeyStream {
165 round_keys: GateIndex,
166 iv: GateIndex,
167 n_ciphertext_blocks: u32,
168 },
169 GhashPowersOfH {
170 h: GateIndex,
171 n_ciphertext_blocks: u32,
172 },
173 Ghash {
174 x: GateIndex,
175 powers_of_h: GateIndex,
176 },
177 AesKeySchedule {
179 key: GateIndex,
180 },
181 ConstrainPlaintextBits {
201 x: GateIndex,
202 clauses: Vec<ConstraintClause>,
203 on_ambiguity: OnAmbiguity,
206 },
207}
208
209impl<C: MpcConfig> Gate<C> {
210 pub fn is_input(&self) -> bool {
212 matches!(self, Gate::Input { .. })
213 }
214
215 pub fn get_inputs(&self) -> Vec<GateIndex> {
217 match &self {
218 Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => {
219 Vec::new()
220 }
221
222 Gate::FieldShareUnaryOp { x, .. }
223 | Gate::BatchSummation { x, .. }
224 | Gate::BitShareUnaryOp { x, .. }
225 | Gate::PointShareUnaryOp { p: x, .. }
226 | Gate::FieldPlaintextUnaryOp { x, .. }
227 | Gate::BitPlaintextUnaryOp { x, .. }
228 | Gate::PointPlaintextUnaryOp { p: x, .. }
229 | Gate::GetDaBitFieldShare { x, .. }
230 | Gate::GetDaBitSharedBit { x, .. }
231 | Gate::BaseFieldPow { x, .. }
232 | Gate::BitPlaintextToField { x, .. }
233 | Gate::FieldPlaintextToBit { x, .. }
234 | Gate::ExtractFromBatch { x, .. }
235 | Gate::PlaintextPointToCoordinates { point: x, .. }
236 | Gate::CompressPlaintextPoint { point: x, .. }
237 | Gate::PlaintextKeccakF1600 { x }
238 | Gate::GhashPowersOfH { h: x, .. }
239 | Gate::AesKeySchedule { key: x } => {
240 vec![*x]
241 }
242
243 Gate::FieldShareBinaryOp { x, y, .. }
244 | Gate::BitShareBinaryOp { x, y, .. }
245 | Gate::PointShareBinaryOp { p: x, y, .. }
246 | Gate::FieldPlaintextBinaryOp { x, y, .. }
247 | Gate::BitPlaintextBinaryOp { x, y, .. }
248 | Gate::PointPlaintextBinaryOp { p: x, y, .. }
249 | Gate::KeyRecoveryPlaintextComputeErrors {
250 d_minus_one: x,
251 syndromes: y,
252 ..
253 }
254 | Gate::AesGcmKeyStream {
255 round_keys: x,
256 iv: y,
257 ..
258 }
259 | Gate::Ghash { x, powers_of_h: y } => {
260 vec![*x, *y]
261 }
262
263 Gate::CollectToBatch { wires, .. }
264 | Gate::PointFromPlaintextCoordinates { wires, .. } => wires.clone(),
265
266 Gate::ConstrainPlaintextBits { x, clauses, .. } => std::iter::once(*x)
267 .chain(clauses.iter().flat_map(ConstraintClause::wires))
268 .collect(),
269 }
270 }
271
272 pub fn map_inputs<F: FnMut(GateIndex) -> GateIndex>(mut self, mut f: F) -> Self {
274 match &mut self {
275 Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => (),
276
277 Gate::FieldShareUnaryOp { x, .. }
278 | Gate::BatchSummation { x, .. }
279 | Gate::BitShareUnaryOp { x, .. }
280 | Gate::PointShareUnaryOp { p: x, .. }
281 | Gate::FieldPlaintextUnaryOp { x, .. }
282 | Gate::BitPlaintextUnaryOp { x, .. }
283 | Gate::PointPlaintextUnaryOp { p: x, .. }
284 | Gate::GetDaBitFieldShare { x, .. }
285 | Gate::GetDaBitSharedBit { x, .. }
286 | Gate::BaseFieldPow { x, .. }
287 | Gate::BitPlaintextToField { x, .. }
288 | Gate::FieldPlaintextToBit { x, .. }
289 | Gate::ExtractFromBatch { x, .. }
290 | Gate::PlaintextPointToCoordinates { point: x, .. }
291 | Gate::CompressPlaintextPoint { point: x, .. }
292 | Gate::PlaintextKeccakF1600 { x }
293 | Gate::GhashPowersOfH { h: x, .. }
294 | Gate::AesKeySchedule { key: x } => {
295 *x = f(*x);
296 }
297
298 Gate::FieldShareBinaryOp { x, y, .. }
299 | Gate::BitShareBinaryOp { x, y, .. }
300 | Gate::PointShareBinaryOp { p: x, y, .. }
301 | Gate::FieldPlaintextBinaryOp { x, y, .. }
302 | Gate::BitPlaintextBinaryOp { x, y, .. }
303 | Gate::PointPlaintextBinaryOp { p: x, y, .. }
304 | Gate::KeyRecoveryPlaintextComputeErrors {
305 d_minus_one: x,
306 syndromes: y,
307 ..
308 }
309 | Gate::AesGcmKeyStream {
310 round_keys: x,
311 iv: y,
312 ..
313 }
314 | Gate::Ghash { x, powers_of_h: y } => {
315 *x = f(*x);
316 *y = f(*y);
317 }
318
319 Gate::CollectToBatch { wires, .. }
320 | Gate::PointFromPlaintextCoordinates { wires, .. } => {
321 wires.iter_mut().for_each(|x| *x = f(*x))
322 }
323
324 Gate::ConstrainPlaintextBits { x, clauses, .. } => {
325 *x = f(*x);
326 clauses
327 .iter_mut()
328 .flat_map(ConstraintClause::wires_mut)
329 .for_each(|input| *input = f(*input));
330 }
331 };
332
333 self
334 }
335
336 pub fn try_replace_inputs(mut self, inputs: Vec<GateIndex>) -> Result<Self, CircuitError<C>> {
341 if inputs.len() != self.get_inputs().len() {
342 return Err(CircuitError::InvalidGateInputCount {
343 expected: self.get_inputs().len(),
344 found: inputs.len(),
345 });
346 }
347
348 match &mut self {
349 Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => (),
350
351 Gate::FieldShareUnaryOp { x, .. }
352 | Gate::BatchSummation { x, .. }
353 | Gate::BitShareUnaryOp { x, .. }
354 | Gate::PointShareUnaryOp { p: x, .. }
355 | Gate::FieldPlaintextUnaryOp { x, .. }
356 | Gate::BitPlaintextUnaryOp { x, .. }
357 | Gate::PointPlaintextUnaryOp { p: x, .. }
358 | Gate::GetDaBitFieldShare { x, .. }
359 | Gate::GetDaBitSharedBit { x, .. }
360 | Gate::BaseFieldPow { x, .. }
361 | Gate::BitPlaintextToField { x, .. }
362 | Gate::FieldPlaintextToBit { x, .. }
363 | Gate::ExtractFromBatch { x, .. }
364 | Gate::PlaintextPointToCoordinates { point: x, .. }
365 | Gate::CompressPlaintextPoint { point: x, .. }
366 | Gate::PlaintextKeccakF1600 { x }
367 | Gate::GhashPowersOfH { h: x, .. }
368 | Gate::AesKeySchedule { key: x } => {
369 *x = inputs[0];
370 }
371
372 Gate::FieldShareBinaryOp { x, y, .. }
373 | Gate::BitShareBinaryOp { x, y, .. }
374 | Gate::PointShareBinaryOp { p: x, y, .. }
375 | Gate::FieldPlaintextBinaryOp { x, y, .. }
376 | Gate::BitPlaintextBinaryOp { x, y, .. }
377 | Gate::PointPlaintextBinaryOp { p: x, y, .. }
378 | Gate::KeyRecoveryPlaintextComputeErrors {
379 d_minus_one: x,
380 syndromes: y,
381 ..
382 }
383 | Gate::AesGcmKeyStream {
384 round_keys: x,
385 iv: y,
386 ..
387 }
388 | Gate::Ghash { x, powers_of_h: y } => {
389 *x = inputs[0];
390 *y = inputs[1];
391 }
392
393 Gate::CollectToBatch { wires, .. }
394 | Gate::PointFromPlaintextCoordinates { wires, .. } => *wires = inputs,
395
396 Gate::ConstrainPlaintextBits { x, clauses, .. } => {
397 let mut inputs = inputs.into_iter();
398 *x = inputs.next().expect("input count checked above");
401 clauses
402 .iter_mut()
403 .flat_map(ConstraintClause::wires_mut)
404 .zip(inputs)
405 .for_each(|(slot, input)| *slot = input);
406 }
407 };
408
409 Ok(self)
410 }
411}
412
413#[cfg(test)]
414mod tests {
415 use std::collections::HashSet;
416
417 use primitives::utils::codec::bincode_io;
418
419 use super::*;
420 use crate::{circuit::FieldShareBinaryOp, config::DefaultConfig as C};
421
422 #[test]
423 fn test_ser_gate() {
424 let scalar_gate: Gate<C> = Gate::FieldShareBinaryOp {
425 x: 1,
426 y: 3,
427 op: FieldShareBinaryOp::Add,
428 };
429 let point_gate: Gate<C> = Gate::PointShareBinaryOp {
430 p: 1,
431 y: 3,
432 op: PointShareBinaryOp::Add,
433 };
434
435 let scalar_gate_ser = bincode_io::serialize(&scalar_gate).unwrap();
436 let point_gate_ser = bincode_io::serialize(&point_gate).unwrap();
437
438 let scalar_gate_de: Gate<C> = bincode_io::deserialize(&scalar_gate_ser).unwrap();
439 let point_gate_de: Gate<C> = bincode_io::deserialize(&point_gate_ser).unwrap();
440
441 assert_eq!(scalar_gate, scalar_gate_de);
442 assert_eq!(point_gate, point_gate_de);
443 let set = HashSet::from([scalar_gate, scalar_gate_de, point_gate, point_gate_de]);
444 assert_eq!(set.len(), 2)
445 }
446}