diff --git a/arkworks/curves/ed_on_bls12_377/Cargo.toml b/arkworks/curves/ed_on_bls12_377/Cargo.toml index 6731491a..3b515c5d 100644 --- a/arkworks/curves/ed_on_bls12_377/Cargo.toml +++ b/arkworks/curves/ed_on_bls12_377/Cargo.toml @@ -29,3 +29,4 @@ ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-featu default = [] std = [ "ark-std/std", "ark-ff/std", "ark-ec/std", "ark-bls12-377/std" ] r1cs = [ "ark-r1cs-std" ] +ed_on_bls12_377 = [] \ No newline at end of file diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index 03cad338..38e8f930 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -1,6 +1,5 @@ use ark_bls12_377::{Fr, FrParameters}; use ark_crypto_primitives::encryption::AsymmetricEncryptionScheme; -use ark_ec::twisted_edwards_extended::GroupAffine; use ark_ec::AffineCurve; use ark_ff::FpParameters; use ark_marlin::IndexProverKey; @@ -8,6 +7,7 @@ use ark_mnt4_753::FqParameters; use ark_serialize::{CanonicalDeserialize, Read}; use ark_std::test_rng; use core::panic; +use mpc_algebra::encryption::elgamal::elgamal::Parameters; use mpc_algebra::malicious_majority::*; use mpc_algebra::Reveal; use mpc_net::{MpcMultiNet as Net, MpcNet}; @@ -368,7 +368,7 @@ fn multi_divination(_opt: &Opt) -> Result<(), std::io::Error> { mpc_input: mpc_input.clone(), }; - let _peculiar_is_werewolf_commitment: Vec> = mpc_input + let _peculiar_is_werewolf_commitment: Vec = mpc_input .peculiar .clone() .unwrap() @@ -377,7 +377,7 @@ fn multi_divination(_opt: &Opt) -> Result<(), std::io::Error> { .map(|x| x.commitment) .collect::>(); - let _peculiar_is_target_commitment: Vec> = mpc_input + let _peculiar_is_target_commitment: Vec = mpc_input .peculiar .clone() .unwrap() @@ -386,12 +386,10 @@ fn multi_divination(_opt: &Opt) -> Result<(), std::io::Error> { .map(|x| x.commitment) .collect::>(); - let elgamal_generator: ark_crypto_primitives::encryption::elgamal::Parameters< - MpcEdwardsProjective, - > = mpc_input.clone().common.unwrap().elgamal_param; + let elgamal_generator: Parameters = + mpc_input.clone().common.unwrap().elgamal_param; - let elgamal_pubkey: GroupAffine = - mpc_input.clone().common.unwrap().pub_key; + let elgamal_pubkey: MpcEdwardsAffine = mpc_input.clone().common.unwrap().pub_key; let message = >::ElGamalPlaintext::prime_subgroup_generator(); @@ -408,17 +406,17 @@ fn multi_divination(_opt: &Opt) -> Result<(), std::io::Error> { let mut inputs = Vec::new(); // elgamal param - inputs.push(elgamal_generator.generator.x.reveal()); - inputs.push(elgamal_generator.generator.y.reveal()); + inputs.push(elgamal_generator.generator.reveal().x); + inputs.push(elgamal_generator.generator.reveal().y); // elgamal pubkey - inputs.push(elgamal_pubkey.x.reveal()); - inputs.push(elgamal_pubkey.y.reveal()); + inputs.push(elgamal_pubkey.reveal().x); + inputs.push(elgamal_pubkey.reveal().y); // elgamal ciphertext - inputs.push(enc_result.0.x.reveal()); - inputs.push(enc_result.0.y.reveal()); - inputs.push(enc_result.1.x.reveal()); - inputs.push(enc_result.1.y.reveal()); + inputs.push(enc_result.0.reveal().x); + inputs.push(enc_result.0.reveal().y); + inputs.push(enc_result.1.reveal().x); + inputs.push(enc_result.1.reveal().y); // input commitment // inputs.push(peculiar_is_werewolf_commitment[0].x.reveal()); diff --git a/examples/online.rs b/examples/online.rs index 5c9dba01..a5b66ddd 100644 --- a/examples/online.rs +++ b/examples/online.rs @@ -288,10 +288,10 @@ fn main() -> Result<(), Box> { let peculiar_a_commitment = mpc_input.peculiar.clone().unwrap().a.commitment; let peculiar_b_commitment = mpc_input.peculiar.unwrap().b.commitment; - inputs.push(peculiar_a_commitment.x.reveal()); - inputs.push(peculiar_a_commitment.y.reveal()); - inputs.push(peculiar_b_commitment.x.reveal()); - inputs.push(peculiar_b_commitment.y.reveal()); + inputs.push(peculiar_a_commitment.reveal().x); + inputs.push(peculiar_a_commitment.reveal().y); + inputs.push(peculiar_b_commitment.reveal().x); + inputs.push(peculiar_b_commitment.reveal().y); assert!(LocalMarlin::verify(&index_vk, &inputs, &proof, rng).unwrap()); } diff --git a/mpc-algebra/Cargo.toml b/mpc-algebra/Cargo.toml index a00707cf..4a25ee9c 100644 --- a/mpc-algebra/Cargo.toml +++ b/mpc-algebra/Cargo.toml @@ -12,7 +12,7 @@ ark-poly = { path = "../arkworks/algebra/poly", version = "0.3.0" } ark-serialize = { path = "../arkworks/algebra/serialize", version = "0.3.0" } ark-std = { path = "../arkworks/std", version = "0.3.0", features = ["std", "print-trace"] } ark-r1cs-std = { path = "../arkworks/r1cs-std", version = "0.3.0", default-features = false, optional = true } -ark-crypto-primitives = { path = "../arkworks/crypto-primitives", version = "0.3.0" } +ark-crypto-primitives = { path = "../arkworks/crypto-primitives", version = "0.3.0", features = ["r1cs"] } ark-relations = { path = "../arkworks/snark/relations", version = "^0.3.0", default-features = false } ark-bls12-377 = { path = "../arkworks/curves/bls12_377", version = "0.3.0" } diff --git a/mpc-algebra/examples/algebra.rs b/mpc-algebra/examples/algebra.rs index bc8a8010..8ea7da20 100644 --- a/mpc-algebra/examples/algebra.rs +++ b/mpc-algebra/examples/algebra.rs @@ -1,14 +1,20 @@ use std::path::PathBuf; +use ark_crypto_primitives::{CommitmentScheme, CRH}; use ark_ff::PubUniformRand; use ark_ff::{BigInteger, BigInteger256, FpParameters, PrimeField, UniformRand}; use ark_ff::{One, Zero}; +use ark_poly::reveal; +use ark_std::PubUniformRand; use ark_std::{end_timer, start_timer}; use log::debug; +use mpc_algebra::pedersen::Randomness; use mpc_algebra::boolean_field::MpcBooleanField; use mpc_algebra::{ - AdditiveFieldShare, BitAdd, BitDecomposition, BitwiseLessThan, BooleanWire, EqualityZero, - LessThan, LogicalOperations, MpcField, Reveal, UniformBitRand, + edwards2, share, AdditiveFieldShare, BitAdd, BitDecomposition, BitwiseLessThan, BooleanWire, + CommitmentScheme as MpcCommitmentScheme, EqualityZero, LessThan, + LogicalOperations, + MpcEdwardsProjective, MpcField, Reveal, UniformBitRand, }; use mpc_net::{MpcMultiNet as Net, MpcNet}; @@ -375,6 +381,71 @@ fn test_bit_decomposition() { assert_eq!(res, random.reveal()); } +pub const PERDERSON_WINDOW_SIZE: usize = 256; +pub const PERDERSON_WINDOW_NUM: usize = 1; + +#[derive(Clone)] +pub struct Window; +impl ark_crypto_primitives::crh::pedersen::Window for Window { + const WINDOW_SIZE: usize = PERDERSON_WINDOW_SIZE; + const NUM_WINDOWS: usize = PERDERSON_WINDOW_NUM; +} + +impl mpc_algebra::crh::pedersen::Window for Window { + const WINDOW_SIZE: usize = PERDERSON_WINDOW_SIZE; + const NUM_WINDOWS: usize = PERDERSON_WINDOW_NUM; +} + +type LocalPed = ark_crypto_primitives::commitment::pedersen::Commitment< + ark_ed_on_bls12_377::EdwardsProjective, + Window, +>; +type MpcPed = mpc_algebra::commitment::pedersen::Commitment; + +fn test_pedersen_commitment() { + let rng = &mut ark_std::test_rng(); + + let x = F::rand(rng); + let x_bytes = x.into_repr().to_bytes_le(); + let x_bits = x.into_repr().to_bits_le(); + + // mpc calculation + let mpc_parameters = MpcPed::setup(rng).unwrap(); + + let scalar_x_bytes = if Net::am_king() { + x_bits + .iter() + .map(|b| { + MpcField::>::from_add_shared(ark_ed_on_bls12_377::Fr::from(*b)) + }) + .collect::>() + } else { + x_bits + .iter() + .map(|b| { + MpcField::>::from_add_shared(ark_ed_on_bls12_377::Fr::zero()) + }) + .collect::>() + }; + + let randomness = Randomness::::rand(rng); + + let result_mpc = MpcPed::commit(&mpc_parameters, &scalar_x_bytes, &randomness).unwrap(); + + // local calculation + let local_parameters = ark_crypto_primitives::commitment::pedersen::Parameters { + randomness_generator: mpc_parameters.randomness_generator.clone().reveal(), + generators: mpc_parameters.generators.reveal(), + }; + + let local_randomness = + ark_crypto_primitives::commitment::pedersen::Randomness(randomness.0.reveal()); + + let result_local = LocalPed::commit(&local_parameters, &x_bytes, &local_randomness).unwrap(); + + assert_eq!(result_local, result_mpc.reveal()); +} + fn test_share() { let rng = &mut ark_std::test_rng(); @@ -432,6 +503,9 @@ fn main() { test_bit_decomposition(); println!("Test bit_decomposition passed"); + test_pedersen_commitment(); + println!("Test pedersen commitment passed"); + test_share(); println!("Test share passed"); } diff --git a/mpc-algebra/src/commitment.rs b/mpc-algebra/src/commitment.rs new file mode 100644 index 00000000..f8100666 --- /dev/null +++ b/mpc-algebra/src/commitment.rs @@ -0,0 +1,25 @@ +use ark_ff::UniformRand; +use ark_std::rand::Rng; +use ark_std::{fmt::Debug, hash::Hash}; + +use ark_ff::bytes::ToBytes; + +pub mod constraints; +pub mod pedersen; + +use ark_crypto_primitives::Error; + +pub trait CommitmentScheme { + type Input; + type Output: ToBytes + Clone + Default + Eq + Hash + Debug; + type Parameters: Clone; + type Randomness: Clone + ToBytes + Default + Eq + UniformRand + Debug; + + fn setup(r: &mut R) -> Result; + + fn commit( + parameters: &Self::Parameters, + input: &Self::Input, + r: &Self::Randomness, + ) -> Result; +} diff --git a/mpc-algebra/src/commitment/constraints.rs b/mpc-algebra/src/commitment/constraints.rs new file mode 100644 index 00000000..d4bf53cf --- /dev/null +++ b/mpc-algebra/src/commitment/constraints.rs @@ -0,0 +1,23 @@ +use crate::commitment::CommitmentScheme; +use ark_ff::PrimeField; +use ark_r1cs_std::{alloc::AllocVar, R1CSVar, ToBytesGadget}; +use ark_relations::r1cs::SynthesisError; +use core::fmt::Debug; + +pub trait CommitmentGadget { + type OutputVar: ToBytesGadget + + AllocVar + + R1CSVar + + Clone + + Sized + + Debug; + type ParametersVar: AllocVar + Clone; + type RandomnessVar: AllocVar + Clone; + type InputVar: AllocVar + Clone; + + fn commit( + parameters: &Self::ParametersVar, + input: &Self::InputVar, + r: &Self::RandomnessVar, + ) -> Result; +} diff --git a/mpc-algebra/src/commitment/pedersen.rs b/mpc-algebra/src/commitment/pedersen.rs new file mode 100644 index 00000000..97901549 --- /dev/null +++ b/mpc-algebra/src/commitment/pedersen.rs @@ -0,0 +1,5 @@ +pub mod pedersen; +pub use pedersen::*; +pub mod constraints; +pub use constraints::*; +pub mod local_pedersen; diff --git a/mpc-algebra/src/commitment/pedersen/constraints.rs b/mpc-algebra/src/commitment/pedersen/constraints.rs new file mode 100644 index 00000000..15bcd632 --- /dev/null +++ b/mpc-algebra/src/commitment/pedersen/constraints.rs @@ -0,0 +1,225 @@ +use crate::mpc_primitives::ModulusConversion; +use crate::{ + commitment::{ + constraints::CommitmentGadget, + pedersen::{Commitment, Input, Parameters, Randomness}, + }, + mpc_primitives, MpcUInt8, +}; + +use crate::crh::pedersen::Window; + +// use ark_crypto_primitives::{ +// commitment::pedersen::{Commitment, Parameters, Randomness}, +// crh::pedersen::Window, +// }; +use ark_ec::ProjectiveCurve; +use ark_ff::BigInteger; +use ark_ff::{ + fields::{Field, PrimeField}, + to_bytes, SquareRootField, Zero, +}; +use ark_r1cs_std::alloc::{AllocVar, AllocationMode}; +use ark_relations::r1cs::{Namespace, SynthesisError}; +use mpc_trait::MpcWire; + +// use ark_r1cs_std::prelude::*; +use crate::groups::GroupOpsBounds; + +use crate::mpc_primitives::BitDecomposition; +use crate::r1cs_helper::groups::MpcCurveVar; +use crate::r1cs_helper::mpc_bits::MpcToBitsGadget; +use crate::wire::boolean_field::BooleanWire; +use crate::MpcBoolean; + +use core::{borrow::Borrow, marker::PhantomData}; +use derivative::Derivative; + +type ConstraintF = <::BaseField as Field>::BasePrimeField; + +#[derive(Derivative)] +#[derivative(Clone(bound = "C: ProjectiveCurve, GG: MpcCurveVar>"))] +pub struct ParametersVar>> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + pub params: Parameters, + #[doc(hidden)] + _group_var: PhantomData, +} + +#[derive(Clone, Debug)] +// pub struct RandomnessVar(Vec>); +pub struct RandomnessVar(Vec>); + +#[derive(Clone, Debug)] +pub struct InputVar(Vec>); + +pub struct CommGadget>, W: Window> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + #[doc(hidden)] + _curve: PhantomData<*const C>, + #[doc(hidden)] + _group_var: PhantomData<*const GG>, + #[doc(hidden)] + _window: PhantomData<*const W>, +} + +impl CommitmentGadget, ConstraintF> for CommGadget +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + W: Window, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, + ::ScalarField: + mpc_primitives::BitDecomposition + mpc_primitives::ModulusConversion>, +{ + type OutputVar = GG; + type ParametersVar = ParametersVar; + type RandomnessVar = RandomnessVar>; + type InputVar = InputVar>; + + #[tracing::instrument(target = "r1cs", skip(parameters, r))] + fn commit( + parameters: &Self::ParametersVar, + input: &Self::InputVar, + r: &Self::RandomnessVar, + ) -> Result { + assert!((input.0.len()) <= (W::WINDOW_SIZE * W::NUM_WINDOWS)); + + let mut padded_input: Vec>> = input.0.clone(); + // Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`. + if (input.0.len()) < W::WINDOW_SIZE * W::NUM_WINDOWS { + let current_length = input.0.len(); + for _ in current_length..(W::WINDOW_SIZE * W::NUM_WINDOWS) { + padded_input.push(MpcBoolean::constant(false)); + } + } + + assert_eq!(padded_input.len(), W::WINDOW_SIZE * W::NUM_WINDOWS); + assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS); + + // Allocate new variable for commitment output. + + let input_in_bits = padded_input.chunks(W::WINDOW_SIZE); + let mut result = + GG::precomputed_base_multiscalar_mul_le(¶meters.params.generators, input_in_bits)?; + + // Compute h^r + // let rand_bits: Vec<_> = + // r.0.iter() + // .flat_map(|byte| byte.to_bits_le().unwrap()) + // .collect(); + + let rand_bits: Vec<_> = r.0.clone(); + result.precomputed_base_scalar_mul_le( + rand_bits + .iter() + .zip(¶meters.params.randomness_generator), + )?; + + Ok(result) + } +} + +impl AllocVar, ConstraintF> for ParametersVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + fn new_variable>>( + _cs: impl Into>>, + f: impl FnOnce() -> Result, + _mode: AllocationMode, + ) -> Result { + let params = f()?.borrow().clone(); + Ok(ParametersVar { + params, + _group_var: PhantomData, + }) + } +} + +impl AllocVar, F> for RandomnessVar +where + C: ProjectiveCurve, + F: PrimeField, + ::ScalarField: + mpc_primitives::BitDecomposition + ModulusConversion, +{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let r = f().map(|r| r.borrow().0).unwrap_or(C::ScalarField::zero()); + + let bits_r = if r.is_shared() { + // shared + r.bit_decomposition() + .iter() + .map(|b| b.field().modulus_conversion()) + .collect::>() + } else { + // public + r.into_repr() + .to_bits_le() + .iter() + .map(|b| F::from(*b)) + .collect::>() + }; + + // padding + let mut bits_r = bits_r; + for _ in bits_r.len()..F::BigInt::NUM_LIMBS * 64 { + bits_r.push(F::zero()); + } + + match mode { + AllocationMode::Constant => unimplemented!(), + AllocationMode::Input => MpcBoolean::new_input_vec(cs, &bits_r).map(Self), + AllocationMode::Witness => MpcBoolean::new_witness_vec(cs, &bits_r).map(Self), + } + } +} + +impl AllocVar, F> for InputVar +where + C: ProjectiveCurve, + F: PrimeField, + ::ScalarField: + mpc_primitives::BitDecomposition + ModulusConversion, +{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let x = f().map(|x| x.borrow().0).unwrap_or(C::ScalarField::zero()); + + let bits_x = x + .bit_decomposition() + .iter() + .map(|b| b.field().modulus_conversion()) + .collect::>(); + + // padding + let mut bits_x = bits_x; + for _ in bits_x.len()..F::BigInt::NUM_LIMBS * 64 { + bits_x.push(F::zero()); + } + + match mode { + AllocationMode::Constant => unimplemented!(), + AllocationMode::Input => MpcBoolean::new_input_vec(cs, &bits_x).map(Self), + AllocationMode::Witness => MpcBoolean::new_witness_vec(cs, &bits_x).map(Self), + } + } +} diff --git a/mpc-algebra/src/commitment/pedersen/local_pedersen.rs b/mpc-algebra/src/commitment/pedersen/local_pedersen.rs new file mode 100644 index 00000000..73937863 --- /dev/null +++ b/mpc-algebra/src/commitment/pedersen/local_pedersen.rs @@ -0,0 +1,91 @@ +use std::borrow::Borrow; + +use ark_crypto_primitives::{ + commitment::pedersen::{ + constraints::{CommGadget, ParametersVar, RandomnessVar}, + Commitment, Parameters, Randomness, + }, + crh::pedersen::Window, +}; +use ark_ec::ProjectiveCurve; +use ark_ff::{to_bytes, Field, PrimeField}; +use ark_r1cs_std::{ + alloc::{AllocVar, AllocationMode}, + groups::{CurveVar, GroupOpsBounds}, + uint8::UInt8, +}; +use ark_relations::r1cs::{Namespace, SynthesisError}; + +use crate::{constraints::CommitmentGadget, CommitmentScheme}; + +impl CommitmentScheme for Commitment { + type Input = Vec; + type Parameters = Parameters; + type Randomness = Randomness; + type Output = C::Affine; + + fn setup( + r: &mut R, + ) -> Result { + ::setup(r) + } + + fn commit( + parameters: &Self::Parameters, + input: &Self::Input, + r: &Self::Randomness, + ) -> Result { + ::commit(parameters, input, r) + } +} + +// constraint +type ConstraintF = <::BaseField as Field>::BasePrimeField; + +#[derive(Clone, Debug)] +pub struct InputVar(Vec>); + +impl CommitmentGadget, ConstraintF> for CommGadget +where + C: ProjectiveCurve, + GG: CurveVar>, + W: Window, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, +{ + type OutputVar = GG; + type ParametersVar = ParametersVar; + type RandomnessVar = RandomnessVar>; + type InputVar = InputVar>; + + fn commit( + parameters: &Self::ParametersVar, + input: &Self::InputVar, + r: &Self::RandomnessVar, + ) -> Result { + , ConstraintF>>::commit( + parameters, + input.0.as_slice(), + r, + ) + } +} + +impl AllocVar, F> for InputVar +where + F: PrimeField, +{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let obj = f()?; + let x = obj.borrow(); + + match mode { + AllocationMode::Constant => Ok(Self(UInt8::constant_vec(x))), + AllocationMode::Input => UInt8::new_input_vec(cs, x).map(Self), + AllocationMode::Witness => UInt8::new_witness_vec(cs, x).map(Self), + } + } +} diff --git a/mpc-algebra/src/commitment/pedersen/pedersen.rs b/mpc-algebra/src/commitment/pedersen/pedersen.rs new file mode 100644 index 00000000..9d546c8a --- /dev/null +++ b/mpc-algebra/src/commitment/pedersen/pedersen.rs @@ -0,0 +1,229 @@ +use ark_crypto_primitives::Error; +use ark_ec::{AffineCurve, ProjectiveCurve}; +use ark_ff::{bytes::ToBytes, BitIteratorLE, Field, FpParameters, PrimeField, ToConstraintField}; +use ark_std::io::{Result as IoResult, Write}; +use ark_std::marker::PhantomData; +use ark_std::rand::Rng; +use ark_std::{cfg_chunks, end_timer, start_timer, One, Zero}; +use ark_std::{PubUniformRand, UniformRand}; +use derivative::Derivative; +use mpc_trait::MpcWire; + +use crate::crh::{pedersen, pedersen::Window, CRH}; +use crate::wire::boolean_field::BooleanWire; +use crate::{BitDecomposition, CommitmentScheme, FieldShare, MpcField, Reveal}; + +// pub use ark_crypto_primitives::crh::pedersen::Window; +// use ark_crypto_primitives::crh::{pedersen, CRH}; + +#[derive(Clone, Debug)] +pub struct Parameters { + pub randomness_generator: Vec, + pub generators: Vec>, +} + +pub struct Commitment { + group: PhantomData, + window: PhantomData, +} + +#[derive(Derivative)] +#[derivative(Clone, Copy, PartialEq, Debug, Eq, Default)] +pub struct Input(pub C::ScalarField); + +impl Input { + pub fn new(input: C::ScalarField) -> Self { + Self(input) + } +} + +#[derive(Derivative)] +#[derivative(Clone, Copy, PartialEq, Debug, Eq, Default)] +pub struct Randomness(pub C::ScalarField); + +impl UniformRand for Randomness { + #[inline] + fn rand(rng: &mut R) -> Self { + Randomness(UniformRand::rand(rng)) + } +} + +impl MpcWire for Randomness {} + +impl PubUniformRand for Randomness { + #[inline] + fn pub_rand(rng: &mut R) -> Self { + Randomness(PubUniformRand::pub_rand(rng)) + } +} + +impl ToBytes for Randomness { + fn write(&self, writer: W) -> IoResult<()> { + self.0.write(writer) + } +} + +impl CommitmentScheme for Commitment +where + // ::ScalarField: Reveal + BitDecomposition>, + // C: Reveal, + // ::Base: ProjectiveCurve, + // ::Base: PrimeField, + ::ScalarField: BitDecomposition, +{ + // Input is expected to be a vector of field elements. Each field element represents bool. + type Input = Input; + type Parameters = Parameters; + type Randomness = Randomness; + type Output = C::Affine; + + fn setup(rng: &mut R) -> Result { + // let time = start_timer!(|| format!( + // "PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> C", + // W::NUM_WINDOWS, + // W::WINDOW_SIZE, + // W::NUM_WINDOWS * W::WINDOW_SIZE + // )); + let num_powers = ::Params::MODULUS_BITS as usize; + let randomness_generator = pedersen::CRH::::generator_powers(num_powers, rng); + let generators = pedersen::CRH::::create_generators(rng); + // end_timer!(time); + + Ok(Self::Parameters { + randomness_generator, + generators, + }) + } + + fn commit( + parameters: &Self::Parameters, + input: &Self::Input, + randomness: &Self::Randomness, + ) -> Result { + let commit_time = start_timer!(|| "PedersenCOMM::Commit"); + + let input_bits = input + .0 + .bit_decomposition() + .iter() + .map(|x| x.field()) + .collect::>(); + + // If the input is too long, return an error. + if input_bits.len() / 8 > W::WINDOW_SIZE * W::NUM_WINDOWS { + panic!("incorrect input length: {:?}", input_bits.len()); + } + + // check each element of input is bool if debug + #[cfg(debug_assertions)] + for i in input_bits.iter() { + let mut j = i.clone(); + j.publicize(); + assert!(j.is_zero() || j.is_one()); + } + + // Pad the input to the necessary length. + let mut padded_input = Vec::with_capacity(input_bits.len()); + let mut input = input_bits; + + let padded_input_vec; + if input.len() < W::WINDOW_SIZE * W::NUM_WINDOWS { + padded_input.extend_from_slice(input.as_slice()); + let padded_length = W::WINDOW_SIZE * W::NUM_WINDOWS; + padded_input.resize(padded_length, ::ScalarField::zero()); + padded_input_vec = padded_input.as_slice().to_vec(); + input = padded_input_vec; + } + assert_eq!(parameters.generators.len(), W::NUM_WINDOWS); + + // Invoke Pedersen CRH here, to prevent code duplication. + + let crh_parameters = pedersen::Parameters { + generators: parameters.generators.clone(), + }; + + let mut result: C = pedersen::CRH::::evaluate(&crh_parameters, &input)?.into(); + let randomize_time = start_timer!(|| "Randomize"); + + // Compute h^r.K + let iter_bases = parameters + .randomness_generator + .iter() + .map(|x| x.into_affine()) + .collect::>(); + let bits = randomness + .0 + .bit_decomposition() + .iter() + .map(|x| x.field()) + .collect::>(); + + result += cfg_chunks!(bits, W::WINDOW_SIZE) + .map(|bits| { + let mut encoded = C::zero(); + + encoded += C::Affine::multi_scalar_mul(&iter_bases, bits); + encoded + }) + .sum::(); + + // result += C::Affine::multi_scalar_mul(&iter_bases, &bits); + + end_timer!(randomize_time); + end_timer!(commit_time); + + Ok(result.into()) + } +} + +impl> + ToConstraintField for Parameters +{ + #[inline] + fn to_field_elements(&self) -> Option> { + Some(Vec::new()) + } +} + +impl Reveal for Parameters +where + ::Base: ProjectiveCurve, +{ + type Base = ark_crypto_primitives::commitment::pedersen::Parameters<::Base>; + + fn reveal(self) -> Self::Base { + Self::Base { + randomness_generator: self.randomness_generator.reveal(), + generators: self.generators.reveal(), + } + } + + fn from_add_shared(_b: Self::Base) -> Self { + unimplemented!() + } + + fn from_public(_b: Self::Base) -> Self { + unimplemented!() + } +} + +impl Reveal for Randomness +where + ::Base: ProjectiveCurve, + ::ScalarField: + Reveal::Base as ProjectiveCurve>::ScalarField>, +{ + type Base = ark_crypto_primitives::commitment::pedersen::Randomness<::Base>; + + fn reveal(self) -> Self::Base { + Self::Base { 0: self.0.reveal() } + } + + fn from_add_shared(_b: Self::Base) -> Self { + unimplemented!() + } + + fn from_public(_b: Self::Base) -> Self { + unimplemented!() + } +} diff --git a/mpc-algebra/src/crh.rs b/mpc-algebra/src/crh.rs new file mode 100644 index 00000000..7a2357cc --- /dev/null +++ b/mpc-algebra/src/crh.rs @@ -0,0 +1,60 @@ +use ark_ff::bytes::ToBytes; +use ark_std::hash::Hash; +use ark_std::rand::Rng; + +pub mod pedersen; + +use ark_crypto_primitives::Error; + +pub mod constraints; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +pub use constraints::*; + +pub trait CRH { + const INPUT_SIZE_BITS: usize; + + type Output: ToBytes + + Clone + + Eq + + core::fmt::Debug + + Hash + + Default + + CanonicalSerialize + + CanonicalDeserialize; + type Parameters: Clone + Default; + type Input; + + fn setup(r: &mut R) -> Result; + fn evaluate(parameters: &Self::Parameters, input: &Self::Input) -> Result; +} + +pub trait TwoToOneCRH { + /// The bit size of the left input. + const LEFT_INPUT_SIZE_BITS: usize; + /// The bit size of the right input. + const RIGHT_INPUT_SIZE_BITS: usize; + + type Output: ToBytes + + Clone + + Eq + + core::fmt::Debug + + Hash + + Default + + CanonicalSerialize + + CanonicalDeserialize; + type Parameters: Clone + Default; + type Input; + + fn setup(r: &mut R) -> Result; + /// Evaluates this CRH on the left and right inputs. + /// + /// # Panics + /// + /// If `left_input.len() != Self::LEFT_INPUT_SIZE_BITS`, or if + /// `right_input.len() != Self::RIGHT_INPUT_SIZE_BITS`, then this method panics. + fn evaluate( + parameters: &Self::Parameters, + left_input: &Self::Input, + right_input: &Self::Input, + ) -> Result; +} diff --git a/mpc-algebra/src/crh/constraints.rs b/mpc-algebra/src/crh/constraints.rs new file mode 100644 index 00000000..4c539d0c --- /dev/null +++ b/mpc-algebra/src/crh/constraints.rs @@ -0,0 +1,47 @@ +use ark_ff::{Field, PrimeField}; +use core::fmt::Debug; + +use crate::{ + crh::{TwoToOneCRH, CRH}, + FieldShare, MpcCondSelectGadget, MpcEqGadget, MpcUInt8, +}; +use ark_relations::r1cs::SynthesisError; + +use ark_r1cs_std::prelude::*; + +pub trait CRHGadget: Sized { + type OutputVar: MpcEqGadget + + ToBytesGadget + + MpcCondSelectGadget + + AllocVar + + R1CSVar + + Debug + + Clone + + Sized; + + type ParametersVar: AllocVar + Clone; + + fn evaluate( + parameters: &Self::ParametersVar, + input: &[MpcUInt8], + ) -> Result; +} + +pub trait TwoToOneCRHGadget: Sized { + type OutputVar: MpcEqGadget + + ToBytesGadget + + MpcCondSelectGadget + + AllocVar + + R1CSVar + + Debug + + Clone + + Sized; + + type ParametersVar: AllocVar + Clone; + + fn evaluate( + parameters: &Self::ParametersVar, + left_input: &[MpcUInt8], + right_input: &[MpcUInt8], + ) -> Result; +} diff --git a/mpc-algebra/src/crh/pedersen.rs b/mpc-algebra/src/crh/pedersen.rs new file mode 100644 index 00000000..9ec2a7a4 --- /dev/null +++ b/mpc-algebra/src/crh/pedersen.rs @@ -0,0 +1,4 @@ +pub mod pedersen; +pub use pedersen::*; +pub mod constraints; +pub use constraints::*; diff --git a/mpc-algebra/src/crh/pedersen/constraints.rs b/mpc-algebra/src/crh/pedersen/constraints.rs new file mode 100644 index 00000000..9ee9f293 --- /dev/null +++ b/mpc-algebra/src/crh/pedersen/constraints.rs @@ -0,0 +1,230 @@ +use crate::{ + crh::{ + pedersen::{Parameters, Window, CRH}, + CRHGadget as CRHGadgetTrait, + }, + FieldShare, MpcBoolean, MpcUInt8, +}; +use ark_ec::ProjectiveCurve; +use ark_ff::Field; +use ark_r1cs_std::alloc::{AllocVar, AllocationMode}; +// use ark_r1cs_std::prelude::*; + +use crate::r1cs_helper::groups::{GroupOpsBounds, MpcCurveVar}; +use ark_relations::r1cs::{Namespace, SynthesisError}; +use ark_std::vec::Vec; +use derivative::Derivative; + +use ark_ff::SquareRootField; + +use crate::crh::TwoToOneCRHGadget; +use crate::r1cs_helper::mpc_bits::MpcToBitsGadget; +use core::{borrow::Borrow, marker::PhantomData}; + +#[derive(Derivative)] +#[derivative(Clone(bound = "C: ProjectiveCurve, GG: MpcCurveVar>"))] +pub struct CRHParametersVar>> +where + <::BaseField as Field>::BasePrimeField: SquareRootField, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, +{ + params: Parameters, + #[doc(hidden)] + _group_g: PhantomData, + + // remove later + #[doc(hidden)] + _curve: PhantomData, +} + +type ConstraintF = <::BaseField as Field>::BasePrimeField; +pub struct CRHGadget>, W: Window> +where + <::BaseField as Field>::BasePrimeField: SquareRootField, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, +{ + #[doc(hidden)] + _group: PhantomData<*const C>, + #[doc(hidden)] + _group_var: PhantomData<*const GG>, + #[doc(hidden)] + _window: PhantomData<*const W>, +} + +impl CRHGadgetTrait, ConstraintF> for CRHGadget +where + C: ProjectiveCurve, + <::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + GG: MpcCurveVar>, + W: Window, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, +{ + type OutputVar = GG; + type ParametersVar = CRHParametersVar; + + #[tracing::instrument(target = "r1cs", skip(parameters, input))] + fn evaluate( + parameters: &Self::ParametersVar, + input: &[MpcUInt8>], + ) -> Result { + let mut padded_input = input.to_vec(); + // Pad the input if it is not the current length. + if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS { + let current_length = input.len(); + for _ in current_length..(W::WINDOW_SIZE * W::NUM_WINDOWS / 8) { + padded_input.push(MpcUInt8::constant(0u8)); + } + } + assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS); + assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS); + + // Allocate new variable for the result. + let input_in_bits: Vec> = padded_input + .iter() + .flat_map(|b| b.to_bits_le().unwrap()) + .collect(); + let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); + let result = + GG::precomputed_base_multiscalar_mul_le(¶meters.params.generators, input_in_bits)?; + Ok(result) + } +} + +impl TwoToOneCRHGadget, ConstraintF> for CRHGadget +where + C: ProjectiveCurve, + <::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + GG: MpcCurveVar>, + W: Window, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, +{ + type OutputVar = GG; + type ParametersVar = CRHParametersVar; + + #[tracing::instrument(target = "r1cs", skip(parameters))] + fn evaluate( + parameters: &Self::ParametersVar, + left_input: &[MpcUInt8>], + right_input: &[MpcUInt8>], + ) -> Result { + // assume equality of left and right length + assert_eq!(left_input.len(), right_input.len()); + let chained_input: Vec<_> = left_input + .to_vec() + .into_iter() + .chain(right_input.to_vec().into_iter()) + .collect(); + >::evaluate(parameters, &chained_input) + } +} + +impl AllocVar, ConstraintF> for CRHParametersVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + <::BaseField as Field>::BasePrimeField: SquareRootField, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, +{ + #[tracing::instrument(target = "r1cs", skip(_cs, f))] + fn new_variable>>( + _cs: impl Into>>, + f: impl FnOnce() -> Result, + _mode: AllocationMode, + ) -> Result { + let params = f()?.borrow().clone(); + Ok(CRHParametersVar { + params, + _group_g: PhantomData, + _curve: PhantomData, + }) + } +} + +// #[cfg(test)] +// mod test { +// use crate::crh::{pedersen, CRHGadget, TwoToOneCRH, TwoToOneCRHGadget, CRH}; +// use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective as JubJub, Fq as Fr}; +// use ark_r1cs_std::prelude::*; +// use ark_relations::r1cs::{ConstraintSystem, ConstraintSystemRef}; +// use ark_std::rand::Rng; +// use ark_std::test_rng; + +// type TestCRH = pedersen::CRH; +// type TestCRHGadget = pedersen::constraints::CRHGadget; + +// #[derive(Clone, PartialEq, Eq, Hash)] +// pub(super) struct Window; + +// impl pedersen::Window for Window { +// const WINDOW_SIZE: usize = 128; +// const NUM_WINDOWS: usize = 8; +// } + +// fn generate_u8_input( +// cs: ConstraintSystemRef, +// size: usize, +// rng: &mut R, +// ) -> (Vec, Vec>) { +// let mut input = vec![1u8; size]; +// rng.fill_bytes(&mut input); + +// let mut input_bytes = vec![]; +// for byte in input.iter() { +// input_bytes.push(UInt8::new_witness(cs.clone(), || Ok(byte)).unwrap()); +// } +// (input, input_bytes) +// } + +// #[test] +// fn test_native_equality() { +// let rng = &mut test_rng(); +// let cs = ConstraintSystem::::new_ref(); + +// let (input, input_var) = generate_u8_input(cs.clone(), 128, rng); + +// let parameters = ::setup(rng).unwrap(); +// let primitive_result = ::evaluate(¶meters, &input).unwrap(); + +// let parameters_var = pedersen::constraints::CRHParametersVar::new_constant( +// ark_relations::ns!(cs, "CRH Parameters"), +// ¶meters, +// ) +// .unwrap(); + +// let result_var = +// >::evaluate(¶meters_var, &input_var).unwrap(); + +// let primitive_result = primitive_result; +// assert_eq!(primitive_result, result_var.value().unwrap()); +// assert!(cs.is_satisfied().unwrap()); +// } + +// #[test] +// fn test_naive_two_to_one_equality() { +// let rng = &mut test_rng(); +// let cs = ConstraintSystem::::new_ref(); + +// let (left_input, left_input_var) = generate_u8_input(cs.clone(), 64, rng); +// let (right_input, right_input_var) = generate_u8_input(cs.clone(), 64, rng); +// let parameters = ::setup(rng).unwrap(); +// let primitive_result = +// ::evaluate(¶meters, &left_input, &right_input).unwrap(); + +// let parameters_var = pedersen::constraints::CRHParametersVar::new_constant( +// ark_relations::ns!(cs, "CRH Parameters"), +// ¶meters, +// ) +// .unwrap(); + +// let result_var = >::evaluate( +// ¶meters_var, +// &left_input_var, +// &right_input_var, +// ) +// .unwrap(); + +// let primitive_result = primitive_result; +// assert_eq!(primitive_result, result_var.value().unwrap()); +// assert!(cs.is_satisfied().unwrap()); +// } +// } diff --git a/mpc-algebra/src/crh/pedersen/pedersen.rs b/mpc-algebra/src/crh/pedersen/pedersen.rs new file mode 100644 index 00000000..e84bf0df --- /dev/null +++ b/mpc-algebra/src/crh/pedersen/pedersen.rs @@ -0,0 +1,206 @@ +use ark_crypto_primitives::Error; +use ark_std::rand::Rng; +use ark_std::vec::Vec; +use ark_std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + marker::PhantomData, +}; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +use crate::crh::{TwoToOneCRH, CRH as CRHTrait}; +use ark_ec::AffineCurve; +use ark_ec::ProjectiveCurve; +use ark_ff::{Field, ToConstraintField}; +use ark_std::Zero; +use ark_std::{cfg_chunks, end_timer, start_timer}; + +#[cfg(feature = "r1cs")] +pub mod constraints; + +pub trait Window: Clone { + const WINDOW_SIZE: usize; + const NUM_WINDOWS: usize; +} + +#[derive(Clone, Default)] +pub struct Parameters { + pub generators: Vec>, +} + +pub struct CRH { + group: PhantomData, + window: PhantomData, +} + +impl CRH { + pub fn create_generators(rng: &mut R) -> Vec> { + let mut generators_powers = Vec::new(); + for _ in 0..W::NUM_WINDOWS { + generators_powers.push(Self::generator_powers(W::WINDOW_SIZE, rng)); + } + generators_powers + } + + pub fn generator_powers(num_powers: usize, _rng: &mut R) -> Vec { + let mut cur_gen_powers = Vec::with_capacity(num_powers); + let mut base = C::prime_subgroup_generator(); + for _ in 0..num_powers { + cur_gen_powers.push(base); + base.double_in_place(); + } + cur_gen_powers + } +} + +impl CRHTrait for CRH { + const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS; + type Output = C::Affine; + type Parameters = Parameters; + type Input = Vec; + + fn setup(rng: &mut R) -> Result { + let time = start_timer!(|| format!( + "PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> C", + W::NUM_WINDOWS, + W::WINDOW_SIZE, + W::NUM_WINDOWS * W::WINDOW_SIZE + )); + let generators = Self::create_generators(rng); + end_timer!(time); + Ok(Self::Parameters { generators }) + } + + fn evaluate(parameters: &Self::Parameters, input: &Self::Input) -> Result { + let eval_time = start_timer!(|| "PedersenCRH::Eval"); + + if (input.len()) > W::WINDOW_SIZE * W::NUM_WINDOWS { + panic!( + "incorrect input length {:?} for window params {:?}✕{:?}", + input.len(), + W::WINDOW_SIZE, + W::NUM_WINDOWS + ); + } + + let mut padded_input = Vec::with_capacity(input.len()); + let mut input = input; + // Pad the input if it is not the current length. + if (input.len()) < W::WINDOW_SIZE * W::NUM_WINDOWS { + padded_input.extend_from_slice(input); + let padded_length = (W::WINDOW_SIZE * W::NUM_WINDOWS); + padded_input.resize(padded_length, C::ScalarField::zero()); + input = &padded_input; + } + + assert_eq!( + parameters.generators.len(), + W::NUM_WINDOWS, + "Incorrect pp of size {:?}✕{:?} for window params {:?}✕{:?}", + parameters.generators[0].len(), + parameters.generators.len(), + W::WINDOW_SIZE, + W::NUM_WINDOWS + ); + + // Compute sum of h_i^{m_i} for all i. + // let bits = bytes_to_bits(input); + let result = cfg_chunks!(input, W::WINDOW_SIZE) + .zip(¶meters.generators) + .map(|(bits, generator_powers)| { + let mut encoded = C::zero(); + // for (bit, base) in bits.iter().zip(generator_powers.iter()) { + // // if *bit { + // // encoded += base; + // // } + + // let mut a = *base; + // a *= *bit; + // encoded += a; + // } + + let iter_bases = generator_powers + .iter() + .map(|x| x.into_affine()) + .collect::>(); + + encoded += C::Affine::multi_scalar_mul(&iter_bases, bits); + encoded + }) + .sum::(); + + end_timer!(eval_time); + + Ok(result.into()) + } +} + +impl TwoToOneCRH for CRH { + const LEFT_INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS / 2; + const RIGHT_INPUT_SIZE_BITS: usize = Self::LEFT_INPUT_SIZE_BITS; + type Output = C::Affine; + type Parameters = Parameters; + type Input = Vec; + + fn setup(r: &mut R) -> Result { + ::setup(r) + } + + /// A simple implementation method: just concat the left input and right input together + /// + /// `evaluate` requires that `left_input` and `right_input` are of equal length. + fn evaluate( + parameters: &Self::Parameters, + left_input: &Self::Input, + right_input: &Self::Input, + ) -> Result { + // assert_eq!( + // left_input.len(), + // right_input.len(), + // "left and right input should be of equal length" + // ); + // // check overflow + + // debug_assert!(left_input.len() * 8 <= Self::LEFT_INPUT_SIZE_BITS); + + // let mut buffer = vec![0u8; (Self::LEFT_INPUT_SIZE_BITS + Self::RIGHT_INPUT_SIZE_BITS) / 8]; + + // buffer + // .iter_mut() + // .zip(left_input.iter().chain(right_input.iter())) + // .for_each(|(b, l_b)| *b = *l_b); + + // ::evaluate(parameters, &buffer) + unimplemented!() + } +} + +pub fn bytes_to_bits(bytes: &[u8]) -> Vec { + let mut bits = Vec::with_capacity(bytes.len() * 8); + for byte in bytes { + for i in 0..8 { + let bit = (*byte >> i) & 1; + bits.push(bit == 1) + } + } + bits +} + +impl Debug for Parameters { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + writeln!(f, "Pedersen Hash Parameters {{")?; + for (i, g) in self.generators.iter().enumerate() { + writeln!(f, "\t Generator {}: {:?}", i, g)?; + } + writeln!(f, "}}") + } +} + +impl> + ToConstraintField for Parameters +{ + #[inline] + fn to_field_elements(&self) -> Option> { + Some(Vec::new()) + } +} diff --git a/mpc-algebra/src/encryption.rs b/mpc-algebra/src/encryption.rs new file mode 100644 index 00000000..a047eb10 --- /dev/null +++ b/mpc-algebra/src/encryption.rs @@ -0,0 +1,2 @@ +pub mod constraints; +pub mod elgamal; diff --git a/mpc-algebra/src/encryption/constraints.rs b/mpc-algebra/src/encryption/constraints.rs new file mode 100644 index 00000000..37e9de8a --- /dev/null +++ b/mpc-algebra/src/encryption/constraints.rs @@ -0,0 +1,26 @@ +use ark_crypto_primitives::encryption::AsymmetricEncryptionScheme; + +use ark_r1cs_std::prelude::*; +use ark_relations::r1cs::SynthesisError; +use core::fmt::Debug; + +use ark_ff::fields::Field; + +pub trait AsymmetricEncryptionGadget { + type OutputVar: AllocVar + // + EqGadget + + Clone + + Sized + + Debug; + type ParametersVar: AllocVar + Clone; + type PlaintextVar: AllocVar + Clone; + type PublicKeyVar: AllocVar + Clone; + type RandomnessVar: AllocVar + Clone; + + fn encrypt( + parameters: &Self::ParametersVar, + message: &Self::PlaintextVar, + randomness: &Self::RandomnessVar, + public_key: &Self::PublicKeyVar, + ) -> Result; +} diff --git a/mpc-algebra/src/encryption/elgamal.rs b/mpc-algebra/src/encryption/elgamal.rs new file mode 100644 index 00000000..cc99f5bb --- /dev/null +++ b/mpc-algebra/src/encryption/elgamal.rs @@ -0,0 +1,3 @@ +pub mod constraints; +pub mod elgamal; +pub mod local_elgamal; diff --git a/mpc-algebra/src/encryption/elgamal/constraints.rs b/mpc-algebra/src/encryption/elgamal/constraints.rs new file mode 100644 index 00000000..62f4d744 --- /dev/null +++ b/mpc-algebra/src/encryption/elgamal/constraints.rs @@ -0,0 +1,240 @@ +use ark_ff::SquareRootField; +use ark_r1cs_std::alloc::{AllocVar, AllocationMode}; +// use ark_r1cs_std::prelude::*; +use ark_relations::r1cs::{Namespace, SynthesisError}; +use derivative::Derivative; + +use crate::encryption::constraints::AsymmetricEncryptionGadget; +use crate::encryption::elgamal::elgamal::{ + Ciphertext, ElGamal, Parameters, Plaintext, PublicKey, Randomness, +}; +use crate::groups::MpcCurveVar; +use crate::{MpcBoolean, MpcEqGadget, MpcUInt8}; +use ark_ec::ProjectiveCurve; +use ark_ff::{ + fields::{Field, PrimeField}, + to_bytes, Zero, +}; +use ark_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; + +use crate::groups::GroupOpsBounds; + +pub type ConstraintF = <::BaseField as Field>::BasePrimeField; + +#[derive(Clone, Debug)] +pub struct RandomnessVar(Vec>); + +impl AllocVar, F> for RandomnessVar +where + C: ProjectiveCurve, + F: PrimeField, +{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let r = to_bytes![&f().map(|b| b.borrow().0).unwrap_or(C::ScalarField::zero())].unwrap(); + match mode { + AllocationMode::Constant => Ok(Self(MpcUInt8::constant_vec(&r))), + AllocationMode::Input => todo!(), //MpcUInt8::new_input_vec(cs, &r).map(Self), + AllocationMode::Witness => MpcUInt8::new_witness_vec(cs, &r).map(Self), + } + } +} + +#[derive(Derivative)] +#[derivative(Clone(bound = "C: ProjectiveCurve, GG: MpcCurveVar>"))] +pub struct ParametersVar>> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + pub generator: GG, + #[doc(hidden)] + _curve: PhantomData, +} + +impl AllocVar, ConstraintF> for ParametersVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + fn new_variable>>( + cs: impl Into>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let generator = GG::new_variable(cs, || f().map(|g| g.borrow().generator), mode)?; + Ok(Self { + generator, + _curve: PhantomData, + }) + } +} + +#[derive(Derivative)] +#[derivative(Clone(bound = "C: ProjectiveCurve, GG: MpcCurveVar>"))] +pub struct PlaintextVar>> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + pub plaintext: GG, + #[doc(hidden)] + _curve: PhantomData, +} + +impl AllocVar, ConstraintF> for PlaintextVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + fn new_variable>>( + cs: impl Into>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let plaintext = GG::new_variable(cs, f, mode)?; + Ok(Self { + plaintext, + _curve: PhantomData, + }) + } +} + +#[derive(Derivative)] +#[derivative(Clone(bound = "C: ProjectiveCurve, GG: MpcCurveVar>"))] +pub struct PublicKeyVar>> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + pub pk: GG, + #[doc(hidden)] + _curve: PhantomData, +} + +impl AllocVar, ConstraintF> for PublicKeyVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + fn new_variable>>( + cs: impl Into>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let pk = GG::new_variable(cs, f, mode)?; + Ok(Self { + pk, + _curve: PhantomData, + }) + } +} + +#[derive(Derivative, Debug)] +#[derivative(Clone(bound = "C: ProjectiveCurve, GG: MpcCurveVar>"))] +pub struct OutputVar>> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + pub c1: GG, + pub c2: GG, + #[doc(hidden)] + _curve: PhantomData, +} + +impl OutputVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + pub fn new(c1: GG, c2: GG) -> Self { + Self { + c1, + c2, + _curve: PhantomData, + } + } +} + +impl AllocVar, ConstraintF> for OutputVar +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + fn new_variable>>( + cs: impl Into>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let prep = f().map(|g| *g.borrow()); + let c1 = GG::new_variable(cs.clone(), || prep.map(|g| g.borrow().0), mode)?; + let c2 = GG::new_variable(cs.clone(), || prep.map(|g| g.borrow().1), mode)?; + Ok(Self { + c1, + c2, + _curve: PhantomData, + }) + } +} + +impl MpcEqGadget> for OutputVar +where + C: ProjectiveCurve, + GC: MpcCurveVar>, + for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, + ConstraintF: PrimeField + SquareRootField, +{ + #[inline] + fn is_eq(&self, other: &Self) -> Result>, SynthesisError> { + self.c1.is_eq(&other.c1)?.and(&self.c2.is_eq(&other.c2)?) + } +} + +pub struct ElGamalEncGadget>> +where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + #[doc(hidden)] + _curve: PhantomData<*const C>, + _group_var: PhantomData<*const GG>, +} + +impl AsymmetricEncryptionGadget, ConstraintF> for ElGamalEncGadget +where + C: ProjectiveCurve, + GG: MpcCurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField + SquareRootField, +{ + type OutputVar = OutputVar; + type ParametersVar = ParametersVar; + type PlaintextVar = PlaintextVar; + type PublicKeyVar = PublicKeyVar; + type RandomnessVar = RandomnessVar>; + + fn encrypt( + parameters: &Self::ParametersVar, + message: &Self::PlaintextVar, + randomness: &Self::RandomnessVar, + public_key: &Self::PublicKeyVar, + ) -> Result { + todo!(); + } +} diff --git a/mpc-algebra/src/encryption/elgamal/elgamal.rs b/mpc-algebra/src/encryption/elgamal/elgamal.rs new file mode 100644 index 00000000..b0ce1b82 --- /dev/null +++ b/mpc-algebra/src/encryption/elgamal/elgamal.rs @@ -0,0 +1,116 @@ +use ark_crypto_primitives::encryption::AsymmetricEncryptionScheme; +use ark_crypto_primitives::Error; +use ark_ec::{AffineCurve, ProjectiveCurve}; +use ark_ff::{fields::PrimeField, UniformRand}; +use ark_std::marker::PhantomData; +use ark_std::rand::Rng; + +pub struct ElGamal { + _group: PhantomData, +} + +#[derive(Clone)] +pub struct Parameters { + pub generator: C::Affine, +} + +impl Parameters { + pub fn new(generator: C::Affine) -> Self { + Parameters { generator } + } +} + +pub type PublicKey = ::Affine; + +pub struct SecretKey(pub C::ScalarField); + +impl SecretKey { + pub fn new(secret_key: C::ScalarField) -> Self { + SecretKey(secret_key) + } +} + +#[derive(Clone)] +pub struct Randomness(pub C::ScalarField); + +impl UniformRand for Randomness { + #[inline] + fn rand(rng: &mut R) -> Self { + Randomness(::ScalarField::rand(rng)) + } +} + +pub type Plaintext = ::Affine; + +pub type Ciphertext = ( + ::Affine, + ::Affine, +); + +impl AsymmetricEncryptionScheme for ElGamal +where + C::ScalarField: PrimeField, +{ + type Parameters = Parameters; + type PublicKey = PublicKey; + type SecretKey = SecretKey; + type Randomness = Randomness; + type Plaintext = Plaintext; + type Ciphertext = Ciphertext; + + fn setup(rng: &mut R) -> Result { + // get a random generator + let generator = C::rand(rng).into(); + + Ok(Parameters { generator }) + } + + fn keygen( + pp: &Self::Parameters, + rng: &mut R, + ) -> Result<(Self::PublicKey, Self::SecretKey), Error> { + // get a random element from the scalar field + let secret_key: ::ScalarField = C::ScalarField::rand(rng); + + // compute secret_key*generator to derive the public key + let public_key = pp.generator.mul(secret_key.into_repr()).into(); + + Ok((public_key, SecretKey(secret_key))) + } + + fn encrypt( + pp: &Self::Parameters, + pk: &Self::PublicKey, + message: &Self::Plaintext, + r: &Self::Randomness, + ) -> Result { + // compute s = r*pk + let s = pk.scalar_mul(r.0).into(); + + // compute c1 = r*generator + let c1 = pp.generator.scalar_mul(r.0).into(); + + // compute c2 = m + s + let c2 = *message + s; + + Ok((c1, c2)) + } + + fn decrypt( + _pp: &Self::Parameters, + sk: &Self::SecretKey, + ciphertext: &Self::Ciphertext, + ) -> Result { + let c1: ::Affine = ciphertext.0; + let c2: ::Affine = ciphertext.1; + + // compute s = secret_key * c1 + let s = c1.mul(sk.0.into_repr()); + let s_inv = -s; + + // compute message = c2 - s + let m = c2 + s_inv.into_affine(); + + Ok(m) + } +} diff --git a/mpc-algebra/src/encryption/elgamal/local_elgamal.rs b/mpc-algebra/src/encryption/elgamal/local_elgamal.rs new file mode 100644 index 00000000..d5afd3b3 --- /dev/null +++ b/mpc-algebra/src/encryption/elgamal/local_elgamal.rs @@ -0,0 +1,39 @@ +use crate::encryption::constraints::AsymmetricEncryptionGadget; + +use ark_crypto_primitives::encryption::elgamal::{ + constraints::{ + ConstraintF, ElGamalEncGadget, OutputVar, ParametersVar, PlaintextVar, PublicKeyVar, + RandomnessVar, + }, + ElGamal, +}; +use ark_ec::ProjectiveCurve; +use ark_ff::PrimeField; + +use ark_r1cs_std::groups::{CurveVar, GroupOpsBounds}; +use ark_relations::r1cs::SynthesisError; + +impl AsymmetricEncryptionGadget, ConstraintF> for ElGamalEncGadget +where + C: ProjectiveCurve, + GG: CurveVar>, + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + ConstraintF: PrimeField, +{ + type OutputVar = OutputVar; + type ParametersVar = ParametersVar; + type PlaintextVar = PlaintextVar; + type PublicKeyVar = PublicKeyVar; + type RandomnessVar = RandomnessVar>; + + fn encrypt( + parameters: &Self::ParametersVar, + message: &Self::PlaintextVar, + randomness: &Self::RandomnessVar, + public_key: &Self::PublicKeyVar, + ) -> Result { + , ConstraintF>>::encrypt( + parameters, message, randomness, public_key, + ) + } +} diff --git a/mpc-algebra/src/lib.rs b/mpc-algebra/src/lib.rs index 1248773b..c831843c 100644 --- a/mpc-algebra/src/lib.rs +++ b/mpc-algebra/src/lib.rs @@ -10,6 +10,10 @@ pub mod mpc_primitives; pub use mpc_primitives::*; pub mod r1cs_helper; pub use r1cs_helper::*; +pub mod commitment; +pub use commitment::*; +pub mod crh; +pub mod encryption; pub mod channel; @@ -17,7 +21,7 @@ pub mod honest_but_curious { use super::{ share::additive::*, share::msm::NaiveMsm, - wire::{edwards, field, group, pairing, uint8}, + wire::{edwards2, field, group, pairing, uint8}, }; pub type MpcField = field::MpcField>; pub type MpcGroup = group::MpcGroup>>; @@ -31,17 +35,18 @@ pub mod honest_but_curious { pub type MpcU8Field = uint8::MpcU8Field>; - pub type MpcEdwardsParameters = edwards::AdditiveMpcEdwardsParameters; - pub type MpcEdwardsAffine = edwards::AdditiveMpcEdwardsAffine; - pub type MpcEdwardsProjective = edwards::AdditiveMpcEdwardsProjective; + pub type MpcEdwardsAffine = edwards2::AdditiveMpcEdwardsAffine; + pub type MpcEdwardsProjective = edwards2::AdditiveMpcEdwardsProjective; - pub type MpcEdwardsVar = edwards::AdditiveMpcEdwardsVar; + pub type AffProjShare

= edwards2::AdditiveAffProjShare

; + + pub type MpcEdwardsVar = edwards2::AdditiveMpcEdwardsVar; } pub mod malicious_majority { use super::{ share::msm::NaiveMsm, share::spdz::*, - wire::{edwards, field, group, pairing, uint8}, + wire::{edwards2, field, group, pairing, uint8}, }; pub type MpcField = field::MpcField>; pub type MpcGroup = group::MpcGroup>>; @@ -55,9 +60,10 @@ pub mod malicious_majority { pub type MpcU8Field = uint8::MpcU8Field>; - pub type MpcEdwardsParameters = edwards::SpdzMpcEdwardsParameters; - pub type MpcEdwardsAffine = edwards::SpdzMpcEdwardsAffine; - pub type MpcEdwardsProjective = edwards::SpdzMpcEdwardsProjective; + pub type MpcEdwardsAffine = edwards2::SpdzMpcEdwardsAffine; + pub type MpcEdwardsProjective = edwards2::SpdzMpcEdwardsProjective; + + pub type AffProjShare

= edwards2::SpdzAffProjShare

; - pub type MpcEdwardsVar = edwards::SpdzMpcEdwardsVar; + pub type MpcEdwardsVar = edwards2::SpdzMpcEdwardsVar; } diff --git a/mpc-algebra/src/mpc_primitives.rs b/mpc-algebra/src/mpc_primitives.rs index 59974e04..f821426e 100644 --- a/mpc-algebra/src/mpc_primitives.rs +++ b/mpc-algebra/src/mpc_primitives.rs @@ -1,3 +1,5 @@ +use ark_ff::BigInteger; +use ark_ff::PrimeField; use rand::Rng; use crate::boolean_field::BooleanWire; @@ -52,3 +54,10 @@ pub trait BitAdd { fn bit_add(self, other: &Self) -> Self::Output; } + +pub trait ModulusConversion: PrimeField { + fn modulus_conversion(&mut self) -> F { + let bits = self.into_repr().to_bits_le(); + F::from_repr(BigInteger::from_bits_le(&bits)).unwrap() + } +} diff --git a/mpc-algebra/src/r1cs_helper.rs b/mpc-algebra/src/r1cs_helper.rs index a9c29f36..c4c6a806 100644 --- a/mpc-algebra/src/r1cs_helper.rs +++ b/mpc-algebra/src/r1cs_helper.rs @@ -12,4 +12,5 @@ pub mod mpc_bits; pub use mpc_bits::*; pub mod mpc_uint8; pub use mpc_uint8::*; +pub mod groups; pub mod mpc_fields; diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/mod.rs b/mpc-algebra/src/r1cs_helper/groups/curves/mod.rs new file mode 100644 index 00000000..12489dfd --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/mod.rs @@ -0,0 +1,9 @@ +// /// This module generically implements arithmetic for Short +// /// Weierstrass elliptic curves by following the complete formulae of +// /// [[Renes, Costello, Batina 2015]](https://eprint.iacr.org/2015/1060). +// // pub mod short_weierstrass; + +/// This module generically implements arithmetic for Twisted +/// Edwards elliptic curves by following the complete formulae described in the +/// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html). +pub mod twisted_edwards; diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/bls12/mod.rs b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/bls12/mod.rs new file mode 100644 index 00000000..a9384a20 --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/bls12/mod.rs @@ -0,0 +1,247 @@ +use ark_ec::{ + bls12::{Bls12Parameters, G1Prepared, G2Prepared, TwistType}, + short_weierstrass_jacobian::GroupAffine, +}; +use ark_ff::{BitIteratorBE, Field, One}; +use ark_relations::r1cs::{Namespace, SynthesisError}; + +use crate::{ + fields::{fp::FpVar, fp2::Fp2Var, FieldVar}, + groups::curves::short_weierstrass::*, + Vec, +}; + +use core::fmt::Debug; + +/// Represents a projective point in G1. +pub type G1Var

= + ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; + +/// Represents an affine point on G1. Should be used only for comparison and +/// when a canonical representation of a point is required, and not for +/// arithmetic. +pub type G1AffineVar

= + AffineVar<

::G1Parameters, FpVar<

::Fp>>; + +/// Represents a projective point in G2. +pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp2G

>; +/// Represents an affine point on G2. Should be used only for comparison and +/// when a canonical representation of a point is required, and not for +/// arithmetic. +pub type G2AffineVar

= AffineVar<

::G2Parameters, Fp2G

>; + +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. +#[derive(Derivative)] +#[derivative(Clone(bound = "G1Var

: Clone"), Debug(bound = "G1Var

: Debug"))] +pub struct G1PreparedVar(pub AffineVar>); + +impl G1PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let x = self.0.x.value()?; + let y = self.0.y.value()?; + let infinity = self.0.infinity.value()?; + let g = GroupAffine::new(x, y, infinity); + Ok(g.into()) + } + + /// Constructs `Self` from a `G1Var`. + pub fn from_group_var(q: &G1Var

) -> Result { + let g = q.to_affine()?; + Ok(Self(g)) + } +} + +impl AllocVar, P::Fp> for G1PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let g1_prep = f().map(|b| b.borrow().0); + + let x = FpVar::new_variable(ark_relations::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?; + let y = FpVar::new_variable(ark_relations::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?; + let infinity = Boolean::new_variable( + ark_relations::ns!(cs, "inf"), + || g1_prep.map(|g| g.infinity), + mode, + )?; + let g = AffineVar::new(x, y, infinity); + Ok(Self(g)) + } +} + +impl ToBytesGadget for G1PreparedVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut bytes = self.0.x.to_bytes()?; + let y_bytes = self.0.y.to_bytes()?; + let inf_bytes = self.0.infinity.to_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut bytes = self.0.x.to_non_unique_bytes()?; + let y_bytes = self.0.y.to_non_unique_bytes()?; + let inf_bytes = self.0.infinity.to_non_unique_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) + } +} + +type Fp2G

