Skip to main content

core_utils/circuit/old/v1/
circuit.rs

1use primitives::algebra::{
2    elliptic_curve::{Curve, Point, Scalar},
3    field::mersenne::Mersenne107,
4};
5use serde::Deserialize;
6#[cfg(test)]
7use serde::Serialize;
8
9use super::{
10    constants::{
11        BaseFieldPlaintext,
12        BaseFieldPlaintextBatch,
13        BitPlaintext,
14        BitPlaintextBatch,
15        Mersenne107Plaintext,
16        Mersenne107PlaintextBatch,
17        PointPlaintext,
18        PointPlaintextBatch,
19        ScalarPlaintext,
20        ScalarPlaintextBatch,
21    },
22    gate::Gate,
23    ops::Input,
24};
25use crate::{
26    circuit::{
27        errors::CircuitError,
28        latest,
29        old::v1::errors::ConversionError,
30        AlgebraicType,
31        BatchSize,
32        Slice,
33    },
34    config::MpcConfig,
35};
36
37#[derive(Deserialize)]
38#[cfg_attr(test, derive(Serialize))]
39#[repr(transparent)]
40pub struct GateIndex(u32);
41
42impl GateIndex {
43    /// Field is private in production; only tests need to hand-build one.
44    #[cfg(test)]
45    pub(crate) fn new(index: u32) -> Self {
46        Self(index)
47    }
48}
49
50impl From<GateIndex> for u32 {
51    fn from(index: GateIndex) -> Self {
52        index.0
53    }
54}
55
56/// The circuit, represented as a vector of `Op`s.
57#[derive(Deserialize)]
58#[cfg_attr(test, derive(Serialize))]
59#[serde(bound(
60    deserialize = "Scalar<C>: Deserialize<'de>, Point<C>: Deserialize<'de>",
61    serialize = "Scalar<C>: Serialize, Point<C>: Serialize"
62))]
63#[repr(C)]
64pub struct Circuit<C: Curve> {
65    /// The circuit operations.
66    ops: Vec<Gate<C>>,
67    /// The input gates in order of definition
68    input_gates: Vec<GateIndex>,
69    /// The output gates in order of definition
70    output_gates: Vec<GateIndex>,
71}
72
73impl<C: Curve> Circuit<C> {
74    /// Fields are private in production; only tests need to hand-build one.
75    #[cfg(test)]
76    pub(crate) fn new_for_test(
77        ops: Vec<Gate<C>>,
78        input_gates: Vec<GateIndex>,
79        output_gates: Vec<GateIndex>,
80    ) -> Self {
81        Self {
82            ops,
83            input_gates,
84            output_gates,
85        }
86    }
87
88    /// Converts this legacy v1 circuit into the latest representation.
89    ///
90    /// v1 circuits predate the configurable MPC field and embed `Mersenne107` constants, so the
91    /// target config must use `Mersenne107` as its MPC field.
92    pub fn into_latest<Cfg>(self) -> Result<latest::Circuit<Cfg>, ConversionError<Cfg>>
93    where
94        Cfg: MpcConfig<Curve = C, Field = Mersenne107>,
95    {
96        let mut circuit = latest::Circuit::new();
97        let mut old_to_new_idx = vec![0; self.ops.len()];
98
99        let nb_gates: u32 = self
100            .ops
101            .len()
102            .try_into()
103            .map_err(|_| ConversionError::CircuitError(CircuitError::CircuitTooBig))?;
104
105        for (old_gate_idx, gate) in self.ops.into_iter().enumerate() {
106            let gate = if let Gate::PlaintextKeccakF1600 { wires } = gate {
107                // Check gate old input indices are in range
108                let wires = wires
109                    .into_iter()
110                    .map(|w| {
111                        let w: u32 = w.into();
112                        if w < old_gate_idx as u32 {
113                            Ok(old_to_new_idx[w as usize])
114                        } else {
115                            Err(ConversionError::CircuitError(
116                                CircuitError::GateIndexOutOfBounds(w, old_gate_idx as u32),
117                            ))
118                        }
119                    })
120                    .collect::<Result<_, _>>()?;
121                let x = circuit.add_gate(latest::Gate::CollectToBatch { wires })?;
122                latest::Gate::PlaintextKeccakF1600 { x }
123            } else {
124                let gate = match gate {
125                    Gate::Input { input_type } => match input_type {
126                        Input::SecretPlaintext {
127                            inputer,
128                            algebraic_type,
129                            batched,
130                        } => latest::Gate::Input(latest::Input::SecretPlaintext {
131                            inputer,
132                            algebraic_type,
133                            batch_size: batched.count() as BatchSize,
134                        }),
135                        Input::Share {
136                            algebraic_type,
137                            batched,
138                        } => latest::Gate::Input(latest::Input::Share {
139                            algebraic_type,
140                            batch_size: batched.count() as BatchSize,
141                        }),
142                        Input::RandomShare {
143                            algebraic_type,
144                            batched,
145                        } => latest::Gate::Random {
146                            algebraic_type,
147                            batch_size: batched.count() as BatchSize,
148                        },
149                        Input::Scalar(val) => match val {
150                            ScalarPlaintext::<C>::Fixed(val) => {
151                                latest::Gate::Constant(latest::Constant::Scalar(val))
152                            }
153                            ScalarPlaintext::<C>::Input(val) => {
154                                latest::Gate::Input(latest::Input::Plaintext {
155                                    algebraic_type: AlgebraicType::ScalarField,
156                                    batch_size: val as BatchSize,
157                                })
158                            }
159                        },
160                        Input::ScalarBatch(val) => match val {
161                            ScalarPlaintextBatch::<C>::Fixed(val) => {
162                                latest::Gate::Constant(latest::Constant::ScalarBatch(val))
163                            }
164                            ScalarPlaintextBatch::<C>::Input(val) => {
165                                latest::Gate::Input(latest::Input::Plaintext {
166                                    algebraic_type: AlgebraicType::ScalarField,
167                                    batch_size: val as BatchSize,
168                                })
169                            }
170                        },
171                        Input::BaseField(val) => match val {
172                            BaseFieldPlaintext::<C>::Fixed(val) => {
173                                latest::Gate::Constant(latest::Constant::BaseField(val))
174                            }
175                            BaseFieldPlaintext::<C>::Input(val) => {
176                                latest::Gate::Input(latest::Input::Plaintext {
177                                    algebraic_type: AlgebraicType::BaseField,
178                                    batch_size: val as BatchSize,
179                                })
180                            }
181                        },
182                        Input::BaseFieldBatch(val) => match val {
183                            BaseFieldPlaintextBatch::<C>::Fixed(val) => {
184                                latest::Gate::Constant(latest::Constant::BaseFieldBatch(val))
185                            }
186                            BaseFieldPlaintextBatch::<C>::Input(val) => {
187                                latest::Gate::Input(latest::Input::Plaintext {
188                                    algebraic_type: AlgebraicType::BaseField,
189                                    batch_size: val as BatchSize,
190                                })
191                            }
192                        },
193                        Input::Mersenne107(val) => match val {
194                            Mersenne107Plaintext::Fixed(val) => {
195                                latest::Gate::Constant(latest::Constant::MpcField(val))
196                            }
197                            Mersenne107Plaintext::Input(val) => {
198                                latest::Gate::Input(latest::Input::Plaintext {
199                                    algebraic_type: AlgebraicType::MpcField,
200                                    batch_size: val as BatchSize,
201                                })
202                            }
203                        },
204                        Input::Mersenne107Batch(val) => match val {
205                            Mersenne107PlaintextBatch::Fixed(val) => {
206                                latest::Gate::Constant(latest::Constant::MpcFieldBatch(val))
207                            }
208                            Mersenne107PlaintextBatch::Input(val) => {
209                                latest::Gate::Input(latest::Input::Plaintext {
210                                    algebraic_type: AlgebraicType::MpcField,
211                                    batch_size: val as BatchSize,
212                                })
213                            }
214                        },
215                        Input::Bit(val) => match val {
216                            BitPlaintext::Fixed(val) => {
217                                latest::Gate::Constant(latest::Constant::Bit(val))
218                            }
219                            BitPlaintext::Input(val) => {
220                                latest::Gate::Input(latest::Input::Plaintext {
221                                    algebraic_type: AlgebraicType::Bit,
222                                    batch_size: val as BatchSize,
223                                })
224                            }
225                        },
226                        Input::BitBatch(val) => match val {
227                            BitPlaintextBatch::Fixed(val) => {
228                                latest::Gate::Constant(latest::Constant::BitBatch(val))
229                            }
230                            BitPlaintextBatch::Input(val) => {
231                                latest::Gate::Input(latest::Input::Plaintext {
232                                    algebraic_type: AlgebraicType::Bit,
233                                    batch_size: val as BatchSize,
234                                })
235                            }
236                        },
237                        Input::Point(val) => match val {
238                            PointPlaintext::<C>::Fixed(val) => {
239                                latest::Gate::Constant(latest::Constant::Point(val))
240                            }
241                            PointPlaintext::<C>::Input(val) => {
242                                latest::Gate::Input(latest::Input::Plaintext {
243                                    algebraic_type: AlgebraicType::Point,
244                                    batch_size: val as BatchSize,
245                                })
246                            }
247                        },
248                        Input::PointBatch(val) => match val {
249                            PointPlaintextBatch::<C>::Fixed(val) => {
250                                latest::Gate::Constant(latest::Constant::PointBatch(val))
251                            }
252                            PointPlaintextBatch::<C>::Input(val) => {
253                                latest::Gate::Input(latest::Input::Plaintext {
254                                    algebraic_type: AlgebraicType::Point,
255                                    batch_size: val as BatchSize,
256                                })
257                            }
258                        },
259                    },
260                    Gate::FieldShareUnaryOp { x, op, .. } => {
261                        latest::Gate::FieldShareUnaryOp { x: x.into(), op }
262                    }
263                    Gate::FieldShareBinaryOp { x, y, op, .. } => latest::Gate::FieldShareBinaryOp {
264                        x: x.into(),
265                        y: y.into(),
266                        op,
267                    },
268                    Gate::BatchSummation { x, .. } => latest::Gate::BatchSummation { x: x.into() },
269                    Gate::BitShareUnaryOp { x, op } => {
270                        latest::Gate::BitShareUnaryOp { x: x.into(), op }
271                    }
272                    Gate::BitShareBinaryOp { x, y, op, .. } => latest::Gate::BitShareBinaryOp {
273                        x: x.into(),
274                        y: y.into(),
275                        op,
276                    },
277                    Gate::PointShareUnaryOp { p, op } => {
278                        latest::Gate::PointShareUnaryOp { p: p.into(), op }
279                    }
280                    Gate::PointShareBinaryOp { p, y, op, .. } => latest::Gate::PointShareBinaryOp {
281                        p: p.into(),
282                        y: y.into(),
283                        op,
284                    },
285                    Gate::FieldPlaintextUnaryOp { x, op, .. } => {
286                        latest::Gate::FieldPlaintextUnaryOp { x: x.into(), op }
287                    }
288                    Gate::FieldPlaintextBinaryOp { x, y, op, .. } => {
289                        latest::Gate::FieldPlaintextBinaryOp {
290                            x: x.into(),
291                            y: y.into(),
292                            op,
293                        }
294                    }
295                    Gate::BitPlaintextUnaryOp { x, op } => latest::Gate::BitPlaintextUnaryOp {
296                        x: x.into(),
297                        op: op.try_into()?,
298                    },
299                    Gate::BitPlaintextBinaryOp { x, y, op } => latest::Gate::BitPlaintextBinaryOp {
300                        x: x.into(),
301                        y: y.into(),
302                        op: op.try_into()?,
303                    },
304                    Gate::PointPlaintextUnaryOp { p, op } => {
305                        latest::Gate::PointPlaintextUnaryOp { p: p.into(), op }
306                    }
307                    Gate::PointPlaintextBinaryOp { p, y, op } => {
308                        latest::Gate::PointPlaintextBinaryOp {
309                            p: p.into(),
310                            y: y.into(),
311                            op,
312                        }
313                    }
314                    Gate::DaBit {
315                        field_type,
316                        batched,
317                    } => latest::Gate::DaBit {
318                        field_type,
319                        batch_size: batched.count() as u32,
320                    },
321                    Gate::GetDaBitFieldShare { x, .. } => {
322                        latest::Gate::GetDaBitFieldShare { x: x.into() }
323                    }
324                    Gate::GetDaBitSharedBit { x, .. } => {
325                        latest::Gate::GetDaBitSharedBit { x: x.into() }
326                    }
327                    Gate::BaseFieldPow { x, exp } => {
328                        latest::Gate::BaseFieldPow { x: x.into(), exp }
329                    }
330                    Gate::BitPlaintextToField { x, field_type } => {
331                        latest::Gate::BitPlaintextToField {
332                            x: x.into(),
333                            field_type,
334                        }
335                    }
336                    Gate::FieldPlaintextToBit { x, .. } => {
337                        latest::Gate::FieldPlaintextToBit { x: x.into() }
338                    }
339                    Gate::BatchGetIndex { x, index, .. } => latest::Gate::ExtractFromBatch {
340                        x: x.into(),
341                        slice: Slice::single(index as u32),
342                    },
343                    Gate::CollectToBatch { wires, .. } => latest::Gate::CollectToBatch {
344                        wires: wires.into_iter().map(u32::from).collect(),
345                    },
346                    Gate::PointFromPlaintextCoordinates { wires } => {
347                        latest::Gate::PointFromPlaintextCoordinates {
348                            wires: wires.into_iter().map(u32::from).collect(),
349                        }
350                    }
351                    Gate::PlaintextPointToCoordinates { point } => {
352                        latest::Gate::PlaintextPointToCoordinates {
353                            point: point.into(),
354                        }
355                    }
356                    Gate::PlaintextKeccakF1600 { .. } => unreachable!("handled above"),
357                    Gate::CompressPlaintextPoint { point } => {
358                        latest::Gate::CompressPlaintextPoint {
359                            point: point.into(),
360                        }
361                    }
362                    Gate::KeyRecoveryPlaintextComputeErrors {
363                        d_minus_one,
364                        syndromes,
365                    } => latest::Gate::KeyRecoveryPlaintextComputeErrors {
366                        d_minus_one: d_minus_one.into(),
367                        syndromes: syndromes.into(),
368                    },
369                };
370
371                // Check gate old input indices are in range
372                gate.get_inputs().into_iter().try_for_each(|idx| {
373                    if idx < old_gate_idx as u32 {
374                        Ok(())
375                    } else {
376                        Err(ConversionError::CircuitError(
377                            CircuitError::GateIndexOutOfBounds(idx, old_gate_idx as u32),
378                        ))
379                    }
380                })?;
381                gate.map_inputs(|old_idx| old_to_new_idx[old_idx as usize])
382            };
383
384            let new_gate_idx = circuit.add_gate(gate)?;
385            old_to_new_idx[old_gate_idx] = new_gate_idx;
386        }
387
388        // Check output indices are in range
389        self.output_gates.iter().try_for_each(|idx| {
390            let idx = idx.0;
391            if idx < nb_gates {
392                Ok(())
393            } else {
394                Err(ConversionError::CircuitError(
395                    CircuitError::GateIndexOutOfBounds(idx, nb_gates),
396                ))
397            }
398        })?;
399
400        for output in self.output_gates {
401            let output = old_to_new_idx[u32::from(output) as usize];
402            circuit.add_output(output)?;
403        }
404        Ok(circuit)
405    }
406}