Skip to main content

core_utils/circuit/latest/
gate.rs

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/// Gate operations, where the operation arguments correspond to _wire_ label.
36#[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 a wire
44    Input(Input),
45    /// Input a constant value
46    Constant(Constant<C>),
47    /// Generate random shares
48    Random {
49        algebraic_type: AlgebraicType,
50        batch_size: BatchSize,
51    },
52    /// Field share unary operations
53    FieldShareUnaryOp {
54        x: GateIndex,
55        op: FieldShareUnaryOp,
56    },
57    /// Field share binary operations, where the second wire may be a plaintext.
58    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    /// Operations with elliptic curve points
76    PointShareUnaryOp {
77        p: GateIndex,
78        op: PointShareUnaryOp,
79    },
80    PointShareBinaryOp {
81        p: GateIndex,
82        y: GateIndex,
83        op: PointShareBinaryOp,
84    },
85    /// Field plaintext unary operations
86    FieldPlaintextUnaryOp {
87        x: GateIndex,
88        op: FieldPlaintextUnaryOp,
89    },
90    /// Field plaintext binary operations
91    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    /// Request a daBit
115    DaBit {
116        field_type: FieldType,
117        batch_size: BatchSize,
118    },
119    GetDaBitFieldShare {
120        x: GateIndex,
121    },
122    GetDaBitSharedBit {
123        x: GateIndex,
124    },
125    /// Base field exponentiation operation
126    BaseFieldPow {
127        x: GateIndex,
128        exp: BoxedUint,
129    },
130    /// Bit plaintext conversion operations
131    BitPlaintextToField {
132        x: GateIndex,
133        field_type: FieldType,
134    },
135    FieldPlaintextToBit {
136        x: GateIndex,
137    },
138    /// Get a slice of elements from a batched wire
139    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    // Keep conditional gates here to ensure correct fingerprinting !!!
177    #[cfg(any(test, feature = "dev"))]
178    AesKeySchedule {
179        key: GateIndex,
180    },
181}
182
183impl<C: MpcConfig> Gate<C> {
184    /// Check whether the gate is an input gate.
185    pub fn is_input(&self) -> bool {
186        matches!(self, Gate::Input { .. })
187    }
188
189    /// Returns the indices of gate inputs.
190    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    /// Maps inplace gate inputs using the given function.
246    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    /// Tries to replace the gate inputs with the given ones.
306    ///
307    /// This function returns an error if the number of given inputs does not match the number of
308    /// gate inputs.
309    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}