= Fp2Var<

::Fp2Params>; +type LCoeff

= (Fp2G

, Fp2G

); +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. +#[derive(Derivative)] +#[derivative( + Clone(bound = "Fp2Var: Clone"), + Debug(bound = "Fp2Var: Debug") +)] +pub struct G2PreparedVar { + #[doc(hidden)] + pub ell_coeffs: Vec>, +} + +impl AllocVar, P::Fp> for G2PreparedVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f, mode))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let g2_prep = f().map(|b| { + let projective_coeffs = &b.borrow().ell_coeffs; + let mut z_s = projective_coeffs + .iter() + .map(|(_, _, z)| *z) + .collect::>(); + ark_ff::fields::batch_inversion(&mut z_s); + projective_coeffs + .iter() + .zip(z_s) + .map(|((x, y, _), z_inv)| (*x * &z_inv, *y * &z_inv)) + .collect::>() + }); + + let l = Vec::new_variable( + ark_relations::ns!(cs, "l"), + || { + g2_prep + .clone() + .map(|c| c.iter().map(|(l, _)| *l).collect::>()) + }, + mode, + )?; + let r = Vec::new_variable( + ark_relations::ns!(cs, "r"), + || g2_prep.map(|c| c.iter().map(|(_, r)| *r).collect::>()), + mode, + )?; + let ell_coeffs = l.into_iter().zip(r).collect(); + Ok(Self { ell_coeffs }) + } +} + +impl ToBytesGadget for G2PreparedVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut bytes = Vec::new(); + for coeffs in &self.ell_coeffs { + bytes.extend_from_slice(&coeffs.0.to_bytes()?); + bytes.extend_from_slice(&coeffs.1.to_bytes()?); + } + Ok(bytes) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut bytes = Vec::new(); + for coeffs in &self.ell_coeffs { + bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes()?); + bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes()?); + } + Ok(bytes) + } +} + +impl G2PreparedVar

