Skip to main content

core_utils/circuit/v2/
gate.rs

1use std::hash::Hash;
2
3use primitives::algebra::{
4    elliptic_curve::{Curve, Point, Scalar},
5    BoxedUint,
6};
7use serde::{Deserialize, Serialize};
8use wincode::{SchemaRead, SchemaWrite};
9
10use crate::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
33/// Gate operations, where the operation arguments correspond to _wire_ label.
34#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, SchemaRead, SchemaWrite)]
35#[serde(bound(
36    serialize = "Scalar<C>: Serialize, Point<C>: Serialize",
37    deserialize = "Scalar<C>: Deserialize<'de>, Point<C>: Deserialize<'de>"
38))]
39#[repr(C)]
40pub enum Gate<C: Curve> {
41    /// Input a wire
42    Input(Input),
43    /// Input a constant value
44    Constant(Constant<C>),
45    /// Generate random shares
46    Random {
47        algebraic_type: AlgebraicType,
48        batch_size: BatchSize,
49    },
50    /// Field share unary operations
51    FieldShareUnaryOp {
52        x: GateIndex,
53        op: FieldShareUnaryOp,
54    },
55    /// Field share binary operations, where the second wire may be a plaintext.
56    FieldShareBinaryOp {
57        x: GateIndex,
58        y: GateIndex,
59        op: FieldShareBinaryOp,
60    },
61    BatchSummation {
62        x: GateIndex,
63    },
64    BitShareUnaryOp {
65        x: GateIndex,
66        op: BitShareUnaryOp,
67    },
68    BitShareBinaryOp {
69        x: GateIndex,
70        y: GateIndex,
71        op: BitShareBinaryOp,
72    },
73    /// Operations with elliptic curve points
74    PointShareUnaryOp {
75        p: GateIndex,
76        op: PointShareUnaryOp,
77    },
78    PointShareBinaryOp {
79        p: GateIndex,
80        y: GateIndex,
81        op: PointShareBinaryOp,
82    },
83    /// Field plaintext unary operations
84    FieldPlaintextUnaryOp {
85        x: GateIndex,
86        op: FieldPlaintextUnaryOp,
87    },
88    /// Field plaintext binary operations
89    FieldPlaintextBinaryOp {
90        x: GateIndex,
91        y: GateIndex,
92        op: FieldPlaintextBinaryOp,
93    },
94    BitPlaintextUnaryOp {
95        x: GateIndex,
96        op: BitPlaintextUnaryOp,
97    },
98    BitPlaintextBinaryOp {
99        x: GateIndex,
100        y: GateIndex,
101        op: BitPlaintextBinaryOp,
102    },
103    PointPlaintextUnaryOp {
104        p: GateIndex,
105        op: PointPlaintextUnaryOp,
106    },
107    PointPlaintextBinaryOp {
108        p: GateIndex,
109        y: GateIndex,
110        op: PointPlaintextBinaryOp,
111    },
112    /// Request a daBit
113    DaBit {
114        field_type: FieldType,
115        batch_size: BatchSize,
116    },
117    GetDaBitFieldShare {
118        x: GateIndex,
119    },
120    GetDaBitSharedBit {
121        x: GateIndex,
122    },
123    /// Base field exponentiation operation
124    BaseFieldPow {
125        x: GateIndex,
126        exp: BoxedUint,
127    },
128    /// Bit plaintext conversion operations
129    BitPlaintextToField {
130        x: GateIndex,
131        field_type: FieldType,
132    },
133    FieldPlaintextToBit {
134        x: GateIndex,
135    },
136    /// Get a slice of elements from a batched wire
137    ExtractFromBatch {
138        x: GateIndex,
139        slice: Slice,
140    },
141    CollectToBatch {
142        wires: Vec<GateIndex>,
143    },
144    PointFromPlaintextCoordinates {
145        wires: Vec<GateIndex>,
146    },
147    PlaintextPointToCoordinates {
148        point: GateIndex,
149    },
150    PlaintextKeccakF1600 {
151        x: GateIndex,
152    },
153    CompressPlaintextPoint {
154        point: GateIndex,
155    },
156    KeyRecoveryPlaintextComputeErrors {
157        d_minus_one: GateIndex,
158        syndromes: GateIndex,
159    },
160    AesGcmKeyStream {
161        round_keys: GateIndex,
162        iv: GateIndex,
163        n_ciphertext_blocks: u32,
164    },
165    #[cfg(any(test, feature = "dev"))]
166    AesKeySchedule {
167        key: GateIndex,
168    },
169}
170
171impl<C: Curve> Gate<C> {
172    /// Check whether the gate is an input gate.
173    pub fn is_input(&self) -> bool {
174        matches!(self, Gate::Input { .. })
175    }
176
177    /// Returns the indices of gate inputs.
178    pub fn get_inputs(&self) -> Vec<GateIndex> {
179        match &self {
180            Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => {
181                Vec::new()
182            }
183
184            Gate::FieldShareUnaryOp { x, .. }
185            | Gate::BatchSummation { x, .. }
186            | Gate::BitShareUnaryOp { x, .. }
187            | Gate::PointShareUnaryOp { p: x, .. }
188            | Gate::FieldPlaintextUnaryOp { x, .. }
189            | Gate::BitPlaintextUnaryOp { x, .. }
190            | Gate::PointPlaintextUnaryOp { p: x, .. }
191            | Gate::GetDaBitFieldShare { x, .. }
192            | Gate::GetDaBitSharedBit { x, .. }
193            | Gate::BaseFieldPow { x, .. }
194            | Gate::BitPlaintextToField { x, .. }
195            | Gate::FieldPlaintextToBit { x, .. }
196            | Gate::ExtractFromBatch { x, .. }
197            | Gate::PlaintextPointToCoordinates { point: x, .. }
198            | Gate::CompressPlaintextPoint { point: x, .. }
199            | Gate::PlaintextKeccakF1600 { x } => {
200                vec![*x]
201            }
202            #[cfg(any(test, feature = "dev"))]
203            Gate::AesKeySchedule { key } => {
204                vec![*key]
205            }
206
207            Gate::FieldShareBinaryOp { x, y, .. }
208            | Gate::BitShareBinaryOp { x, y, .. }
209            | Gate::PointShareBinaryOp { p: x, y, .. }
210            | Gate::FieldPlaintextBinaryOp { x, y, .. }
211            | Gate::BitPlaintextBinaryOp { x, y, .. }
212            | Gate::PointPlaintextBinaryOp { p: x, y, .. }
213            | Gate::KeyRecoveryPlaintextComputeErrors {
214                d_minus_one: x,
215                syndromes: y,
216                ..
217            }
218            | Gate::AesGcmKeyStream {
219                round_keys: x,
220                iv: y,
221                ..
222            } => {
223                vec![*x, *y]
224            }
225
226            Gate::CollectToBatch { wires, .. }
227            | Gate::PointFromPlaintextCoordinates { wires, .. } => wires.clone(),
228        }
229    }
230
231    /// Maps inplace gate inputs using the given function.
232    pub fn map_inputs<F: FnMut(GateIndex) -> GateIndex>(mut self, mut f: F) -> Self {
233        match &mut self {
234            Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => (),
235
236            Gate::FieldShareUnaryOp { x, .. }
237            | Gate::BatchSummation { x, .. }
238            | Gate::BitShareUnaryOp { x, .. }
239            | Gate::PointShareUnaryOp { p: x, .. }
240            | Gate::FieldPlaintextUnaryOp { x, .. }
241            | Gate::BitPlaintextUnaryOp { x, .. }
242            | Gate::PointPlaintextUnaryOp { p: x, .. }
243            | Gate::GetDaBitFieldShare { x, .. }
244            | Gate::GetDaBitSharedBit { x, .. }
245            | Gate::BaseFieldPow { x, .. }
246            | Gate::BitPlaintextToField { x, .. }
247            | Gate::FieldPlaintextToBit { x, .. }
248            | Gate::ExtractFromBatch { x, .. }
249            | Gate::PlaintextPointToCoordinates { point: x, .. }
250            | Gate::CompressPlaintextPoint { point: x, .. }
251            | Gate::PlaintextKeccakF1600 { x } => {
252                *x = f(*x);
253            }
254
255            #[cfg(any(test, feature = "dev"))]
256            Gate::AesKeySchedule { key } => {
257                *key = f(*key);
258            }
259
260            Gate::FieldShareBinaryOp { x, y, .. }
261            | Gate::BitShareBinaryOp { x, y, .. }
262            | Gate::PointShareBinaryOp { p: x, y, .. }
263            | Gate::FieldPlaintextBinaryOp { x, y, .. }
264            | Gate::BitPlaintextBinaryOp { x, y, .. }
265            | Gate::PointPlaintextBinaryOp { p: x, y, .. }
266            | Gate::KeyRecoveryPlaintextComputeErrors {
267                d_minus_one: x,
268                syndromes: y,
269                ..
270            }
271            | Gate::AesGcmKeyStream {
272                round_keys: x,
273                iv: y,
274                ..
275            } => {
276                *x = f(*x);
277                *y = f(*y);
278            }
279
280            Gate::CollectToBatch { wires, .. }
281            | Gate::PointFromPlaintextCoordinates { wires, .. } => {
282                wires.iter_mut().for_each(|x| *x = f(*x))
283            }
284        };
285
286        self
287    }
288
289    /// Tries to replace the gate inputs with the given ones.
290    ///
291    /// This function returns an error if the number of given inputs does not match the number of
292    /// gate inputs.
293    pub fn try_replace_inputs(mut self, inputs: Vec<GateIndex>) -> Result<Self, CircuitError<C>> {
294        if inputs.len() != self.get_inputs().len() {
295            return Err(CircuitError::InvalidGateInputCount {
296                expected: self.get_inputs().len(),
297                found: inputs.len(),
298            });
299        }
300
301        match &mut self {
302            Gate::Input(_) | Gate::Random { .. } | Gate::Constant(_) | Gate::DaBit { .. } => (),
303
304            Gate::FieldShareUnaryOp { x, .. }
305            | Gate::BatchSummation { x, .. }
306            | Gate::BitShareUnaryOp { x, .. }
307            | Gate::PointShareUnaryOp { p: x, .. }
308            | Gate::FieldPlaintextUnaryOp { x, .. }
309            | Gate::BitPlaintextUnaryOp { x, .. }
310            | Gate::PointPlaintextUnaryOp { p: x, .. }
311            | Gate::GetDaBitFieldShare { x, .. }
312            | Gate::GetDaBitSharedBit { x, .. }
313            | Gate::BaseFieldPow { x, .. }
314            | Gate::BitPlaintextToField { x, .. }
315            | Gate::FieldPlaintextToBit { x, .. }
316            | Gate::ExtractFromBatch { x, .. }
317            | Gate::PlaintextPointToCoordinates { point: x, .. }
318            | Gate::CompressPlaintextPoint { point: x, .. }
319            | Gate::PlaintextKeccakF1600 { x } => {
320                *x = inputs[0];
321            }
322            #[cfg(any(test, feature = "dev"))]
323            Gate::AesKeySchedule { key } => {
324                *key = inputs[0];
325            }
326
327            Gate::FieldShareBinaryOp { x, y, .. }
328            | Gate::BitShareBinaryOp { x, y, .. }
329            | Gate::PointShareBinaryOp { p: x, y, .. }
330            | Gate::FieldPlaintextBinaryOp { x, y, .. }
331            | Gate::BitPlaintextBinaryOp { x, y, .. }
332            | Gate::PointPlaintextBinaryOp { p: x, y, .. }
333            | Gate::KeyRecoveryPlaintextComputeErrors {
334                d_minus_one: x,
335                syndromes: y,
336                ..
337            }
338            | Gate::AesGcmKeyStream {
339                round_keys: x,
340                iv: y,
341                ..
342            } => {
343                *x = inputs[0];
344                *y = inputs[1];
345            }
346
347            Gate::CollectToBatch { wires, .. }
348            | Gate::PointFromPlaintextCoordinates { wires, .. } => *wires = inputs,
349        };
350
351        Ok(self)
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use std::collections::HashSet;
358
359    use primitives::algebra::elliptic_curve::Curve25519Ristretto as C;
360
361    use super::*;
362    use crate::circuit::FieldShareBinaryOp;
363
364    #[test]
365    fn test_ser_gate() {
366        let scalar_gate: Gate<C> = Gate::FieldShareBinaryOp {
367            x: 1,
368            y: 3,
369            op: FieldShareBinaryOp::Add,
370        };
371        let point_gate: Gate<C> = Gate::PointShareBinaryOp {
372            p: 1,
373            y: 3,
374            op: PointShareBinaryOp::Add,
375        };
376
377        let scalar_gate_ser = bincode::serialize(&scalar_gate).unwrap();
378        let point_gate_ser = bincode::serialize(&point_gate).unwrap();
379
380        let scalar_gate_de: Gate<C> = bincode::deserialize(&scalar_gate_ser).unwrap();
381        let point_gate_de: Gate<C> = bincode::deserialize(&point_gate_ser).unwrap();
382
383        assert_eq!(scalar_gate, scalar_gate_de);
384        assert_eq!(point_gate, point_gate_de);
385        let set = HashSet::from([scalar_gate, scalar_gate_de, point_gate, point_gate_de]);
386        assert_eq!(set.len(), 2)
387    }
388}