Skip to main content

core_utils/circuit/old/v2/
circuit.rs

1use primitives::algebra::{
2    elliptic_curve::{Curve, Point, Scalar},
3    field::mersenne::Mersenne107,
4};
5use serde::{Deserialize, Serialize};
6
7use super::GateIndex;
8use crate::{
9    circuit::{
10        errors::CircuitError,
11        latest,
12        old::v2::{errors::ConversionError, gate::Gate},
13    },
14    config::MpcConfig,
15};
16
17/// Serialization/deserialization optimized representation of a circuit.
18#[derive(Serialize, Deserialize, Default)]
19#[serde(bound(
20    serialize = "Scalar<C>: Serialize, Point<C>: Serialize",
21    deserialize = "Scalar<C>: Deserialize<'de>, Point<C>: Deserialize<'de>"
22))]
23#[repr(C)]
24pub struct Circuit<C: Curve> {
25    /// The circuit operations.
26    pub ops: Vec<Gate<C>>,
27    /// The output gates in order of definition
28    pub output_gates: Vec<GateIndex>,
29}
30
31impl<C: Curve> Circuit<C> {
32    pub fn into_latest<Cfg>(self) -> Result<latest::Circuit<Cfg>, ConversionError<Cfg>>
33    where
34        Cfg: MpcConfig<Curve = C, Field = Mersenne107>,
35    {
36        let mut circuit = latest::Circuit::new();
37        let mut old_to_new_idx = vec![0; self.ops.len()];
38
39        let nb_gates: u32 = self
40            .ops
41            .len()
42            .try_into()
43            .map_err(|_| ConversionError::CircuitError(CircuitError::CircuitTooBig))?;
44
45        for (old_gate_idx, gate) in self.ops.into_iter().enumerate() {
46            let gate = match gate {
47                Gate::Input(input) => latest::Gate::Input(input),
48                Gate::Constant(constant) => latest::Gate::Constant(constant.into()),
49                Gate::Random {
50                    algebraic_type,
51                    batch_size,
52                } => latest::Gate::Random {
53                    algebraic_type,
54                    batch_size,
55                },
56                Gate::FieldShareUnaryOp { x, op } => latest::Gate::FieldShareUnaryOp { x, op },
57                Gate::FieldShareBinaryOp { x, y, op } => {
58                    latest::Gate::FieldShareBinaryOp { x, y, op }
59                }
60                Gate::BatchSummation { x } => latest::Gate::BatchSummation { x },
61                Gate::BitShareUnaryOp { x, op } => latest::Gate::BitShareUnaryOp { x, op },
62                Gate::BitShareBinaryOp { x, y, op } => latest::Gate::BitShareBinaryOp { x, y, op },
63                Gate::PointShareUnaryOp { p, op } => latest::Gate::PointShareUnaryOp { p, op },
64                Gate::PointShareBinaryOp { p, y, op } => {
65                    latest::Gate::PointShareBinaryOp { p, y, op }
66                }
67                Gate::FieldPlaintextUnaryOp { x, op } => {
68                    latest::Gate::FieldPlaintextUnaryOp { x, op }
69                }
70                Gate::FieldPlaintextBinaryOp { x, y, op } => {
71                    latest::Gate::FieldPlaintextBinaryOp { x, y, op }
72                }
73                Gate::BitPlaintextUnaryOp { x, op } => latest::Gate::BitPlaintextUnaryOp { x, op },
74                Gate::BitPlaintextBinaryOp { x, y, op } => {
75                    latest::Gate::BitPlaintextBinaryOp { x, y, op }
76                }
77                Gate::PointPlaintextUnaryOp { p, op } => {
78                    latest::Gate::PointPlaintextUnaryOp { p, op }
79                }
80                Gate::PointPlaintextBinaryOp { p, y, op } => {
81                    latest::Gate::PointPlaintextBinaryOp { p, y, op }
82                }
83                Gate::DaBit {
84                    field_type,
85                    batch_size,
86                } => latest::Gate::DaBit {
87                    field_type: field_type.into(),
88                    batch_size,
89                },
90                Gate::GetDaBitFieldShare { x } => latest::Gate::GetDaBitFieldShare { x },
91                Gate::GetDaBitSharedBit { x } => latest::Gate::GetDaBitSharedBit { x },
92                Gate::BaseFieldPow { x, exp } => latest::Gate::BaseFieldPow { x, exp },
93                Gate::BitPlaintextToField { x, field_type } => latest::Gate::BitPlaintextToField {
94                    x,
95                    field_type: field_type.into(),
96                },
97                Gate::FieldPlaintextToBit { x } => latest::Gate::FieldPlaintextToBit { x },
98                Gate::ExtractFromBatch { x, slice } => latest::Gate::ExtractFromBatch { x, slice },
99                Gate::CollectToBatch { wires } => latest::Gate::CollectToBatch { wires },
100                Gate::PointFromPlaintextCoordinates { wires } => {
101                    latest::Gate::PointFromPlaintextCoordinates { wires }
102                }
103                Gate::PlaintextPointToCoordinates { point } => {
104                    latest::Gate::PlaintextPointToCoordinates { point }
105                }
106                Gate::PlaintextKeccakF1600 { x } => latest::Gate::PlaintextKeccakF1600 { x },
107                Gate::CompressPlaintextPoint { point } => {
108                    latest::Gate::CompressPlaintextPoint { point }
109                }
110                Gate::KeyRecoveryPlaintextComputeErrors {
111                    d_minus_one,
112                    syndromes,
113                } => latest::Gate::KeyRecoveryPlaintextComputeErrors {
114                    d_minus_one,
115                    syndromes,
116                },
117                Gate::AesGcmKeyStream {
118                    round_keys,
119                    iv,
120                    n_ciphertext_blocks,
121                } => latest::Gate::AesGcmKeyStream {
122                    round_keys,
123                    iv,
124                    n_ciphertext_blocks,
125                },
126                #[cfg(any(test, feature = "dev"))]
127                Gate::AesKeySchedule { key } => latest::Gate::AesKeySchedule { key },
128                Gate::GhashPowersOfH {
129                    h,
130                    n_ciphertext_blocks,
131                } => latest::Gate::GhashPowersOfH {
132                    h,
133                    n_ciphertext_blocks,
134                },
135                Gate::Ghash { x, powers_of_h } => latest::Gate::Ghash { x, powers_of_h },
136            };
137
138            // Check gate old input indices are in range and remap them
139            gate.get_inputs().into_iter().try_for_each(|idx| {
140                if idx < old_gate_idx as u32 {
141                    Ok(())
142                } else {
143                    Err(ConversionError::CircuitError(
144                        CircuitError::GateIndexOutOfBounds(idx, old_gate_idx as u32),
145                    ))
146                }
147            })?;
148            let gate = gate.map_inputs(|old_idx| old_to_new_idx[old_idx as usize]);
149
150            let new_gate_idx = circuit.add_gate(gate)?;
151            old_to_new_idx[old_gate_idx] = new_gate_idx;
152        }
153
154        // Check output indices are in range
155        self.output_gates.iter().try_for_each(|idx| {
156            if *idx < nb_gates {
157                Ok(())
158            } else {
159                Err(ConversionError::CircuitError(
160                    CircuitError::GateIndexOutOfBounds(*idx, nb_gates),
161                ))
162            }
163        })?;
164
165        for output in self.output_gates {
166            let output = old_to_new_idx[output as usize];
167            circuit.add_output(output)?;
168        }
169        Ok(circuit)
170    }
171}