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