From 2598f1181f686e31bdc0615de9f33daa6cf77dd9 Mon Sep 17 00:00:00 2001 From: taskooh Date: Thu, 27 Jun 2024 21:29:50 +0900 Subject: [PATCH] :sparkles: Mpc Adjustment --- mpc-algebra/examples/algebra.rs | 9 +- mpc-algebra/src/r1cs_helper.rs | 2 + mpc-algebra/src/r1cs_helper/mpc_bits.rs | 37 ++++ mpc-algebra/src/r1cs_helper/mpc_fp.rs | 209 +++++++--------------- mpc-algebra/src/r1cs_helper/mpc_fp_cmp.rs | 156 ++++++++++++++++ 5 files changed, 266 insertions(+), 147 deletions(-) create mode 100644 mpc-algebra/src/r1cs_helper/mpc_fp_cmp.rs diff --git a/mpc-algebra/examples/algebra.rs b/mpc-algebra/examples/algebra.rs index 5e46d152..bc8a8010 100644 --- a/mpc-algebra/examples/algebra.rs +++ b/mpc-algebra/examples/algebra.rs @@ -1,19 +1,18 @@ use std::path::PathBuf; use ark_ff::PubUniformRand; -use ark_ff::{BigInteger, BigInteger256, Field, FpParameters, PrimeField, UniformRand}; +use ark_ff::{BigInteger, BigInteger256, FpParameters, PrimeField, UniformRand}; use ark_ff::{One, Zero}; -use ark_poly::reveal; use ark_std::{end_timer, start_timer}; use log::debug; use mpc_algebra::boolean_field::MpcBooleanField; use mpc_algebra::{ - share, AdditiveFieldShare, BitAdd, BitDecomposition, BitwiseLessThan, EqualityZero, LessThan, - LogicalOperations, MpcField, Reveal, UniformBitRand, + AdditiveFieldShare, BitAdd, BitDecomposition, BitwiseLessThan, BooleanWire, EqualityZero, + LessThan, LogicalOperations, MpcField, Reveal, UniformBitRand, }; use mpc_net::{MpcMultiNet as Net, MpcNet}; -use rand::{thread_rng, Rng}; +use rand::thread_rng; use structopt::StructOpt; #[derive(Debug, StructOpt)] diff --git a/mpc-algebra/src/r1cs_helper.rs b/mpc-algebra/src/r1cs_helper.rs index dcdfa3f4..a9c29f36 100644 --- a/mpc-algebra/src/r1cs_helper.rs +++ b/mpc-algebra/src/r1cs_helper.rs @@ -4,6 +4,8 @@ pub mod mpc_eq; pub use mpc_eq::*; pub mod mpc_fp; pub use mpc_fp::*; +pub mod mpc_fp_cmp; +pub use mpc_fp_cmp::*; pub mod mpc_select; pub use mpc_select::*; pub mod mpc_bits; diff --git a/mpc-algebra/src/r1cs_helper/mpc_bits.rs b/mpc-algebra/src/r1cs_helper/mpc_bits.rs index 3584dadb..e47a89b6 100644 --- a/mpc-algebra/src/r1cs_helper/mpc_bits.rs +++ b/mpc-algebra/src/r1cs_helper/mpc_bits.rs @@ -57,3 +57,40 @@ impl MpcToBitsGadget for [MpcUInt8] { Ok(bits) } } + +/// Specifies constraints for conversion to a little-endian byte representation +/// of `self`. +pub trait MpcToBytesGadget { + /// Outputs a canonical, little-endian, byte decomposition of `self`. + /// + /// This is the correct default for 99% of use cases. + fn to_bytes(&self) -> Result>, SynthesisError>; + + /// Outputs a possibly non-unique byte decomposition of `self`. + /// + /// If you're not absolutely certain that your usecase can get away with a + /// non-canonical representation, please use `self.to_bytes(cs)` instead. + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + self.to_bytes() + } +} + +impl MpcToBytesGadget for [MpcUInt8] { + fn to_bytes(&self) -> Result>, SynthesisError> { + Ok(self.to_vec()) + } +} + +impl<'a, F: PrimeField + SquareRootField, T: 'a + MpcToBytesGadget> MpcToBytesGadget + for &'a T +{ + fn to_bytes(&self) -> Result>, SynthesisError> { + (*self).to_bytes() + } +} + +impl<'a, F: PrimeField + SquareRootField> MpcToBytesGadget for &'a [MpcUInt8] { + fn to_bytes(&self) -> Result>, SynthesisError> { + Ok(self.to_vec()) + } +} diff --git a/mpc-algebra/src/r1cs_helper/mpc_fp.rs b/mpc-algebra/src/r1cs_helper/mpc_fp.rs index 57b31534..15955996 100644 --- a/mpc-algebra/src/r1cs_helper/mpc_fp.rs +++ b/mpc-algebra/src/r1cs_helper/mpc_fp.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; -use ark_ff::{Field, PrimeField, SquareRootField}; +use ark_ff::{BigInteger, PrimeField, SquareRootField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, impl_ops, R1CSVar, @@ -19,7 +19,7 @@ use crate::{ mpc_eq::MpcEqGadget, mpc_fields::{FieldOpsBounds, MpcFieldVar}, mpc_select::{MpcCondSelectGadget, MpcTwoBitLookupGadget}, - BitDecomposition, EqualityZero, MpcBoolean, MpcToBitsGadget, Reveal, + BitDecomposition, EqualityZero, MpcBoolean, MpcToBitsGadget, MpcToBytesGadget, MpcUInt8, }; // TODO: MpcAllocatedFp is required? @@ -456,64 +456,6 @@ impl MpcAllocatedFp { } } -/// ************************************************************************* -/// ************************************************************************* - -// impl ToBitsGadget for AllocatedFp { -// /// Outputs the unique bit-wise decomposition of `self` in *little-endian* -// /// form. -// /// -// /// This method enforces that the output is in the field, i.e. -// /// it invokes `Boolean::enforce_in_field_le` on the bit decomposition. -// #[tracing::instrument(target = "r1cs")] -// fn to_bits_le(&self) -> Result>, SynthesisError> { -// let bits = self.to_non_unique_bits_le()?; -// Boolean::enforce_in_field_le(&bits)?; -// Ok(bits) -// } - -// #[tracing::instrument(target = "r1cs")] -// fn to_non_unique_bits_le(&self) -> Result>, SynthesisError> { -// let cs = self.cs.clone(); -// use ark_ff::BitIteratorBE; -// let mut bits = if let Some(value) = self.value { -// let field_char = BitIteratorBE::new(F::characteristic()); -// let bits: Vec<_> = BitIteratorBE::new(value.into_repr()) -// .zip(field_char) -// .skip_while(|(_, c)| !c) -// .map(|(b, _)| Some(b)) -// .collect(); -// assert_eq!(bits.len(), F::Params::MODULUS_BITS as usize); -// bits -// } else { -// vec![None; F::Params::MODULUS_BITS as usize] -// }; - -// // Convert to little-endian -// bits.reverse(); - -// let bits: Vec<_> = bits -// .into_iter() -// .map(|b| Boolean::new_witness(cs.clone(), || b.get())) -// .collect::>()?; - -// let mut lc = LinearCombination::zero(); -// let mut coeff = F::one(); - -// for bit in bits.iter() { -// lc = &lc + bit.lc() * coeff; - -// coeff.double_in_place(); -// } - -// lc = lc - &self.variable; - -// cs.enforce_constraint(lc!(), lc!(), lc)?; - -// Ok(bits) -// } -// } - impl MpcToBitsGadget for MpcAllocatedFp { /// Outputs the unique bit-wise decomposition of `self` in *little-endian* /// form. @@ -569,47 +511,54 @@ impl MpcToBitsGadget for } } -// impl ToBytesGadget for AllocatedFp { -// /// Outputs the unique byte decomposition of `self` in *little-endian* -// /// form. -// /// -// /// This method enforces that the decomposition represents -// /// an integer that is less than `F::MODULUS`. -// #[tracing::instrument(target = "r1cs")] -// fn to_bytes(&self) -> Result>, SynthesisError> { -// let num_bits = F::BigInt::NUM_LIMBS * 64; -// let mut bits = self.to_bits_le()?; -// let remainder = core::iter::repeat(Boolean::constant(false)).take(num_bits - bits.len()); -// bits.extend(remainder); -// let bytes = bits -// .chunks(8) -// .map(|chunk| UInt8::from_bits_le(chunk)) -// .collect(); -// Ok(bytes) -// } +impl MpcToBytesGadget for MpcAllocatedFp { + /// Outputs the unique byte decomposition of `self` in *little-endian* + /// form. + /// + /// This method enforces that the decomposition represents + /// an integer that is less than `F::MODULUS`. + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let num_bits = F::BigInt::NUM_LIMBS * 64; + let mut bits = self.to_bits_le()?; + let remainder = core::iter::repeat(MpcBoolean::constant(false)).take(num_bits - bits.len()); + bits.extend(remainder); + let bytes = bits + .chunks(8) + .map(|chunk| MpcUInt8::from_bits_le(chunk)) + .collect(); + Ok(bytes) + } -// #[tracing::instrument(target = "r1cs")] -// fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { -// let num_bits = F::BigInt::NUM_LIMBS * 64; -// let mut bits = self.to_non_unique_bits_le()?; -// let remainder = core::iter::repeat(Boolean::constant(false)).take(num_bits - bits.len()); -// bits.extend(remainder); -// let bytes = bits -// .chunks(8) -// .map(|chunk| UInt8::from_bits_le(chunk)) -// .collect(); -// Ok(bytes) -// } -// } + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let num_bits = F::BigInt::NUM_LIMBS * 64; + let mut bits = self.to_non_unique_bits_le()?; + let remainder = core::iter::repeat(MpcBoolean::constant(false)).take(num_bits - bits.len()); + bits.extend(remainder); + let bytes = bits + .chunks(8) + .map(|chunk| MpcUInt8::from_bits_le(chunk)) + .collect(); + Ok(bytes) + } +} -// impl> ToConstraintFieldGadget> -// for MpcAllocatedFp -// { -// #[tracing::instrument(target = "r1cs")] -// fn to_constraint_field(&self) -> Result>, SynthesisError> { -// Ok(vec![self.clone().into()]) -// } -// } +/// Specifies how to convert a variable of type `Self` to variables of +/// type `FpVar` +pub trait MpcToConstraintFieldGadget { + /// Converts `self` to `FpVar` variables. + fn to_constraint_field( + &self, + ) -> Result>, ark_relations::r1cs::SynthesisError>; +} + +impl MpcToConstraintFieldGadget for MpcAllocatedFp { + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> Result>, SynthesisError> { + Ok(vec![self.clone().into()]) + } +} impl MpcCondSelectGadget for MpcAllocatedFp { #[inline] @@ -974,31 +923,7 @@ impl MpcFpVar { } } -// impl ToBitsGadget for FpVar { -// #[tracing::instrument(target = "r1cs")] -// fn to_bits_le(&self) -> Result>, SynthesisError> { -// match self { -// Self::Constant(_) => self.to_non_unique_bits_le(), -// Self::Var(v) => v.to_bits_le(), -// } -// } - -// #[tracing::instrument(target = "r1cs")] -// fn to_non_unique_bits_le(&self) -> Result>, SynthesisError> { -// use ark_ff::BitIteratorLE; -// match self { -// Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_repr()) -// .take((F::Params::MODULUS_BITS) as usize) -// .map(Boolean::constant) -// .collect::>()), -// Self::Var(v) => v.to_non_unique_bits_le(), -// } -// } -// } - -impl MpcToBitsGadget - for MpcFpVar -{ +impl MpcToBitsGadget for MpcFpVar { fn to_bits_le(&self) -> Result>, SynthesisError> { match self { Self::Constant(_) => self.to_non_unique_bits_le(), @@ -1018,25 +943,25 @@ impl MpcToBit } } -// impl ToBytesGadget for FpVar { -// /// Outputs the unique byte decomposition of `self` in *little-endian* -// /// form. -// #[tracing::instrument(target = "r1cs")] -// fn to_bytes(&self) -> Result>, SynthesisError> { -// match self { -// Self::Constant(c) => Ok(UInt8::constant_vec(&ark_ff::to_bytes![c].unwrap())), -// Self::Var(v) => v.to_bytes(), -// } -// } +impl MpcToBytesGadget for MpcFpVar { + /// Outputs the unique byte decomposition of `self` in *little-endian* + /// form. + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + match self { + Self::Constant(c) => Ok(MpcUInt8::constant_vec(&ark_ff::to_bytes![c].unwrap())), + Self::Var(v) => v.to_bytes(), + } + } -// #[tracing::instrument(target = "r1cs")] -// fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { -// match self { -// Self::Constant(c) => Ok(UInt8::constant_vec(&ark_ff::to_bytes![c].unwrap())), -// Self::Var(v) => v.to_non_unique_bytes(), -// } -// } -// } + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + match self { + Self::Constant(c) => Ok(MpcUInt8::constant_vec(&ark_ff::to_bytes![c].unwrap())), + Self::Var(v) => v.to_non_unique_bytes(), + } + } +} // impl> ToConstraintFieldGadget> for MpcFpVar { // #[tracing::instrument(target = "r1cs")] diff --git a/mpc-algebra/src/r1cs_helper/mpc_fp_cmp.rs b/mpc-algebra/src/r1cs_helper/mpc_fp_cmp.rs new file mode 100644 index 00000000..3338ecc1 --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/mpc_fp_cmp.rs @@ -0,0 +1,156 @@ +use crate::{ + mpc_fields::MpcFieldVar, BitDecomposition, EqualityZero, MpcBoolean, MpcFpVar, MpcToBitsGadget, +}; +use ark_ff::{PrimeField, SquareRootField}; +use ark_r1cs_std::R1CSVar; +use ark_relations::{ + lc, + r1cs::{SynthesisError, Variable}, +}; +use core::cmp::Ordering; + +impl MpcFpVar { + /// This function enforces the ordering between `self` and `other`. The + /// constraint system will not be satisfied otherwise. If `self` should + /// also be checked for equality, e.g. `self <= other` instead of `self < + /// other`, set `should_also_check_quality` to `true`. This variant + /// verifies `self` and `other` are `<= (p-1)/2`. + #[tracing::instrument(target = "r1cs")] + pub fn enforce_cmp( + &self, + other: &MpcFpVar, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result<(), SynthesisError> { + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.enforce_smaller_than(&right) + } + + /// This function enforces the ordering between `self` and `other`. The + /// constraint system will not be satisfied otherwise. If `self` should + /// also be checked for equality, e.g. `self <= other` instead of `self < + /// other`, set `should_also_check_quality` to `true`. This variant + /// assumes `self` and `other` are `<= (p-1)/2` and does not generate + /// constraints to verify that. + #[tracing::instrument(target = "r1cs")] + pub fn enforce_cmp_unchecked( + &self, + other: &MpcFpVar, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result<(), SynthesisError> { + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.enforce_smaller_than_unchecked(&right) + } + + /// This function checks the ordering between `self` and `other`. It outputs + /// self `Boolean` that contains the result - `1` if true, `0` + /// otherwise. The constraint system will be satisfied in any case. If + /// `self` should also be checked for equality, e.g. `self <= other` + /// instead of `self < other`, set `should_also_check_quality` to + /// `true`. This variant verifies `self` and `other` are `<= (p-1)/2`. + #[tracing::instrument(target = "r1cs")] + pub fn is_cmp( + &self, + other: &MpcFpVar, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result, SynthesisError> { + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.is_smaller_than(&right) + } + + /// This function checks the ordering between `self` and `other`. It outputs + /// a `Boolean` that contains the result - `1` if true, `0` otherwise. + /// The constraint system will be satisfied in any case. If `self` + /// should also be checked for equality, e.g. `self <= other` instead of + /// `self < other`, set `should_also_check_quality` to `true`. This + /// variant assumes `self` and `other` are `<= (p-1)/2` and does not + /// generate constraints to verify that. + #[tracing::instrument(target = "r1cs")] + pub fn is_cmp_unchecked( + &self, + other: &MpcFpVar, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result, SynthesisError> { + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.is_smaller_than_unchecked(&right) + } + + fn process_cmp_inputs( + &self, + other: &Self, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result<(Self, Self), SynthesisError> { + let (left, right) = match ordering { + Ordering::Less => (self, other), + Ordering::Greater => (other, self), + Ordering::Equal => return Err(SynthesisError::Unsatisfiable), + }; + let right_for_check = if should_also_check_equality { + right + F::one() + } else { + right.clone() + }; + + Ok((left.clone(), right_for_check)) + } + + /// Helper function to enforce that `self <= (p-1)/2`. + #[tracing::instrument(target = "r1cs")] + pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two( + &self, + ) -> Result<(), SynthesisError> { + // It's okay to use `to_non_unique_bits` bits here because we're enforcing + // self <= (p-1)/2, which implies self < p. + let _ = MpcBoolean::enforce_smaller_or_equal_than_le( + &self.to_non_unique_bits_le()?, + F::modulus_minus_one_div_two(), + )?; + Ok(()) + } + + /// Helper function to check `self < other` and output a result bit. This + /// function verifies `self` and `other` are `<= (p-1)/2`. + fn is_smaller_than(&self, other: &MpcFpVar) -> Result, SynthesisError> { + self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + self.is_smaller_than_unchecked(other) + } + + /// Helper function to check `self < other` and output a result bit. This + /// function assumes `self` and `other` are `<= (p-1)/2` and does not + /// generate constraints to verify that. + fn is_smaller_than_unchecked( + &self, + other: &MpcFpVar, + ) -> Result, SynthesisError> { + Ok((self - other) + .double()? + .to_bits_le()? + .first() + .unwrap() + .clone()) + } + + /// Helper function to enforce `self < other`. This function verifies `self` + /// and `other` are `<= (p-1)/2`. + fn enforce_smaller_than(&self, other: &MpcFpVar) -> Result<(), SynthesisError> { + self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + self.enforce_smaller_than_unchecked(other) + } + + /// Helper function to enforce `self < other`. This function assumes `self` + /// and `other` are `<= (p-1)/2` and does not generate constraints to + /// verify that. + fn enforce_smaller_than_unchecked(&self, other: &MpcFpVar) -> Result<(), SynthesisError> { + let is_smaller_than = self.is_smaller_than_unchecked(other)?; + let lc_one = lc!() + Variable::One; + [self, other] + .cs() + .enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one) + } +}