arcium-core-utils 0.8.6

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

use super::{
    constants::{
        BaseFieldPlaintext,
        BaseFieldPlaintextBatch,
        BitPlaintext,
        BitPlaintextBatch,
        Mersenne107Plaintext,
        Mersenne107PlaintextBatch,
        PointPlaintext,
        PointPlaintextBatch,
        ScalarPlaintext,
        ScalarPlaintextBatch,
    },
    gate::Gate,
    ops::Input,
};
use crate::{
    circuit::{
        errors::CircuitError,
        latest,
        old::v1::errors::ConversionError,
        AlgebraicType,
        BatchSize,
        Slice,
    },
    config::MpcConfig,
};

#[derive(Deserialize)]
#[cfg_attr(test, derive(PartialEq, Debug))]
#[repr(transparent)]
pub struct GateIndex(u32);

impl GateIndex {
    /// Field is private in production; only tests need to hand-build one.
    #[cfg(test)]
    pub(crate) fn new(index: u32) -> Self {
        Self(index)
    }
}

impl From<GateIndex> for u32 {
    fn from(index: GateIndex) -> Self {
        index.0
    }
}

/// The circuit, represented as a vector of `Op`s.
#[derive(Deserialize)]
#[cfg_attr(test, derive(PartialEq, Debug))]
#[serde(bound(
    deserialize = "Scalar<C>: Deserialize<'de>, Point<C>: Deserialize<'de>",
    serialize = "Scalar<C>: Serialize, Point<C>: Serialize"
))]
#[repr(C)]
pub struct Circuit<C: Curve> {
    /// The circuit operations.
    ops: Vec<Gate<C>>,
    /// The input gates in order of definition
    input_gates: Vec<GateIndex>,
    /// The output gates in order of definition
    output_gates: Vec<GateIndex>,
}

impl<C: Curve> Circuit<C> {
    /// Fields are private in production; only tests need to hand-build one.
    #[cfg(test)]
    pub(crate) fn new_for_test(
        ops: Vec<Gate<C>>,
        input_gates: Vec<GateIndex>,
        output_gates: Vec<GateIndex>,
    ) -> Self {
        Self {
            ops,
            input_gates,
            output_gates,
        }
    }