{ + /// Constructs `Self` from a `G2Var`. + #[tracing::instrument(target = "r1cs")] + pub fn from_group_var(q: &G2Var

) -> Result { + let q = q.to_affine()?; + let two_inv = P::Fp::one().double().inverse().unwrap(); + // Enforce that `q` is not the point at infinity. + q.infinity.enforce_not_equal(&Boolean::Constant(true))?; + let mut ell_coeffs = vec![]; + let mut r = q.clone(); + + for i in BitIteratorBE::new(P::X).skip(1) { + ell_coeffs.push(Self::double(&mut r, &two_inv)?); + + if i { + ell_coeffs.push(Self::add(&mut r, &q)?); + } + } + + Ok(Self { ell_coeffs }) + } + + #[tracing::instrument(target = "r1cs")] + fn double(r: &mut G2AffineVar

, two_inv: &P::Fp) -> Result, SynthesisError> { + let a = r.y.inverse()?; + let mut b = r.x.square()?; + let b_tmp = b.clone(); + b.mul_assign_by_base_field_constant(*two_inv); + b += &b_tmp; + + let c = &a * &b; + let d = r.x.double()?; + let x3 = c.square()? - &d; + let e = &c * &r.x - &r.y; + let c_x3 = &c * &x3; + let y3 = &e - &c_x3; + let mut f = c; + f.negate_in_place()?; + r.x = x3; + r.y = y3; + match P::TWIST_TYPE { + TwistType::M => Ok((e, f)), + TwistType::D => Ok((f, e)), + } + } + + #[tracing::instrument(target = "r1cs")] + fn add(r: &mut G2AffineVar

, q: &G2AffineVar

) -> Result, SynthesisError> { + let a = (&q.x - &r.x).inverse()?; + let b = &q.y - &r.y; + let c = &a * &b; + let d = &r.x + &q.x; + let x3 = c.square()? - &d; + + let e = (&r.x - &x3) * &c; + let y3 = e - &r.y; + let g = &c * &r.x - &r.y; + let mut f = c; + f.negate_in_place()?; + r.x = x3; + r.y = y3; + match P::TWIST_TYPE { + TwistType::M => Ok((g, f)), + TwistType::D => Ok((f, g)), + } + } +} diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mnt4/mod.rs b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mnt4/mod.rs new file mode 100644 index 00000000..bd0bdc6a --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mnt4/mod.rs @@ -0,0 +1,495 @@ +use ark_ec::mnt4::{ + g2::{AteAdditionCoefficients, AteDoubleCoefficients}, + G1Prepared, G2Prepared, MNT4Parameters, +}; +use ark_ff::Field; +use ark_relations::r1cs::{Namespace, SynthesisError}; + +use crate::{ + fields::{fp::FpVar, fp2::Fp2Var, FieldVar}, + groups::curves::short_weierstrass::ProjectiveVar, + pairing::mnt4::PairingVar, + prelude::*, + Vec, +}; +use core::borrow::Borrow; + +/// Represents a projective point in G1. +pub type G1Var

= + ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; + +/// Represents a projective point in G2. +pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp2G

>; + +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] +pub struct G1PreparedVar { + #[doc(hidden)] + pub x: FpVar, + #[doc(hidden)] + pub y: FpVar, + #[doc(hidden)] + pub x_twist: Fp2Var, + #[doc(hidden)] + pub y_twist: Fp2Var, +} + +impl AllocVar, P::Fp> for G1PreparedVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g1_prep = f().map(|b| *b.borrow()); + + let x = FpVar::new_variable(ark_relations::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?; + let y = FpVar::new_variable(ark_relations::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?; + let x_twist = Fp2Var::new_variable( + ark_relations::ns!(cs, "x_twist"), + || g1_prep.map(|g| g.x_twist), + mode, + )?; + let y_twist = Fp2Var::new_variable( + ark_relations::ns!(cs, "y_twist"), + || g1_prep.map(|g| g.y_twist), + mode, + )?; + Ok(Self { + x, + y, + x_twist, + y_twist, + }) + } +} + +impl G1PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let (x, y, x_twist, y_twist) = ( + self.x.value()?, + self.y.value()?, + self.x_twist.value()?, + self.y_twist.value()?, + ); + Ok(G1Prepared { + x, + y, + x_twist, + y_twist, + }) + } + + /// Constructs `Self` from a `G1Var`. + #[tracing::instrument(target = "r1cs")] + pub fn from_group_var(q: &G1Var

) -> Result { + let q = q.to_affine()?; + let x_twist = Fp2Var::new(&q.x * P::TWIST.c0, &q.x * P::TWIST.c1); + let y_twist = Fp2Var::new(&q.y * P::TWIST.c0, &q.y * P::TWIST.c1); + Ok(G1PreparedVar { + x: q.x, + y: q.y, + x_twist, + y_twist, + }) + } +} + +impl ToBytesGadget for G1PreparedVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_twist = self.x_twist.to_bytes()?; + let mut y_twist = self.y_twist.to_bytes()?; + + x.append(&mut y); + x.append(&mut x_twist); + x.append(&mut y_twist); + Ok(x) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_twist = self.x_twist.to_non_unique_bytes()?; + let mut y_twist = self.y_twist.to_non_unique_bytes()?; + + x.append(&mut y); + x.append(&mut x_twist); + x.append(&mut y_twist); + Ok(x) + } +} + +type Fp2G

= Fp2Var<

::Fp2Params>; + +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] +pub struct G2PreparedVar { + #[doc(hidden)] + pub x: Fp2Var, + #[doc(hidden)] + pub y: Fp2Var, + #[doc(hidden)] + pub x_over_twist: Fp2Var, + #[doc(hidden)] + pub y_over_twist: Fp2Var, + #[doc(hidden)] + pub double_coefficients: Vec>, + #[doc(hidden)] + pub addition_coefficients: Vec>, +} + +impl AllocVar, P::Fp> for G2PreparedVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g2_prep = f().map(|b| b.borrow().clone()); + let g2 = g2_prep.as_ref().map_err(|e| *e); + + let x = Fp2Var::new_variable(ark_relations::ns!(cs, "x"), || g2.map(|g| g.x), mode)?; + let y = Fp2Var::new_variable(ark_relations::ns!(cs, "y"), || g2.map(|g| g.y), mode)?; + let x_over_twist = Fp2Var::new_variable( + ark_relations::ns!(cs, "x_over_twist"), + || g2.map(|g| g.x_over_twist), + mode, + )?; + let y_over_twist = Fp2Var::new_variable( + ark_relations::ns!(cs, "y_over_twist"), + || g2.map(|g| g.y_over_twist), + mode, + )?; + let double_coefficients = Vec::new_variable( + ark_relations::ns!(cs, "double coeffs"), + || g2.map(|g| g.double_coefficients.clone()), + mode, + )?; + let addition_coefficients = Vec::new_variable( + ark_relations::ns!(cs, "add coeffs"), + || g2.map(|g| g.addition_coefficients.clone()), + mode, + )?; + Ok(Self { + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, + }) + } +} + +impl ToBytesGadget for G2PreparedVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_over_twist = self.x_over_twist.to_bytes()?; + let mut y_over_twist = self.y_over_twist.to_bytes()?; + + x.append(&mut y); + x.append(&mut x_over_twist); + x.append(&mut y_over_twist); + + for coeff in &self.double_coefficients { + x.extend_from_slice(&coeff.to_bytes()?); + } + for coeff in &self.addition_coefficients { + x.extend_from_slice(&coeff.to_bytes()?); + } + Ok(x) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_over_twist = self.x_over_twist.to_non_unique_bytes()?; + let mut y_over_twist = self.y_over_twist.to_non_unique_bytes()?; + + x.append(&mut y); + x.append(&mut x_over_twist); + x.append(&mut y_over_twist); + + for coeff in &self.double_coefficients { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); + } + for coeff in &self.addition_coefficients { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); + } + Ok(x) + } +} + +impl G2PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let x = self.x.value()?; + let y = self.y.value()?; + let x_over_twist = self.x_over_twist.value()?; + let y_over_twist = self.y_over_twist.value()?; + let double_coefficients = self + .double_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::>, _>>()?; + let addition_coefficients = self + .addition_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::>, _>>()?; + Ok(G2Prepared { + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, + }) + } + + /// Constructs `Self` from a `G2Var`. + #[tracing::instrument(target = "r1cs")] + pub fn from_group_var(q: &G2Var

) -> Result { + let twist_inv = P::TWIST.inverse().unwrap(); + let q = q.to_affine()?; + + let mut g2p = G2PreparedVar { + x: q.x.clone(), + y: q.y.clone(), + x_over_twist: &q.x * twist_inv, + y_over_twist: &q.y * twist_inv, + double_coefficients: vec![], + addition_coefficients: vec![], + }; + + let mut r = G2ProjectiveExtendedVar { + x: q.x.clone(), + y: q.y.clone(), + z: Fp2G::

::one(), + t: Fp2G::

::one(), + }; + + for (idx, value) in P::ATE_LOOP_COUNT.iter().rev().enumerate() { + let mut tmp = *value; + let skip_extraneous_bits = 64 - value.leading_zeros(); + let mut v = Vec::with_capacity(16); + for i in 0..64 { + if idx == 0 && (i == 0 || i >= skip_extraneous_bits) { + continue; + } + v.push(tmp & 1 == 1); + tmp >>= 1; + } + + for bit in v.iter().rev() { + let (r2, coeff) = PairingVar::

::doubling_step_for_flipped_miller_loop(&r)?; + g2p.double_coefficients.push(coeff); + r = r2; + + if *bit { + let (r2, coeff) = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( + &q.x, &q.y, &r, + )?; + g2p.addition_coefficients.push(coeff); + r = r2; + } + + tmp >>= 1; + } + } + + if P::ATE_IS_LOOP_COUNT_NEG { + let rz_inv = r.z.inverse()?; + let rz2_inv = rz_inv.square()?; + let rz3_inv = &rz_inv * &rz2_inv; + + let minus_r_affine_x = &r.x * &rz2_inv; + let minus_r_affine_y = r.y.negate()? * &rz3_inv; + + let add_result = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( + &minus_r_affine_x, + &minus_r_affine_y, + &r, + )?; + g2p.addition_coefficients.push(add_result.1); + } + + Ok(g2p) + } +} + +#[doc(hidden)] +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] +pub struct AteDoubleCoefficientsVar { + pub c_h: Fp2Var, + pub c_4c: Fp2Var, + pub c_j: Fp2Var, + pub c_l: Fp2Var, +} + +impl AllocVar, P::Fp> for AteDoubleCoefficientsVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); + + let c_h = Fp2Var::new_variable(ark_relations::ns!(cs, "c_h"), || c.map(|c| c.c_h), mode)?; + let c_4c = + Fp2Var::new_variable(ark_relations::ns!(cs, "c_4c"), || c.map(|c| c.c_4c), mode)?; + let c_j = Fp2Var::new_variable(ark_relations::ns!(cs, "c_j"), || c.map(|c| c.c_j), mode)?; + let c_l = Fp2Var::new_variable(ark_relations::ns!(cs, "c_l"), || c.map(|c| c.c_l), mode)?; + Ok(Self { + c_h, + c_4c, + c_j, + c_l, + }) + } +} + +impl ToBytesGadget for AteDoubleCoefficientsVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_bytes()?; + let mut c_4c = self.c_4c.to_bytes()?; + let mut c_j = self.c_j.to_bytes()?; + let mut c_l = self.c_l.to_bytes()?; + + c_h.append(&mut c_4c); + c_h.append(&mut c_j); + c_h.append(&mut c_l); + Ok(c_h) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_non_unique_bytes()?; + let mut c_4c = self.c_4c.to_non_unique_bytes()?; + let mut c_j = self.c_j.to_non_unique_bytes()?; + let mut c_l = self.c_l.to_non_unique_bytes()?; + + c_h.append(&mut c_4c); + c_h.append(&mut c_j); + c_h.append(&mut c_l); + Ok(c_h) + } +} + +impl AteDoubleCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let (c_h, c_4c, c_j, c_l) = ( + self.c_l.value()?, + self.c_4c.value()?, + self.c_j.value()?, + self.c_l.value()?, + ); + Ok(AteDoubleCoefficients { + c_h, + c_4c, + c_j, + c_l, + }) + } +} + +#[doc(hidden)] +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] +pub struct AteAdditionCoefficientsVar { + pub c_l1: Fp2Var, + pub c_rz: Fp2Var, +} + +impl AllocVar, P::Fp> + for AteAdditionCoefficientsVar

+{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); + + let c_l1 = + Fp2Var::new_variable(ark_relations::ns!(cs, "c_l1"), || c.map(|c| c.c_l1), mode)?; + let c_rz = + Fp2Var::new_variable(ark_relations::ns!(cs, "c_rz"), || c.map(|c| c.c_rz), mode)?; + Ok(Self { c_l1, c_rz }) + } +} + +impl ToBytesGadget for AteAdditionCoefficientsVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_bytes()?; + let mut c_rz = self.c_rz.to_bytes()?; + + c_l1.append(&mut c_rz); + Ok(c_l1) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_non_unique_bytes()?; + let mut c_rz = self.c_rz.to_non_unique_bytes()?; + + c_l1.append(&mut c_rz); + Ok(c_l1) + } +} + +impl AteAdditionCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let (c_l1, c_rz) = (self.c_l1.value()?, self.c_rz.value()?); + Ok(AteAdditionCoefficients { c_l1, c_rz }) + } +} + +#[doc(hidden)] +pub struct G2ProjectiveExtendedVar { + pub x: Fp2Var, + pub y: Fp2Var, + pub z: Fp2Var, + pub t: Fp2Var, +} diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mnt6/mod.rs b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mnt6/mod.rs new file mode 100644 index 00000000..8234230a --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mnt6/mod.rs @@ -0,0 +1,494 @@ +use ark_ec::mnt6::{ + g2::{AteAdditionCoefficients, AteDoubleCoefficients}, + G1Prepared, G2Prepared, MNT6Parameters, +}; +use ark_ff::Field; +use ark_relations::r1cs::{Namespace, SynthesisError}; + +use crate::{ + fields::{fp::FpVar, fp3::Fp3Var, FieldVar}, + groups::curves::short_weierstrass::ProjectiveVar, + pairing::mnt6::PairingVar, + prelude::*, + Vec, +}; +use core::borrow::Borrow; + +/// Represents a projective point in G1. +pub type G1Var

= + ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; + +/// Represents a projective point in G2. +pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp3G

>; + +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] +pub struct G1PreparedVar { + #[doc(hidden)] + pub x: FpVar, + #[doc(hidden)] + pub y: FpVar, + #[doc(hidden)] + pub x_twist: Fp3Var, + #[doc(hidden)] + pub y_twist: Fp3Var, +} + +impl G1PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let x = self.x.value()?; + let y = self.y.value()?; + let x_twist = self.x_twist.value()?; + let y_twist = self.y_twist.value()?; + Ok(G1Prepared { + x, + y, + x_twist, + y_twist, + }) + } + + /// Constructs `Self` from a `G1Var`. + #[tracing::instrument(target = "r1cs")] + pub fn from_group_var(q: &G1Var

) -> Result { + let q = q.to_affine()?; + let zero = FpVar::::zero(); + let x_twist = Fp3Var::new(q.x.clone(), zero.clone(), zero.clone()) * P::TWIST; + let y_twist = Fp3Var::new(q.y.clone(), zero.clone(), zero) * P::TWIST; + let result = G1PreparedVar { + x: q.x, + y: q.y, + x_twist, + y_twist, + }; + Ok(result) + } +} + +impl AllocVar, P::Fp> for G1PreparedVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g1_prep = f().map(|b| *b.borrow()); + + let x = FpVar::new_variable(ark_relations::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?; + let y = FpVar::new_variable(ark_relations::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?; + let x_twist = Fp3Var::new_variable( + ark_relations::ns!(cs, "x_twist"), + || g1_prep.map(|g| g.x_twist), + mode, + )?; + let y_twist = Fp3Var::new_variable( + ark_relations::ns!(cs, "y_twist"), + || g1_prep.map(|g| g.y_twist), + mode, + )?; + Ok(Self { + x, + y, + x_twist, + y_twist, + }) + } +} + +impl ToBytesGadget for G1PreparedVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_twist = self.x_twist.to_bytes()?; + let mut y_twist = self.y_twist.to_bytes()?; + + x.append(&mut y); + x.append(&mut x_twist); + x.append(&mut y_twist); + Ok(x) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_twist = self.x_twist.to_non_unique_bytes()?; + let mut y_twist = self.y_twist.to_non_unique_bytes()?; + + x.append(&mut y); + x.append(&mut x_twist); + x.append(&mut y_twist); + Ok(x) + } +} + +type Fp3G

= Fp3Var<

::Fp3Params>; + +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] +pub struct G2PreparedVar { + #[doc(hidden)] + pub x: Fp3Var, + #[doc(hidden)] + pub y: Fp3Var, + #[doc(hidden)] + pub x_over_twist: Fp3Var, + #[doc(hidden)] + pub y_over_twist: Fp3Var, + #[doc(hidden)] + pub double_coefficients: Vec>, + #[doc(hidden)] + pub addition_coefficients: Vec>, +} + +impl AllocVar, P::Fp> for G2PreparedVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g2_prep = f().map(|b| b.borrow().clone()); + let g2 = g2_prep.as_ref().map_err(|e| *e); + + let x = Fp3Var::new_variable(ark_relations::ns!(cs, "x"), || g2.map(|g| g.x), mode)?; + let y = Fp3Var::new_variable(ark_relations::ns!(cs, "y"), || g2.map(|g| g.y), mode)?; + let x_over_twist = Fp3Var::new_variable( + ark_relations::ns!(cs, "x_over_twist"), + || g2.map(|g| g.x_over_twist), + mode, + )?; + let y_over_twist = Fp3Var::new_variable( + ark_relations::ns!(cs, "y_over_twist"), + || g2.map(|g| g.y_over_twist), + mode, + )?; + let double_coefficients = Vec::new_variable( + ark_relations::ns!(cs, "double coeffs"), + || g2.map(|g| g.double_coefficients.clone()), + mode, + )?; + let addition_coefficients = Vec::new_variable( + ark_relations::ns!(cs, "add coeffs"), + || g2.map(|g| g.addition_coefficients.clone()), + mode, + )?; + Ok(Self { + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, + }) + } +} + +impl ToBytesGadget for G2PreparedVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_over_twist = self.x_over_twist.to_bytes()?; + let mut y_over_twist = self.y_over_twist.to_bytes()?; + + x.append(&mut y); + x.append(&mut x_over_twist); + x.append(&mut y_over_twist); + + for coeff in self.double_coefficients.iter() { + x.extend_from_slice(&coeff.to_bytes()?); + } + for coeff in self.addition_coefficients.iter() { + x.extend_from_slice(&coeff.to_bytes()?); + } + Ok(x) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_over_twist = self.x_over_twist.to_non_unique_bytes()?; + let mut y_over_twist = self.y_over_twist.to_non_unique_bytes()?; + + x.append(&mut y); + x.append(&mut x_over_twist); + x.append(&mut y_over_twist); + + for coeff in self.double_coefficients.iter() { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); + } + for coeff in self.addition_coefficients.iter() { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); + } + Ok(x) + } +} + +impl G2PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let x = self.x.value()?; + let y = self.y.value()?; + let x_over_twist = self.x_over_twist.value()?; + let y_over_twist = self.y_over_twist.value()?; + let double_coefficients = self + .double_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::, SynthesisError>>()?; + let addition_coefficients = self + .addition_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::, SynthesisError>>()?; + Ok(G2Prepared { + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, + }) + } + + /// Constructs `Self` from a `G2Var`. + #[tracing::instrument(target = "r1cs")] + pub fn from_group_var(q: &G2Var

) -> Result { + let q = q.to_affine()?; + let twist_inv = P::TWIST.inverse().unwrap(); + + let mut g2p = G2PreparedVar { + x: q.x.clone(), + y: q.y.clone(), + x_over_twist: &q.x * twist_inv, + y_over_twist: &q.y * twist_inv, + double_coefficients: vec![], + addition_coefficients: vec![], + }; + + let mut r = G2ProjectiveExtendedVar { + x: q.x.clone(), + y: q.y.clone(), + z: Fp3G::

::one(), + t: Fp3G::

::one(), + }; + + for (idx, value) in P::ATE_LOOP_COUNT.iter().rev().enumerate() { + let mut tmp = *value; + let skip_extraneous_bits = 64 - value.leading_zeros(); + let mut v = Vec::with_capacity(16); + for i in 0..64 { + if idx == 0 && (i == 0 || i >= skip_extraneous_bits) { + continue; + } + v.push(tmp & 1 == 1); + tmp >>= 1; + } + + for bit in v.iter().rev() { + let (r2, coeff) = PairingVar::

::doubling_step_for_flipped_miller_loop(&r)?; + g2p.double_coefficients.push(coeff); + r = r2; + + if *bit { + let (r2, coeff) = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( + &q.x, &q.y, &r, + )?; + g2p.addition_coefficients.push(coeff); + r = r2; + } + + tmp >>= 1; + } + } + + if P::ATE_IS_LOOP_COUNT_NEG { + let rz_inv = r.z.inverse()?; + let rz2_inv = rz_inv.square()?; + let rz3_inv = &rz_inv * &rz2_inv; + + let minus_r_affine_x = &r.x * &rz2_inv; + let minus_r_affine_y = r.y.negate()? * &rz3_inv; + + let add_result = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( + &minus_r_affine_x, + &minus_r_affine_y, + &r, + )?; + g2p.addition_coefficients.push(add_result.1); + } + + Ok(g2p) + } +} + +#[doc(hidden)] +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] +pub struct AteDoubleCoefficientsVar { + pub c_h: Fp3Var, + pub c_4c: Fp3Var, + pub c_j: Fp3Var, + pub c_l: Fp3Var, +} + +impl AllocVar, P::Fp> for AteDoubleCoefficientsVar

{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); + + let c_h = Fp3Var::new_variable(ark_relations::ns!(cs, "c_h"), || c.map(|c| c.c_h), mode)?; + let c_4c = + Fp3Var::new_variable(ark_relations::ns!(cs, "c_4c"), || c.map(|c| c.c_4c), mode)?; + let c_j = Fp3Var::new_variable(ark_relations::ns!(cs, "c_j"), || c.map(|c| c.c_j), mode)?; + let c_l = Fp3Var::new_variable(ark_relations::ns!(cs, "c_l"), || c.map(|c| c.c_l), mode)?; + Ok(Self { + c_h, + c_4c, + c_j, + c_l, + }) + } +} + +impl ToBytesGadget for AteDoubleCoefficientsVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_bytes()?; + let mut c_4c = self.c_4c.to_bytes()?; + let mut c_j = self.c_j.to_bytes()?; + let mut c_l = self.c_l.to_bytes()?; + + c_h.append(&mut c_4c); + c_h.append(&mut c_j); + c_h.append(&mut c_l); + Ok(c_h) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_non_unique_bytes()?; + let mut c_4c = self.c_4c.to_non_unique_bytes()?; + let mut c_j = self.c_j.to_non_unique_bytes()?; + let mut c_l = self.c_l.to_non_unique_bytes()?; + + c_h.append(&mut c_4c); + c_h.append(&mut c_j); + c_h.append(&mut c_l); + Ok(c_h) + } +} + +impl AteDoubleCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let c_h = self.c_h.value()?; + let c_4c = self.c_4c.value()?; + let c_j = self.c_j.value()?; + let c_l = self.c_l.value()?; + Ok(AteDoubleCoefficients { + c_h, + c_4c, + c_j, + c_l, + }) + } +} + +#[doc(hidden)] +#[derive(Derivative)] +#[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] +pub struct AteAdditionCoefficientsVar { + pub c_l1: Fp3Var, + pub c_rz: Fp3Var, +} + +impl AllocVar, P::Fp> + for AteAdditionCoefficientsVar

+{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); + + let c_l1 = + Fp3Var::new_variable(ark_relations::ns!(cs, "c_l1"), || c.map(|c| c.c_l1), mode)?; + let c_rz = + Fp3Var::new_variable(ark_relations::ns!(cs, "c_rz"), || c.map(|c| c.c_rz), mode)?; + Ok(Self { c_l1, c_rz }) + } +} + +impl ToBytesGadget for AteAdditionCoefficientsVar

{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_bytes()?; + let mut c_rz = self.c_rz.to_bytes()?; + + c_l1.append(&mut c_rz); + Ok(c_l1) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_non_unique_bytes()?; + let mut c_rz = self.c_rz.to_non_unique_bytes()?; + + c_l1.append(&mut c_rz); + Ok(c_l1) + } +} + +impl AteAdditionCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint + /// system. + pub fn value(&self) -> Result, SynthesisError> { + let c_l1 = self.c_l1.value()?; + let c_rz = self.c_rz.value()?; + Ok(AteAdditionCoefficients { c_l1, c_rz }) + } +} + +#[doc(hidden)] +pub struct G2ProjectiveExtendedVar { + pub x: Fp3Var, + pub y: Fp3Var, + pub z: Fp3Var, + pub t: Fp3Var, +} diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mod.rs b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mod.rs new file mode 100644 index 00000000..cf023c44 --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/mod.rs @@ -0,0 +1,947 @@ +use ark_ec::{ + short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective}, + AffineCurve, ProjectiveCurve, SWModelParameters, +}; +use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; +use core::{borrow::Borrow, marker::PhantomData}; +use non_zero_affine::NonZeroAffineVar; + +use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget, Vec}; + +/// This module provides a generic implementation of G1 and G2 for +/// the [\[BLS12]\]() family of bilinear groups. +pub mod bls12; + +/// This module provides a generic implementation of G1 and G2 for +/// the [\[MNT4]\]() +/// family of bilinear groups. +pub mod mnt4; +/// This module provides a generic implementation of G1 and G2 for +/// the [\[MNT6]\]() +/// family of bilinear groups. +pub mod mnt6; + +mod non_zero_affine; +/// An implementation of arithmetic for Short Weierstrass curves that relies on +/// the complete formulae derived in the paper of +/// [[Renes, Costello, Batina 2015]](). +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[must_use] +pub struct ProjectiveVar< + P: SWModelParameters, + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + /// The x-coordinate. + pub x: F, + /// The y-coordinate. + pub y: F, + /// The z-coordinate. + pub z: F, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +/// An affine representation of a curve point. +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[must_use] +pub struct AffineVar< + P: SWModelParameters, + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + /// The x-coordinate. + pub x: F, + /// The y-coordinate. + pub y: F, + /// Is `self` the point at infinity. + pub infinity: Boolean<::BasePrimeField>, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl AffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new(x: F, y: F, infinity: Boolean<::BasePrimeField>) -> Self { + Self { + x, + y, + infinity, + _params: PhantomData, + } + } + + /// Returns the value assigned to `self` in the underlying + /// constraint system. + pub fn value(&self) -> Result, SynthesisError> { + Ok(SWAffine::new( + self.x.value()?, + self.y.value()?, + self.infinity.value()?, + )) + } +} + +impl ToConstraintFieldGadget<::BasePrimeField> for AffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + F: ToConstraintFieldGadget<::BasePrimeField>, +{ + fn to_constraint_field( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut res = Vec::::BasePrimeField>>::new(); + + res.extend_from_slice(&self.x.to_constraint_field()?); + res.extend_from_slice(&self.y.to_constraint_field()?); + res.extend_from_slice(&self.infinity.to_constraint_field()?); + + Ok(res) + } +} + +impl R1CSVar<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + type Value = SWProjective

