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 FieldPlaintextBinaryOp,
20 FieldPlaintextUnaryOp,
21 FieldShareBinaryOp,
22 FieldShareUnaryOp,
23 FieldType,
24 GateIndex,
25 Input,
26 PointPlaintextBinaryOp,
27 PointPlaintextUnaryOp,
28 PointShareBinaryOp,
29 PointShareUnaryOp,
30 Slice,
31 },
32 config::MpcConfig,
33};
34
35#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[serde(bound(
38 serialize = "Scalar<C::Curve>: Serialize, Point<C::Curve>: Serialize",
39 deserialize = "Scalar<C::Curve>: Deserialize<'de>, Point<C::Curve>: Deserialize<'de>"
40))]
41#[repr(C)]
42pub enum Gate<C: MpcConfig> {
43 Input(Input),
45 Constant(Constant<C>),
47 Random {
49 algebraic_type: AlgebraicType,
50 batch_size: BatchSize,
51 },
52 FieldShareUnaryOp {
54 x: GateIndex,
55 op: FieldShareUnaryOp,
56 },
57 FieldShareBinaryOp {
59 x: GateIndex,
60 y: GateIndex,
61 op: FieldShareBinaryOp,
62 },
63 BatchSummation {
64 x: GateIndex,
65 },
66 BitShareUnaryOp {
67 x: GateIndex,
68 op: BitShareUnaryOp,
69 },
70 BitShareBinaryOp {
71 x: GateIndex,
72 y: GateIndex,
73 op: BitShareBinaryOp,
74 },
75 PointShareUnaryOp {
77 p: GateIndex,
78 op: PointShareUnaryOp,
79 },
80 PointShareBinaryOp {
81 p: GateIndex,
82 y: GateIndex,
83 op: PointShareBinaryOp,
84 },
85 FieldPlaintextUnaryOp {
87 x: GateIndex,
88 op: FieldPlaintextUnaryOp,
89 },
90 FieldPlaintextBinaryOp {
92 x: GateIndex,
93 y: GateIndex,
94 op: FieldPlaintextBinaryOp,
95 },
96 BitPlaintextUnaryOp {
97 x: GateIndex,
98 op: BitPlaintextUnaryOp,
99 },
100 BitPlaintextBinaryOp {
101 x: GateIndex,
102 y: GateIndex,
103 op: BitPlaintextBinaryOp,
104 },
105 PointPlaintextUnaryOp {
106 p: GateIndex,
107 op: PointPlaintextUnaryOp,
108 },
109 PointPlaintextBinaryOp {
110 p: GateIndex,
111 y: GateIndex,
112 op: PointPlaintextBinaryOp,
113 },
114 DaBit {
116 field_type: FieldType,
117 batch_size: BatchSize,
118 },
119 GetDaBitFieldShare {
120 x: GateIndex,
121 },
122 GetDaBitSharedBit {
123 x: GateIndex,
124 },
125 BaseFieldPow {
127 x: GateIndex,
128 exp: BoxedUint,
129 },
130 BitPlaintextToField {
132 x: GateIndex,
133 field_type: FieldType,
134 },
135 FieldPlaintextToBit {
136 x: GateIndex,
137 },
138 ExtractFromBatch {
140 x: GateIndex,
141 slice: Slice,
142 },
143 CollectToBatch {
144 wires: Vec<GateIndex>,
145 },
146 PointFromPlaintextCoordinates {
147 wires: Vec<GateIndex>,
148 },
149 PlaintextPointToCoordinates {
150 point: GateIndex,
151 },
152 PlaintextKeccakF1600 {
153 x: GateIndex,
154 },
155 CompressPlaintextPoint {
156 point: GateIndex,
157 },
158 KeyRecoveryPlaintextComputeErrors {
159 d_minus_one: GateIndex,
160 syndromes: GateIndex,
161 },
162 AesGcmKeyStream {
163 round_keys: GateIndex,
164 iv: GateIndex,
165 n_ciphertext_blocks: u32,
166 },
167 GhashPowersOfH {
168 h: GateIndex,
169 n_ciphertext_blocks: u32,
170 },
171 Ghash {
172 x: GateIndex,
173 powers_of_h: GateIndex,
174 },
175
176 #[cfg(any(test, feature = "dev"))]
178 AesKeySchedule {
179 key: GateIndex,
180 },
181}
182
183impl<C: MpcConfig> Gate<C> {
184 pub fn is_input(&self) -> bool {
186 matches!(self, Gate::Input { .. })
187 }
188
189 pub fn get_inputs(&self) -> Vec<GateIndex> {
191 match &self {
192 Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => {
193 Vec::new()
194 }
195
196 Gate::FieldShareUnaryOp { x, .. }
197 | Gate::BatchSummation { x, .. }
198 | Gate::BitShareUnaryOp { x, .. }
199 | Gate::PointShareUnaryOp { p: x, .. }
200 | Gate::FieldPlaintextUnaryOp { x, .. }
201 | Gate::BitPlaintextUnaryOp { x, .. }
202 | Gate::PointPlaintextUnaryOp { p: x, .. }
203 | Gate::GetDaBitFieldShare { x, .. }
204 | Gate::GetDaBitSharedBit { x, .. }
205 | Gate::BaseFieldPow { x, .. }
206 | Gate::BitPlaintextToField { x, .. }
207 | Gate::FieldPlaintextToBit { x, .. }
208 | Gate::ExtractFromBatch { x, .. }
209 | Gate::PlaintextPointToCoordinates { point: x, .. }
210 | Gate::CompressPlaintextPoint { point: x, .. }
211 | Gate::PlaintextKeccakF1600 { x }
212 | Gate::GhashPowersOfH { h: x, .. } => {
213 vec![*x]
214 }
215 #[cfg(any(test, feature = "dev"))]
216 Gate::AesKeySchedule { key } => {
217 vec![*key]
218 }
219
220 Gate::FieldShareBinaryOp { x, y, .. }
221 | Gate::BitShareBinaryOp { x, y, .. }
222 | Gate::PointShareBinaryOp { p: x, y, .. }
223 | Gate::FieldPlaintextBinaryOp { x, y, .. }
224 | Gate::BitPlaintextBinaryOp { x, y, .. }
225 | Gate::PointPlaintextBinaryOp { p: x, y, .. }
226 | Gate::KeyRecoveryPlaintextComputeErrors {
227 d_minus_one: x,
228 syndromes: y,
229 ..
230 }
231 | Gate::AesGcmKeyStream {
232 round_keys: x,
233 iv: y,
234 ..
235 }
236 | Gate::Ghash { x, powers_of_h: y } => {
237 vec![*x, *y]
238 }
239
240 Gate::CollectToBatch { wires, .. }
241 | Gate::PointFromPlaintextCoordinates { wires, .. } => wires.clone(),
242 }
243 }
244
245 pub fn map_inputs<F: FnMut(GateIndex) -> GateIndex>(mut self, mut f: F) -> Self {
247 match &mut self {
248 Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => (),
249
250 Gate::FieldShareUnaryOp { x, .. }
251 | Gate::BatchSummation { x, .. }
252 | Gate::BitShareUnaryOp { x, .. }
253 | Gate::PointShareUnaryOp { p: x, .. }
254 | Gate::FieldPlaintextUnaryOp { x, .. }
255 | Gate::BitPlaintextUnaryOp { x, .. }
256 | Gate::PointPlaintextUnaryOp { p: x, .. }
257 | Gate::GetDaBitFieldShare { x, .. }
258 | Gate::GetDaBitSharedBit { x, .. }
259 | Gate::BaseFieldPow { x, .. }
260 | Gate::BitPlaintextToField { x, .. }
261 | Gate::FieldPlaintextToBit { x, .. }
262 | Gate::ExtractFromBatch { x, .. }
263 | Gate::PlaintextPointToCoordinates { point: x, .. }
264 | Gate::CompressPlaintextPoint { point: x, .. }
265 | Gate::PlaintextKeccakF1600 { x }
266 | Gate::GhashPowersOfH { h: x, .. } => {
267 *x = f(*x);
268 }
269
270 #[cfg(any(test, feature = "dev"))]
271 Gate::AesKeySchedule { key } => {
272 *key = f(*key);
273 }
274
275 Gate::FieldShareBinaryOp { x, y, .. }
276 | Gate::BitShareBinaryOp { x, y, .. }
277 | Gate::PointShareBinaryOp { p: x, y, .. }
278 | Gate::FieldPlaintextBinaryOp { x, y, .. }
279 | Gate::BitPlaintextBinaryOp { x, y, .. }
280 | Gate::PointPlaintextBinaryOp { p: x, y, .. }
281 | Gate::KeyRecoveryPlaintextComputeErrors {
282 d_minus_one: x,
283 syndromes: y,
284 ..
285 }
286 | Gate::AesGcmKeyStream {
287 round_keys: x,
288 iv: y,
289 ..
290 }
291 | Gate::Ghash { x, powers_of_h: y } => {
292 *x = f(*x);
293 *y = f(*y);
294 }
295
296 Gate::CollectToBatch { wires, .. }
297 | Gate::PointFromPlaintextCoordinates { wires, .. } => {
298 wires.iter_mut().for_each(|x| *x = f(*x))
299 }
300 };
301
302 self
303 }
304
305 pub fn try_replace_inputs(mut self, inputs: Vec<GateIndex>) -> Result<Self, CircuitError<C>> {
310 if inputs.len() != self.get_inputs().len() {
311 return Err(CircuitError::InvalidGateInputCount {
312 expected: self.get_inputs().len(),
313 found: inputs.len(),
314 });
315 }
316
317 match &mut self {
318 Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => (),
319
320 Gate::FieldShareUnaryOp { x, .. }
321 | Gate::BatchSummation { x, .. }
322 | Gate::BitShareUnaryOp { x, .. }
323 | Gate::PointShareUnaryOp { p: x, .. }
324 | Gate::FieldPlaintextUnaryOp { x, .. }
325 | Gate::BitPlaintextUnaryOp { x, .. }
326 | Gate::PointPlaintextUnaryOp { p: x, .. }
327 | Gate::GetDaBitFieldShare { x, .. }
328 | Gate::GetDaBitSharedBit { x, .. }
329 | Gate::BaseFieldPow { x, .. }
330 | Gate::BitPlaintextToField { x, .. }
331 | Gate::FieldPlaintextToBit { x, .. }
332 | Gate::ExtractFromBatch { x, .. }
333 | Gate::PlaintextPointToCoordinates { point: x, .. }
334 | Gate::CompressPlaintextPoint { point: x, .. }
335 | Gate::PlaintextKeccakF1600 { x }
336 | Gate::GhashPowersOfH { h: x, .. } => {
337 *x = inputs[0];
338 }
339 #[cfg(any(test, feature = "dev"))]
340 Gate::AesKeySchedule { key } => {
341 *key = inputs[0];
342 }
343
344 Gate::FieldShareBinaryOp { x, y, .. }
345 | Gate::BitShareBinaryOp { x, y, .. }
346 | Gate::PointShareBinaryOp { p: x, y, .. }
347 | Gate::FieldPlaintextBinaryOp { x, y, .. }
348 | Gate::BitPlaintextBinaryOp { x, y, .. }
349 | Gate::PointPlaintextBinaryOp { p: x, y, .. }
350 | Gate::KeyRecoveryPlaintextComputeErrors {
351 d_minus_one: x,
352 syndromes: y,
353 ..
354 }
355 | Gate::AesGcmKeyStream {
356 round_keys: x,
357 iv: y,
358 ..
359 }
360 | Gate::Ghash { x, powers_of_h: y } => {
361 *x = inputs[0];
362 *y = inputs[1];
363 }
364
365 Gate::CollectToBatch { wires, .. }
366 | Gate::PointFromPlaintextCoordinates { wires, .. } => *wires = inputs,
367 };
368
369 Ok(self)
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use std::collections::HashSet;
376
377 use primitives::utils::codec::bincode_io;
378
379 use super::*;
380 use crate::{circuit::FieldShareBinaryOp, config::DefaultConfig as C};
381
382 #[test]
383 fn test_ser_gate() {
384 let scalar_gate: Gate<C> = Gate::FieldShareBinaryOp {
385 x: 1,
386 y: 3,
387 op: FieldShareBinaryOp::Add,
388 };
389 let point_gate: Gate<C> = Gate::PointShareBinaryOp {
390 p: 1,
391 y: 3,
392 op: PointShareBinaryOp::Add,
393 };
394
395 let scalar_gate_ser = bincode_io::serialize(&scalar_gate).unwrap();
396 let point_gate_ser = bincode_io::serialize(&point_gate).unwrap();
397
398 let scalar_gate_de: Gate<C> = bincode_io::deserialize(&scalar_gate_ser).unwrap();
399 let point_gate_de: Gate<C> = bincode_io::deserialize(&point_gate_ser).unwrap();
400
401 assert_eq!(scalar_gate, scalar_gate_de);
402 assert_eq!(point_gate, point_gate_de);
403 let set = HashSet::from([scalar_gate, scalar_gate_de, point_gate, point_gate_de]);
404 assert_eq!(set.len(), 2)
405 }
406}