Skip to main content

core_utils/circuit/v2/
gate.rs

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