; + + fn cs(&self) -> ConstraintSystemRef<::BasePrimeField> { + self.x.cs().or(self.y.cs()).or(self.z.cs()) + } + + fn value(&self) -> Result { + let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?); + let result = if let Some(z_inv) = z.inverse() { + SWAffine::new(x * &z_inv, y * &z_inv, false) + } else { + SWAffine::zero() + }; + Ok(result.into()) + } +} + +impl::BasePrimeField>> + ProjectiveVar +where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + /// Constructs `Self` from an `(x, y, z)` coordinate triple. + pub fn new(x: F, y: F, z: F) -> Self { + Self { + x, + y, + z, + _params: PhantomData, + } + } + + /// Convert this point into affine form. + #[tracing::instrument(target = "r1cs")] + pub fn to_affine(&self) -> Result, SynthesisError> { + if self.is_constant() { + let point = self.value()?.into_affine(); + let x = F::new_constant(ConstraintSystemRef::None, point.x)?; + let y = F::new_constant(ConstraintSystemRef::None, point.y)?; + let infinity = Boolean::constant(point.infinity); + Ok(AffineVar::new(x, y, infinity)) + } else { + let cs = self.cs(); + let infinity = self.is_zero()?; + let zero_x = F::zero(); + let zero_y = F::one(); + // Allocate a variable whose value is either `self.z.inverse()` if the inverse exists, + // and is zero otherwise. + let z_inv = F::new_witness(ark_relations::ns!(cs, "z_inverse"), || { + Ok(self.z.value()?.inverse().unwrap_or_else(P::BaseField::zero)) + })?; + // The inverse exists if `!self.is_zero()`. + // This means that `z_inv * self.z = 1` if `self.is_not_zero()`, and + // `z_inv * self.z = 0` if `self.is_zero()`. + // + // Thus, `z_inv * self.z = !self.is_zero()`. + z_inv.mul_equals(&self.z, &F::from(infinity.not()))?; + + let non_zero_x = &self.x * &z_inv; + let non_zero_y = &self.y * &z_inv; + + let x = infinity.select(&zero_x, &non_zero_x)?; + let y = infinity.select(&zero_y, &non_zero_y)?; + + Ok(AffineVar::new(x, y, infinity)) + } + } + + /// Allocates a new variable without performing an on-curve check, which is + /// useful if the variable is known to be on the curve (eg., if the point + /// is a constant or is a public input). + #[tracing::instrument(target = "r1cs", skip(cs, f))] + pub fn new_variable_omit_on_curve_check( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, SynthesisError>, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let (x, y, z) = match f() { + Ok(ge) => { + let ge = ge.into_affine(); + if ge.is_zero() { + ( + Ok(P::BaseField::zero()), + Ok(P::BaseField::one()), + Ok(P::BaseField::zero()), + ) + } else { + (Ok(ge.x), Ok(ge.y), Ok(P::BaseField::one())) + } + } + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let x = F::new_variable(ark_relations::ns!(cs, "x"), || x, mode)?; + let y = F::new_variable(ark_relations::ns!(cs, "y"), || y, mode)?; + let z = F::new_variable(ark_relations::ns!(cs, "z"), || z, mode)?; + + Ok(Self::new(x, y, z)) + } + + /// Mixed addition, which is useful when `other = (x2, y2)` is known to have z = 1. + #[tracing::instrument(target = "r1cs", skip(self, other))] + pub(crate) fn add_mixed(&self, other: &NonZeroAffineVar) -> Result { + // Complete mixed addition formula from Renes-Costello-Batina 2015 + // Algorithm 2 + // (https://eprint.iacr.org/2015/1060). + // Below, comments at the end of a line denote the corresponding + // step(s) of the algorithm + // + // Adapted from code in + // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic/projective.rs + let three_b = P::COEFF_B.double() + &P::COEFF_B; + let (x1, y1, z1) = (&self.x, &self.y, &self.z); + let (x2, y2) = (&other.x, &other.y); + + let xx = x1 * x2; // 1 + let yy = y1 * y2; // 2 + let xy_pairs = ((x1 + y1) * &(x2 + y2)) - (&xx + &yy); // 4, 5, 6, 7, 8 + let xz_pairs = (x2 * z1) + x1; // 8, 9 + let yz_pairs = (y2 * z1) + y1; // 10, 11 + + let axz = mul_by_coeff_a::(&xz_pairs); // 12 + + let bz3_part = &axz + z1 * three_b; // 13, 14 + + let yy_m_bz3 = &yy - &bz3_part; // 15 + let yy_p_bz3 = &yy + &bz3_part; // 16 + + let azz = mul_by_coeff_a::(z1); // 20 + let xx3_p_azz = xx.double().unwrap() + &xx + &azz; // 18, 19, 22 + + let bxz3 = &xz_pairs * three_b; // 21 + let b3_xz_pairs = mul_by_coeff_a::(&(&xx - &azz)) + &bxz3; // 23, 24, 25 + + let x = (&yy_m_bz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; // 28,29, 30 + let y = (&yy_p_bz3 * &yy_m_bz3) + &xx3_p_azz * b3_xz_pairs; // 17, 26, 27 + let z = (&yy_p_bz3 * &yz_pairs) + xy_pairs * xx3_p_azz; // 31, 32, 33 + + Ok(ProjectiveVar::new(x, y, z)) + } + + /// Computes a scalar multiplication with a little-endian scalar of size `P::ScalarField::MODULUS_BITS`. + #[tracing::instrument( + target = "r1cs", + skip(self, mul_result, multiple_of_power_of_two, bits) + )] + fn fixed_scalar_mul_le( + &self, + mul_result: &mut Self, + multiple_of_power_of_two: &mut NonZeroAffineVar, + bits: &[&Boolean<::BasePrimeField>], + ) -> Result<(), SynthesisError> { + let scalar_modulus_bits = ::size_in_bits(); + + assert!(scalar_modulus_bits >= bits.len()); + let split_len = ark_std::cmp::min(scalar_modulus_bits - 2, bits.len()); + let (affine_bits, proj_bits) = bits.split_at(split_len); + // Computes the standard little-endian double-and-add algorithm + // (Algorithm 3.26, Guide to Elliptic Curve Cryptography) + // + // We rely on *incomplete* affine formulae for partially computing this. + // However, we avoid exceptional edge cases because we partition the scalar + // into two chunks: one guaranteed to be less than p - 2, and the rest. + // We only use incomplete formulae for the first chunk, which means we avoid exceptions: + // + // `add_unchecked(a, b)` is incomplete when either `b.is_zero()`, or when + // `b = ±a`. During scalar multiplication, we don't hit either case: + // * `b = ±a`: `b = accumulator = k * a`, where `2 <= k < p - 1`. + // This implies that `k != p ± 1`, and so `b != (p ± 1) * a`. + // Because the group is finite, this in turn means that `b != ±a`, as required. + // * `a` or `b` is zero: for `a`, we handle the zero case after the loop; for `b`, notice + // that it is monotonically increasing, and furthermore, equals `k * a`, where + // `k != p = 0 mod p`. + + // Unlike normal double-and-add, here we start off with a non-zero `accumulator`, + // because `NonZeroAffineVar::add_unchecked` doesn't support addition with `zero`. + // In more detail, we initialize `accumulator` to be the initial value of + // `multiple_of_power_of_two`. This ensures that all unchecked additions of `accumulator` + // with later values of `multiple_of_power_of_two` are safe. + // However, to do this correctly, we need to perform two steps: + // * We must skip the LSB, and instead proceed assuming that it was 1. Later, we will + // conditionally subtract the initial value of `accumulator`: + // if LSB == 0: subtract initial_acc_value; else, subtract 0. + // * Because we are assuming the first bit, we must double `multiple_of_power_of_two`. + + let mut accumulator = multiple_of_power_of_two.clone(); + let initial_acc_value = accumulator.into_projective(); + + // The powers start at 2 (instead of 1) because we're skipping the first bit. + multiple_of_power_of_two.double_in_place()?; + + // As mentioned, we will skip the LSB, and will later handle it via a conditional subtraction. + for bit in affine_bits.iter().skip(1) { + if bit.is_constant() { + if *bit == &Boolean::TRUE { + accumulator = accumulator.add_unchecked(&multiple_of_power_of_two)?; + } + } else { + let temp = accumulator.add_unchecked(&multiple_of_power_of_two)?; + accumulator = bit.select(&temp, &accumulator)?; + } + multiple_of_power_of_two.double_in_place()?; + } + // Perform conditional subtraction: + + // We can convert to projective safely because the result is guaranteed to be non-zero + // by the condition on `affine_bits.len()`, and by the fact that `accumulator` is non-zero + let result = accumulator.into_projective(); + // If bits[0] is 0, then we have to subtract `self`; else, we subtract zero. + let subtrahend = bits[0].select(&Self::zero(), &initial_acc_value)?; + *mul_result += result - subtrahend; + + // Now, let's finish off the rest of the bits using our complete formulae + for bit in proj_bits { + if bit.is_constant() { + if *bit == &Boolean::TRUE { + *mul_result += &multiple_of_power_of_two.into_projective(); + } + } else { + let temp = &*mul_result + &multiple_of_power_of_two.into_projective(); + *mul_result = bit.select(&temp, &mul_result)?; + } + multiple_of_power_of_two.double_in_place()?; + } + Ok(()) + } +} + +impl CurveVar, ::BasePrimeField> + for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn constant(g: SWProjective

) -> Self { + let cs = ConstraintSystemRef::None; + Self::new_variable_omit_on_curve_check(cs, || Ok(g), AllocationMode::Constant).unwrap() + } + + fn zero() -> Self { + Self::new(F::zero(), F::one(), F::zero()) + } + + fn is_zero(&self) -> Result::BasePrimeField>, SynthesisError> { + self.z.is_zero() + } + + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable_omit_prime_order_check( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, SynthesisError>, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + // Curve equation in projective form: + // E: Y² * Z = X³ + aX * Z² + bZ³ + // + // This can be re-written as + // E: Y² * Z - bZ³ = X³ + aX * Z² + // E: Z * (Y² - bZ²) = X * (X² + aZ²) + // so, compute X², Y², Z², + // compute temp = X * (X² + aZ²) + // check Z.mul_equals((Y² - bZ²), temp) + // + // A total of 5 multiplications + + let g = Self::new_variable_omit_on_curve_check(cs, f, mode)?; + + if mode != AllocationMode::Constant { + // Perform on-curve check. + let b = P::COEFF_B; + let a = P::COEFF_A; + + let x2 = g.x.square()?; + let y2 = g.y.square()?; + let z2 = g.z.square()?; + let t = &g.x * (x2 + &z2 * a); + + g.z.mul_equals(&(y2 - z2 * b), &t)?; + } + Ok(g) + } + + /// Enforce that `self` is in the prime-order subgroup. + /// + /// Does so by multiplying by the prime order, and checking that the result + /// is unchanged. + // TODO: at the moment this doesn't work, because the addition and doubling + // formulae are incomplete for even-order points. + #[tracing::instrument(target = "r1cs")] + fn enforce_prime_order(&self) -> Result<(), SynthesisError> { + unimplemented!("cannot enforce prime order"); + // let r_minus_1 = (-P::ScalarField::one()).into_repr(); + + // let mut result = Self::zero(); + // for b in BitIteratorBE::without_leading_zeros(r_minus_1) { + // result.double_in_place()?; + + // if b { + // result += self; + // } + // } + // self.negate()?.enforce_equal(&result)?; + // Ok(()) + } + + #[inline] + #[tracing::instrument(target = "r1cs")] + fn double_in_place(&mut self) -> Result<(), SynthesisError> { + // Complete doubling formula from Renes-Costello-Batina 2015 + // Algorithm 3 + // (https://eprint.iacr.org/2015/1060). + // Below, comments at the end of a line denote the corresponding + // step(s) of the algorithm + // + // Adapted from code in + // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic/projective.rs + let three_b = P::COEFF_B.double() + &P::COEFF_B; + + let xx = self.x.square()?; // 1 + let yy = self.y.square()?; // 2 + let zz = self.z.square()?; // 3 + let xy2 = (&self.x * &self.y).double()?; // 4, 5 + let xz2 = (&self.x * &self.z).double()?; // 6, 7 + + let axz2 = mul_by_coeff_a::(&xz2); // 8 + + let bzz3_part = &axz2 + &zz * three_b; // 9, 10 + let yy_m_bzz3 = &yy - &bzz3_part; // 11 + let yy_p_bzz3 = &yy + &bzz3_part; // 12 + let y_frag = yy_p_bzz3 * &yy_m_bzz3; // 13 + let x_frag = yy_m_bzz3 * &xy2; // 14 + + let bxz3 = xz2 * three_b; // 15 + let azz = mul_by_coeff_a::(&zz); // 16 + let b3_xz_pairs = mul_by_coeff_a::(&(&xx - &azz)) + &bxz3; // 15, 16, 17, 18, 19 + let xx3_p_azz = (xx.double()? + &xx + &azz) * &b3_xz_pairs; // 23, 24, 25 + + let y = y_frag + &xx3_p_azz; // 26, 27 + let yz2 = (&self.y * &self.z).double()?; // 28, 29 + let x = x_frag - &(b3_xz_pairs * &yz2); // 30, 31 + let z = (yz2 * &yy).double()?.double()?; // 32, 33, 34 + self.x = x; + self.y = y; + self.z = z; + Ok(()) + } + + #[tracing::instrument(target = "r1cs")] + fn negate(&self) -> Result { + Ok(Self::new(self.x.clone(), self.y.negate()?, self.z.clone())) + } + + /// Computes `bits * self`, where `bits` is a little-endian + /// `Boolean` representation of a scalar. + #[tracing::instrument(target = "r1cs", skip(bits))] + fn scalar_mul_le<'a>( + &self, + bits: impl Iterator::BasePrimeField>>, + ) -> Result { + if self.is_constant() { + if self.value().unwrap().is_zero() { + return Ok(self.clone()); + } + } + let self_affine = self.to_affine()?; + let (x, y, infinity) = (self_affine.x, self_affine.y, self_affine.infinity); + // We first handle the non-zero case, and then later + // will conditionally select zero if `self` was zero. + let non_zero_self = NonZeroAffineVar::new(x, y); + + let mut bits = bits.collect::>(); + if bits.len() == 0 { + return Ok(Self::zero()); + } + // Remove unnecessary constant zeros in the most-significant positions. + bits = bits + .into_iter() + // We iterate from the MSB down. + .rev() + // Skip leading zeros, if they are constants. + .skip_while(|b| b.is_constant() && (b.value().unwrap() == false)) + .collect(); + // After collecting we are in big-endian form; we have to reverse to get back to + // little-endian. + bits.reverse(); + + let scalar_modulus_bits = ::size_in_bits(); + let mut mul_result = Self::zero(); + let mut power_of_two_times_self = non_zero_self; + // We chunk up `bits` into `p`-sized chunks. + for bits in bits.chunks(scalar_modulus_bits) { + self.fixed_scalar_mul_le(&mut mul_result, &mut power_of_two_times_self, bits)?; + } + + // The foregoing algorithm relies on incomplete addition, and so does not + // work when the input (`self`) is zero. We hence have to perform + // a check to ensure that if the input is zero, then so is the output. + // The cost of this check should be less than the benefit of using + // mixed addition in almost all cases. + infinity.select(&Self::zero(), &mul_result) + } + + #[tracing::instrument(target = "r1cs", skip(scalar_bits_with_bases))] + fn precomputed_base_scalar_mul_le<'a, I, B>( + &mut self, + scalar_bits_with_bases: I, + ) -> Result<(), SynthesisError> + where + I: Iterator)>, + B: Borrow::BasePrimeField>>, + { + // We just ignore the provided bases and use the faster scalar multiplication. + let (bits, bases): (Vec<_>, Vec<_>) = scalar_bits_with_bases + .map(|(b, c)| (b.borrow().clone(), *c)) + .unzip(); + let base = bases[0]; + *self = Self::constant(base).scalar_mul_le(bits.iter())?; + Ok(()) + } +} + +impl ToConstraintFieldGadget<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + F: ToConstraintFieldGadget<::BasePrimeField>, +{ + fn to_constraint_field( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + self.to_affine()?.to_constraint_field() + } +} + +fn mul_by_coeff_a< + P: SWModelParameters, + F: FieldVar::BasePrimeField>, +>( + f: &F, +) -> F +where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + if !P::COEFF_A.is_zero() { + f * P::COEFF_A + } else { + F::zero() + } +} + +impl_bounded_ops!( + ProjectiveVar, + SWProjective

, + Add, + add, + AddAssign, + add_assign, + |mut this: &'a ProjectiveVar, mut other: &'a ProjectiveVar| { + // Implement complete addition for Short Weierstrass curves, following + // the complete addition formula from Renes-Costello-Batina 2015 + // (https://eprint.iacr.org/2015/1060). + // + // We special case handling of constants to get better constraint weight. + if this.is_constant() { + // we'll just act like `other` is constant. + core::mem::swap(&mut this, &mut other); + } + + if other.is_constant() { + // The value should exist because `other` is a constant. + let other = other.value().unwrap(); + if other.is_zero() { + // this + 0 = this + this.clone() + } else { + // We'll use mixed addition to add non-zero constants. + let x = F::constant(other.x); + let y = F::constant(other.y); + this.add_mixed(&NonZeroAffineVar::new(x, y)).unwrap() + } + } else { + // Complete addition formula from Renes-Costello-Batina 2015 + // Algorithm 1 + // (https://eprint.iacr.org/2015/1060). + // Below, comments at the end of a line denote the corresponding + // step(s) of the algorithm + // + // Adapted from code in + // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic/projective.rs + let three_b = P::COEFF_B.double() + &P::COEFF_B; + let (x1, y1, z1) = (&this.x, &this.y, &this.z); + let (x2, y2, z2) = (&other.x, &other.y, &other.z); + + let xx = x1 * x2; // 1 + let yy = y1 * y2; // 2 + let zz = z1 * z2; // 3 + let xy_pairs = ((x1 + y1) * &(x2 + y2)) - (&xx + &yy); // 4, 5, 6, 7, 8 + let xz_pairs = ((x1 + z1) * &(x2 + z2)) - (&xx + &zz); // 9, 10, 11, 12, 13 + let yz_pairs = ((y1 + z1) * &(y2 + z2)) - (&yy + &zz); // 14, 15, 16, 17, 18 + + let axz = mul_by_coeff_a::(&xz_pairs); // 19 + + let bzz3_part = &axz + &zz * three_b; // 20, 21 + + let yy_m_bzz3 = &yy - &bzz3_part; // 22 + let yy_p_bzz3 = &yy + &bzz3_part; // 23 + + let azz = mul_by_coeff_a::(&zz); + let xx3_p_azz = xx.double().unwrap() + &xx + &azz; // 25, 26, 27, 29 + + let bxz3 = &xz_pairs * three_b; // 28 + let b3_xz_pairs = mul_by_coeff_a::(&(&xx - &azz)) + &bxz3; // 30, 31, 32 + + let x = (&yy_m_bzz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; // 35, 39, 40 + let y = (&yy_p_bzz3 * &yy_m_bzz3) + &xx3_p_azz * b3_xz_pairs; // 24, 36, 37, 38 + let z = (&yy_p_bzz3 * &yz_pairs) + xy_pairs * xx3_p_azz; // 41, 42, 43 + + ProjectiveVar::new(x, y, z) + } + + }, + |this: &'a ProjectiveVar, other: SWProjective

| { + this + ProjectiveVar::constant(other) + }, + (F: FieldVar::BasePrimeField>, P: SWModelParameters), + for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +); + +impl_bounded_ops!( + ProjectiveVar, + SWProjective

, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a ProjectiveVar, other: &'a ProjectiveVar| this + other.negate().unwrap(), + |this: &'a ProjectiveVar, other: SWProjective

| this - ProjectiveVar::constant(other), + (F: FieldVar::BasePrimeField>, P: SWModelParameters), + for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F> +); + +impl<'a, P, F> GroupOpsBounds<'a, SWProjective

, ProjectiveVar> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +{ +} + +impl<'a, P, F> GroupOpsBounds<'a, SWProjective

, ProjectiveVar> for &'a ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +{ +} + +impl CondSelectGadget<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditionally_select( + cond: &Boolean<::BasePrimeField>, + true_value: &Self, + false_value: &Self, + ) -> Result { + let x = cond.select(&true_value.x, &false_value.x)?; + let y = cond.select(&true_value.y, &false_value.y)?; + let z = cond.select(&true_value.z, &false_value.z)?; + + Ok(Self::new(x, y, z)) + } +} + +impl EqGadget<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + #[tracing::instrument(target = "r1cs")] + fn is_eq( + &self, + other: &Self, + ) -> Result::BasePrimeField>, SynthesisError> { + let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?; + let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?; + let coordinates_equal = x_equal.and(&y_equal)?; + let both_are_zero = self.is_zero()?.and(&other.is_zero()?)?; + both_are_zero.or(&coordinates_equal) + } + + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditional_enforce_equal( + &self, + other: &Self, + condition: &Boolean<::BasePrimeField>, + ) -> Result<(), SynthesisError> { + let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?; + let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?; + let coordinates_equal = x_equal.and(&y_equal)?; + let both_are_zero = self.is_zero()?.and(&other.is_zero()?)?; + both_are_zero + .or(&coordinates_equal)? + .conditional_enforce_equal(&Boolean::Constant(true), condition)?; + Ok(()) + } + + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditional_enforce_not_equal( + &self, + other: &Self, + condition: &Boolean<::BasePrimeField>, + ) -> Result<(), SynthesisError> { + let is_equal = self.is_eq(other)?; + is_equal + .and(condition)? + .enforce_equal(&Boolean::Constant(false)) + } +} + +impl AllocVar, ::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new_variable>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode) + } +} + +impl AllocVar, ::BasePrimeField> + for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new_variable>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let f = || Ok(*f()?.borrow()); + match mode { + AllocationMode::Constant => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Input => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Witness => { + // if cofactor.is_even(): + // divide until you've removed all even factors + // else: + // just directly use double and add. + let mut power_of_2: u32 = 0; + let mut cofactor = P::COFACTOR.to_vec(); + while cofactor[0] % 2 == 0 { + div2(&mut cofactor); + power_of_2 += 1; + } + + let cofactor_weight = BitIteratorBE::new(cofactor.as_slice()) + .filter(|b| *b) + .count(); + let modulus_minus_1 = (-P::ScalarField::one()).into_repr(); // r - 1 + let modulus_minus_1_weight = + BitIteratorBE::new(modulus_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // equals the identity. + + let (mut ge, iter) = if cofactor_weight < modulus_minus_1_weight { + let ge = Self::new_variable_omit_prime_order_check( + ark_relations::ns!(cs, "Witness without subgroup check with cofactor mul"), + || f().map(|g| g.borrow().into_affine().mul_by_cofactor_inv().into()), + mode, + )?; + ( + ge, + BitIteratorBE::without_leading_zeros(cofactor.as_slice()), + ) + } else { + let ge = Self::new_variable_omit_prime_order_check( + ark_relations::ns!(cs, "Witness without subgroup check with `r` check"), + || { + f().map(|g| { + let g = g.into_affine(); + let mut power_of_two = P::ScalarField::one().into_repr(); + power_of_two.muln(power_of_2); + let power_of_two_inv = P::ScalarField::from_repr(power_of_two) + .and_then(|n| n.inverse()) + .unwrap(); + g.mul(power_of_two_inv) + }) + }, + mode, + )?; + + ( + ge, + BitIteratorBE::without_leading_zeros(modulus_minus_1.as_ref()), + ) + }; + // Remove the even part of the cofactor + for _ in 0..power_of_2 { + ge.double_in_place()?; + } + + let mut result = Self::zero(); + for b in iter { + result.double_in_place()?; + + if b { + result += &ge + } + } + if cofactor_weight < modulus_minus_1_weight { + Ok(result) + } else { + ge.enforce_equal(&ge)?; + Ok(ge) + } + } + } + } +} + +#[inline] +fn div2(limbs: &mut [u64]) { + let mut t = 0; + for i in limbs.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } +} + +impl ToBitsGadget<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + #[tracing::instrument(target = "r1cs")] + fn to_bits_le( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bits = g.x.to_bits_le()?; + let y_bits = g.y.to_bits_le()?; + bits.extend_from_slice(&y_bits); + bits.push(g.infinity); + Ok(bits) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bits_le( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bits = g.x.to_non_unique_bits_le()?; + let y_bits = g.y.to_non_unique_bits_le()?; + bits.extend_from_slice(&y_bits); + bits.push(g.infinity); + Ok(bits) + } +} + +impl ToBytesGadget<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + #[tracing::instrument(target = "r1cs")] + fn to_bytes( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bytes = g.x.to_bytes()?; + let y_bytes = g.y.to_bytes()?; + let inf_bytes = g.infinity.to_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bytes = g.x.to_non_unique_bytes()?; + let y_bytes = g.y.to_non_unique_bytes()?; + let inf_bytes = g.infinity.to_non_unique_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) + } +} diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/non_zero_affine.rs b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/non_zero_affine.rs new file mode 100644 index 00000000..a598bdd0 --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/short_weierstrass/non_zero_affine.rs @@ -0,0 +1,163 @@ +use super::*; +/// An affine representation of a prime order curve point that is guaranteed +/// to *not* be the point at infinity. +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[must_use] +pub struct NonZeroAffineVar< + P: SWModelParameters, + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + /// The x-coordinate. + pub x: F, + /// The y-coordinate. + pub y: F, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl NonZeroAffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + pub(crate) fn new(x: F, y: F) -> Self { + Self { + x, + y, + _params: PhantomData, + } + } + + /// Converts self into a non-zero projective point. + #[tracing::instrument(target = "r1cs", skip(self))] + pub(crate) fn into_projective(&self) -> ProjectiveVar { + ProjectiveVar::new(self.x.clone(), self.y.clone(), F::one()) + } + + /// Performs an addition without checking that other != ±self. + #[tracing::instrument(target = "r1cs", skip(self, other))] + pub(crate) fn add_unchecked(&self, other: &Self) -> Result { + if [self, other].is_constant() { + let result = + (self.value()?.into_projective() + other.value()?.into_projective()).into_affine(); + Ok(Self::new(F::constant(result.x), F::constant(result.y))) + } else { + let (x1, y1) = (&self.x, &self.y); + let (x2, y2) = (&other.x, &other.y); + // Then, + // slope lambda := (y2 - y1)/(x2 - x1); + // x3 = lambda^2 - x1 - x2; + // y3 = lambda * (x1 - x3) - y1 + let numerator = y2 - y1; + let denominator = x2 - x1; + let lambda = numerator.mul_by_inverse(&denominator)?; + let x3 = lambda.square()? - x1 - x2; + let y3 = lambda * &(x1 - &x3) - y1; + Ok(Self::new(x3, y3)) + } + } + + /// Doubles `self`. As this is a prime order curve point, + /// the output is guaranteed to not be the point at infinity. + #[tracing::instrument(target = "r1cs", skip(self))] + pub(crate) fn double(&self) -> Result { + if [self].is_constant() { + let result = self.value()?.into_projective().double().into_affine(); + // Panic if the result is zero. + assert!(!result.is_zero()); + Ok(Self::new(F::constant(result.x), F::constant(result.y))) + } else { + let (x1, y1) = (&self.x, &self.y); + let x1_sqr = x1.square()?; + // Then, + // tangent lambda := (3 * x1^2 + a) / (2 * y1); + // x3 = lambda^2 - 2x1 + // y3 = lambda * (x1 - x3) - y1 + let numerator = x1_sqr.double()? + &x1_sqr + P::COEFF_A; + let denominator = y1.double()?; + let lambda = numerator.mul_by_inverse(&denominator)?; + let x3 = lambda.square()? - x1.double()?; + let y3 = lambda * &(x1 - &x3) - y1; + Ok(Self::new(x3, y3)) + } + } + + /// Computes `(self + other) + self`. This method requires only 5 constraints, + /// less than the 7 required when computing via `self.double() + other`. + /// + /// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038). + #[tracing::instrument(target = "r1cs", skip(self))] + pub(crate) fn double_and_add(&self, other: &Self) -> Result { + if [self].is_constant() || other.is_constant() { + self.double()?.add_unchecked(other) + } else { + let (x1, y1) = (&self.x, &self.y); + let (x2, y2) = (&other.x, &other.y); + + // Calculate self + other: + // slope lambda := (y2 - y1)/(x2 - x1); + // x3 = lambda^2 - x1 - x2; + // y3 = lambda * (x1 - x3) - y1 + let numerator = y2 - y1; + let denominator = x2 - x1; + let lambda_1 = numerator.mul_by_inverse(&denominator)?; + + let x3 = lambda_1.square()? - x1 - x2; + + // Calculate final addition slope: + let lambda_2 = (lambda_1 + y1.double()?.mul_by_inverse(&(&x3 - x1))?).negate()?; + + let x4 = lambda_2.square()? - x1 - x3; + let y4 = lambda_2 * &(x1 - &x4) - y1; + Ok(Self::new(x4, y4)) + } + } + + /// Doubles `self` in place. + #[tracing::instrument(target = "r1cs", skip(self))] + pub(crate) fn double_in_place(&mut self) -> Result<(), SynthesisError> { + *self = self.double()?; + Ok(()) + } +} + +impl R1CSVar<::BasePrimeField> for NonZeroAffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + type Value = SWAffine

; + + fn cs(&self) -> ConstraintSystemRef<::BasePrimeField> { + self.x.cs().or(self.y.cs()) + } + + fn value(&self) -> Result, SynthesisError> { + Ok(SWAffine::new(self.x.value()?, self.y.value()?, false)) + } +} + +impl CondSelectGadget<::BasePrimeField> for NonZeroAffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditionally_select( + cond: &Boolean<::BasePrimeField>, + true_value: &Self, + false_value: &Self, + ) -> Result { + let x = cond.select(&true_value.x, &false_value.x)?; + let y = cond.select(&true_value.y, &false_value.y)?; + + Ok(Self::new(x, y)) + } +} diff --git a/mpc-algebra/src/r1cs_helper/groups/curves/twisted_edwards/mod.rs b/mpc-algebra/src/r1cs_helper/groups/curves/twisted_edwards/mod.rs new file mode 100644 index 00000000..5497c264 --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/curves/twisted_edwards/mod.rs @@ -0,0 +1,1069 @@ +use ark_ec::{ + twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective}, + AffineCurve, ModelParameters, MontgomeryModelParameters, ProjectiveCurve, TEModelParameters, +}; +use ark_ff::{ + BigInteger, BitIteratorBE, Field, One, PrimeField, SquareRootField, UniformRand, Zero, +}; + +use ark_r1cs_std::{alloc::AllocationMode, impl_bounded_ops}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; +use derivative::Derivative; +use mpc_trait::MpcWire; + +use crate::{ + groups::{GroupOpsBounds, MpcCurveVar}, + mpc_fields::FieldOpsBounds, + r1cs_helper::mpc_fields::MpcFieldVar, + AdditiveAffProjShare, FieldShare, MpcBoolean, MpcCondSelectGadget, MpcTwoBitLookupGadget, + Reveal, +}; + +use crate::MpcGroupProjectiveVariant; + +use crate::MpcEqGadget; +use crate::MpcToBitsGadget; +use ark_r1cs_std::{prelude::*, ToConstraintFieldGadget}; + +use ark_r1cs_std::fields::fp::FpVar; + +use core::{borrow::Borrow, marker::PhantomData}; + +use crate::wire::MpcGroupAffine as MpcTEAffine; +use crate::wire::MpcGroupProjective as MpcTEProjective; + +// use crate::honest_but_curious::*; +use crate::malicious_majority::*; + +use mpc_net::{MpcMultiNet as Net, MpcNet}; + +type MpcBaseField = MpcField; +type MpcScalarField = MpcField; + +// /// An implementation of arithmetic for Montgomery curves that relies on +// /// incomplete addition formulae for the affine model, as outlined in the +// /// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-montgom.html). +// /// +// /// This is intended for use primarily for implementing efficient +// /// multi-scalar-multiplication in the Bowe-Hopwood-Pedersen hash. +// #[derive(Derivative)] +// #[derivative(Debug, Clone)] +// #[must_use] +// pub struct MontgomeryAffineVar< +// P: TEModelParameters, +// F: FieldVar::BasePrimeField>, +// > where +// for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +// { +// /// The x-coordinate. +// pub x: F, +// /// The y-coordinate. +// pub y: F, +// #[derivative(Debug = "ignore")] +// _params: PhantomData

, +// } + +// mod montgomery_affine_impl { +// use super::*; +// use ark_ec::twisted_edwards_extended::GroupAffine; +// use ark_ff::Field; +// use core::ops::Add; + +// impl R1CSVar<::BasePrimeField> for MontgomeryAffineVar +// where +// P: TEModelParameters, +// F: FieldVar::BasePrimeField>, +// for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +// { +// type Value = (P::BaseField, P::BaseField); + +// fn cs(&self) -> ConstraintSystemRef<::BasePrimeField> { +// self.x.cs().or(self.y.cs()) +// } + +// fn value(&self) -> Result { +// let x = self.x.value()?; +// let y = self.y.value()?; +// Ok((x, y)) +// } +// } + +// impl< +// P: TEModelParameters, +// F: FieldVar::BasePrimeField>, +// > MontgomeryAffineVar +// where +// for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +// { +// /// Constructs `Self` from an `(x, y)` coordinate pair. +// pub fn new(x: F, y: F) -> Self { +// Self { +// x, +// y, +// _params: PhantomData, +// } +// } + +// /// Converts a Twisted Edwards curve point to coordinates for the +// /// corresponding affine Montgomery curve point. +// #[tracing::instrument(target = "r1cs")] +// pub fn from_edwards_to_coords( +// p: &TEAffine

, +// ) -> Result<(P::BaseField, P::BaseField), SynthesisError> { +// let montgomery_point: GroupAffine

