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 #[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#[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 ops: Vec<Gate<C>>,
67 input_gates: Vec<GateIndex>,
69 output_gates: Vec<GateIndex>,
71}
72
73impl<C: Curve> Circuit<C> {
74 #[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 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 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 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 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}