    /// Converts this legacy v1 circuit into the latest representation.
    ///
    /// v1 circuits predate the configurable MPC field and embed `Mersenne107` constants. The
    /// target config MUST use `Mersenne107` as its MPC field.
    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 = if let Gate::PlaintextKeccakF1600 { wires } = gate {
                let wires = wires
                    .into_iter()
                    .map(|w| {
                        let w: u32 = w.into();
                        if w < old_gate_idx as u32 {
                            Ok(old_to_new_idx[w as usize])
                        } else {
                            Err(ConversionError::CircuitError(
                                CircuitError::GateIndexOutOfBounds(w, old_gate_idx as u32),
                            ))
                        }
                    })
                    .collect::<Result<_, _>>()?;
                let x = circuit.add_gate(latest::Gate::CollectToBatch { wires })?;
                latest::Gate::PlaintextKeccakF1600 { x }
            } else {
                let gate = match gate {
                    Gate::Input { input_type } => match input_type {
                        Input::SecretPlaintext {
                            inputer,
                            algebraic_type,
                            batched,
                        } => latest::Gate::Input(latest::Input::SecretPlaintext {
                            inputer,
                            algebraic_type,
                            batch_size: batched.count() as BatchSize,
                        }),
                        Input::Share {
                            algebraic_type,
                            batched,
                        } => latest::Gate::Input(latest::Input::Share {
                            algebraic_type,
                            batch_size: batched.count() as BatchSize,
                        }),
                        Input::RandomShare {
                            algebraic_type,
                            batched,
                        } => latest::Gate::Random {
                            algebraic_type,
                            batch_size: batched.count() as BatchSize,
                        },
                        Input::Scalar(val) => match val {
                            ScalarPlaintext::<C>::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::Scalar(val))
                            }
                            ScalarPlaintext::<C>::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::ScalarField,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::ScalarBatch(val) => match val {
                            ScalarPlaintextBatch::<C>::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::ScalarBatch(val))
                            }
                            ScalarPlaintextBatch::<C>::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::ScalarField,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::BaseField(val) => match val {
                            BaseFieldPlaintext::<C>::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::BaseField(val))
                            }
                            BaseFieldPlaintext::<C>::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::BaseField,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::BaseFieldBatch(val) => match val {
                            BaseFieldPlaintextBatch::<C>::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::BaseFieldBatch(val))
                            }
                            BaseFieldPlaintextBatch::<C>::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::BaseField,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::Mersenne107(val) => match val {
                            Mersenne107Plaintext::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::MpcField(val))
                            }
                            Mersenne107Plaintext::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::MpcField,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::Mersenne107Batch(val) => match val {
                            Mersenne107PlaintextBatch::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::MpcFieldBatch(val))
                            }
                            Mersenne107PlaintextBatch::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::MpcField,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::Bit(val) => match val {
                            BitPlaintext::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::Bit(val))
                            }
                            BitPlaintext::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::Bit,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::BitBatch(val) => match val {
                            BitPlaintextBatch::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::BitBatch(val))
                            }
                            BitPlaintextBatch::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::Bit,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::Point(val) => match val {
                            PointPlaintext::<C>::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::Point(Box::new(val)))
                            }
                            PointPlaintext::<C>::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::Point,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                        Input::PointBatch(val) => match val {
                            PointPlaintextBatch::<C>::Fixed(val) => {
                                latest::Gate::Constant(latest::Constant::PointBatch(val))
                            }
                            PointPlaintextBatch::<C>::Input(val) => {
                                latest::Gate::Input(latest::Input::Plaintext {
                                    algebraic_type: AlgebraicType::Point,
                                    batch_size: val as BatchSize,
                                })
                            }
                        },
                    },
                    Gate::FieldShareUnaryOp { x, op, .. } => {
                        latest::Gate::FieldShareUnaryOp { x: x.into(), op }
                    }
                    Gate::FieldShareBinaryOp { x, y, op, .. } => latest::Gate::FieldShareBinaryOp {
                        x: x.into(),
                        y: y.into(),
                        op,
                    },
                    Gate::BatchSummation { x, .. } => latest::Gate::BatchSummation { x: x.into() },
                    Gate::BitShareUnaryOp { x, op } => {
                        latest::Gate::BitShareUnaryOp { x: x.into(), op }
                    }
                    Gate::BitShareBinaryOp { x, y, op, .. } => latest::Gate::BitShareBinaryOp {
                        x: x.into(),
                        y: y.into(),
                        op,
                    },
                    Gate::PointShareUnaryOp { p, op } => {
                        latest::Gate::PointShareUnaryOp { p: p.into(), op }
                    }
                    Gate::PointShareBinaryOp { p, y, op, .. } => latest::Gate::PointShareBinaryOp {
                        p: p.into(),
                        y: y.into(),
                        op,
                    },
                    Gate::FieldPlaintextUnaryOp { x, op, .. } => {
                        latest::Gate::FieldPlaintextUnaryOp { x: x.into(), op }
                    }
                    Gate::FieldPlaintextBinaryOp { x, y, op, .. } => {
                        latest::Gate::FieldPlaintextBinaryOp {
                            x: x.into(),
                            y: y.into(),
                            op,
                        }
                    }
                    Gate::BitPlaintextUnaryOp { x, op } => latest::Gate::BitPlaintextUnaryOp {
                        x: x.into(),
                        op: op.try_into()?,
                    },
                    Gate::BitPlaintextBinaryOp { x, y, op } => latest::Gate::BitPlaintextBinaryOp {
                        x: x.into(),
                        y: y.into(),
                        op: op.try_into()?,
                    },
                    Gate::PointPlaintextUnaryOp { p, op } => {
                        latest::Gate::PointPlaintextUnaryOp { p: p.into(), op }
                    }
                    Gate::PointPlaintextBinaryOp { p, y, op } => {
                        latest::Gate::PointPlaintextBinaryOp {
                            p: p.into(),
                            y: y.into(),
                            op,
                        }
                    }
                    Gate::DaBit {
                        field_type,
                        batched,
                    } => latest::Gate::DaBit {
                        field_type,
                        batch_size: batched.count() as u32,
                    },
                    Gate::GetDaBitFieldShare { x, .. } => {
                        latest::Gate::GetDaBitFieldShare { x: x.into() }
                    }
                    Gate::GetDaBitSharedBit { x, .. } => {
                        latest::Gate::GetDaBitSharedBit { x: x.into() }
                    }
                    Gate::BaseFieldPow { x, exp } => {
                        latest::Gate::BaseFieldPow { x: x.into(), exp }
                    }
                    Gate::BitPlaintextToField { x, field_type } => {
                        latest::Gate::BitPlaintextToField {
                            x: x.into(),
                            field_type,
                        }
                    }
                    Gate::FieldPlaintextToBit { x, .. } => {
                        latest::Gate::FieldPlaintextToBit { x: x.into() }
                    }
                    Gate::BatchGetIndex { x, index, .. } => latest::Gate::ExtractFromBatch {
                        x: x.into(),
                        slice: Slice::single(index as u32),
                    },
                    Gate::CollectToBatch { wires, .. } => latest::Gate::CollectToBatch {
                        wires: wires.into_iter().map(u32::from).collect(),
                    },
                    Gate::PointFromPlaintextCoordinates { wires } => {
                        latest::Gate::PointFromPlaintextCoordinates {
                            wires: wires.into_iter().map(u32::from).collect(),
                        }
                    }
                    Gate::PlaintextPointToCoordinates { point } => {
                        latest::Gate::PlaintextPointToCoordinates {
                            point: point.into(),
                        }
                    }
                    Gate::PlaintextKeccakF1600 { .. } => unreachable!("handled above"),
                    Gate::CompressPlaintextPoint { point } => {
                        latest::Gate::CompressPlaintextPoint {
                            point: point.into(),
                        }
                    }
                    Gate::KeyRecoveryPlaintextComputeErrors {
                        d_minus_one,
                        syndromes,
                    } => latest::Gate::KeyRecoveryPlaintextComputeErrors {
                        d_minus_one: d_minus_one.into(),
                        syndromes: syndromes.into(),
                    },
                };

                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),
                        ))
                    }
                })?;
                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;
        }

        self.output_gates.iter().try_for_each(|idx| {
            let idx = idx.0;
            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[u32::from(output) as usize];
            circuit.add_output(output)?;
        }
        Ok(circuit)
    }
}