= if p.y == P::BaseField::one() { +// GroupAffine::zero() +// } else if p.x == P::BaseField::zero() { +// GroupAffine::new(P::BaseField::zero(), P::BaseField::zero()) +// } else { +// let u = +// (P::BaseField::one() + &p.y) * &(P::BaseField::one() - &p.y).inverse().unwrap(); +// let v = u * &p.x.inverse().unwrap(); +// GroupAffine::new(u, v) +// }; + +// Ok((montgomery_point.x, montgomery_point.y)) +// } + +// /// Converts a Twisted Edwards curve point to coordinates for the +// /// corresponding affine Montgomery curve point. +// #[tracing::instrument(target = "r1cs")] +// pub fn new_witness_from_edwards( +// cs: ConstraintSystemRef<::BasePrimeField>, +// p: &TEAffine

, +// ) -> Result { +// let montgomery_coords = Self::from_edwards_to_coords(p)?; +// let u = F::new_witness(ark_relations::ns!(cs, "u"), || Ok(montgomery_coords.0))?; +// let v = F::new_witness(ark_relations::ns!(cs, "v"), || Ok(montgomery_coords.1))?; +// Ok(Self::new(u, v)) +// } + +// /// Converts `self` into a Twisted Edwards curve point variable. +// #[tracing::instrument(target = "r1cs")] +// pub fn into_edwards(&self) -> Result, SynthesisError> { +// let cs = self.cs(); + +// let mode = if cs.is_none() { +// AllocationMode::Constant +// } else { +// AllocationMode::Witness +// }; + +// // Compute u = x / y +// let u = F::new_variable( +// ark_relations::ns!(cs, "u"), +// || { +// let y_inv = self +// .y +// .value()? +// .inverse() +// .ok_or(SynthesisError::DivisionByZero)?; +// Ok(self.x.value()? * &y_inv) +// }, +// mode, +// )?; + +// u.mul_equals(&self.y, &self.x)?; + +// let v = F::new_variable( +// ark_relations::ns!(cs, "v"), +// || { +// let mut t0 = self.x.value()?; +// let mut t1 = t0; +// t0 -= &P::BaseField::one(); +// t1 += &P::BaseField::one(); + +// Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) +// }, +// mode, +// )?; + +// let xplusone = &self.x + P::BaseField::one(); +// let xminusone = &self.x - P::BaseField::one(); +// v.mul_equals(&xplusone, &xminusone)?; + +// Ok(AffineVar::new(u, v)) +// } +// } + +// impl<'a, P, F> Add<&'a MontgomeryAffineVar> for MontgomeryAffineVar +// where +// P: TEModelParameters, +// F: FieldVar::BasePrimeField>, +// for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +// { +// type Output = MontgomeryAffineVar; + +// #[tracing::instrument(target = "r1cs")] +// fn add(self, other: &'a Self) -> Self::Output { +// let cs = [&self, other].cs(); +// let mode = if cs.is_none() { +// AllocationMode::Constant +// } else { +// AllocationMode::Witness +// }; + +// let coeff_b = P::MontgomeryModelParameters::COEFF_B; +// let coeff_a = P::MontgomeryModelParameters::COEFF_A; + +// let lambda = F::new_variable( +// ark_relations::ns!(cs, "lambda"), +// || { +// let n = other.y.value()? - &self.y.value()?; +// let d = other.x.value()? - &self.x.value()?; +// Ok(n * &d.inverse().ok_or(SynthesisError::DivisionByZero)?) +// }, +// mode, +// ) +// .unwrap(); +// let lambda_n = &other.y - &self.y; +// let lambda_d = &other.x - &self.x; +// lambda_d.mul_equals(&lambda, &lambda_n).unwrap(); + +// // Compute x'' = B*lambda^2 - A - x - x' +// let xprime = F::new_variable( +// ark_relations::ns!(cs, "xprime"), +// || { +// Ok(lambda.value()?.square() * &coeff_b +// - &coeff_a +// - &self.x.value()? +// - &other.x.value()?) +// }, +// mode, +// ) +// .unwrap(); + +// let xprime_lc = &self.x + &other.x + &xprime + coeff_a; +// // (lambda) * (lambda) = (A + x + x' + x'') +// let lambda_b = &lambda * coeff_b; +// lambda_b.mul_equals(&lambda, &xprime_lc).unwrap(); + +// let yprime = F::new_variable( +// ark_relations::ns!(cs, "yprime"), +// || { +// Ok(-(self.y.value()? +// + &(lambda.value()? * &(xprime.value()? - &self.x.value()?)))) +// }, +// mode, +// ) +// .unwrap(); + +// let xres = &self.x - &xprime; +// let yres = &self.y + &yprime; +// lambda.mul_equals(&xres, &yres).unwrap(); +// MontgomeryAffineVar::new(xprime, yprime) +// } +// } +// } + +/// An implementation of arithmetic for Twisted Edwards curves that relies on +/// the complete formulae for the affine model, as outlined in the +/// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html). +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[must_use] +pub struct MpcAffineVar< + P: TEModelParameters, + F: MpcFieldVar< + as Field>::BasePrimeField, + as Field>::BasePrimeField, + >, +> where + P::BaseField: PrimeField, + <

::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, +{ + /// The x-coordinate. + pub x: F, + /// The y-coordinate. + pub y: F, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl< + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField>, + > MpcAffineVar +where + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: ark_ff::SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, +{ + /// Constructs `Self` from an `(x, y)` coordinate triple. + pub fn new(x: F, y: F) -> Self { + Self { + x, + y, + _params: PhantomData, + } + } + + /// Allocates a new variable without performing an on-curve check, which is + /// useful if the variable is known to be on the curve (eg., if the point + /// is a constant or is a public input). + #[tracing::instrument(target = "r1cs", skip(cs, f))] + pub fn new_variable_omit_on_curve_check>>>( + cs: impl Into as Field>::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let (x, y) = match f() { + Ok(ge) => { + let ge: MpcTEAffine> = ge.into(); + + // TODO: Remove reveal operation. + let revealed_ge = ge.reveal(); + (Ok(revealed_ge.x), Ok(revealed_ge.y)) + } + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let wrapped_x = MpcBaseField::

::from_public(x.unwrap()); + let wrapped_y = MpcBaseField::

::from_public(y.unwrap()); + + let x = F::new_variable(ark_relations::ns!(cs, "x"), || Ok(wrapped_x), mode)?; + let y = F::new_variable(ark_relations::ns!(cs, "y"), || Ok(wrapped_y), mode)?; + + Ok(Self::new(x, y)) + } +} + +// impl::BasePrimeField>> +// AffineVar +// where +// P: TEModelParameters, +// F: FieldVar::BasePrimeField> +// + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> +// + ThreeBitCondNegLookupGadget< +// ::BasePrimeField, +// TableConstant = P::BaseField, +// >, +// for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +// { +// /// Compute a scalar multiplication of `bases` with respect to `scalars`, +// /// where the elements of `scalars` are length-three slices of bits, and +// /// which such that the first two bits are use to select one of the +// /// bases, while the third bit is used to conditionally negate the +// /// selection. +// #[tracing::instrument(target = "r1cs", skip(bases, scalars))] +// pub fn precomputed_base_3_bit_signed_digit_scalar_mul( +// bases: &[impl Borrow<[TEProjective

]>], +// scalars: &[impl Borrow<[J]>], +// ) -> Result +// where +// J: Borrow<[Boolean<::BasePrimeField>]>, +// { +// const CHUNK_SIZE: usize = 3; +// let mut ed_result: Option> = None; +// let mut result: Option> = None; + +// let mut process_segment_result = |result: &MontgomeryAffineVar| { +// let sgmt_result = result.into_edwards()?; +// ed_result = match ed_result.as_ref() { +// None => Some(sgmt_result), +// Some(r) => Some(sgmt_result + r), +// }; +// Ok::<(), SynthesisError>(()) +// }; + +// // Compute ∏(h_i^{m_i}) for all i. +// for (segment_bits_chunks, segment_powers) in scalars.iter().zip(bases) { +// for (bits, base_power) in segment_bits_chunks +// .borrow() +// .iter() +// .zip(segment_powers.borrow()) +// { +// let base_power = base_power; +// let mut acc_power = *base_power; +// let mut coords = vec![]; +// for _ in 0..4 { +// coords.push(acc_power); +// acc_power += base_power; +// } + +// let bits = bits.borrow().to_bits_le()?; +// if bits.len() != CHUNK_SIZE { +// return Err(SynthesisError::Unsatisfiable); +// } + +// let coords = coords +// .iter() +// .map(|p| MontgomeryAffineVar::from_edwards_to_coords(&p.into_affine())) +// .collect::, _>>()?; + +// let x_coeffs = coords.iter().map(|p| p.0).collect::>(); +// let y_coeffs = coords.iter().map(|p| p.1).collect::>(); + +// let precomp = bits[0].and(&bits[1])?; + +// let x = F::zero() +// + x_coeffs[0] +// + F::from(bits[0].clone()) * (x_coeffs[1] - &x_coeffs[0]) +// + F::from(bits[1].clone()) * (x_coeffs[2] - &x_coeffs[0]) +// + F::from(precomp.clone()) +// * (x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0]); + +// let y = F::three_bit_cond_neg_lookup(&bits, &precomp, &y_coeffs)?; + +// let tmp = MontgomeryAffineVar::new(x, y); +// result = match result.as_ref() { +// None => Some(tmp), +// Some(r) => Some(tmp + r), +// }; +// } + +// process_segment_result(&result.unwrap())?; +// result = None; +// } +// if result.is_some() { +// process_segment_result(&result.unwrap())?; +// } +// Ok(ed_result.unwrap()) +// } +// } + +impl R1CSVar< as Field>::BasePrimeField> for MpcAffineVar +where + P: TEModelParameters, + P::BaseField: PrimeField, + F: MpcFieldVar, as Field>::BasePrimeField>, + <

::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, +{ + type Value = MpcTEProjective>; + + fn cs(&self) -> ConstraintSystemRef< as Field>::BasePrimeField> { + self.x.cs().or(self.y.cs()) + } + + #[inline] + fn value(&self) -> Result>, SynthesisError> { + let (x, y) = (self.x.value()?, self.y.value()?); + + let proj_variant = if x.is_shared() { + let t = MpcBaseField::

::king_share(P::BaseField::zero(), &mut ark_std::test_rng()); + let z = MpcBaseField::

::king_share(P::BaseField::one(), &mut ark_std::test_rng()); + MpcGroupProjectiveVariant::>::new(x, y, t, z) + } else { + let t = MpcBaseField::

::zero(); + let z = MpcBaseField::

::one(); + MpcGroupProjectiveVariant::>::new(x, y, t, z) + }; + + // step1: generate random rsuv + let rsuv = MpcTEProjective::>::rand(&mut ark_std::test_rng()); + + // step2: convert rstu to variant + let rsuv_variant = rsuv.convert_xytz(); + + // step3: calculate (x,y,t,z) + (r,s,u,v) + let xytzrsuv = rsuv_variant + proj_variant; + + // step4: reveal + let revealed_xr = xytzrsuv.reveal(); + + // step5: allocate share + let share = if Net::am_king() { + MpcTEProjective::from_public(revealed_xr) - rsuv + } else { + -rsuv + }; + + Ok(share) + } +} + +impl + MpcCurveVar>, as Field>::BasePrimeField> + for MpcAffineVar +where + P: TEModelParameters, + P::BaseField: PrimeField, + F: MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< + as Field>::BasePrimeField, + TableConstant = MpcBaseField

, + >, + <

::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, +{ + fn constant(g: MpcTEProjective>) -> Self { + let cs = ConstraintSystemRef::None; + Self::new_variable_omit_on_curve_check(cs, || Ok(g), AllocationMode::Constant).unwrap() + } + + fn zero() -> Self { + Self::new(F::zero(), F::one()) + } + + fn is_zero( + &self, + ) -> Result as Field>::BasePrimeField>, SynthesisError> { + self.x.is_zero()?.and(&self.x.is_one()?) + } + + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable_omit_prime_order_check( + cs: impl Into as Field>::BasePrimeField>>, + f: impl FnOnce() -> Result>, SynthesisError>, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g = Self::new_variable_omit_on_curve_check(cs, f, mode)?; + + if mode != AllocationMode::Constant { + let d = MpcBaseField::

::from_public(P::COEFF_D); + let a = MpcBaseField::

::from_public(P::COEFF_A); + // Check that ax^2 + y^2 = 1 + dx^2y^2 + // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) + let x2 = g.x.square()?; + let y2 = g.y.square()?; + + let one = MpcBaseField::

::one(); + let d_x2_minus_one = &x2 * d - one; + let a_x2_minus_one = &x2 * a - one; + + d_x2_minus_one.mul_equals(&y2, &a_x2_minus_one)?; + } + Ok(g) + } + + /// Enforce that `self` is in the prime-order subgroup. + /// + /// Does so by multiplying by the prime order, and checking that the result + /// is unchanged. + #[tracing::instrument(target = "r1cs")] + fn enforce_prime_order(&self) -> Result<(), SynthesisError> { + // let r_minus_1 = (-hbc_ScalarField::one()).into_repr(); + + // let mut result = Self::zero(); + // for b in BitIteratorBE::without_leading_zeros(r_minus_1) { + // result.double_in_place()?; + + // if b { + // result += self; + // } + // } + // self.negate()?.enforce_equal(&result)?; + + unimplemented!(); + Ok(()) + } + + #[inline] + #[tracing::instrument(target = "r1cs")] + fn double_in_place(&mut self) -> Result<(), SynthesisError> { + if self.is_constant() { + let value = self.value()?; + *self = Self::constant(value.double()); + } else { + let cs = self.cs(); + let a = MpcBaseField::

::from_public(P::COEFF_A); + + // xy + let xy = &self.x * &self.y; + let x2 = self.x.square()?; + let y2 = self.y.square()?; + + let a_x2 = &x2 * a; + + // Compute x3 = (2xy) / (ax^2 + y^2) + let x3 = F::new_witness(ark_relations::ns!(cs, "x3"), || { + let t0 = xy.value()?.double(); + let t1 = a * &x2.value()? + &y2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) + })?; + + let a_x2_plus_y2 = &a_x2 + &y2; + let two_xy = xy.double()?; + x3.mul_equals(&a_x2_plus_y2, &two_xy)?; + + // Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2) + let two = MpcBaseField::

::one().double(); + let y3 = F::new_witness(ark_relations::ns!(cs, "y3"), || { + let a_x2 = a * &x2.value()?; + let t0 = y2.value()? - &a_x2; + let t1 = two - &a_x2 - &y2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) + })?; + let y2_minus_a_x2 = &y2 - &a_x2; + let two_minus_ax2_minus_y2 = (&a_x2 + &y2).negate()? + two; + + y3.mul_equals(&two_minus_ax2_minus_y2, &y2_minus_a_x2)?; + self.x = x3; + self.y = y3; + } + Ok(()) + } + + #[tracing::instrument(target = "r1cs")] + fn negate(&self) -> Result { + Ok(Self::new(self.x.negate()?, self.y.clone())) + } + + #[tracing::instrument(target = "r1cs", skip(scalar_bits_with_base_multiples))] + fn precomputed_base_scalar_mul_le<'a, I, B>( + &mut self, + scalar_bits_with_base_multiples: I, + ) -> Result<(), SynthesisError> + where + I: Iterator>)>, + B: Borrow as Field>::BasePrimeField>>, + { + let (bits, multiples): (Vec<_>, Vec<_>) = scalar_bits_with_base_multiples + .map(|(bit, base)| (bit.borrow().clone(), *base)) + .unzip(); + let proj_zero: MpcTEProjective> = MpcTEProjective::zero(); + for (bits, multiples) in bits.chunks(2).zip(multiples.chunks(2)) { + if bits.len() == 2 { + let table = [multiples[0], multiples[1], multiples[0] + multiples[1]] + .iter() + .map(|&g| g.convert_xytz()) + .collect::>(); + + let normalized_table = MpcGroupProjectiveVariant::batch_normalization(&table); + + let zero_xy = proj_zero.convert_xytz(); + let x_s = [ + zero_xy.x, + normalized_table[0].x, + normalized_table[1].x, + normalized_table[2].x, + ]; + let y_s = [ + zero_xy.y, + normalized_table[0].y, + normalized_table[1].y, + normalized_table[2].y, + ]; + + let x = F::two_bit_lookup(&bits, &x_s)?; + let y = F::two_bit_lookup(&bits, &y_s)?; + *self += Self::new(x, y); + } else if bits.len() == 1 { + let bit = &bits[0]; + let tmp = &*self + multiples[0]; + *self = bit.select(&tmp, &*self)?; + } + } + + Ok(()) + } +} + +impl AllocVar>, as Field>::BasePrimeField> + for MpcAffineVar +where + P: TEModelParameters, + P::BaseField: PrimeField, + F: MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< + as Field>::BasePrimeField, + TableConstant = MpcBaseField

, + >, + <

::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>>( + cs: impl Into as Field>::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let f = || Ok(*f()?.borrow()); + match mode { + AllocationMode::Constant => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Input => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Witness => { + // if cofactor.is_even(): + // divide until you've removed all even factors + // else: + // just directly use double and add. + let mut power_of_2: u32 = 0; + let mut cofactor = P::COFACTOR.to_vec(); + while cofactor[0] % 2 == 0 { + div2(&mut cofactor); + power_of_2 += 1; + } + + let cofactor_weight = BitIteratorBE::new(cofactor.as_slice()) + .filter(|b| *b) + .count(); + let modulus_minus_1 = (-P::ScalarField::one()).into_repr(); // r - 1 + let modulus_minus_1_weight = + BitIteratorBE::new(modulus_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // equals the identity. + + let (mut ge, iter) = if cofactor_weight < modulus_minus_1_weight { + let ge = Self::new_variable_omit_prime_order_check( + ark_relations::ns!(cs, "Witness without subgroup check with cofactor mul"), + || f().map(|g| g.borrow().into_affine().mul_by_cofactor_inv().into()), + mode, + )?; + ( + ge, + BitIteratorBE::without_leading_zeros(cofactor.as_slice()), + ) + } else { + let ge = Self::new_variable_omit_prime_order_check( + ark_relations::ns!(cs, "Witness without subgroup check with `r` check"), + || { + f().map(|g| { + let g = g.into_affine(); + let mut power_of_two = P::ScalarField::one().into_repr(); + power_of_two.muln(power_of_2); + let power_of_two_inv = P::ScalarField::from_repr(power_of_two) + .and_then(|n| n.inverse()) + .unwrap(); + g.mul(power_of_two_inv) + }) + }, + mode, + )?; + + ( + ge, + BitIteratorBE::without_leading_zeros(modulus_minus_1.as_ref()), + ) + }; + // Remove the even part of the cofactor + for _ in 0..power_of_2 { + ge.double_in_place()?; + } + + let mut result = Self::zero(); + for b in iter { + result.double_in_place()?; + if b { + result += ≥ + } + } + if cofactor_weight < modulus_minus_1_weight { + Ok(result) + } else { + ge.enforce_equal(&ge)?; + Ok(ge) + } + } + } + } +} + +impl AllocVar>, as Field>::BasePrimeField> + for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< + as Field>::BasePrimeField, + TableConstant = MpcBaseField

, + >, + P::BaseField: PrimeField, + <

::BaseField as ark_ff::Field>::BasePrimeField: ark_ff::SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable>>>( + cs: impl Into as Field>::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode) + } +} + +impl ToConstraintFieldGadget< as Field>::BasePrimeField> + for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField>, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'a> &'a F: FieldOpsBounds<'a, MpcBaseField

, F>, + F: ToConstraintFieldGadget< as Field>::BasePrimeField>, +{ + fn to_constraint_field( + &self, + ) -> Result as Field>::BasePrimeField>>, SynthesisError> { + let mut res = Vec::new(); + + res.extend_from_slice(&self.x.to_constraint_field()?); + res.extend_from_slice(&self.y.to_constraint_field()?); + + Ok(res) + } +} + +#[inline] +fn div2(limbs: &mut [u64]) { + let mut t = 0; + for i in limbs.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } +} + +impl_bounded_ops!( + MpcAffineVar, + MpcTEProjective>, + Add, + add, + AddAssign, + add_assign, + |this: &'a MpcAffineVar, other: &'a MpcAffineVar| { + + if [this, other].is_constant() { + assert!(this.is_constant() && other.is_constant()); + MpcAffineVar::constant(this.value().unwrap() + &other.value().unwrap()) + } else { + let cs = [this, other].cs(); + let a = MpcBaseField::

::from_public(P::COEFF_A); + let d = MpcBaseField::

::from_public(P::COEFF_D); + + // Compute U = (x1 + y1) * (x2 + y2) + let u1 = (&this.x * -a) + &this.y; + let u2 = &other.x + &other.y; + + let u = u1 * &u2; + + // Compute v0 = x1 * y2 + let v0 = &other.y * &this.x; + + // Compute v1 = x2 * y1 + let v1 = &other.x * &this.y; + + // Compute C = d*v0*v1 + let v2 = &v0 * &v1 * d; + + // Compute x3 = (v0 + v1) / (1 + v2) + let x3 = F::new_witness(ark_relations::ns!(cs, "x3"), || { + let t0 = v0.value()? + &v1.value()?; + let t1 = MpcBaseField::

::one() + &v2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) + }).unwrap(); + + let v2_plus_one = &v2 + MpcBaseField::

::one(); + let v0_plus_v1 = &v0 + &v1; + x3.mul_equals(&v2_plus_one, &v0_plus_v1).unwrap(); + + // Compute y3 = (U + a * v0 - v1) / (1 - v2) + let y3 = F::new_witness(ark_relations::ns!(cs, "y3"), || { + let t0 = u.value()? + &(a * &v0.value()?) - &v1.value()?; + let t1 = MpcBaseField::

::one() - &v2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) + }).unwrap(); + + let one_minus_v2 = (&v2 - MpcBaseField::

::one()).negate().unwrap(); + let a_v0 = &v0 * a; + let u_plus_a_v0_minus_v1 = &u + &a_v0 - &v1; + + y3.mul_equals(&one_minus_v2, &u_plus_a_v0_minus_v1).unwrap(); + + MpcAffineVar::new(x3, y3) + } + }, + |this: &'a MpcAffineVar, other: MpcTEProjective>| this + MpcAffineVar::constant(other), + ( + F :MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< as Field>::BasePrimeField,TableConstant = MpcBaseField

>, + P: TEModelParameters, + ), + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for <'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +); + +impl_bounded_ops!( + MpcAffineVar, + MpcTEProjective>, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a MpcAffineVar, other: &'a MpcAffineVar| this + other.negate().unwrap(), + |this: &'a MpcAffineVar, other: MpcTEProjective>| this - MpcAffineVar::constant(other), + ( + F :MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< as Field>::BasePrimeField,TableConstant = MpcBaseField

>, + P: TEModelParameters, + ), + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for <'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F> +); + +impl<'a, P, F> GroupOpsBounds<'a, MpcTEProjective>, MpcAffineVar> + for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< + as Field>::BasePrimeField, + TableConstant = MpcBaseField

, + >, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +{ +} + +impl<'a, P, F> GroupOpsBounds<'a, MpcTEProjective>, MpcAffineVar> + for &'a MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField> + + MpcTwoBitLookupGadget< + as Field>::BasePrimeField, + TableConstant = MpcBaseField

