arcium-core-utils 0.8.0

Arcium core utils
Documentation
pub mod batcher;
pub mod circuit;
pub(super) mod compressed_circuit;
pub mod errors;
#[cfg(test)]
mod format_fingerprint;
pub mod gate;
#[cfg(any(test, feature = "dev"))]
pub mod mock_eval;
pub mod ops;
pub mod preprocessing;
pub mod slice;

pub use circuit::*;
pub use gate::*;
pub use ops::*;
use serde::{Deserialize, Serialize};
pub use slice::*;

use crate::circuit::errors::ConversionError;

pub type GateIndex = u32;
pub type BatchSize = GateIndex;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
#[repr(C)]
pub enum FieldType {
    BaseField,
    ScalarField,
    MpcField,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
#[repr(C)]
pub enum AlgebraicType {
    BaseField,
    ScalarField,
    Point,
    Bit,
    MpcField,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
#[repr(C)]
pub enum ShareOrPlaintext {
    Share,
    Plaintext,
}

impl From<FieldType> for AlgebraicType {
    fn from(field_type: FieldType) -> Self {
        match field_type {
            FieldType::BaseField => AlgebraicType::BaseField,
            FieldType::ScalarField => AlgebraicType::ScalarField,
            FieldType::MpcField => AlgebraicType::MpcField,
        }
    }
}

impl TryFrom<AlgebraicType> for FieldType {
    type Error = ConversionError;

    fn try_from(value: AlgebraicType) -> Result<Self, Self::Error> {
        match value {
            AlgebraicType::BaseField => Ok(FieldType::BaseField),
            AlgebraicType::ScalarField => Ok(FieldType::ScalarField),
            AlgebraicType::Point => Err(ConversionError::IntoFieldTypeError(value)),
            AlgebraicType::Bit => Err(ConversionError::IntoFieldTypeError(value)),
            AlgebraicType::MpcField => Ok(FieldType::MpcField),
        }
    }
}

#[cfg(test)]
mod tests {
    use itertools::Itertools;
    use primitives::utils::bincode_io;

    use crate::{
        circuit::{AlgebraicType, Circuit, FieldShareBinaryOp, Gate, Input},
        config::MpcConfig,
    };

    pub fn create_add_tree_circuit<C: MpcConfig>(depth: usize) -> Circuit<C> {
        let mut circuit = Circuit::<C>::new();
        let inputs = (0..(1 << depth))
            .map(|_| {
                circuit
                    .add_gate(Gate::Input(Input::SecretPlaintext {
                        inputer: 0,
                        algebraic_type: AlgebraicType::MpcField,
                        batch_size: 3,
                    }))
                    .unwrap()
            })
            .collect_vec();

        let mut level_gates = inputs;
        while level_gates.len() > 1 {
            let mut next_level = vec![];
            for chunk in level_gates.chunks(2) {
                let index = if chunk.len() == 2 {
                    circuit
                        .add_gate(Gate::FieldShareBinaryOp {
                            x: chunk[0],
                            y: chunk[1],
                            op: FieldShareBinaryOp::Add,
                        })
                        .unwrap()
                } else {
                    assert_eq!(chunk.len(), 1);
                    chunk[0]
                };
                next_level.push(index);
            }
            level_gates = next_level;
        }
        assert_eq!(level_gates.len(), 1);

        circuit.add_output(level_gates[0]).unwrap();

        circuit
    }

    pub fn create_mul_tree_circuit<C: MpcConfig>(depth: usize) -> Circuit<C> {
        let mut circuit = Circuit::<C>::new();
        let inputs = (0..(1 << depth))
            .map(|_| {
                circuit
                    .add_gate(Gate::Input(Input::SecretPlaintext {
                        inputer: 0,
                        algebraic_type: AlgebraicType::MpcField,
                        batch_size: 3,
                    }))
                    .unwrap()
            })
            .collect_vec();

        let mut level_gates = inputs;
        while level_gates.len() > 1 {
            let mut next_level = vec![];
            for chunk in level_gates.chunks(2) {
                let index = if chunk.len() == 2 {
                    circuit
                        .add_gate(Gate::FieldShareBinaryOp {
                            x: chunk[0],
                            y: chunk[1],
                            op: FieldShareBinaryOp::Mul,
                        })
                        .unwrap()
                } else {
                    assert_eq!(chunk.len(), 1);
                    chunk[0]
                };
                next_level.push(index);
            }
            level_gates = next_level;
        }
        assert_eq!(level_gates.len(), 1);

        circuit.add_output(level_gates[0]).unwrap();

        circuit
    }

    /// Pins the serialized discriminants of the field-type enums: `MpcField` replaced the
    /// `Mersenne107` variant in place, so existing circuit blobs must keep decoding. Reordering or
    /// inserting variants breaks the wire format.
    #[test]
    fn field_type_discriminants_are_stable() {
        use primitives::algebra::field::{mersenne::Mersenne107, SubfieldElement};

        use crate::{
            circuit::latest::{Constant, FieldType},
            config::DefaultConfig,
        };

        assert_eq!(
            bincode_io::serialize(&FieldType::MpcField).unwrap(),
            [2, 0, 0, 0]
        );
        assert_eq!(
            bincode_io::serialize(&AlgebraicType::MpcField).unwrap(),
            [4, 0, 0, 0]
        );

        // Constant::MpcField: variant index 4, then the 14-byte length-prefixed Mersenne107
        // canonical encoding.
        let constant: Constant<DefaultConfig> =
            Constant::MpcField(SubfieldElement::<Mersenne107>::from(42u64));
        assert_eq!(
            bincode_io::serialize(&constant).unwrap(),
            [4, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        );
    }
}