arcium-core-utils 0.8.0

Arcium core utils
Documentation
use primitives::algebra::{
    elliptic_curve::{Curve, Point, Scalar},
    field::mersenne::Mersenne107,
};
use serde::{Deserialize, Serialize};

use super::GateIndex;
use crate::{
    circuit::{
        errors::CircuitError,
        latest,
        old::v2::{errors::ConversionError, gate::Gate},
    },
    config::MpcConfig,
};

/// Serialization/deserialization optimized representation of a circuit.
#[derive(Serialize, Deserialize, Default)]
#[serde(bound(
    serialize = "Scalar<C>: Serialize, Point<C>: Serialize",
    deserialize = "Scalar<C>: Deserialize<'de>, Point<C>: Deserialize<'de>"
))]
#[repr(C)]
pub struct Circuit<C: Curve> {
    /// The circuit operations.
    pub ops: Vec<Gate<C>>,
    /// The output gates in order of definition
    pub output_gates: Vec<GateIndex>,
}

impl<C: Curve> Circuit<C> {
    pub fn into_latest<Cfg>(self) -> Result<latest::Circuit<Cfg>, ConversionError<Cfg>>
    where
        Cfg: MpcConfig<Curve = C, Field = Mersenne107>,
    {
        let mut circuit = latest::Circuit::new();
        let mut old_to_new_idx = vec![0; self.ops.len()];

        let nb_gates: u32 = self
            .ops
            .len()
            .try_into()
            .map_err(|_| ConversionError::CircuitError(CircuitError::CircuitTooBig))?;

        for (old_gate_idx, gate) in self.ops.into_iter().enumerate() {
            let gate = match gate {
                Gate::Input(input) => latest::Gate::Input(input),
                Gate::Constant(constant) => latest::Gate::Constant(constant.into()),
                Gate::Random {
                    algebraic_type,
                    batch_size,
                } => latest::Gate::Random {
                    algebraic_type,
                    batch_size,
                },
                Gate::FieldShareUnaryOp { x, op } => latest::Gate::FieldShareUnaryOp { x, op },
                Gate::FieldShareBinaryOp { x, y, op } => {
                    latest::Gate::FieldShareBinaryOp { x, y, op }
                }
                Gate::BatchSummation { x } => latest::Gate::BatchSummation { x },
                Gate::BitShareUnaryOp { x, op } => latest::Gate::BitShareUnaryOp { x, op },
                Gate::BitShareBinaryOp { x, y, op } => latest::Gate::BitShareBinaryOp { x, y, op },
                Gate::PointShareUnaryOp { p, op } => latest::Gate::PointShareUnaryOp { p, op },
                Gate::PointShareBinaryOp { p, y, op } => {
                    latest::Gate::PointShareBinaryOp { p, y, op }
                }
                Gate::FieldPlaintextUnaryOp { x, op } => {
                    latest::Gate::FieldPlaintextUnaryOp { x, op }
                }
                Gate::FieldPlaintextBinaryOp { x, y, op } => {
                    latest::Gate::FieldPlaintextBinaryOp { x, y, op }
                }
                Gate::BitPlaintextUnaryOp { x, op } => latest::Gate::BitPlaintextUnaryOp { x, op },
                Gate::BitPlaintextBinaryOp { x, y, op } => {
                    latest::Gate::BitPlaintextBinaryOp { x, y, op }
                }
                Gate::PointPlaintextUnaryOp { p, op } => {
                    latest::Gate::PointPlaintextUnaryOp { p, op }
                }
                Gate::PointPlaintextBinaryOp { p, y, op } => {
                    latest::Gate::PointPlaintextBinaryOp { p, y, op }
                }
                Gate::DaBit {
                    field_type,
                    batch_size,
                } => latest::Gate::DaBit {
                    field_type: field_type.into(),
                    batch_size,
                },
                Gate::GetDaBitFieldShare { x } => latest::Gate::GetDaBitFieldShare { x },
                Gate::GetDaBitSharedBit { x } => latest::Gate::GetDaBitSharedBit { x },
                Gate::BaseFieldPow { x, exp } => latest::Gate::BaseFieldPow { x, exp },
                Gate::BitPlaintextToField { x, field_type } => latest::Gate::BitPlaintextToField {
                    x,
                    field_type: field_type.into(),
                },
                Gate::FieldPlaintextToBit { x } => latest::Gate::FieldPlaintextToBit { x },
                Gate::ExtractFromBatch { x, slice } => latest::Gate::ExtractFromBatch { x, slice },
                Gate::CollectToBatch { wires } => latest::Gate::CollectToBatch { wires },
                Gate::PointFromPlaintextCoordinates { wires } => {
                    latest::Gate::PointFromPlaintextCoordinates { wires }
                }
                Gate::PlaintextPointToCoordinates { point } => {
                    latest::Gate::PlaintextPointToCoordinates { point }
                }
                Gate::PlaintextKeccakF1600 { x } => latest::Gate::PlaintextKeccakF1600 { x },
                Gate::CompressPlaintextPoint { point } => {
                    latest::Gate::CompressPlaintextPoint { point }
                }
                Gate::KeyRecoveryPlaintextComputeErrors {
                    d_minus_one,
                    syndromes,
                } => latest::Gate::KeyRecoveryPlaintextComputeErrors {
                    d_minus_one,
                    syndromes,
                },
                Gate::AesGcmKeyStream {
                    round_keys,
                    iv,
                    n_ciphertext_blocks,
                } => latest::Gate::AesGcmKeyStream {
                    round_keys,
                    iv,
                    n_ciphertext_blocks,
                },
                #[cfg(any(test, feature = "dev"))]
                Gate::AesKeySchedule { key } => latest::Gate::AesKeySchedule { key },
                Gate::GhashPowersOfH {
                    h,
                    n_ciphertext_blocks,
                } => latest::Gate::GhashPowersOfH {
                    h,
                    n_ciphertext_blocks,
                },
                Gate::Ghash { x, powers_of_h } => latest::Gate::Ghash { x, powers_of_h },
            };

            // Check gate old input indices are in range and remap them
            gate.get_inputs().into_iter().try_for_each(|idx| {
                if idx < old_gate_idx as u32 {
                    Ok(())
                } else {
                    Err(ConversionError::CircuitError(
                        CircuitError::GateIndexOutOfBounds(idx, old_gate_idx as u32),
                    ))
                }
            })?;
            let gate = gate.map_inputs(|old_idx| old_to_new_idx[old_idx as usize]);

            let new_gate_idx = circuit.add_gate(gate)?;
            old_to_new_idx[old_gate_idx] = new_gate_idx;
        }

        // Check output indices are in range
        self.output_gates.iter().try_for_each(|idx| {
            if *idx < nb_gates {
                Ok(())
            } else {
                Err(ConversionError::CircuitError(
                    CircuitError::GateIndexOutOfBounds(*idx, nb_gates),
                ))
            }
        })?;

        for output in self.output_gates {
            let output = old_to_new_idx[output as usize];
            circuit.add_output(output)?;
        }
        Ok(circuit)
    }
}