, + >, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +{ +} + +impl MpcCondSelectGadget< as Field>::BasePrimeField> for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField>, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +{ + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditionally_select( + cond: &MpcBoolean< as Field>::BasePrimeField>, + true_value: &Self, + false_value: &Self, + ) -> Result { + let x = cond.select(&true_value.x, &false_value.x)?; + let y = cond.select(&true_value.y, &false_value.y)?; + + Ok(Self::new(x, y)) + } +} + +impl MpcEqGadget< as Field>::BasePrimeField> for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField>, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +{ + #[tracing::instrument(target = "r1cs")] + fn is_eq( + &self, + other: &Self, + ) -> Result as Field>::BasePrimeField>, SynthesisError> { + let x_equal = self.x.clone().sub(&other.x).is_zero()?; + let y_equal = self.y.clone().sub(&other.y).is_zero()?; + x_equal.and(&y_equal) + } + + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditional_enforce_equal( + &self, + other: &Self, + condition: &MpcBoolean< as Field>::BasePrimeField>, + ) -> Result<(), SynthesisError> { + self.x.conditional_enforce_equal(&other.x, condition)?; + self.y.conditional_enforce_equal(&other.y, condition)?; + Ok(()) + } + + #[inline] + #[tracing::instrument(target = "r1cs")] + fn conditional_enforce_not_equal( + &self, + other: &Self, + condition: &MpcBoolean< as Field>::BasePrimeField>, + ) -> Result<(), SynthesisError> { + self.is_eq(other)? + .and(condition)? + .enforce_equal(&MpcBoolean::Constant(false)) + } +} + +impl MpcToBitsGadget< as Field>::BasePrimeField> for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField>, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +{ + #[tracing::instrument(target = "r1cs")] + fn to_bits_le( + &self, + ) -> Result as Field>::BasePrimeField>>, SynthesisError> { + let mut x_bits = self.x.to_bits_le()?; + let y_bits = self.y.to_bits_le()?; + x_bits.extend_from_slice(&y_bits); + Ok(x_bits) + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bits_le( + &self, + ) -> Result as Field>::BasePrimeField>>, SynthesisError> { + let mut x_bits = self.x.to_non_unique_bits_le()?; + let y_bits = self.y.to_non_unique_bits_le()?; + x_bits.extend_from_slice(&y_bits); + + Ok(x_bits) + } +} + +impl ToBytesGadget< as Field>::BasePrimeField> for MpcAffineVar +where + P: TEModelParameters, + F: MpcFieldVar, as Field>::BasePrimeField>, + P::BaseField: PrimeField, + <

::BaseField as Field>::BasePrimeField: SquareRootField, + for<'b> &'b F: FieldOpsBounds<'b, MpcBaseField

, F>, +{ + #[tracing::instrument(target = "r1cs")] + fn to_bytes( + &self, + ) -> Result as Field>::BasePrimeField>>, SynthesisError> { + // let mut x_bytes = self.x.to_bytes()?; + // let y_bytes = self.y.to_bytes()?; + // x_bytes.extend_from_slice(&y_bytes); + // Ok(x_bytes) + unimplemented!() + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes( + &self, + ) -> Result as Field>::BasePrimeField>>, SynthesisError> { + // let mut x_bytes = self.x.to_non_unique_bytes()?; + // let y_bytes = self.y.to_non_unique_bytes()?; + // x_bytes.extend_from_slice(&y_bytes); + + // Ok(x_bytes) + unimplemented!() + } +} diff --git a/mpc-algebra/src/r1cs_helper/groups/mod.rs b/mpc-algebra/src/r1cs_helper/groups/mod.rs new file mode 100644 index 00000000..dfc8d6c7 --- /dev/null +++ b/mpc-algebra/src/r1cs_helper/groups/mod.rs @@ -0,0 +1,169 @@ +use ark_ec::ProjectiveCurve; +use ark_ff::{PrimeField, SquareRootField}; +use ark_r1cs_std::prelude::*; +use ark_relations::r1cs::{Namespace, SynthesisError}; +use core::ops::{Add, AddAssign, Sub, SubAssign}; + +use core::{borrow::Borrow, fmt::Debug}; + +use crate::MpcBoolean; +use crate::MpcToBitsGadget; +use crate::{FieldShare, MpcCondSelectGadget}; + +use crate::MpcEqGadget; + +/// This module contains implementations of arithmetic for various curve models. +pub mod curves; + +// pub use self::curves::short_weierstrass::{bls12, mnt4, mnt6}; + +/// A hack used to work around the lack of implied bounds. +pub trait GroupOpsBounds<'a, F, T: 'a>: + Sized + + Add<&'a T, Output = T> + + Sub<&'a T, Output = T> + + Add + + Sub + + Add + + Sub +{ +} + +/// A variable that represents a curve point for +/// the curve `C`. +pub trait MpcCurveVar: + 'static + + Sized + + Clone + + Debug + + R1CSVar + + MpcToBitsGadget + + ToBytesGadget + + MpcEqGadget + + MpcCondSelectGadget + + AllocVar + + AllocVar + + for<'a> GroupOpsBounds<'a, C, Self> + + for<'a> AddAssign<&'a Self> + + for<'a> SubAssign<&'a Self> + + AddAssign + + SubAssign + + AddAssign + + SubAssign +{ + /// Returns the constant `F::zero()`. This is the identity + /// of the group. + fn zero() -> Self; + + /// Returns a `Boolean` representing whether `self == Self::zero()`. + #[tracing::instrument(target = "r1cs")] + fn is_zero(&self) -> Result, SynthesisError> { + self.is_eq(&Self::zero()) + } + + /// Returns a constant with value `v`. + /// + /// This *should not* allocate any variables. + fn constant(other: C) -> Self; + + /// Allocates a variable in the subgroup without checking if it's in the + /// prime-order subgroup. + fn new_variable_omit_prime_order_check( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result; + + /// Enforce that `self` is in the prime-order subgroup. + fn enforce_prime_order(&self) -> Result<(), SynthesisError>; + + /// Computes `self + self`. + #[tracing::instrument(target = "r1cs")] + fn double(&self) -> Result { + let mut result = self.clone(); + result.double_in_place()?; + Ok(result) + } + + /// Sets `self = self + self`. + fn double_in_place(&mut self) -> Result<(), SynthesisError>; + + /// Coputes `-self`. + fn negate(&self) -> Result; + + /// Computes `bits * self`, where `bits` is a little-endian + /// `Boolean` representation of a scalar. + #[tracing::instrument(target = "r1cs", skip(bits))] + fn scalar_mul_le<'a>( + &self, + bits: impl Iterator>, + ) -> Result { + // TODO: in the constant case we should call precomputed_scalar_mul_le, + // but rn there's a bug when doing this with TE curves. + + // Computes the standard little-endian double-and-add algorithm + // (Algorithm 3.26, Guide to Elliptic Curve Cryptography) + let mut res = Self::zero(); + let mut multiple = self.clone(); + for bit in bits { + let tmp = res.clone() + &multiple; + res = bit.select(&tmp, &res)?; + multiple.double_in_place()?; + } + Ok(res) + } + + /// Computes a `I * self` in place, where `I` is a `Boolean` *little-endian* + /// representation of the scalar. + /// + /// The bases are precomputed power-of-two multiples of a single + /// base. + #[tracing::instrument(target = "r1cs", skip(scalar_bits_with_bases))] + fn precomputed_base_scalar_mul_le<'a, I, B>( + &mut self, + scalar_bits_with_bases: I, + ) -> Result<(), SynthesisError> + where + I: Iterator, + B: Borrow>, + C: 'a, + { + // Computes the standard little-endian double-and-add algorithm + // (Algorithm 3.26, Guide to Elliptic Curve Cryptography) + + // Let `original` be the initial value of `self`. + let mut result = Self::zero(); + for (bit, base) in scalar_bits_with_bases { + // Compute `self + 2^i * original` + let self_plus_base = result.clone() + *base; + // If `bit == 1`, set self = self + 2^i * original; + // else, set self = self; + result = bit.borrow().select(&self_plus_base, &result)?; + } + *self = result; + Ok(()) + } + + /// Computes `Σⱼ(scalarⱼ * baseⱼ)` for all j, + /// where `scalarⱼ` is a `Boolean` *little-endian* + /// representation of the j-th scalar. + #[tracing::instrument(target = "r1cs", skip(bases, scalars))] + fn precomputed_base_multiscalar_mul_le<'a, T, I, B>( + bases: &[B], + scalars: I, + ) -> Result + where + T: 'a + MpcToBitsGadget + ?Sized, + I: Iterator, + B: Borrow<[C]>, + { + let mut result = Self::zero(); + // Compute Σᵢ(bitᵢ * baseᵢ) for all i. + for (bits, bases) in scalars.zip(bases) { + let bases = bases.borrow(); + let bits = bits.to_bits_le()?; + result.precomputed_base_scalar_mul_le(bits.iter().zip(bases))?; + } + Ok(result) + } +} diff --git a/mpc-algebra/src/r1cs_helper/mpc_boolean.rs b/mpc-algebra/src/r1cs_helper/mpc_boolean.rs index cf4e0323..ea8eb30a 100644 --- a/mpc-algebra/src/r1cs_helper/mpc_boolean.rs +++ b/mpc-algebra/src/r1cs_helper/mpc_boolean.rs @@ -29,7 +29,6 @@ pub struct MpcAllocatedBool { } pub(crate) fn bool_to_field(val: impl Borrow) -> F { - // TODO: MPC Fieldの元として返す if *val.borrow() { F::one() } else { @@ -42,10 +41,8 @@ impl MpcAllocatedBool { pub fn value(&self) -> Result { let value = self.cs.assigned_value(self.variable).get()?; if value.is_zero() { - println!("ZEROZEROZERO."); Ok(false) } else if value.is_one() { - println!("ONEONEONE"); Ok(true) } else { unreachable!("Incorrect value assigned: {:?}", value); diff --git a/mpc-algebra/src/r1cs_helper/mpc_fp.rs b/mpc-algebra/src/r1cs_helper/mpc_fp.rs index 15955996..80a03284 100644 --- a/mpc-algebra/src/r1cs_helper/mpc_fp.rs +++ b/mpc-algebra/src/r1cs_helper/mpc_fp.rs @@ -20,10 +20,9 @@ use crate::{ mpc_fields::{FieldOpsBounds, MpcFieldVar}, mpc_select::{MpcCondSelectGadget, MpcTwoBitLookupGadget}, BitDecomposition, EqualityZero, MpcBoolean, MpcToBitsGadget, MpcToBytesGadget, MpcUInt8, + Reveal, }; -// TODO: MpcAllocatedFp is required? - /// Represents a variable in the constraint system whose /// value can be an arbitrary field element. #[derive(Debug, Clone)] @@ -438,21 +437,45 @@ impl MpcAllocatedFp { impl MpcAllocatedFp { pub fn is_zero(&self) -> Result, SynthesisError> { - let is_zero_value = self.value.get()?.is_zero_shared(); + let value = self.value.get()?; - let is_not_zero = - MpcBoolean::new_witness(self.cs.clone(), || Ok((!is_zero_value).field()))?; + if value.is_shared() { + let is_zero_value = value.is_zero_shared(); - let multiplier = self - .cs - .new_witness_variable(|| (self.value.get()? + is_zero_value.field()).inverse().get())?; + let is_not_zero = + MpcBoolean::new_witness(self.cs.clone(), || Ok((!is_zero_value).field()))?; - self.cs - .enforce_constraint(lc!() + self.variable, lc!() + multiplier, is_not_zero.lc())?; - self.cs - .enforce_constraint(lc!() + self.variable, is_not_zero.not().lc(), lc!())?; + let multiplier = self.cs.new_witness_variable(|| { + (self.value.get()? + is_zero_value.field()).inverse().get() + })?; + + self.cs.enforce_constraint( + lc!() + self.variable, + lc!() + multiplier, + is_not_zero.lc(), + )?; + self.cs + .enforce_constraint(lc!() + self.variable, is_not_zero.not().lc(), lc!())?; + Ok(is_not_zero.not()) + } else { + let is_zero_value = F::from(value.is_zero()); - Ok(is_not_zero.not()) + let is_not_zero = + MpcBoolean::new_witness(self.cs.clone(), || Ok(F::one() - is_zero_value))?; + + let multiplier = self + .cs + .new_witness_variable(|| (self.value.get()? + is_zero_value).inverse().get())?; + + self.cs.enforce_constraint( + lc!() + self.variable, + lc!() + multiplier, + is_not_zero.lc(), + )?; + self.cs + .enforce_constraint(lc!() + self.variable, is_not_zero.not().lc(), lc!())?; + Ok(is_not_zero.not()) + } } } @@ -703,6 +726,10 @@ impl MpcField Self::Constant(F::zero()) } + fn is_zero(&self) -> Result, SynthesisError> { + self.is_zero() + } + fn one() -> Self { Self::Constant(F::one()) } diff --git a/mpc-algebra/src/share/additive.rs b/mpc-algebra/src/share/additive.rs index 8081f479..c477536c 100644 --- a/mpc-algebra/src/share/additive.rs +++ b/mpc-algebra/src/share/additive.rs @@ -5,6 +5,7 @@ use std::io::{self, Read, Write}; use std::marker::PhantomData; use ark_ec::{group::Group, PairingEngine, ProjectiveCurve}; +use ark_ff::BigInteger; use ark_ff::{Field, FromBytes, ToBytes}; use ark_poly::UVPolynomial; use ark_serialize::{ @@ -158,6 +159,18 @@ impl FieldShare for AdditiveFieldShare { num.divide_with_q_and_r(&den) .map(|(q, r)| (Self::d_poly_unshare(q), Self::d_poly_unshare(r))) } + + fn modulus_conversion>(&mut self) -> S2 + where + F: ark_ff::PrimeField, + { + // TODO: bad implementation, so it's just for testing + let revealed_val = self.reveal(); + let bits = revealed_val.into_repr().to_bits_le(); + let converted_val = F2::from_repr(BigInteger::from_bits_le(&bits)).unwrap(); + + S2::king_share(converted_val, &mut ark_std::test_rng()) + } } macro_rules! impl_field_basics { @@ -390,8 +403,8 @@ impl Reveal for AdditiveGroupShare { macro_rules! impl_group_basics { ($share:ident, $bound:ident) => { impl Debug for $share { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - todo!() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.val) } } impl ToBytes for $share { diff --git a/mpc-algebra/src/share/field.rs b/mpc-algebra/src/share/field.rs index 2bae1df6..c2a11088 100644 --- a/mpc-algebra/src/share/field.rs +++ b/mpc-algebra/src/share/field.rs @@ -161,6 +161,13 @@ pub trait FieldShare: ) -> Option<(DensePolynomial, DensePolynomial)> { todo!("Implement generic poly div") } + + fn modulus_conversion>(&mut self) -> S2 + where + F: PrimeField, + { + todo!() + } } pub type DensePolynomial = Vec; diff --git a/mpc-algebra/src/share/pairing.rs b/mpc-algebra/src/share/pairing.rs index 1c0f3c98..7cff695f 100644 --- a/mpc-algebra/src/share/pairing.rs +++ b/mpc-algebra/src/share/pairing.rs @@ -58,7 +58,7 @@ pub trait AffProjShare< Fr: Field, A: AffineCurve + Group, P: ProjectiveCurve, -> +>: 'static { type FrShare: FieldShare; type AffineShare: GroupShare; diff --git a/mpc-algebra/src/share/spdz.rs b/mpc-algebra/src/share/spdz.rs index 0dd46926..0341b426 100644 --- a/mpc-algebra/src/share/spdz.rs +++ b/mpc-algebra/src/share/spdz.rs @@ -246,6 +246,18 @@ impl FieldShare for SpdzFieldShare { .collect(), )) } + + fn modulus_conversion>(&mut self) -> S2 + where + F: PrimeField, + { + // TODO: bad implementation, so it's just for testing + let revealed_val = self.reveal(); + let bits = revealed_val.into_repr().to_bits_le(); + let converted_val = F2::from_repr(BigInteger::from_bits_le(&bits)).unwrap(); + + S2::king_share(converted_val, &mut ark_std::test_rng()) + } } #[derive(Derivative)] @@ -260,7 +272,7 @@ impl FieldShare for SpdzFieldShare { Hash(bound = "T: Hash") )] pub struct SpdzGroupShare { - sh: AdditiveGroupShare, + pub sh: AdditiveGroupShare, mac: AdditiveGroupShare, } @@ -301,6 +313,9 @@ impl Reveal for SpdzGroupShare { }), } } + fn unwrap_as_public(self) -> Self::Base { + self.sh.unwrap_as_public() + } fn king_share(f: Self::Base, rng: &mut R) -> Self { let mut r: Vec = (0..(Net::n_parties() - 1)).map(|_| G::rand(rng)).collect(); let sum_r: G = r.iter().sum(); @@ -380,17 +395,17 @@ macro_rules! impl_spdz_basics_2_param { unimplemented!("deserialize_with_flags") } } - impl UniformRand for $share { - fn rand(_rng: &mut R) -> Self { - todo!() - //Self::from_add_shared(::rand(rng)) - } - } }; } impl_spdz_basics_2_param!(SpdzGroupShare, Group); +impl UniformRand for SpdzGroupShare { + fn rand(rng: &mut R) -> Self { + Self::from_add_shared(::rand(rng)) + } +} + impl> GroupShare for SpdzGroupShare { type FieldShare = SpdzFieldShare; @@ -475,6 +490,12 @@ pub struct SpdzMulFieldShare { } impl_spdz_basics_2_param!(SpdzMulFieldShare, Field); +impl UniformRand for SpdzMulFieldShare { + fn rand(rng: &mut R) -> Self { + Self::from_add_shared(::rand(rng)) + } +} + impl Reveal for SpdzMulFieldShare { type Base = F; diff --git a/mpc-algebra/src/wire.rs b/mpc-algebra/src/wire.rs index b6eee390..42b56af0 100644 --- a/mpc-algebra/src/wire.rs +++ b/mpc-algebra/src/wire.rs @@ -8,6 +8,6 @@ pub mod pairing; pub use pairing::*; pub mod uint8; pub use uint8::*; -pub mod edwards; -pub use edwards::*; +pub mod edwards2; +pub use edwards2::*; pub mod macros; diff --git a/mpc-algebra/src/wire/edwards.rs b/mpc-algebra/src/wire/edwards.rs index ac7abf59..360e7989 100644 --- a/mpc-algebra/src/wire/edwards.rs +++ b/mpc-algebra/src/wire/edwards.rs @@ -299,12 +299,20 @@ macro_rules! impl_edwards_related { Self::Base::new(x.reveal(), y.reveal(), t.reveal(), z.reveal()) } - fn from_add_shared(_b: Self::Base) -> Self { - unimplemented!() + fn from_add_shared(b: Self::Base) -> Self { + let x = <$param as ModelParameters>::BaseField::from_add_shared(b.x); + let y = <$param as ModelParameters>::BaseField::from_add_shared(b.y); + let t = <$param as ModelParameters>::BaseField::from_add_shared(b.t); + let z = <$param as ModelParameters>::BaseField::from_add_shared(b.z); + GroupProjective::new(x, y, t, z) } - fn from_public(_b: Self::Base) -> Self { - unimplemented!() + fn from_public(b: Self::Base) -> Self { + let x = <$param as ModelParameters>::BaseField::from_public(b.x); + let y = <$param as ModelParameters>::BaseField::from_public(b.y); + let t = <$param as ModelParameters>::BaseField::from_public(b.t); + let z = <$param as ModelParameters>::BaseField::from_public(b.z); + GroupProjective::new(x, y, t, z) } } diff --git a/mpc-algebra/src/wire/edwards2.rs b/mpc-algebra/src/wire/edwards2.rs new file mode 100644 index 00000000..4f77d139 --- /dev/null +++ b/mpc-algebra/src/wire/edwards2.rs @@ -0,0 +1,1287 @@ +use ark_ec::group::Group; +use ark_ec::models::TEModelParameters as Parameters; +use ark_ec::twisted_edwards_extended::GroupAffine; +use ark_ec::twisted_edwards_extended::GroupProjective; +use ark_ec::{AffineCurve, ProjectiveCurve}; +use ark_ed_on_bls12_377::EdwardsParameters; +use ark_ed_on_bls12_377::EdwardsProjective; +use ark_ff::BitIteratorBE; +use ark_ff::{Field, FromBytes, One, PrimeField, PubUniformRand, ToBytes, UniformRand, Zero}; +use ark_serialize::{ + CanonicalDeserialize, CanonicalDeserializeWithFlags, CanonicalSerialize, + CanonicalSerializeWithFlags, Flags, SerializationError, +}; + +use crate::commitment::pedersen::{Parameters as PedersenParameters, Randomness}; +use crate::encryption::elgamal::elgamal::{ + Parameters as ElGamalParameters, Randomness as ElGamalRandomness, +}; + +use ark_crypto_primitives::commitment::pedersen::Parameters as LocalPedersenParameters; +use ark_crypto_primitives::encryption::elgamal::{ + Parameters as LocalElGamalParameters, Randomness as LocalElGamalRandomness, +}; + +use derivative::Derivative; +use mpc_net::{MpcMultiNet as Net, MpcNet}; +use mpc_trait::MpcWire; +use rand::Rng; +use zeroize::Zeroize; + +use std::fmt::{self, Display}; +use std::io::{self, Read, Write}; +use std::iter::Sum; +use std::marker::PhantomData; +use std::ops::*; + +use crate::groups::curves::twisted_edwards::MpcAffineVar; +use crate::*; + +pub trait APShare: + AffProjShare, GroupProjective

> +{ + type BaseShare: FieldShare; +} + +pub type AdditiveMpcEdwardsProjective = + MpcGroupProjective>; +pub type AdditiveMpcEdwardsAffine = + MpcGroupAffine>; + +pub type SpdzMpcEdwardsProjective = + MpcGroupProjective>; +pub type SpdzMpcEdwardsAffine = + MpcGroupAffine>; + +type AdditiveFqVar = MpcFpVar>; +pub type AdditiveMpcEdwardsVar = MpcAffineVar; + +type SpdzFqVar = MpcFpVar>; +pub type SpdzMpcEdwardsVar = MpcAffineVar; + +#[derive(Derivative)] +#[derivative( + Clone(bound = "P:Parameters"), + Copy(bound = "P: Parameters"), + Debug(bound = "P: Parameters"), + PartialEq(bound = "P: Parameters"), + Hash(bound = "P: Parameters"), + Eq(bound = "P: Parameters") +)] +pub struct MpcGroupAffine> { + val: MpcGroup, S::AffineShare>, +} + +#[derive(Derivative)] +#[derivative( + Clone(bound = "P: Parameters"), + Copy(bound = "P: Parameters"), + Debug(bound = "P: Parameters"), + PartialEq(bound = "P: Parameters"), + Hash(bound = "P: Parameters"), + Eq(bound = "P: Parameters") +)] +pub struct MpcGroupProjective> { + val: MpcGroup, S::ProjectiveShare>, +} + +#[derive(Derivative)] +#[derivative( + Copy(bound = "P:Parameters"), + Clone(bound = "P: Parameters"), + Debug(bound = "P: Parameters") +)] +pub struct MpcGroupProjectiveVariant> { + pub x: MpcField, + pub y: MpcField, + pub t: MpcField, + pub z: MpcField, +} + +impl> MpcGroupProjectiveVariant { + pub fn new( + x: MpcField, + y: MpcField, + t: MpcField, + z: MpcField, + ) -> Self { + Self { x, y, t, z } + } +} + +impl> Reveal for MpcGroupProjectiveVariant { + type Base = GroupProjective

; + + fn reveal(self) -> Self::Base { + Self::Base::new( + self.x.reveal(), + self.y.reveal(), + self.t.reveal(), + self.z.reveal(), + ) + } + + fn from_add_shared(_b: Self::Base) -> Self { + unimplemented!() + } + + fn from_public(b: Self::Base) -> Self { + Self { + x: MpcField::::from_public(b.x), + y: MpcField::::from_public(b.y), + t: MpcField::::from_public(b.t), + z: MpcField::::from_public(b.z), + } + } +} + +impl> MpcGroupProjectiveVariant +where +

::BaseField: ark_ff::PrimeField, +{ + pub fn batch_normalization(v: &[Self]) -> Vec { + let z_s = v.iter().map(|g| g.z).collect::>(); + let inversed_z_s = z_s.iter().map(|z| z.inverse().unwrap()).collect::>(); + + v.iter() + .zip(inversed_z_s.iter()) + .map(|(g, z)| { + // let z = inversed_z_s.pop().unwrap(); + Self { + x: g.x * z, + y: g.y * z, + t: g.t * z, + z: MpcField::::one(), + } + }) + .collect::>() + } +} + +impl> Group for MpcGroupAffine { + type ScalarField = MpcField; + + fn double(&self) -> Self { + unimplemented!() + } + + fn double_in_place(&mut self) -> &mut Self { + unimplemented!() + } +} + +// for MpcGroupProjective + +impl> Reveal for MpcGroupProjective { + type Base = GroupProjective

; + #[inline] + fn reveal(self) -> Self::Base { + self.val.reveal() + } + #[inline] + fn from_public(t: Self::Base) -> Self { + Self { + val: MpcGroup::from_public(t), + } + } + #[inline] + fn from_add_shared(_t: Self::Base) -> Self { + unimplemented!() + } + #[inline] + fn unwrap_as_public(self) -> Self::Base { + // self.val.unwrap_as_public() + unimplemented!() + } + #[inline] + fn king_share(_f: Self::Base, _rng: &mut R) -> Self { + unimplemented!() + } + #[inline] + fn king_share_batch(_f: Vec, _rng: &mut R) -> Vec { + unimplemented!() + } +} + +impl> Display for MpcGroupProjective { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.val) + } +} + +impl> ToBytes for MpcGroupProjective { + fn write(&self, writer: W) -> io::Result<()> { + self.val.write(writer) + } +} + +impl> FromBytes for MpcGroupProjective { + fn read(_reader: R) -> io::Result { + unimplemented!() + } +} + +impl> CanonicalSerialize for MpcGroupProjective { + fn serialize(&self, writer: W) -> Result<(), SerializationError> { + self.val.serialize(writer) + } + + fn serialized_size(&self) -> usize { + unimplemented!() + } +} + +impl> CanonicalSerializeWithFlags for MpcGroupProjective { + fn serialize_with_flags( + &self, + _writer: W, + _flags: Fl, + ) -> Result<(), SerializationError> { + unimplemented!() + } + + fn serialized_size_with_flags(&self) -> usize { + unimplemented!() + } +} + +impl> CanonicalDeserialize for MpcGroupProjective { + fn deserialize(_reader: R) -> Result { + unimplemented!() + } +} + +impl> CanonicalDeserializeWithFlags for MpcGroupProjective { + fn deserialize_with_flags( + _reader: R, + ) -> Result<(Self, Fl), SerializationError> { + unimplemented!() + } +} + +impl> UniformRand for MpcGroupProjective { + fn rand(rng: &mut R) -> Self { + Self { + val: MpcGroup::rand(rng), + } + } +} + +impl> PubUniformRand for MpcGroupProjective { + fn pub_rand(rng: &mut R) -> Self { + Self { + val: MpcGroup::pub_rand(rng), + } + } +} + +impl> AddAssign for MpcGroupProjective { + fn add_assign(&mut self, rhs: Self) { + self.add_assign(&rhs) + } +} + +impl<'a, P: Parameters, S: APShare

> AddAssign<&'a MpcGroupProjective> + for MpcGroupProjective +{ + fn add_assign(&mut self, rhs: &'a MpcGroupProjective) { + self.val += &rhs.val; + } +} + +impl> Add for MpcGroupProjective { + type Output = Self; + + fn add(mut self, rhs: Self) -> Self::Output { + self.add_assign(&rhs); + self + } +} + +impl<'a, P: Parameters, S: APShare

> Add<&'a MpcGroupProjective> + for MpcGroupProjective +{ + type Output = Self; + + fn add(mut self, rhs: &'a MpcGroupProjective) -> Self::Output { + self.add_assign(rhs); + self + } +} + +impl<'a, P: Parameters, S: APShare

> SubAssign<&'a MpcGroupProjective> + for MpcGroupProjective +{ + fn sub_assign(&mut self, rhs: &'a MpcGroupProjective) { + self.val -= &rhs.val; + } +} + +impl> SubAssign for MpcGroupProjective { + fn sub_assign(&mut self, rhs: Self) { + self.sub_assign(&rhs) + } +} + +impl> Sub for MpcGroupProjective { + type Output = Self; + + fn sub(mut self, rhs: Self) -> Self::Output { + self.sub_assign(&rhs); + self + } +} + +impl<'a, P: Parameters, S: APShare

> Sub<&'a MpcGroupProjective> + for MpcGroupProjective +{ + type Output = Self; + + fn sub(mut self, rhs: &'a MpcGroupProjective) -> Self::Output { + self.sub_assign(rhs); + self + } +} + +impl> MulAssign for MpcGroupProjective { + fn mul_assign(&mut self, _rhs: Self) { + unimplemented!() + } +} + +impl> Neg for MpcGroupProjective { + type Output = Self; + + fn neg(self) -> Self::Output { + Self { val: -self.val } + } +} + +impl> Sum for MpcGroupProjective { + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), core::ops::Add::add) + } +} + +impl<'a, P: Parameters, S: APShare

> Sum<&'a MpcGroupProjective> + for MpcGroupProjective +{ + fn sum>>(_iter: I) -> Self { + unimplemented!() + } +} + +impl> Zero for MpcGroupProjective { + fn zero() -> Self { + Self { + val: MpcGroup::zero(), + } + } + + fn is_zero(&self) -> bool { + unimplemented!() + } +} + +impl> Zeroize for MpcGroupProjective { + fn zeroize(&mut self) { + unimplemented!() + } +} + +impl> Default for MpcGroupProjective { + fn default() -> Self { + unimplemented!() + } +} + +impl> MpcWire for MpcGroupProjective { + #[inline] + fn publicize(&mut self) { + self.val.publicize(); + } + #[inline] + fn is_shared(&self) -> bool { + self.val.is_shared() + } +} + +impl> MulAssign> + for MpcGroupProjective +{ + fn mul_assign(&mut self, _rhs: MpcField) { + unimplemented!() + } +} + +impl> ProjectiveCurve for MpcGroupProjective +where + P::BaseField: PrimeField, +{ + const COFACTOR: &'static [u64] = GroupProjective::

::COFACTOR; + + type ScalarField = MpcField; + + type BaseField = MpcField; + + type Affine = MpcGroupAffine; + + fn prime_subgroup_generator() -> Self { + MpcGroupAffine::prime_subgroup_generator().into() + } + + fn batch_normalization(v: &mut [Self]) { + unimplemented!(); + } + + fn is_normalized(&self) -> bool { + unimplemented!() + } + + fn double_in_place(&mut self) -> &mut Self { + self.val.double_in_place(); + self + } + + fn add_assign_mixed(&mut self, other: &Self::Affine) { + let new_self = match (&self.val, &other.val) { + (MpcGroup::Shared(a), MpcGroup::Shared(b)) => { + MpcGroup::Shared(S::add_sh_proj_sh_aff(a.clone(), b)) + } + (MpcGroup::Shared(a), MpcGroup::Public(b)) => { + MpcGroup::Shared(S::add_sh_proj_pub_aff(a.clone(), b)) + } + (MpcGroup::Public(a), MpcGroup::Shared(b)) => { + MpcGroup::Shared(S::add_pub_proj_sh_aff(a, b.clone())) + } + (MpcGroup::Public(a), MpcGroup::Public(b)) => MpcGroup::Public({ + let mut a = a.clone(); + a.add_assign_mixed(b); + a + }), + }; + self.val = new_self; + } +} + +// for MpcGroupAffine + +impl> Reveal for MpcGroupAffine { + type Base = GroupAffine

; + #[inline] + fn reveal(self) -> Self::Base { + self.val.reveal() + } + #[inline] + fn from_public(t: Self::Base) -> Self { + Self { + val: MpcGroup::from_public(t), + } + } + #[inline] + fn from_add_shared(t: Self::Base) -> Self { + Self { + val: MpcGroup::from_add_shared(t), + } + } + #[inline] + fn unwrap_as_public(self) -> Self::Base { + // self.val.unwrap_as_public() + unimplemented!() + } + #[inline] + fn king_share(_f: Self::Base, _rng: &mut R) -> Self { + unimplemented!() + } + #[inline] + fn king_share_batch(_f: Vec, _rng: &mut R) -> Vec { + unimplemented!() + } +} + +impl> Display for MpcGroupAffine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.val) + } +} + +impl> ToBytes for MpcGroupAffine { + fn write(&self, writer: W) -> io::Result<()> { + self.val.write(writer) + } +} + +impl> FromBytes for MpcGroupAffine { + fn read(_reader: R) -> io::Result { + unimplemented!() + } +} + +impl> CanonicalSerialize for MpcGroupAffine { + fn serialize(&self, writer: W) -> Result<(), SerializationError> { + self.val.serialize(writer) + } + + fn serialized_size(&self) -> usize { + unimplemented!() + } +} + +impl> CanonicalSerializeWithFlags for MpcGroupAffine { + fn serialize_with_flags( + &self, + _writer: W, + _flags: Fl, + ) -> Result<(), SerializationError> { + unimplemented!() + } + + fn serialized_size_with_flags(&self) -> usize { + unimplemented!() + } +} + +impl> CanonicalDeserialize for MpcGroupAffine { + fn deserialize(_reader: R) -> Result { + unimplemented!() + } +} + +impl> CanonicalDeserializeWithFlags for MpcGroupAffine { + fn deserialize_with_flags( + _reader: R, + ) -> Result<(Self, Fl), SerializationError> { + unimplemented!() + } +} + +impl> UniformRand for MpcGroupAffine { + fn rand(rng: &mut R) -> Self { + Self { + val: MpcGroup::rand(rng), + } + } +} + +impl> PubUniformRand for MpcGroupAffine { + fn pub_rand(rng: &mut R) -> Self { + Self { + val: MpcGroup::pub_rand(rng), + } + } +} + +impl> AddAssign for MpcGroupAffine { + fn add_assign(&mut self, rhs: Self) { + self.add_assign(&rhs) + } +} + +impl<'a, P: Parameters, S: APShare

> AddAssign<&'a MpcGroupAffine> + for MpcGroupAffine +{ + fn add_assign(&mut self, rhs: &'a MpcGroupAffine) { + self.val += &rhs.val; + } +} + +impl> Add for MpcGroupAffine { + type Output = Self; + + fn add(mut self, rhs: Self) -> Self::Output { + self.add_assign(&rhs); + self + } +} + +impl<'a, P: Parameters, S: APShare

> Add<&'a MpcGroupAffine> for MpcGroupAffine { + type Output = Self; + + fn add(mut self, rhs: &'a MpcGroupAffine) -> Self::Output { + self.add_assign(rhs); + self + } +} + +impl<'a, P: Parameters, S: APShare

> SubAssign<&'a MpcGroupAffine> + for MpcGroupAffine +{ + fn sub_assign(&mut self, rhs: &'a MpcGroupAffine) { + self.val -= &rhs.val; + } +} + +impl> SubAssign for MpcGroupAffine { + fn sub_assign(&mut self, rhs: Self) { + self.sub_assign(&rhs) + } +} + +impl> Sub for MpcGroupAffine { + type Output = Self; + + fn sub(mut self, rhs: Self) -> Self::Output { + self.sub_assign(&rhs); + self + } +} + +impl<'a, P: Parameters, S: APShare

> Sub<&'a MpcGroupAffine> for MpcGroupAffine { + type Output = Self; + + fn sub(mut self, rhs: &'a MpcGroupAffine) -> Self::Output { + self.sub_assign(rhs); + self + } +} + +impl> MulAssign for MpcGroupAffine { + fn mul_assign(&mut self, _rhs: Self) { + unimplemented!() + } +} + +impl> Neg for MpcGroupAffine { + type Output = Self; + + fn neg(self) -> Self::Output { + unimplemented!() + } +} + +impl> Sum for MpcGroupAffine { + fn sum>(_iter: I) -> Self { + unimplemented!() + } +} + +impl<'a, P: Parameters, S: APShare

> Sum<&'a MpcGroupAffine> for MpcGroupAffine { + fn sum>>(_iter: I) -> Self { + unimplemented!() + } +} + +impl> Zero for MpcGroupAffine { + fn zero() -> Self { + Self { + val: MpcGroup::zero(), + } + } + + fn is_zero(&self) -> bool { + unimplemented!() + } +} + +impl> Zeroize for MpcGroupAffine { + fn zeroize(&mut self) { + unimplemented!() + } +} + +impl> Default for MpcGroupAffine { + #[inline] + fn default() -> Self { + Self::zero() + } +} + +impl> MpcWire for MpcGroupAffine { + #[inline] + fn publicize(&mut self) { + self.val.publicize(); + } + #[inline] + fn is_shared(&self) -> bool { + self.val.is_shared() + } +} + +impl> MulAssign> + for MpcGroupAffine +{ + fn mul_assign(&mut self, rhs: MpcField) { + unimplemented!() + } +} + +impl> AffineCurve for MpcGroupAffine +where + P::BaseField: PrimeField, +{ + const COFACTOR: &'static [u64] = P::COFACTOR; + + type ScalarField = MpcField; + + type BaseField = MpcField; + + type Projective = MpcGroupProjective; + + fn prime_subgroup_generator() -> Self { + Self { + val: MpcGroup::from_public(GroupAffine::

::new( + P::AFFINE_GENERATOR_COEFFS.0, + P::AFFINE_GENERATOR_COEFFS.1, + )), + } + } + + fn from_random_bytes(bytes: &[u8]) -> Option { + unimplemented!() + } + + fn mul::BigInt>>( + &self, + other: T, + ) -> Self::Projective { + self.mul_bits(BitIteratorBE::new(other.into())) + } + + fn mul_by_cofactor_to_projective(&self) -> Self::Projective { + unimplemented!() + } + + fn mul_by_cofactor_inv(&self) -> Self { + ::mul(self, P::COFACTOR_INV).into() + } + + fn multi_scalar_mul(bases: &[Self], scalars: &[Self::ScalarField]) -> Self::Projective { + let b = { + assert!(bases.iter().all(|b| !b.is_shared())); + // let scalars_shared = scalars.first().map(|s| s.is_shared()).unwrap_or(true); + // assert!(scalars.iter().all(|b| scalars_shared == b.is_shared())); + let bases = + MpcGroup::all_public_or_shared(bases.into_iter().map(|i| i.val.clone())).unwrap(); + match MpcField::all_public_or_shared(scalars.into_iter().cloned()) { + Ok(pub_scalars) => { + // let t = start_timer!(|| "MSM inner"); + let r = Self::Projective { + // wat? + val: if true { + // let t1 = start_timer!(|| "do msm"); + let r = as AffineCurve>::multi_scalar_mul( + &bases, + &pub_scalars, + ); + // end_timer!(t1); + // let t1 = start_timer!(|| "cast"); + let r = + MpcGroup::Shared(::from_public(r)); + // end_timer!(t1); + r + } else { + MpcGroup::Public( as AffineCurve>::multi_scalar_mul( + &bases, + &pub_scalars, + )) + }, + }; + // end_timer!(t); + r + } + Err(priv_scalars) => { + // let t = start_timer!(|| "MSM inner"); + let r = Self::Projective { + val: MpcGroup::Shared(S::sh_aff_to_proj(, + >>::multi_scale_pub_group( + &bases, &priv_scalars + ))), + }; + // end_timer!(t); + r + } + } + }; + // { + // let mut pa = a; + // let mut pb = b; + // pa.publicize(); + // pb.publicize(); + // println!("{}\n->\n{}", a, pa); + // println!("{}\n->\n{}", b, pb); + // println!("Check eq!"); + // //assert_eq!(a, b); + // assert_eq!(pa, pb); + // } + b + } + + fn scalar_mul>(&self, other: T) -> Self::Projective { + let res = Self { + val: self.val.mul(other.into()), + }; + + res.into() + } +} + +impl> MpcGroupAffine +where + P::BaseField: PrimeField, +{ + pub(crate) fn mul_bits(&self, bits: impl Iterator) -> MpcGroupProjective { + let mut res = MpcGroupProjective::zero(); + for i in bits.skip_while(|b| !b) { + as ProjectiveCurve>::double_in_place(&mut res); + if i { + res.add_assign_mixed(self) + } + } + res + } +} + +type FrShare = AdditiveFieldShare; +type FqShare = AdditiveFieldShare; +type SpdzFrShare = SpdzFieldShare; +type SpdzFqShare = SpdzFieldShare; + +type AffineShare = AdditiveGroupShare, AffineMsm>>; +type ProjectiveShare = + AdditiveGroupShare, crate::msm::ProjectiveMsm>>; + +// type APShare

= AffProjShare< +// P::ScalarField, +// GroupAffine

, +// GroupProjective

, +// FrShare = AdditiveFieldShare, +// AffineShare = AffineShare

, +// ProjectiveShare = ProjectiveShare

, +// >; + +pub struct AdditiveAffProjShare { + pub PhantomData: PhantomData

, +} + +pub struct SpdzAffProjShare { + pub PhantomData: PhantomData

, +} + +impl AffProjShare, GroupProjective

> + for AdditiveAffProjShare

+{ + type FrShare = FrShare

; + type AffineShare = AdditiveGroupShare, AffineMsm>>; + type ProjectiveShare = + AdditiveGroupShare, ProjectiveMsm>>; + + fn sh_aff_to_proj(g: Self::AffineShare) -> Self::ProjectiveShare { + g.map_homo(|s| s.into()) + } + + fn sh_proj_to_aff(g: Self::ProjectiveShare) -> Self::AffineShare { + g.map_homo(|s| s.into()) + } + + fn add_sh_proj_sh_aff( + mut a: Self::ProjectiveShare, + o: &Self::AffineShare, + ) -> Self::ProjectiveShare { + a.val.add_assign_mixed(&o.val); + a + } + fn add_sh_proj_pub_aff( + mut a: Self::ProjectiveShare, + o: &GroupAffine

, + ) -> Self::ProjectiveShare { + if Net::am_king() { + a.val.add_assign_mixed(&o); + } + a + } + fn add_pub_proj_sh_aff( + _a: &GroupProjective

, + _o: Self::AffineShare, + ) -> Self::ProjectiveShare { + unimplemented!() + } +} + +impl APShare

for AdditiveAffProjShare

{ + type BaseShare = FqShare

; +} + +impl AffProjShare, GroupProjective

> + for SpdzAffProjShare

+{ + type FrShare = SpdzFrShare

; + type AffineShare = SpdzGroupShare, AffineMsm>>; + type ProjectiveShare = SpdzGroupShare, ProjectiveMsm>>; + + fn sh_aff_to_proj(g: Self::AffineShare) -> Self::ProjectiveShare { + g.map_homo(|s| s.into()) + } + + fn sh_proj_to_aff(g: Self::ProjectiveShare) -> Self::AffineShare { + g.map_homo(|s| s.into()) + } + + fn add_sh_proj_sh_aff( + mut a: Self::ProjectiveShare, + o: &Self::AffineShare, + ) -> Self::ProjectiveShare { + a.sh.val.add_assign_mixed(&o.sh.val); + a + } + fn add_sh_proj_pub_aff( + mut a: Self::ProjectiveShare, + o: &GroupAffine

, + ) -> Self::ProjectiveShare { + if Net::am_king() { + a.sh.val.add_assign_mixed(&o); + } + a + } + fn add_pub_proj_sh_aff( + _a: &GroupProjective

, + _o: Self::AffineShare, + ) -> Self::ProjectiveShare { + unimplemented!() + } +} + +impl APShare

for SpdzAffProjShare

{ + type BaseShare = SpdzFqShare

; +} + +impl> From> for MpcGroupProjective { + fn from(p: MpcGroupAffine) -> MpcGroupProjective { + // Self::new(p.x, p.y, p.x * &p.y, P::BaseField::one()) + Self { + val: p.val.map(|s| s.into(), S::sh_aff_to_proj), + } + } +} + +impl> From> for MpcGroupAffine { + fn from(p: MpcGroupProjective) -> MpcGroupAffine { + Self { + val: p.val.map(|s| s.into(), S::sh_proj_to_aff), + } + } +} + +type hbc_BaseField = honest_but_curious::MpcField; + +impl> MpcGroupProjective { + pub fn convert_xytz(&self) -> MpcGroupProjectiveVariant { + match self.val { + MpcGroup::Shared(s) => { + // 1. get unprocessed x, y + let unprocessed = self.val.unwrap_as_public(); + + // 2. generate shares and communicate + let vec_x = (0..Net::n_parties()) + .map(|i| { + MpcField::::from_add_shared( + if Net::party_id() == i { + unprocessed.x + } else { + P::BaseField::zero() + }, + ) + }) + .collect::>>(); + + let vec_y = (0..Net::n_parties()) + .map(|i| { + MpcField::::from_add_shared( + if Net::party_id() == i { + unprocessed.y + } else { + P::BaseField::zero() + }, + ) + }) + .collect::>>(); + + let vec_t = (0..Net::n_parties()) + .map(|i| { + MpcField::::from_add_shared( + if Net::party_id() == i { + unprocessed.t + } else { + P::BaseField::zero() + }, + ) + }) + .collect::>>(); + + let vec_z = (0..Net::n_parties()) + .map(|i| { + MpcField::::from_add_shared( + if Net::party_id() == i { + unprocessed.z + } else { + P::BaseField::zero() + }, + ) + }) + .collect::>>(); + + // 3. summand and make converted share + // addition of elliptic curve points in (x,y,t,z) form. + let (sum_x, sum_y, sum_t, sum_z) = vec_x + .iter() + .zip(vec_y.iter()) + .zip(vec_t.iter()) + .zip(vec_z.iter()) + .fold( + ( + MpcField::::zero(), + MpcField::::one(), + MpcField::::zero(), + MpcField::::one(), + ), + |(acc_x, acc_y, acc_t, acc_z), (((&x, &y), &t), &z)| { + // A = x1 * x2 + let a = acc_x * x; + + // B = y1 * y2 + let b = acc_y * y; + + // C = d * t1 * t2 + let c = MpcField::::from_public(P::COEFF_D) + * acc_t + * t; + + // D = z1 * z2 + let d = acc_z * z; + + // H = B - aA + let h = b - a * &MpcField::::from_public( + P::COEFF_A, + ); + + // E = (x1 + y1) * (x2 + y2) - A - B + let e = (acc_x + &acc_y) * &(x + y) - &a - &b; + + // F = D - C + let f = d - &c; + + // G = D + C + let g = d + &c; + + // x3 = E * F + let sum_x = e * &f; + + // y3 = G * H + let sum_y = g * &h; + + // t3 = E * H + let sum_t = e * &h; + + // z3 = F * G + let sum_z = f * &g; + + (sum_x, sum_y, sum_t, sum_z) + }, + ); + + MpcGroupProjectiveVariant { + x: sum_x, + y: sum_y, + t: sum_t, + z: sum_z, + } + } + MpcGroup::Public(s) => MpcGroupProjectiveVariant { + x: MpcField::::from_public(s.x), + y: MpcField::::from_public(s.y), + t: MpcField::::from_public(s.t), + z: MpcField::::from_public(s.z), + }, + } + } +} + +impl> Add for MpcGroupProjectiveVariant { + type Output = Self; + fn add(mut self, other: Self) -> Self::Output { + self.add_assign(&other); + self + } +} + +impl<'a, P: Parameters, S: APShare

> AddAssign<&'a Self> for MpcGroupProjectiveVariant { + fn add_assign(&mut self, other: &'a Self) { + // See "Twisted Edwards Curves Revisited" (https://eprint.iacr.org/2008/522.pdf) + // by Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // 3.1 Unified Addition in E^e + + // A = x1 * x2 + let a = self.x * &other.x; + + // B = y1 * y2 + let b = self.y * &other.y; + + // C = d * t1 * t2 + let c = MpcField::::from_public(P::COEFF_D) * self.t * other.t; + + // D = z1 * z2 + let d = self.z * &other.z; + + // H = B - aA + let h = b - a * &MpcField::::from_public(P::COEFF_A); + + // E = (x1 + y1) * (x2 + y2) - A - B + let e = (self.x + &self.y) * &(other.x + &other.y) - &a - &b; + + // F = D - C + let f = d - &c; + + // G = D + C + let g = d + &c; + + // x3 = E * F + self.x = e * &f; + + // y3 = G * H + self.y = g * &h; + + // t3 = E * H + self.t = e * &h; + + // z3 = F * G + self.z = f * &g; + } +} + +pub trait ToLocal { + type Local; + + // lift objects to local + fn to_local(&self) -> Self::Local; +} + +pub trait FromLocal { + type Local; + + // lift objects from local + fn from_local(local: &Self::Local) -> Self; +} + +macro_rules! impl_edwards_related { + ($curve:ident, $affine:ident) => { + impl ToLocal for $curve { + type Local = GroupProjective; + fn to_local(&self) -> GroupProjective { + self.val.unwrap_as_public() + } + } + + impl ToLocal for PedersenParameters<$curve> { + type Local = LocalPedersenParameters; + + fn to_local(&self) -> Self::Local { + let randomness_generator = self + .randomness_generator + .iter() + .map(|x| x.to_local()) + .collect::>(); + let generators = self + .generators + .iter() + .map(|vec_g| vec_g.iter().map(|g| g.to_local()).collect::>()) + .collect::>(); + + Self::Local { + randomness_generator, + generators, + } + } + } + + impl FromLocal for $curve { + type Local = GroupProjective; + fn from_local(local: &Self::Local) -> Self { + $curve::from_public(*local) + } + } + + impl FromLocal for PedersenParameters<$curve> { + type Local = LocalPedersenParameters; + + fn from_local(local: &Self::Local) -> Self { + let randomness_generator = local + .randomness_generator + .iter() + .map(|x| $curve::from_local(x)) + .collect::>(); + let generators = local + .generators + .iter() + .map(|vec_g| { + vec_g + .iter() + .map(|g| $curve::from_local(g)) + .collect::>() + }) + .collect::>(); + + Self { + randomness_generator, + generators, + } + } + } + + impl FromLocal for $affine { + type Local = GroupAffine; + + fn from_local(local: &Self::Local) -> Self { + $affine::from_public(*local) + } + } + + impl ToLocal for $affine { + type Local = GroupAffine; + + fn to_local(&self) -> Self::Local { + self.val.unwrap_as_public() + } + } + + impl Reveal for ElGamalParameters<$curve> { + type Base = LocalElGamalParameters; + + fn reveal(self) -> Self::Base { + Self::Base { + generator: self.generator.to_local(), + } + } + + fn from_add_shared(_b: Self::Base) -> Self { + unimplemented!() + } + + fn from_public(b: Self::Base) -> Self { + Self { + generator: $affine::from_local(&b.generator), + } + } + } + + impl Reveal for ElGamalRandomness<$curve> { + type Base = LocalElGamalRandomness; + + fn reveal(self) -> Self::Base { + unimplemented!() + } + + fn from_add_shared(b: Self::Base) -> Self { + Self(<$curve as ProjectiveCurve>::ScalarField::from_add_shared( + b.0, + )) + } + + fn from_public(b: Self::Base) -> Self { + Self(<$curve as ProjectiveCurve>::ScalarField::from_public(b.0)) + } + } + }; +} + +impl_edwards_related!(AdditiveMpcEdwardsProjective, AdditiveMpcEdwardsAffine); +impl_edwards_related!(SpdzMpcEdwardsProjective, SpdzMpcEdwardsAffine); diff --git a/mpc-algebra/src/wire/field.rs b/mpc-algebra/src/wire/field.rs index 9d2c2750..765e6c60 100644 --- a/mpc-algebra/src/wire/field.rs +++ b/mpc-algebra/src/wire/field.rs @@ -25,7 +25,8 @@ use crate::boolean_field::{BooleanWire, MpcBooleanField}; // use crate::channel::MpcSerNet; use crate::share::field::FieldShare; use crate::{ - BeaverSource, BitAdd, BitDecomposition, BitwiseLessThan, LessThan, LogicalOperations, Reveal, + mpc_primitives, BeaverSource, BitAdd, BitDecomposition, BitwiseLessThan, LessThan, + LogicalOperations, Reveal, }; use crate::{EqualityZero, UniformBitRand}; use mpc_net::{MpcMultiNet as Net, MpcNet}; @@ -1054,6 +1055,20 @@ impl> SquareRootField for MpcF } } +impl, F2: PrimeField, S2: FieldShare> + mpc_primitives::ModulusConversion> for MpcField +{ + fn modulus_conversion(&mut self) -> MpcField { + match self { + MpcField::Public(x) => { + let bits = x.into_repr().to_bits_le(); + MpcField::Public(F2::from_repr(BigInteger::from_bits_le(&bits)).unwrap()) + } + MpcField::Shared(x) => MpcField::Shared(x.modulus_conversion()), + } + } +} + mod poly_impl { use crate::share::*; diff --git a/mpc-algebra/src/wire/group.rs b/mpc-algebra/src/wire/group.rs index fd6cceff..d0f90528 100644 --- a/mpc-algebra/src/wire/group.rs +++ b/mpc-algebra/src/wire/group.rs @@ -50,13 +50,22 @@ impl> Reveal for MpcGroup { result } - fn from_add_shared(_b: Self::Base) -> Self { - todo!() + #[inline] + fn from_add_shared(b: Self::Base) -> Self { + Self::Shared(S::from_add_shared(b)) } + #[inline] fn from_public(b: Self::Base) -> Self { Self::Public(b) } + + fn unwrap_as_public(self) -> Self::Base { + match self { + Self::Shared(s) => s.unwrap_as_public(), + Self::Public(s) => s, + } + } } impl> Mul> for MpcGroup { @@ -128,8 +137,8 @@ impl> CanonicalDeserializeWithFlags for MpcGroup> UniformRand for MpcGroup { - fn rand(_rng: &mut R) -> Self { - todo!() + fn rand(rng: &mut R) -> Self { + Self::Shared(::rand(rng)) } } @@ -155,7 +164,13 @@ impl> Neg for MpcGroup { type Output = Self; fn neg(self) -> Self::Output { - todo!() + match self { + MpcGroup::Public(x) => MpcGroup::Public(-x), + MpcGroup::Shared(mut x) => MpcGroup::Shared({ + x.neg(); + x + }), + } } } @@ -209,30 +224,51 @@ impl<'a, G: Group, S: GroupShare> Add<&'a MpcGroup> for MpcGroup } impl> SubAssign for MpcGroup { - fn sub_assign(&mut self, _rhs: Self) { - todo!() + fn sub_assign(&mut self, rhs: Self) { + self.sub_assign(&rhs); } } impl<'a, G: Group, S: GroupShare> SubAssign<&'a MpcGroup> for MpcGroup { - fn sub_assign(&mut self, _rhs: &'a MpcGroup) { - todo!() + fn sub_assign(&mut self, rhs: &'a MpcGroup) { + match self { + MpcGroup::Public(a) => match rhs { + MpcGroup::Public(b) => { + *a -= b; + } + MpcGroup::Shared(b) => { + let mut tmp = *b; + tmp.neg().shift(a); + *self = MpcGroup::Shared(tmp); + } + }, + MpcGroup::Shared(a) => match rhs { + MpcGroup::Public(b) => { + a.shift(&-*b); + } + MpcGroup::Shared(b) => { + a.sub(b); + } + }, + } } } impl> Sub for MpcGroup { type Output = Self; - fn sub(self, _rhs: Self) -> Self::Output { - todo!() + fn sub(mut self, rhs: Self) -> Self::Output { + self.sub_assign(&rhs); + self } } impl<'a, G: Group, S: GroupShare> Sub<&'a MpcGroup> for MpcGroup { type Output = Self; - fn sub(self, _rhs: &'a MpcGroup) -> Self::Output { - todo!() + fn sub(mut self, rhs: &'a MpcGroup) -> Self::Output { + self.sub_assign(rhs); + self } } diff --git a/src/circuits.rs b/src/circuits.rs index 7b6b991c..338c5b04 100644 --- a/src/circuits.rs +++ b/src/circuits.rs @@ -1,10 +1,13 @@ pub mod circuit; pub mod input_circuit; -pub mod pedersen; -pub use pedersen::*; pub mod werewolf; pub use werewolf::*; + +pub mod equality_zero; + +pub mod pedersen_revised; +pub use pedersen_revised::*; + pub mod bit_decomposition; pub mod enforce_smaller_or_eq_than; -pub mod equality_zero; pub mod smaller_than; diff --git a/src/circuits/circuit.rs b/src/circuits/circuit.rs index f1708431..d5ce7f0a 100644 --- a/src/circuits/circuit.rs +++ b/src/circuits/circuit.rs @@ -18,16 +18,8 @@ impl> MyCircuit { let a_com_circuit = PedersenComCircuit { param: Some(self.clone().mpc_input.common.unwrap().pedersen_param), input: self.clone().mpc_input.peculiar.unwrap().a.input, - input_bit: self.clone().mpc_input.peculiar.unwrap().a.input_bit.clone(), - open_bit: self - .clone() - .mpc_input - .peculiar - .unwrap() - .a - .randomness_bit - .clone(), - commit: Some(self.clone().mpc_input.peculiar.unwrap().a.commitment), + open: self.clone().mpc_input.peculiar.unwrap().a.randomness, + commit: self.clone().mpc_input.peculiar.unwrap().a.commitment, }; a_com_circuit.generate_constraints(cs.clone())?; @@ -35,16 +27,8 @@ impl> MyCircuit { let b_com_circuit = PedersenComCircuit { param: Some(self.clone().mpc_input.common.unwrap().pedersen_param), input: self.clone().mpc_input.peculiar.unwrap().b.input, - input_bit: self.clone().mpc_input.peculiar.unwrap().b.input_bit.clone(), - open_bit: self - .clone() - .mpc_input - .peculiar - .unwrap() - .b - .randomness_bit - .clone(), - commit: Some(self.clone().mpc_input.peculiar.unwrap().b.commitment), + open: self.clone().mpc_input.peculiar.unwrap().b.randomness, + commit: self.clone().mpc_input.peculiar.unwrap().b.commitment, }; b_com_circuit.generate_constraints(cs.clone())?; diff --git a/src/circuits/input_circuit.rs b/src/circuits/input_circuit.rs index 2db45f5b..a012c2ad 100644 --- a/src/circuits/input_circuit.rs +++ b/src/circuits/input_circuit.rs @@ -10,8 +10,7 @@ use super::{LocalOrMPC, PedersenComCircuit}; pub struct MySecretInputCircuit> { // private witness to the circuit x: Option, - input_bit: Option>, - open_bit: Option>, + open: Option, params: Option, // public instance to the circuit @@ -23,8 +22,7 @@ pub struct MySecretInputCircuit> { impl> MySecretInputCircuit { pub fn new( x: F, - input_bit: Vec, - open_bit: Vec, + open: F::PedersenRandomness, params: F::PedersenParam, h_x: F::PedersenCommitment, lower_bound: F, @@ -32,8 +30,7 @@ impl> MySecretInputCircuit { ) -> Self { Self { x: Some(x), - input_bit: Some(input_bit), - open_bit: Some(open_bit), + open: Some(open), params: Some(params), h_x: Some(h_x), lower_bound: Some(lower_bound), @@ -64,9 +61,8 @@ impl> MySecretInputCircuit { let x_com_circuit = PedersenComCircuit { param: self.params.clone(), input: self.x.unwrap(), - input_bit: self.input_bit.clone().unwrap(), - open_bit: self.open_bit.clone().unwrap(), - commit: self.h_x.clone(), + open: self.open.clone().unwrap(), + commit: self.h_x.clone().unwrap(), }; x_com_circuit.generate_constraints(cs.clone())?; @@ -109,12 +105,6 @@ mod tests { // generate the setup parameters let x = Fr::from(4); - let input_bit = x - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let lower_bound = Fr::from(3); let upper_bound = Fr::from(7); @@ -122,21 +112,13 @@ mod tests { // Pedersen commitment let params = PedersenComScheme::setup(&mut rng).unwrap(); let randomness = PedersenRandomness::rand(&mut rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let x_bytes = x.into_repr().to_bytes_le(); let h_x = PedersenComScheme::commit(¶ms, &x_bytes, &randomness).unwrap(); let circuit = MySecretInputCircuit { x: Some(x), - input_bit: Some(input_bit), - open_bit: Some(open_bit), + open: Some(randomness), h_x: Some(h_x), lower_bound: Some(lower_bound), upper_bound: Some(upper_bound), @@ -179,12 +161,6 @@ mod tests { // generate the setup parameters let x = Fr::from(4); - let input_bit = x - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let lower_bound = Fr::from(3); let upper_bound = Fr::from(7); @@ -192,21 +168,13 @@ mod tests { // Pedersen commitment let params = PedersenComScheme::setup(&mut rng).unwrap(); let randomness = PedersenRandomness::rand(&mut rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let x_bytes = x.into_repr().to_bytes_le(); let h_x = PedersenComScheme::commit(¶ms, &x_bytes, &randomness).unwrap(); let circuit = MySecretInputCircuit { x: Some(x), - input_bit: Some(input_bit), - open_bit: Some(open_bit), + open: Some(randomness), h_x: Some(h_x), lower_bound: Some(lower_bound), upper_bound: Some(upper_bound), diff --git a/src/circuits/pedersen_revised.rs b/src/circuits/pedersen_revised.rs new file mode 100644 index 00000000..299343f6 --- /dev/null +++ b/src/circuits/pedersen_revised.rs @@ -0,0 +1,393 @@ +use ark_bls12_377::Fr; +use ark_crypto_primitives::{ + commitment::{ + pedersen::{constraints::CommGadget, Commitment, Randomness}, + CommitmentGadget, + }, + crh::pedersen, + CommitmentScheme, +}; +use ark_ec::ProjectiveCurve; +use ark_ed_on_bls12_377::constraints::EdwardsVar; +use ark_ff::bytes::ToBytes; +use ark_ff::BigInteger; +use ark_ff::PrimeField; +use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget}; + +use mpc_algebra::Reveal; + +use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; +use ark_std::{fmt::Debug, hash::Hash}; + +use mpc_algebra::{malicious_majority as mm, pedersen::Input, FieldShare, MpcEqGadget}; + +use mpc_algebra::{ + commitment::constraints::CommitmentGadget as MpcCommitmentGadget, + // commitment::pedersen::local_pedersen::Commitment, + commitment::pedersen::{ + CommGadget as MpcCommGadget, Commitment as MpcCommitment, Randomness as MpcRandomness, + }, + CommitmentScheme as MpcCommitmentScheme, +}; +use mpc_trait::MpcWire; + +pub trait LocalOrMPC { + type JubJub: ProjectiveCurve; + type PedersenComScheme: MpcCommitmentScheme< + Output = Self::PedersenCommitment, + Parameters = Self::PedersenParam, + Input = Self::PedersenInput, + Randomness = Self::PedersenRandomness, + >; + type PedersenCommitment: ToBytes + Clone + Default + Eq + Hash + Debug; + type PedersenParam: Clone; + type PedersenInput; + type PedersenRandomness: Clone + PartialEq + Debug + Eq + Default; + + type PedersenComSchemeVar: MpcCommitmentGadget< + Self::PedersenComScheme, + ConstraintF, + OutputVar = Self::PedersenCommitmentVar, + ParametersVar = Self::PedersenParamVar, + InputVar = Self::PedersenInputVar, + RandomnessVar = Self::PedersenRandomnessVar, + >; + type PedersenParamVar: AllocVar + Clone; + type PedersenRandomnessVar: AllocVar<::Randomness, ConstraintF> + + Clone; + type PedersenInputVar: AllocVar<::Input, ConstraintF> + + Clone; + type PedersenCommitmentVar: AllocVar< + ::Output, + ConstraintF, + >; + + fn convert_input(&self) -> Self::PedersenInput; + + fn enforce_equal_output( + a: &Self::PedersenCommitmentVar, + b: &Self::PedersenCommitmentVar, + ) -> Result<(), SynthesisError>; +} + +impl LocalOrMPC for Fr { + type JubJub = ark_ed_on_bls12_377::EdwardsProjective; + + type PedersenComScheme = Commitment; + type PedersenCommitment = ::Output; + type PedersenParam = ::Parameters; + type PedersenInput = ::Input; + type PedersenRandomness = Randomness; + + type PedersenComSchemeVar = CommGadget; + type PedersenParamVar = >::ParametersVar; + type PedersenRandomnessVar = >::RandomnessVar; + type PedersenInputVar = + >::InputVar; + type PedersenCommitmentVar = EdwardsVar; + + fn convert_input(&self) -> Self::PedersenInput { + self.into_repr().to_bytes_le() + } + + fn enforce_equal_output( + a: &Self::PedersenCommitmentVar, + b: &Self::PedersenCommitmentVar, + ) -> Result<(), SynthesisError> { + a.enforce_equal(b) + } +} + +// impl LocalOrMPC> for hbc::MpcField { +// type JubJub = mpc_algebra::edwards2::AdditiveMpcEdwardsProjective; + +// type PedersenComScheme = MpcCommitment; +// type PedersenCommitment = ::Output; +// type PedersenParam = ::Parameters; +// type PedersenInput = Input; +// type PedersenRandomness = MpcRandomness; + +// type PedersenComSchemeVar = +// MpcCommGadget; +// type PedersenParamVar = , +// >>::ParametersVar; +// type PedersenInputVar = , +// >>::InputVar; +// type PedersenRandomnessVar = , +// >>::RandomnessVar; +// type PedersenCommitmentVar = mpc_algebra::AdditiveMpcEdwardsVar; + +// fn convert_input(&self) -> Self::PedersenInput { +// if self.is_shared() { +// Self::PedersenInput::new( +// ::ScalarField::from_add_shared( +// ark_ed_on_bls12_377::Fr::from_le_bytes_mod_order( +// &self.unwrap_as_public().into_repr().to_bytes_le(), +// ), +// ), +// ) +// } else { +// Self::PedersenInput::new(::ScalarField::from_public( +// ark_ed_on_bls12_377::Fr::from_le_bytes_mod_order( +// &self.unwrap_as_public().into_repr().to_bytes_le(), +// ), +// )) +// } +// } + +// fn enforce_equal_output( +// a: &Self::PedersenCommitmentVar, +// b: &Self::PedersenCommitmentVar, +// ) -> Result<(), SynthesisError> { +// a.enforce_equal(b) +// } +// } + +impl LocalOrMPC> for mm::MpcField { + type JubJub = mpc_algebra::edwards2::SpdzMpcEdwardsProjective; + + type PedersenComScheme = MpcCommitment; + type PedersenCommitment = ::Output; + type PedersenParam = ::Parameters; + type PedersenInput = Input; + type PedersenRandomness = MpcRandomness; + + type PedersenComSchemeVar = MpcCommGadget; + type PedersenParamVar = , + >>::ParametersVar; + type PedersenInputVar = , + >>::InputVar; + type PedersenRandomnessVar = , + >>::RandomnessVar; + type PedersenCommitmentVar = mpc_algebra::SpdzMpcEdwardsVar; + + fn convert_input(&self) -> Self::PedersenInput { + if self.is_shared() { + Self::PedersenInput::new( + ::ScalarField::from_add_shared( + ark_ed_on_bls12_377::Fr::from_le_bytes_mod_order( + &self.unwrap_as_public().into_repr().to_bytes_le(), + ), + ), + ) + } else { + Self::PedersenInput::new(::ScalarField::from_public( + ark_ed_on_bls12_377::Fr::from_le_bytes_mod_order( + &self.unwrap_as_public().into_repr().to_bytes_le(), + ), + )) + } + } + + fn enforce_equal_output( + a: &Self::PedersenCommitmentVar, + b: &Self::PedersenCommitmentVar, + ) -> Result<(), SynthesisError> { + a.enforce_equal(b) + } +} + +pub const PERDERSON_WINDOW_SIZE: usize = 256; +pub const PERDERSON_WINDOW_NUM: usize = 1; + +#[derive(Clone)] +pub struct Window; +impl pedersen::Window for Window { + const WINDOW_SIZE: usize = PERDERSON_WINDOW_SIZE; + const NUM_WINDOWS: usize = PERDERSON_WINDOW_NUM; +} +impl mpc_algebra::crh::pedersen::Window for Window { + const WINDOW_SIZE: usize = PERDERSON_WINDOW_SIZE; + const NUM_WINDOWS: usize = PERDERSON_WINDOW_NUM; +} + +#[derive(Clone)] +pub struct PedersenComCircuit> { + pub param: Option, + pub input: F, + pub open: F::PedersenRandomness, + pub commit: F::PedersenCommitment, +} + +impl> ConstraintSynthesizer for PedersenComCircuit { + fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { + #[cfg(debug_assertions)] + println!("is setup mode?: {}", cs.is_in_setup_mode()); + let _cs_no = cs.num_constraints(); + + // step 1. Allocate Parameters for perdersen commitment + let param_var = + F::PedersenParamVar::new_input(ark_relations::ns!(cs, "gadget_parameters"), || { + self.param.ok_or(SynthesisError::AssignmentMissing) + })?; + let _cs_no = cs.num_constraints() - _cs_no; + #[cfg(debug_assertions)] + println!("cs for parameters: {}", _cs_no); + let _cs_no = cs.num_constraints(); + + // step 2. Allocate inputs + // let mut input_var = vec![]; + // let input_bytes = self.input.into_repr().to_bytes_le(); + // let input_bytes = self.input.to_bytes(); + // assert_eq!(input_bytes.len() % 8, 0); + + // for input_byte in input_bytes.iter() { + // input_var.push(F::UInt8::new_witness(cs.clone(), || Ok(input_byte)).unwrap()); + // } + + let input_var = + F::PedersenInputVar::new_witness(ark_relations::ns!(cs, "gadget_input"), || { + Ok(self.input.convert_input()) + }) + .unwrap(); + + let _cs_no = cs.num_constraints() - _cs_no; + #[cfg(debug_assertions)] + println!("cs for account: {}", _cs_no); + let _cs_no = cs.num_constraints(); + + // step 3. Allocate the opening + let open_var = F::PedersenRandomnessVar::new_witness( + ark_relations::ns!(cs, "gadget_randomness"), + || Ok(&self.open), + ) + .unwrap(); + + let _cs_no = cs.num_constraints() - _cs_no; + #[cfg(debug_assertions)] + println!("cs for opening: {}", _cs_no); + let _cs_no = cs.num_constraints(); + + // step 4. Allocate the output + let result_var = + F::PedersenComSchemeVar::commit(¶m_var, &input_var, &open_var).unwrap(); + + let _cs_no = cs.num_constraints() - _cs_no; + #[cfg(debug_assertions)] + println!("cs for commitment: {}", _cs_no); + let _cs_no = cs.num_constraints(); + + // circuit to compare the commited value with supplied value + let commitment_var2 = F::PedersenCommitmentVar::new_input( + ark_relations::ns!(cs, "gadget_commitment"), + || Ok(self.commit), + ) + .unwrap(); + F::enforce_equal_output(&result_var, &commitment_var2).unwrap(); + + let _cs_no = cs.num_constraints() - _cs_no; + #[cfg(debug_assertions)] + println!("cs for comparison: {}", _cs_no); + + #[cfg(debug_assertions)] + println!("total cs for Commitment: {}", cs.num_constraints()); + Ok(()) + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; + +// use ark_ff::BigInteger; +// use ark_std::{test_rng, UniformRand}; + +// type MFr = mpc_algebra::MpcField>; + +// #[test] +// fn additivity_test_local() { +// let rng = &mut test_rng(); + +// let a = Fr::from(3); + +// let params = >::PedersenComScheme::setup(rng).unwrap(); + +// let randomness_a = >::PedersenRandomness::rand(rng); + +// let a_bytes = a.into_repr().to_bytes_le(); + +// let h_a = +// >::PedersenComScheme::commit(¶ms, &a_bytes, &randomness_a) +// .unwrap(); + +// let b = Fr::from(4); + +// let randomness_b = >::PedersenRandomness::rand(rng); + +// let b_bytes = b.into_repr().to_bytes_le(); + +// let h_b = +// >::PedersenComScheme::commit(¶ms, &b_bytes, &randomness_b) +// .unwrap(); + +// // Note: Do not exceed the modulus. break additivity +// let sum = a + b; + +// let randomness = Randomness(randomness_a.0 + randomness_b.0); + +// let bytes = sum.into_repr().to_bytes_le(); + +// let h_sum = +// >::PedersenComScheme::commit(¶ms, &bytes, &randomness) +// .unwrap(); + +// assert_eq!(h_a + h_b, h_sum) +// } + +// #[test] +// fn additivity_test_mpc() { +// let rng = &mut test_rng(); + +// let a = MFr::Public(Fr::from(3)); + +// let params = >::PedersenComScheme::setup(rng).unwrap(); + +// let randomness_a = >::PedersenRandomness::rand(rng); + +// let a_bytes = a.into_repr().to_bytes_le(); + +// let h_a = +// >::PedersenComScheme::commit(¶ms, &a_bytes, &randomness_a) +// .unwrap(); + +// let b = MFr::Public(Fr::from(4)); + +// let randomness_b = >::PedersenRandomness::rand(rng); + +// let b_bytes = b.into_repr().to_bytes_le(); + +// let h_b = +// >::PedersenComScheme::commit(¶ms, &b_bytes, &randomness_b) +// .unwrap(); + +// let sum = a + b; + +// let randomness = Randomness(randomness_a.0 + randomness_b.0); + +// let bytes = sum.into_repr().to_bytes_le(); + +// let h_sum = +// >::PedersenComScheme::commit(¶ms, &bytes, &randomness) +// .unwrap(); + +// assert_eq!(h_a + h_b, h_sum) +// } +// } diff --git a/src/circuits/werewolf.rs b/src/circuits/werewolf.rs index dcc53861..bbee0830 100644 --- a/src/circuits/werewolf.rs +++ b/src/circuits/werewolf.rs @@ -4,22 +4,27 @@ use ark_crypto_primitives::encryption::*; use ark_ec::AffineCurve; use ark_ec::ProjectiveCurve; use ark_ed_on_bls12_377::constraints::EdwardsVar; -use ark_ff::{Field, PrimeField}; +use ark_ff::PrimeField; use ark_r1cs_std::alloc::AllocVar; -use ark_r1cs_std::boolean::{AllocatedBool, Boolean}; +use ark_r1cs_std::boolean::Boolean; use ark_r1cs_std::eq::EqGadget; use ark_r1cs_std::fields::fp::FpVar; use ark_r1cs_std::fields::FieldVar; use ark_r1cs_std::groups::CurveVar; -use ark_relations::lc; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError, Variable}; +use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use ark_std::One; use ark_std::Zero; -use mpc_algebra::Reveal; +use mpc_algebra::groups::MpcCurveVar; +use mpc_algebra::{MpcBoolean, MpcEqGadget, Reveal}; use mpc_algebra::honest_but_curious as hbc; use mpc_algebra::malicious_majority as mm; +use mpc_algebra::encryption::constraints::AsymmetricEncryptionGadget; +use mpc_algebra::encryption::elgamal::{ + constraints::ElGamalEncGadget as MpcElGamalEncGadget, elgamal::ElGamal as MpcElGamal, +}; + use super::{LocalOrMPC, PedersenComCircuit}; use crate::input::{WerewolfKeyInput, WerewolfMpcInput}; @@ -38,9 +43,8 @@ impl> KeyPublicizeCircuit { let pub_key_or_dummy_x_com_circuit = PedersenComCircuit { param: Some(pedersen_param.clone()), input: pub_key_or_dummy_x.input, - input_bit: pub_key_or_dummy_x.input_bit.clone(), - open_bit: pub_key_or_dummy_x.randomness_bit.clone(), - commit: Some(pub_key_or_dummy_x.commitment.clone()), + open: pub_key_or_dummy_x.randomness.clone(), + commit: pub_key_or_dummy_x.commitment.clone(), }; pub_key_or_dummy_x_com_circuit.generate_constraints(cs.clone())?; @@ -52,9 +56,8 @@ impl> KeyPublicizeCircuit { let pub_key_or_dummy_y_com_circuit = PedersenComCircuit { param: Some(pedersen_param.clone()), input: pub_key_or_dummy_y.input, - input_bit: pub_key_or_dummy_y.input_bit.clone(), - open_bit: pub_key_or_dummy_y.randomness_bit.clone(), - commit: Some(pub_key_or_dummy_y.commitment.clone()), + open: pub_key_or_dummy_y.randomness.clone(), + commit: pub_key_or_dummy_y.commitment.clone(), }; pub_key_or_dummy_y_com_circuit.generate_constraints(cs.clone())?; @@ -139,9 +142,8 @@ impl + ElGamalLocalOrMPC> DivinationCircuit let is_werewolf_com_circuit = PedersenComCircuit { param: Some(pedersen_param.clone()), input: is_werewolf.input, - input_bit: is_werewolf.input_bit.clone(), - open_bit: is_werewolf.randomness_bit.clone(), - commit: Some(is_werewolf.commitment.clone()), + open: is_werewolf.randomness.clone(), + commit: is_werewolf.commitment.clone(), }; is_werewolf_com_circuit.generate_constraints(cs.clone())?; @@ -153,9 +155,8 @@ impl + ElGamalLocalOrMPC> DivinationCircuit let is_target_com_circuit = PedersenComCircuit { param: Some(pedersen_param.clone()), input: is_target.input, - input_bit: is_target.input_bit.clone(), - open_bit: is_target.randomness_bit.clone(), - commit: Some(is_target.commitment.clone()), + open: is_target.randomness.clone(), + commit: is_target.commitment.clone(), }; is_target_com_circuit.generate_constraints(cs.clone())?; @@ -175,52 +176,14 @@ impl ConstraintSynthesizer for DivinationCircuit { .is_werewolf .clone() .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(b.input))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; - - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) + .map(|b| Boolean::new_witness(cs.clone(), || Ok(b.input.is_one()))) .collect::, _>>()?; let is_target_bit = peculiar_input .is_target .clone() .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(b.input))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; - - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) + .map(|b| Boolean::new_witness(cs.clone(), || Ok(b.input.is_one()))) .collect::, _>>()?; let is_wt = is_werewolf_bit @@ -252,27 +215,9 @@ impl ConstraintSynthesizer for DivinationCircuit { let randomness_bits_var = peculiar_input .randomness_bit + .clone() .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(*b))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; - - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) + .map(|b| Boolean::new_witness(cs.clone(), || Ok(b.is_one()))) .collect::, _>>()?; // allocate public key @@ -287,11 +232,12 @@ impl ConstraintSynthesizer for DivinationCircuit { let randomness = randomness_bits_var; // compute s = randomness*pk - let s = pub_key_var.pk().clone().scalar_mul_le(randomness.iter())?; + let s = Fr::get_public_key(&pub_key_var) + .clone() + .scalar_mul_le(randomness.iter())?; // compute c1 = randomness*generator - let c1 = param_var - .generator() + let c1 = Fr::get_generator(¶m_var) .clone() .scalar_mul_le(randomness.iter())?; @@ -339,196 +285,145 @@ impl ConstraintSynthesizer for DivinationCircuit { } } -// Constraint Implementation for Local Field -impl ConstraintSynthesizer> for DivinationCircuit> { - fn generate_constraints( - self, - cs: ConstraintSystemRef>, - ) -> Result<(), SynthesisError> { - let common_input = self.clone().mpc_input.common.unwrap(); - let peculiar_input = self.clone().mpc_input.peculiar.unwrap(); - - let is_werewolf_bit = peculiar_input - .is_werewolf - .clone() - .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(b.input))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; - - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) - .collect::, _>>()?; - - let is_target_bit = peculiar_input - .is_target - .clone() - .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(b.input))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; - - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) - .collect::, _>>()?; - - let is_wt = is_werewolf_bit - .iter() - .zip(is_target_bit.iter()) - .map(|(x, y)| x.and(y)) - .collect::, _>>()?; - - let is_target_werewolf_bit = Boolean::kary_or(is_wt.as_slice())?; - - let one_point = - as ElGamalLocalOrMPC>>::EdwardsVar::new_witness( - ark_relations::ns!(cs, "gadget_randomness"), - || { - Ok( as ElGamalLocalOrMPC< - hbc::MpcField, - >>::ElGamalPlaintext::prime_subgroup_generator( - )) - }, - )?; - - let zero_point = - as ElGamalLocalOrMPC>>::EdwardsVar::new_witness( - ark_relations::ns!(cs, "gadget_randomness"), - || { - Ok( as ElGamalLocalOrMPC< - hbc::MpcField, - >>::ElGamalPlaintext::default()) - }, - )?; - - let is_target_werewolf = is_target_werewolf_bit.select(&one_point, &zero_point)?; - - // elgamal encryption - - let param_var = as ElGamalLocalOrMPC>>::ElGamalParamVar::new_input( - ark_relations::ns!(cs, "gadget_parameters"), - || Ok(common_input.elgamal_param.clone()), - )?; - - let randomness_bits_var = peculiar_input - .randomness_bit - .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(*b))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; - - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) - .collect::, _>>()?; - - // allocate public key - let pub_key_var = as ElGamalLocalOrMPC>>::ElGamalPublicKeyVar::new_input( - ark_relations::ns!(cs, "gadget_public_key"), - || Ok(common_input.pub_key), - )?; - - // allocate the output - let enc_result_var = { - // flatten randomness to little-endian bit vector - let randomness = randomness_bits_var; - - // compute s = randomness*pk - let s = pub_key_var.pk().clone().scalar_mul_le(randomness.iter())?; - - // compute c1 = randomness*generator - let c1 = param_var - .generator() - .clone() - .scalar_mul_le(randomness.iter())?; - - // compute c2 = m + s - let c2 = is_target_werewolf.clone() + s; - - as ElGamalLocalOrMPC>>::ElGamalCiphertextVar::new( - c1, c2, - ) - }; - - // compare - let enc_result_var2 = as ElGamalLocalOrMPC>>::ElGamalCiphertextVar::new_input( - ark_relations::ns!(cs, "gadget_commitment"), - || { - let is_werewolf: hbc::MpcField = peculiar_input - .is_werewolf - .iter() - .zip(peculiar_input.is_target.iter()) - .map(|(x, y)| x.input * y.input) - .sum(); - - let message = match is_werewolf.clone().reveal().is_one() { - true => { - as ElGamalLocalOrMPC>>::ElGamalPlaintext::prime_subgroup_generator( - ) - } - false => as ElGamalLocalOrMPC>>::ElGamalPlaintext::default(), - }; - let enc_result = as ElGamalLocalOrMPC>>::ElGamalScheme::encrypt( - &common_input.elgamal_param, - &common_input.pub_key, - &message, - &peculiar_input.randomness, - ) - .unwrap(); - Ok(enc_result) - }, - )?; - - enc_result_var.enforce_equal(&enc_result_var2)?; - - // self.verify_commitments(cs.clone())?; - - println!("total number of constraints: {}", cs.num_constraints()); - - Ok(()) - } -} +// Constraint Implementation for hbc MpcField +// impl ConstraintSynthesizer> for DivinationCircuit> { +// fn generate_constraints( +// self, +// cs: ConstraintSystemRef>, +// ) -> Result<(), SynthesisError> { +// let common_input = self.clone().mpc_input.common.unwrap(); +// let peculiar_input = self.clone().mpc_input.peculiar.unwrap(); + +// let is_werewolf_bit = MpcBoolean::new_witness_vec( +// cs.clone(), +// &peculiar_input +// .is_werewolf +// .clone() +// .iter() +// .map(|b| b.input) +// .collect::>(), +// )?; + +// let is_target_bit = MpcBoolean::new_witness_vec( +// cs.clone(), +// &peculiar_input +// .is_target +// .clone() +// .iter() +// .map(|b| b.input) +// .collect::>(), +// )?; + +// let is_wt = is_werewolf_bit +// .iter() +// .zip(is_target_bit.iter()) +// .map(|(x, y)| x.and(y)) +// .collect::, _>>()?; + +// let is_target_werewolf_bit = MpcBoolean::kary_or(is_wt.as_slice())?; + +// let one_point = +// as ElGamalLocalOrMPC>>::EdwardsVar::new_witness( +// ark_relations::ns!(cs, "gadget_randomness"), +// || { +// Ok( as ElGamalLocalOrMPC< +// hbc::MpcField, +// >>::ElGamalPlaintext::prime_subgroup_generator( +// )) +// }, +// )?; + +// let zero_point = +// as ElGamalLocalOrMPC>>::EdwardsVar::new_witness( +// ark_relations::ns!(cs, "gadget_randomness"), +// || { +// Ok( as ElGamalLocalOrMPC< +// hbc::MpcField, +// >>::ElGamalPlaintext::default()) +// }, +// )?; + +// let is_target_werewolf = +// hbc::MpcField::::select(&is_target_werewolf_bit, &one_point, &zero_point)?; + +// // elgamal encryption + +// let param_var = +// as ElGamalLocalOrMPC>>::ElGamalParamVar::new_input( +// ark_relations::ns!(cs, "gadget_parameters"), +// || Ok(common_input.elgamal_param.clone()), +// )?; + +// let randomness_bits_var = +// MpcBoolean::new_witness_vec(cs.clone(), &peculiar_input.randomness_bit)?; + +// // allocate public key +// let pub_key_var = as ElGamalLocalOrMPC>>::ElGamalPublicKeyVar::new_input( +// ark_relations::ns!(cs, "gadget_public_key"), +// || Ok(common_input.pub_key), +// )?; + +// // allocate the output +// let enc_result_var = { +// // flatten randomness to little-endian bit vector +// let randomness = randomness_bits_var; + +// // compute s = randomness*pk +// let s = hbc::MpcField::::get_public_key(&pub_key_var) +// .clone() +// .scalar_mul_le(randomness.iter())?; + +// // compute c1 = randomness*generator +// let c1 = hbc::MpcField::::get_generator(¶m_var) +// .clone() +// .scalar_mul_le(randomness.iter())?; + +// // compute c2 = m + s +// let c2 = is_target_werewolf.clone() + s; + +// as ElGamalLocalOrMPC>>::ElGamalCiphertextVar::new( +// c1, c2, +// ) +// }; + +// // compare +// let enc_result_var2 = as ElGamalLocalOrMPC>>::ElGamalCiphertextVar::new_input( +// ark_relations::ns!(cs, "gadget_commitment"), +// || { +// let is_werewolf: hbc::MpcField = peculiar_input +// .is_werewolf +// .iter() +// .zip(peculiar_input.is_target.iter()) +// .map(|(x, y)| x.input * y.input) +// .sum(); + +// let message = match is_werewolf.clone().reveal().is_one() { +// true => { +// as ElGamalLocalOrMPC>>::ElGamalPlaintext::prime_subgroup_generator( +// ) +// } +// false => as ElGamalLocalOrMPC>>::ElGamalPlaintext::default(), +// }; +// let enc_result = as ElGamalLocalOrMPC>>::ElGamalScheme::encrypt( +// &common_input.elgamal_param, +// &common_input.pub_key, +// &message, +// &peculiar_input.randomness, +// ) +// .unwrap(); +// Ok(enc_result) +// }, +// )?; + +// enc_result_var.enforce_equal(&enc_result_var2)?; + +// // self.verify_commitments(cs.clone())?; + +// println!("total number of constraints: {}", cs.num_constraints()); + +// Ok(()) +// } +// } impl ConstraintSynthesizer> for DivinationCircuit> { fn generate_constraints( @@ -538,57 +433,25 @@ impl ConstraintSynthesizer> for DivinationCircuit, _>>()?; - - let is_target_bit = peculiar_input - .is_target - .clone() - .iter() - .map(|b| { - let alloc_bool = { - let variable = cs.new_witness_variable(|| Ok(b.input))?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - - cs.enforce_constraint( - lc!() + Variable::One - variable, - lc!() + variable, - lc!(), - )?; + let is_werewolf_bit = MpcBoolean::new_witness_vec( + cs.clone(), + &peculiar_input + .is_werewolf + .clone() + .iter() + .map(|b| b.input) + .collect::>(), + )?; - AllocatedBool { - variable, - cs: cs.clone(), - } - }; - Ok(Boolean::Is(alloc_bool)) - }) - .collect::, _>>()?; + let is_target_bit = MpcBoolean::new_witness_vec( + cs.clone(), + &peculiar_input + .is_target + .clone() + .iter() + .map(|b| b.input) + .collect::>(), + )?; let is_wt = is_werewolf_bit .iter() @@ -596,7 +459,7 @@ impl ConstraintSynthesizer> for DivinationCircuit, _>>()?; - let is_target_werewolf_bit = Boolean::kary_or(is_wt.as_slice())?; + let is_target_werewolf_bit = MpcBoolean::kary_or(is_wt.as_slice())?; let one_point = as ElGamalLocalOrMPC>>::EdwardsVar::new_witness( @@ -619,7 +482,8 @@ impl ConstraintSynthesizer> for DivinationCircuit::select(&is_target_werewolf_bit, &one_point, &zero_point)?; // elgamal encryption @@ -629,30 +493,8 @@ impl ConstraintSynthesizer> for DivinationCircuit, _>>()?; + let randomness_bits_var = + MpcBoolean::new_witness_vec(cs.clone(), &peculiar_input.randomness_bit)?; // allocate public key let pub_key_var = as ElGamalLocalOrMPC>>::ElGamalPublicKeyVar::new_input( @@ -666,11 +508,12 @@ impl ConstraintSynthesizer> for DivinationCircuit::get_public_key(&pub_key_var) + .clone() + .scalar_mul_le(randomness.iter())?; // compute c1 = randomness*generator - let c1 = param_var - .generator() + let c1 = mm::MpcField::::get_generator(¶m_var) .clone() .scalar_mul_le(randomness.iter())?; @@ -739,7 +582,8 @@ pub trait ElGamalLocalOrMPC { type ElGamalPlaintext: Clone; type ElGamalCiphertext: Clone; - type EdwardsVar: CurveVar; + type BooleanVar; + type EdwardsVar: AllocVar + Clone; type ElGamalGadget: AsymmetricEncryptionGadget< Self::ElGamalScheme, @@ -749,15 +593,26 @@ pub trait ElGamalLocalOrMPC { PublicKeyVar = Self::ElGamalPublicKeyVar, RandomnessVar = Self::ElGamalRandomnessVar, >; - type ElGamalParamVar: AllocVar - + Clone - + GetElGamalParam; - type ElGamalPublicKeyVar: AllocVar - + Clone - + GetPubKey; + type ElGamalParamVar: AllocVar + Clone; + type ElGamalPublicKeyVar: AllocVar + Clone; type ElGamalRandomnessVar: AllocVar + Clone; type ElGamalPlaintextVar: AllocVar + Clone; type ElGamalCiphertextVar: AllocVar + Clone; + + fn get_generator(a: &Self::ElGamalParamVar) -> Self::EdwardsVar; + + fn get_public_key(a: &Self::ElGamalPublicKeyVar) -> Self::EdwardsVar; + + fn select( + boolean: &Self::BooleanVar, + frist: &Self::EdwardsVar, + second: &Self::EdwardsVar, + ) -> Result; + + fn enforce_equal_output( + a: &Self::ElGamalCiphertextVar, + b: &Self::ElGamalCiphertextVar, + ) -> Result<(), SynthesisError>; } impl ElGamalLocalOrMPC for Fr { @@ -772,6 +627,7 @@ impl ElGamalLocalOrMPC for Fr { type ElGamalCiphertext = ::Ciphertext; type EdwardsVar = EdwardsVar; + type BooleanVar = Boolean; type ElGamalGadget = ElGamalEncGadget; type ElGamalParamVar = for Fr { Self::ElGamalScheme, ark_bls12_377::Fr, >>::OutputVar; -} -impl ElGamalLocalOrMPC> for hbc::MpcField { - type JubJub = hbc::MpcEdwardsProjective; + fn get_generator(a: &Self::ElGamalParamVar) -> Self::EdwardsVar { + a.generator.clone() + } - type ElGamalScheme = ElGamal; - type ElGamalParam = ::Parameters; - type ElGamalPubKey = ::PublicKey; - type ElGamalSecretKey = ::SecretKey; - type ElGamalRandomness = ::Randomness; - type ElGamalPlaintext = ::Plaintext; - type ElGamalCiphertext = ::Ciphertext; + fn get_public_key(a: &Self::ElGamalPublicKeyVar) -> Self::EdwardsVar { + a.pk.clone() + } - type EdwardsVar = hbc::MpcEdwardsVar; + fn select( + boolean: &Self::BooleanVar, + frist: &Self::EdwardsVar, + second: &Self::EdwardsVar, + ) -> Result { + boolean.select(frist, second) + } - type ElGamalGadget = ElGamalEncGadget; - type ElGamalParamVar = , - >>::ParametersVar; - type ElGamalRandomnessVar = , - >>::RandomnessVar; - type ElGamalPublicKeyVar = , - >>::PublicKeyVar; - type ElGamalPlaintextVar = , - >>::PlaintextVar; - type ElGamalCiphertextVar = , - >>::OutputVar; + fn enforce_equal_output( + a: &Self::ElGamalCiphertextVar, + b: &Self::ElGamalCiphertextVar, + ) -> Result<(), SynthesisError> { + a.enforce_equal(b) + } } +// impl ElGamalLocalOrMPC> for hbc::MpcField { +// type JubJub = hbc::MpcEdwardsProjective; + +// type ElGamalScheme = MpcElGamal; +// type ElGamalParam = ::Parameters; +// type ElGamalPubKey = ::PublicKey; +// type ElGamalSecretKey = ::SecretKey; +// type ElGamalRandomness = ::Randomness; +// type ElGamalPlaintext = ::Plaintext; +// type ElGamalCiphertext = ::Ciphertext; + +// type EdwardsVar = hbc::MpcEdwardsVar; +// type BooleanVar = MpcBoolean>; + +// type ElGamalGadget = MpcElGamalEncGadget; +// type ElGamalParamVar = , +// >>::ParametersVar; +// type ElGamalRandomnessVar = , +// >>::RandomnessVar; +// type ElGamalPublicKeyVar = , +// >>::PublicKeyVar; +// type ElGamalPlaintextVar = , +// >>::PlaintextVar; +// type ElGamalCiphertextVar = , +// >>::OutputVar; + +// fn get_generator(a: &Self::ElGamalParamVar) -> Self::EdwardsVar { +// a.generator.clone() +// } + +// fn get_public_key(a: &Self::ElGamalPublicKeyVar) -> Self::EdwardsVar { +// a.pk.clone() +// } + +// fn select( +// boolean: &Self::BooleanVar, +// frist: &Self::EdwardsVar, +// second: &Self::EdwardsVar, +// ) -> Result { +// boolean.select(frist, second) +// } + +// fn enforce_equal_output( +// a: &Self::ElGamalCiphertextVar, +// b: &Self::ElGamalCiphertextVar, +// ) -> Result<(), SynthesisError> { +// a.enforce_equal(b) +// } +// } + impl ElGamalLocalOrMPC> for mm::MpcField { type JubJub = mm::MpcEdwardsProjective; - type ElGamalScheme = ElGamal; + type ElGamalScheme = MpcElGamal; type ElGamalParam = ::Parameters; type ElGamalPubKey = ::PublicKey; type ElGamalSecretKey = ::SecretKey; @@ -844,8 +747,9 @@ impl ElGamalLocalOrMPC> for mm::MpcField { type ElGamalCiphertext = ::Ciphertext; type EdwardsVar = mm::MpcEdwardsVar; + type BooleanVar = MpcBoolean>; - type ElGamalGadget = ElGamalEncGadget; + type ElGamalGadget = MpcElGamalEncGadget; type ElGamalParamVar = , @@ -866,76 +770,27 @@ impl ElGamalLocalOrMPC> for mm::MpcField { Self::ElGamalScheme, mm::MpcField, >>::OutputVar; -} -pub trait GetPubKey, ConstraintF: Field> { - fn pk(&self) -> GG; -} - -impl GetPubKey<>::JubJub, EdwardsVar, Fr> - for >::ElGamalPublicKeyVar -{ - fn pk(&self) -> EdwardsVar { - self.pk.clone() + fn get_generator(a: &Self::ElGamalParamVar) -> Self::EdwardsVar { + a.generator.clone() } -} -impl - GetPubKey< - as LocalOrMPC>>::JubJub, - hbc::MpcEdwardsVar, - hbc::MpcField, - > for as ElGamalLocalOrMPC>>::ElGamalPublicKeyVar -{ - fn pk(&self) -> hbc::MpcEdwardsVar { - self.pk.clone() + fn get_public_key(a: &Self::ElGamalPublicKeyVar) -> Self::EdwardsVar { + a.pk.clone() } -} -impl - GetPubKey< - as LocalOrMPC>>::JubJub, - mm::MpcEdwardsVar, - mm::MpcField, - > for as ElGamalLocalOrMPC>>::ElGamalPublicKeyVar -{ - fn pk(&self) -> mm::MpcEdwardsVar { - self.pk.clone() + fn select( + boolean: &Self::BooleanVar, + frist: &Self::EdwardsVar, + second: &Self::EdwardsVar, + ) -> Result { + boolean.select(frist, second) } -} - -pub trait GetElGamalParam, ConstraintF: Field> { - fn generator(&self) -> GG; -} -impl GetElGamalParam<>::JubJub, EdwardsVar, Fr> - for >::ElGamalParamVar -{ - fn generator(&self) -> EdwardsVar { - self.generator.clone() - } -} - -impl - GetElGamalParam< - as LocalOrMPC>>::JubJub, - hbc::MpcEdwardsVar, - hbc::MpcField, - > for as ElGamalLocalOrMPC>>::ElGamalParamVar -{ - fn generator(&self) -> hbc::MpcEdwardsVar { - self.generator.clone() - } -} - -impl - GetElGamalParam< - as LocalOrMPC>>::JubJub, - mm::MpcEdwardsVar, - mm::MpcField, - > for as ElGamalLocalOrMPC>>::ElGamalParamVar -{ - fn generator(&self) -> mm::MpcEdwardsVar { - self.generator.clone() + fn enforce_equal_output( + a: &Self::ElGamalCiphertextVar, + b: &Self::ElGamalCiphertextVar, + ) -> Result<(), SynthesisError> { + a.enforce_equal(b) } } diff --git a/src/input.rs b/src/input.rs index 586faaf6..94c338e6 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,13 +1,9 @@ use std::marker::PhantomData; use ark_bls12_377::Fr; -use ark_crypto_primitives::commitment::pedersen::Randomness; -use ark_crypto_primitives::CommitmentScheme; use ark_ff::{BigInteger, PrimeField}; -use ark_crypto_primitives::commitment::pedersen::Parameters; use ark_crypto_primitives::encryption::AsymmetricEncryptionScheme; -use ark_ec::twisted_edwards_extended::GroupAffine; use ark_ec::AffineCurve; use ark_ff::Field; use ark_std::PubUniformRand; @@ -19,6 +15,9 @@ use num_traits::One; use num_traits::Zero; use rand::Rng; +use mpc_algebra::commitment::pedersen::{Parameters, Randomness as MpcRandomness}; +use mpc_algebra::CommitmentScheme; + // use mpc_algebra::honest_but_curious::*; use mpc_algebra::malicious_majority::*; @@ -52,8 +51,7 @@ pub struct CommonInput> { pub struct InputWithCommit> { pub allocation: usize, pub input: F, - pub input_bit: Vec, - pub randomness_bit: Vec, + pub randomness: F::PedersenRandomness, pub commitment: F::PedersenCommitment, } @@ -61,41 +59,12 @@ impl InputWithCommit { pub fn generate_input( &self, pedersen_param: &Parameters, - common_randomness: &Randomness, + common_randomness: &MpcRandomness, ) -> Self { let mut iwc = self.clone(); iwc.input = iwc.input.generate_share(iwc.allocation); - if Net::party_id() == iwc.allocation { - iwc.input_bit = iwc - .input - .clone() - // .reveal() - .into_repr() - .to_bits_le() - .iter() - .map(|b| MFr::from_add_shared(Fr::from(*b))) - .collect::>(); - } else { - iwc.input_bit = iwc - .input - .clone() - // .reveal() - .into_repr() - .to_bits_le() - .iter() - .map(|_b| MFr::from_add_shared(Fr::from(false))) - .collect::>(); - } - - // let randomness = >::PedersenRandomness::pub_rand(rng); - iwc.randomness_bit = common_randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| MFr::from(*b)) - .collect::>(); + iwc.randomness = *common_randomness; let h_x = >::PedersenComScheme::commit( &pedersen_param.to_local(), @@ -104,18 +73,10 @@ impl InputWithCommit { ) .unwrap(); - let h_x_mpc = match Net::party_id() { - 0 => GroupAffine::::new( - MFr::from_add_shared(h_x.x), - MFr::from_add_shared(h_x.y), - ), - _ => GroupAffine::::new( - MFr::from_add_shared(Fr::default()), - MFr::from_add_shared(Fr::default()), - ), - }; + let h_x_mpc = MpcEdwardsAffine::from_public(h_x.clone()); + + iwc.commitment = h_x_mpc; - iwc.commitment = MpcEdwardsAffine::from_local(&h_x_mpc.reveal()); iwc } } @@ -255,31 +216,16 @@ impl MpcInputTrait for SampleMpcInput { let params = >::PedersenComScheme::setup(rng).unwrap(); let input_a = Fr::rand(rng); - let input_bit = input_a - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let a_bytes = input_a.into_repr().to_bytes_le(); //// randomness let randomness = >::PedersenRandomness::pub_rand(rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); - let a = InputWithCommit { allocation: 0, input: input_a, - input_bit, - randomness_bit: open_bit, + randomness: randomness.clone(), commitment: >::PedersenComScheme::commit( ¶ms, &a_bytes, @@ -465,31 +411,16 @@ impl MpcInputTrait for WerewolfKeyInput { let params = >::PedersenComScheme::setup(rng).unwrap(); let input_a = Fr::rand(rng); - let input_bit = input_a - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let a_bytes = input_a.into_repr().to_bytes_le(); //// randomness let randomness = >::PedersenRandomness::pub_rand(rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); - let a = InputWithCommit { allocation: 0, input: input_a, - input_bit, - randomness_bit: open_bit.clone(), + randomness: randomness.clone(), commitment: >::PedersenComScheme::commit( ¶ms, &a_bytes, @@ -499,20 +430,13 @@ impl MpcInputTrait for WerewolfKeyInput { }; let input_b = Fr::rand(rng); - let input_b_bit = input_b - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let _b_bytes = input_b.into_repr().to_bytes_le(); let b = InputWithCommit { allocation: 0, input: input_b, - input_bit: input_b_bit, - randomness_bit: open_bit, + randomness: randomness.clone(), commitment: >::PedersenComScheme::commit( ¶ms, &a_bytes, @@ -739,31 +663,16 @@ impl MpcInputTrait for WerewolfMpcInput { let params = >::PedersenComScheme::setup(rng).unwrap(); let input_a = Fr::rand(rng); - let input_bit = input_a - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let a_bytes = input_a.into_repr().to_bytes_le(); //// randomness let randomness = >::PedersenRandomness::pub_rand(rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); - let a = InputWithCommit { allocation: 0, input: input_a, - input_bit, - randomness_bit: open_bit, + randomness: randomness.clone(), commitment: >::PedersenComScheme::commit( ¶ms, &a_bytes, diff --git a/src/main.rs b/src/main.rs index 0df60252..065078b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -164,8 +164,7 @@ fn main() -> Result<(), Box> { let circuit = input_circuit::MySecretInputCircuit::new( x, - input_bit, - open_bit, + randomness, params, h_x, lower_bound, diff --git a/src/marlin.rs b/src/marlin.rs index fd3c952f..c9134c28 100644 --- a/src/marlin.rs +++ b/src/marlin.rs @@ -1,13 +1,10 @@ -use std::cmp::Ordering; - -use ark_crypto_primitives::CommitmentScheme; -use ark_ec::twisted_edwards_extended::GroupAffine; use ark_ff::{BigInteger, PrimeField}; use ark_marlin::{ahp::prover::*, *}; use ark_poly::univariate::DensePolynomial; use ark_poly_commit::marlin_pc::MarlinKZG10; use ark_relations::r1cs::ConstraintSynthesizer; use ark_std::{end_timer, start_timer, test_rng, PubUniformRand, UniformRand}; +use std::cmp::Ordering; use blake2::Blake2s; use itertools::Itertools; @@ -15,13 +12,12 @@ use itertools::Itertools; use mpc_algebra::{ malicious_majority::*, BooleanWire, LessThan, MpcBooleanField, SpdzFieldShare, UniformBitRand, }; -use mpc_algebra::{FromLocal, Reveal}; -use mpc_net::{MpcMultiNet, MpcNet}; +use mpc_algebra::{CommitmentScheme, FromLocal, Reveal}; use ark_std::{One, Zero}; use crate::circuits::enforce_smaller_or_eq_than::SmallerEqThanCircuit; -use crate::circuits::smaller_than::{self, SmallerThanCircuit}; +use crate::circuits::smaller_than::SmallerThanCircuit; use crate::{ circuits::{ bit_decomposition::BitDecompositionCircuit, circuit::MyCircuit, @@ -156,10 +152,10 @@ pub fn mpc_test_prove_and_verify(n_iters: usize) { let c = peculiar.a.input * peculiar.b.input; let inputs = vec![ c.reveal(), - peculiar.a.commitment.x.reveal(), - peculiar.a.commitment.y.reveal(), - peculiar.b.commitment.x.reveal(), - peculiar.b.commitment.y.reveal(), + peculiar.a.commitment.reveal().x, + peculiar.a.commitment.reveal().y, + peculiar.b.commitment.reveal().x, + peculiar.b.commitment.reveal().y, ]; let invalid_inputs = vec![c.reveal()]; @@ -182,80 +178,34 @@ pub fn mpc_test_prove_and_verify_pedersen(n_iters: usize) { let rng = &mut test_rng(); let params = >::PedersenComScheme::setup(rng).unwrap(); let x = Fr::from(4); - let input_bit = x - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let x_bytes = x.into_repr().to_bytes_le(); let randomness = >::PedersenRandomness::pub_rand(rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| Fr::from(*b)) - .collect::>(); let h_x_local = >::PedersenComScheme::commit(¶ms, &x_bytes, &randomness).unwrap(); let empty_circuit = PedersenComCircuit { param: Some(params.clone()), input: x, - input_bit, - open_bit, - commit: Some(h_x_local), + open: randomness, + commit: h_x_local, }; let (mpc_index_pk, index_vk) = setup_and_index(empty_circuit); for _ in 0..n_iters { let mpc_params = >::PedersenParam::from_local(¶ms); let x = MFr::rand(rng); - let input_bit = match MpcMultiNet::party_id() { - 0 => x - .clone() - .reveal() - .into_repr() - .to_bits_le() - .iter() - .map(|b| MFr::from_add_shared(Fr::from(*b))) - .collect::>(), - _ => x - .clone() - .reveal() - .into_repr() - .to_bits_le() - .iter() - .map(|_b| MFr::from_add_shared(Fr::from(false))) - .collect::>(), - }; - let randomness = >::PedersenRandomness::pub_rand(rng); - let open_bit = randomness - .0 - .into_repr() - .to_bits_le() - .iter() - .map(|b| MFr::from(*b)) - .collect::>(); - let h_x = >::PedersenComScheme::commit( - ¶ms, - &x.reveal().into_repr().to_bytes_le(), - &randomness.clone().reveal(), - ) - .unwrap(); - let h_x_mpc = GroupAffine::::new( - MFr::from_public(h_x.x), - MFr::from_public(h_x.y), - ); + let input = >::convert_input(&x); + let randomness = >::PedersenRandomness::rand(rng); + let h_x_mpc = + >::PedersenComScheme::commit(&mpc_params, &input, &randomness) + .unwrap(); let circuit = PedersenComCircuit { param: Some(mpc_params.clone()), input: x, - input_bit, - open_bit, - commit: Some(h_x_mpc), + open: randomness, + commit: h_x_mpc, }; - let inputs = vec![h_x_mpc.x.reveal(), h_x_mpc.y.reveal()]; - let invalid_inputs = vec![h_x_mpc.y.reveal(), h_x_mpc.x.reveal()]; + let inputs = vec![h_x_mpc.reveal().x, h_x_mpc.reveal().y]; + let invalid_inputs = vec![h_x_mpc.reveal().y, h_x_mpc.reveal().x]; assert!(prove_and_verify( &mpc_index_pk,