Co-authored-by: Nicholas Ward <npward@berkeley.edu>master
@ -1,6 +0,0 @@ |
|||
Sean Bowe |
|||
Alessandro Chiesa |
|||
Matthew Green |
|||
Ian Miers |
|||
Pratyush Mishra |
|||
Howard Wu |
@ -1,30 +1,35 @@ |
|||
[workspace] |
|||
[package] |
|||
name = "ark-r1cs-std" |
|||
version = "0.1.0" |
|||
authors = [ "arkworks contributors" ] |
|||
description = "A standard library for constraint system gadgets" |
|||
homepage = "https://arworks.rs" |
|||
repository = "https://github.com/arkworks/r1cs-gadgets" |
|||
documentation = "https://docs.rs/ark-r1cs-std/" |
|||
keywords = ["zero knowledge", "cryptography", "zkSNARK", "SNARK", "r1cs"] |
|||
categories = ["cryptography"] |
|||
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] |
|||
license = "MIT/Apache-2.0" |
|||
edition = "2018" |
|||
|
|||
members = [ |
|||
"cp-benches", |
|||
"crypto-primitives", |
|||
"r1cs-std", |
|||
] |
|||
[dependencies] |
|||
ark-ff = { git = "https://github.com/arkworks-rs/algebra", default-features = false } |
|||
ark-ec = { git = "https://github.com/arkworks-rs/algebra", default-features = false } |
|||
ark-std = { git = "https://github.com/arkworks-rs/utils", default-features = false } |
|||
ark-relations = { git = "https://github.com/arkworks-rs/snark", default-features = false } |
|||
|
|||
[profile.release] |
|||
opt-level = 3 |
|||
lto = "thin" |
|||
incremental = true |
|||
derivative = { version = "2", features = ["use_core"] } |
|||
tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } |
|||
|
|||
[profile.bench] |
|||
opt-level = 3 |
|||
debug = false |
|||
rpath = false |
|||
lto = "thin" |
|||
incremental = true |
|||
debug-assertions = false |
|||
[build-dependencies] |
|||
rustc_version = "0.2" |
|||
|
|||
[profile.dev] |
|||
opt-level = 0 |
|||
[dev-dependencies] |
|||
rand = { version = "0.7", default-features = false } |
|||
rand_xorshift = "0.2" |
|||
ark-test-curves = { git = "https://github.com/arkworks-rs/algebra", default-features = false, features = ["bls12_381_scalar_field"] } |
|||
|
|||
[profile.test] |
|||
opt-level = 3 |
|||
lto = "thin" |
|||
incremental = true |
|||
debug-assertions = true |
|||
debug = true |
|||
[features] |
|||
default = ["std"] |
|||
std = [ "ark-ff/std", "ark-relations/std", "ark-std/std" ] |
|||
parallel = [ "std", "ark-ff/parallel" ] |
@ -1,52 +0,0 @@ |
|||
[package] |
|||
name = "cp-benches" |
|||
version = "0.1.1-alpha.0" |
|||
authors = [ |
|||
"Sean Bowe", |
|||
"Alessandro Chiesa", |
|||
"Matthew Green", |
|||
"Ian Miers", |
|||
"Pratyush Mishra", |
|||
"Howard Wu" |
|||
] |
|||
description = "A library of cryptographic primitives that are used by Zexe" |
|||
homepage = "https://libzexe.org" |
|||
repository = "https://github.com/scipr/zexe" |
|||
documentation = "https://docs.rs/crypto-primitives/" |
|||
keywords = ["r1cs", "groth16", "gm17", "pedersen", "blake2s"] |
|||
categories = ["cryptography"] |
|||
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] |
|||
license = "MIT/Apache-2.0" |
|||
edition = "2018" |
|||
|
|||
################################# Dependencies ################################ |
|||
|
|||
[dev-dependencies] |
|||
algebra = { git = "https://github.com/scipr-lab/zexe/", default-features = false, features = [ "ed_on_bls12_377" ] } |
|||
blake2 = { version = "0.8", default-features = false } |
|||
criterion = "0.3.1" |
|||
crypto-primitives = { path = "../crypto-primitives" } |
|||
rand = { version = "0.7" } |
|||
rand_xorshift = { version = "0.2" } |
|||
|
|||
################################# Benchmarks ################################## |
|||
|
|||
[[bench]] |
|||
name = "pedersen_crh" |
|||
path = "benches/crypto_primitives/crh.rs" |
|||
harness = false |
|||
|
|||
[[bench]] |
|||
name = "pedersen_comm" |
|||
path = "benches/crypto_primitives/comm.rs" |
|||
harness = false |
|||
|
|||
[[bench]] |
|||
name = "blake2s_prf" |
|||
path = "benches/crypto_primitives/prf.rs" |
|||
harness = false |
|||
|
|||
[[bench]] |
|||
name = "schnorr_sig" |
|||
path = "benches/crypto_primitives/signature.rs" |
|||
harness = false |
@ -1,53 +0,0 @@ |
|||
use rand;
|
|||
|
|||
#[macro_use]
|
|||
extern crate criterion;
|
|||
|
|||
use algebra::{ed_on_bls12_377::EdwardsProjective as Edwards, UniformRand};
|
|||
use criterion::Criterion;
|
|||
use crypto_primitives::commitment::{pedersen::*, CommitmentScheme};
|
|||
|
|||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|||
pub struct CommWindow;
|
|||
|
|||
impl Window for CommWindow {
|
|||
const WINDOW_SIZE: usize = 250;
|
|||
const NUM_WINDOWS: usize = 8;
|
|||
}
|
|||
|
|||
fn pedersen_comm_setup(c: &mut Criterion) {
|
|||
c.bench_function("Pedersen Commitment Setup", move |b| {
|
|||
b.iter(|| {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
Commitment::<Edwards, CommWindow>::setup(&mut rng).unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
|
|||
fn pedersen_comm_eval(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = Commitment::<Edwards, CommWindow>::setup(&mut rng).unwrap();
|
|||
let input = vec![5u8; 128];
|
|||
c.bench_function("Pedersen Commitment Eval", move |b| {
|
|||
b.iter(|| {
|
|||
let rng = &mut rand::thread_rng();
|
|||
let commitment_randomness = Randomness::rand(rng);
|
|||
Commitment::<Edwards, CommWindow>::commit(¶meters, &input, &commitment_randomness)
|
|||
.unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
|
|||
criterion_group! {
|
|||
name = comm_setup;
|
|||
config = Criterion::default().sample_size(10);
|
|||
targets = pedersen_comm_setup
|
|||
}
|
|||
|
|||
criterion_group! {
|
|||
name = comm_eval;
|
|||
config = Criterion::default().sample_size(10);
|
|||
targets = pedersen_comm_eval
|
|||
}
|
|||
|
|||
criterion_main!(comm_setup, comm_eval);
|
@ -1,48 +0,0 @@ |
|||
use rand;
|
|||
|
|||
#[macro_use]
|
|||
extern crate criterion;
|
|||
|
|||
use algebra::ed_on_bls12_377::EdwardsProjective as Edwards;
|
|||
use criterion::Criterion;
|
|||
use crypto_primitives::crh::{pedersen::*, FixedLengthCRH};
|
|||
|
|||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|||
pub struct HashWindow;
|
|||
|
|||
impl Window for HashWindow {
|
|||
const WINDOW_SIZE: usize = 250;
|
|||
const NUM_WINDOWS: usize = 8;
|
|||
}
|
|||
|
|||
fn pedersen_crh_setup(c: &mut Criterion) {
|
|||
c.bench_function("Pedersen CRH Setup", move |b| {
|
|||
b.iter(|| {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
CRH::<Edwards, HashWindow>::setup(&mut rng).unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
|
|||
fn pedersen_crh_eval(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = CRH::<Edwards, HashWindow>::setup(&mut rng).unwrap();
|
|||
let input = vec![5u8; 128];
|
|||
c.bench_function("Pedersen CRH Eval", move |b| {
|
|||
b.iter(|| CRH::<Edwards, HashWindow>::evaluate(¶meters, &input).unwrap())
|
|||
});
|
|||
}
|
|||
|
|||
criterion_group! {
|
|||
name = crh_setup;
|
|||
config = Criterion::default().sample_size(10);
|
|||
targets = pedersen_crh_setup
|
|||
}
|
|||
|
|||
criterion_group! {
|
|||
name = crh_eval;
|
|||
config = Criterion::default().sample_size(10);
|
|||
targets = pedersen_crh_eval
|
|||
}
|
|||
|
|||
criterion_main!(crh_setup, crh_eval);
|
@ -1,25 +0,0 @@ |
|||
use rand;
|
|||
|
|||
#[macro_use]
|
|||
extern crate criterion;
|
|||
|
|||
use criterion::Criterion;
|
|||
use crypto_primitives::prf::*;
|
|||
use rand::Rng;
|
|||
|
|||
fn blake2s_prf_eval(c: &mut Criterion) {
|
|||
let rng = &mut rand::thread_rng();
|
|||
let input: [u8; 32] = rng.gen();
|
|||
let seed: [u8; 32] = rng.gen();
|
|||
c.bench_function("Blake2s PRF Eval", move |b| {
|
|||
b.iter(|| Blake2s::evaluate(&seed, &input).unwrap())
|
|||
});
|
|||
}
|
|||
|
|||
criterion_group! {
|
|||
name = prf_eval;
|
|||
config = Criterion::default().sample_size(50);
|
|||
targets = blake2s_prf_eval
|
|||
}
|
|||
|
|||
criterion_main!(prf_eval);
|
@ -1,89 +0,0 @@ |
|||
#[macro_use]
|
|||
extern crate criterion;
|
|||
|
|||
use algebra::ed_on_bls12_377::EdwardsProjective as Edwards;
|
|||
use blake2::Blake2s;
|
|||
use criterion::Criterion;
|
|||
use crypto_primitives::signature::{schnorr::*, SignatureScheme};
|
|||
use rand::{self, Rng};
|
|||
|
|||
type SchnorrEdwards = Schnorr<Edwards, Blake2s>;
|
|||
fn schnorr_signature_setup(c: &mut Criterion) {
|
|||
c.bench_function("SchnorrEdwards: Setup", move |b| {
|
|||
b.iter(|| {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
SchnorrEdwards::setup(&mut rng).unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
|
|||
fn schnorr_signature_keygen(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
|||
|
|||
c.bench_function("SchnorrEdwards: KeyGen", move |b| {
|
|||
b.iter(|| {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
SchnorrEdwards::keygen(¶meters, &mut rng).unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
|
|||
fn schnorr_signature_sign(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
|||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
|||
let message = [100u8; 128];
|
|||
|
|||
c.bench_function("SchnorrEdwards: Sign", move |b| {
|
|||
b.iter(|| {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
|
|||
fn schnorr_signature_verify(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
|||
let (pk, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
|||
let message = [100u8; 128];
|
|||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
|||
|
|||
c.bench_function("SchnorrEdwards: Verify", move |b| {
|
|||
b.iter(|| SchnorrEdwards::verify(¶meters, &pk, &message, &signature).unwrap())
|
|||
});
|
|||
}
|
|||
|
|||
fn schnorr_signature_randomize_pk(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
|||
let (pk, _) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
|||
let randomness: [u8; 32] = rng.gen();
|
|||
|
|||
c.bench_function("SchnorrEdwards: Randomize PubKey", move |b| {
|
|||
b.iter(|| SchnorrEdwards::randomize_public_key(¶meters, &pk, &randomness).unwrap())
|
|||
});
|
|||
}
|
|||
|
|||
fn schnorr_signature_randomize_signature(c: &mut Criterion) {
|
|||
let mut rng = &mut rand::thread_rng();
|
|||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
|||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
|||
let randomness: [u8; 32] = rng.gen();
|
|||
let message = [100u8; 128];
|
|||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
|||
|
|||
c.bench_function("SchnorrEdwards: Randomize Signature", move |b| {
|
|||
b.iter(|| {
|
|||
SchnorrEdwards::randomize_signature(¶meters, &signature, &randomness).unwrap()
|
|||
})
|
|||
});
|
|||
}
|
|||
criterion_group! {
|
|||
name = schnorr_sig;
|
|||
config = Criterion::default().sample_size(20);
|
|||
targets = schnorr_signature_setup, schnorr_signature_keygen, schnorr_signature_sign,
|
|||
schnorr_signature_verify, schnorr_signature_randomize_pk, schnorr_signature_randomize_signature
|
|||
}
|
|||
criterion_main!(schnorr_sig);
|
@ -1,52 +0,0 @@ |
|||
[package] |
|||
name = "crypto-primitives" |
|||
version = "0.1.1-alpha.0" |
|||
authors = [ |
|||
"Sean Bowe", |
|||
"Alessandro Chiesa", |
|||
"Matthew Green", |
|||
"Ian Miers", |
|||
"Pratyush Mishra", |
|||
"Howard Wu" |
|||
] |
|||
description = "A library of cryptographic primitives that are used by Zexe" |
|||
homepage = "https://libzexe.org" |
|||
repository = "https://github.com/scipr/zexe" |
|||
documentation = "https://docs.rs/crypto-primitives/" |
|||
keywords = ["r1cs", "groth16", "gm17", "pedersen", "blake2s"] |
|||
categories = ["cryptography"] |
|||
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] |
|||
license = "MIT/Apache-2.0" |
|||
edition = "2018" |
|||
|
|||
################################# Dependencies ################################ |
|||
|
|||
[dependencies] |
|||
algebra-core = { git = "https://github.com/scipr-lab/zexe", default-features = false } |
|||
bench-utils = { git = "https://github.com/scipr-lab/zexe" } |
|||
|
|||
blake2 = { version = "0.8", default-features = false } |
|||
digest = "0.8" |
|||
|
|||
ff-fft = { git = "https://github.com/scipr-lab/zexe", default-features = false } |
|||
gm17 = { git = "https://github.com/scipr-lab/zexe", optional = true, default-features = false } |
|||
groth16 = { git = "https://github.com/scipr-lab/zexe", optional = true, default-features = false } |
|||
|
|||
r1cs-core = { git = "https://github.com/scipr-lab/zexe", optional = true, default-features = false } |
|||
r1cs-std = { path = "../r1cs-std", optional = true, default-features = false } |
|||
|
|||
rand = { version = "0.7", default-features = false } |
|||
rayon = { version = "1.0", optional = true } |
|||
derivative = { version = "2.0", features = ["use_core"] } |
|||
tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } |
|||
|
|||
[features] |
|||
default = ["std", "r1cs"] |
|||
r1cs = ["r1cs-core", "r1cs-std"] |
|||
std = [ "algebra-core/std", "r1cs-core/std", "r1cs-std/std"] |
|||
parallel = ["std", "rayon", "gm17/parallel", "groth16/parallel", "ff-fft/parallel"] |
|||
|
|||
[dev-dependencies] |
|||
algebra = { git = "https://github.com/scipr-lab/zexe", default-features = false, features = [ "ed_on_bls12_381", "bls12_377", "mnt4_298", "mnt6_298" ] } |
|||
r1cs-std = { path = "../r1cs-std", default-features = false, features = [ "ed_on_bls12_381", "bls12_377", "mnt4_298", "mnt6_298" ] } |
|||
rand_xorshift = { version = "0.2" } |
@ -1 +0,0 @@ |
|||
../LICENSE-APACHE |
@ -1 +0,0 @@ |
|||
../LICENSE-MIT |
@ -1,133 +0,0 @@ |
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
|
|||
use crate::{
|
|||
commitment::blake2s,
|
|||
commitment::CommitmentGadget,
|
|||
prf::blake2s::constraints::{evaluate_blake2s, OutputVar},
|
|||
Vec,
|
|||
};
|
|||
use algebra_core::{Field, PrimeField};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use core::borrow::Borrow;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub struct ParametersVar;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub struct RandomnessVar<F: Field>(pub Vec<UInt8<F>>);
|
|||
|
|||
pub struct CommGadget;
|
|||
|
|||
impl<F: PrimeField> CommitmentGadget<blake2s::Commitment, F> for CommGadget {
|
|||
type OutputVar = OutputVar<F>;
|
|||
type ParametersVar = ParametersVar;
|
|||
type RandomnessVar = RandomnessVar<F>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(input, r))]
|
|||
fn commit(
|
|||
_: &Self::ParametersVar,
|
|||
input: &[UInt8<F>],
|
|||
r: &Self::RandomnessVar,
|
|||
) -> Result<Self::OutputVar, SynthesisError> {
|
|||
let mut input_bits = Vec::with_capacity(512);
|
|||
for byte in input.iter().chain(r.0.iter()) {
|
|||
input_bits.extend_from_slice(&byte.to_bits_le()?);
|
|||
}
|
|||
let mut result = Vec::new();
|
|||
for int in evaluate_blake2s(&input_bits)?.into_iter() {
|
|||
let chunk = int.to_bytes()?;
|
|||
result.extend_from_slice(&chunk);
|
|||
}
|
|||
Ok(OutputVar(result))
|
|||
}
|
|||
}
|
|||
|
|||
impl<ConstraintF: Field> AllocVar<(), ConstraintF> for ParametersVar {
|
|||
#[tracing::instrument(target = "r1cs", skip(_cs, _f))]
|
|||
fn new_variable<T: Borrow<()>>(
|
|||
_cs: impl Into<Namespace<ConstraintF>>,
|
|||
_f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
_mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
Ok(ParametersVar)
|
|||
}
|
|||
}
|
|||
|
|||
impl<ConstraintF: PrimeField> AllocVar<[u8; 32], ConstraintF> for RandomnessVar<ConstraintF> {
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<[u8; 32]>>(
|
|||
cs: impl Into<Namespace<ConstraintF>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let bytes = f().map(|b| *b.borrow()).unwrap_or([0u8; 32]);
|
|||
match mode {
|
|||
AllocationMode::Constant => Ok(Self(UInt8::constant_vec(&bytes))),
|
|||
AllocationMode::Input => UInt8::new_input_vec(cs, &bytes).map(Self),
|
|||
AllocationMode::Witness => UInt8::new_witness_vec(cs, &bytes).map(Self),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use crate::{
|
|||
commitment::blake2s::{
|
|||
constraints::{CommGadget, RandomnessVar},
|
|||
Commitment,
|
|||
},
|
|||
commitment::{CommitmentGadget, CommitmentScheme},
|
|||
};
|
|||
use algebra::{ed_on_bls12_381::Fq as Fr, test_rng};
|
|||
use r1cs_core::ConstraintSystem;
|
|||
use r1cs_std::prelude::*;
|
|||
use rand::Rng;
|
|||
|
|||
#[test]
|
|||
fn commitment_gadget_test() {
|
|||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|||
|
|||
let input = [1u8; 32];
|
|||
|
|||
let rng = &mut test_rng();
|
|||
|
|||
type TestCOMM = Commitment;
|
|||
type TestCOMMGadget = CommGadget;
|
|||
|
|||
let mut randomness = [0u8; 32];
|
|||
rng.fill(&mut randomness);
|
|||
|
|||
let parameters = ();
|
|||
let primitive_result = Commitment::commit(¶meters, &input, &randomness).unwrap();
|
|||
|
|||
let mut input_var = vec![];
|
|||
for byte in &input {
|
|||
input_var.push(UInt8::new_witness(cs.clone(), || Ok(*byte)).unwrap());
|
|||
}
|
|||
|
|||
let mut randomness_var = vec![];
|
|||
for r_byte in randomness.iter() {
|
|||
randomness_var.push(UInt8::new_witness(cs.clone(), || Ok(r_byte)).unwrap());
|
|||
}
|
|||
let randomness_var = RandomnessVar(randomness_var);
|
|||
|
|||
let parameters_var =
|
|||
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::ParametersVar::new_witness(
|
|||
r1cs_core::ns!(cs, "gadget_parameters"),
|
|||
|| Ok(¶meters),
|
|||
)
|
|||
.unwrap();
|
|||
let result_var = <TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::commit(
|
|||
¶meters_var,
|
|||
&input_var,
|
|||
&randomness_var,
|
|||
)
|
|||
.unwrap();
|
|||
|
|||
for i in 0..32 {
|
|||
assert_eq!(primitive_result[i], result_var.0[i].value().unwrap());
|
|||
}
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
@ -1,33 +0,0 @@ |
|||
use super::CommitmentScheme;
|
|||
use crate::Error;
|
|||
use blake2::Blake2s as b2s;
|
|||
use digest::Digest;
|
|||
use rand::Rng;
|
|||
|
|||
pub struct Commitment;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
impl CommitmentScheme for Commitment {
|
|||
type Parameters = ();
|
|||
type Randomness = [u8; 32];
|
|||
type Output = [u8; 32];
|
|||
|
|||
fn setup<R: Rng>(_: &mut R) -> Result<Self::Parameters, Error> {
|
|||
Ok(())
|
|||
}
|
|||
|
|||
fn commit(
|
|||
_: &Self::Parameters,
|
|||
input: &[u8],
|
|||
r: &Self::Randomness,
|
|||
) -> Result<Self::Output, Error> {
|
|||
let mut h = b2s::new();
|
|||
h.input(input);
|
|||
h.input(r.as_ref());
|
|||
let mut result = [0u8; 32];
|
|||
result.copy_from_slice(&h.result());
|
|||
Ok(result)
|
|||
}
|
|||
}
|
@ -1,23 +0,0 @@ |
|||
use crate::commitment::CommitmentScheme;
|
|||
use algebra_core::Field;
|
|||
use core::fmt::Debug;
|
|||
use r1cs_core::SynthesisError;
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
pub trait CommitmentGadget<C: CommitmentScheme, ConstraintF: Field> {
|
|||
type OutputVar: EqGadget<ConstraintF>
|
|||
+ ToBytesGadget<ConstraintF>
|
|||
+ AllocVar<C::Output, ConstraintF>
|
|||
+ R1CSVar<ConstraintF>
|
|||
+ Clone
|
|||
+ Sized
|
|||
+ Debug;
|
|||
type ParametersVar: AllocVar<C::Parameters, ConstraintF> + Clone;
|
|||
type RandomnessVar: AllocVar<C::Randomness, ConstraintF> + Clone;
|
|||
|
|||
fn commit(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF>],
|
|||
r: &Self::RandomnessVar,
|
|||
) -> Result<Self::OutputVar, SynthesisError>;
|
|||
}
|
@ -1,59 +0,0 @@ |
|||
use crate::commitment::{
|
|||
injective_map::{InjectiveMap, PedersenCommCompressor},
|
|||
pedersen::{
|
|||
constraints::{CommGadget, ParametersVar, RandomnessVar},
|
|||
Window,
|
|||
},
|
|||
};
|
|||
|
|||
pub use crate::crh::injective_map::constraints::InjectiveMapGadget;
|
|||
use algebra_core::{Field, PrimeField, ProjectiveCurve};
|
|||
use r1cs_core::SynthesisError;
|
|||
use r1cs_std::{
|
|||
groups::{CurveVar, GroupOpsBounds},
|
|||
uint8::UInt8,
|
|||
};
|
|||
|
|||
use core::marker::PhantomData;
|
|||
|
|||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
|||
|
|||
pub struct CommitmentCompressorGadget<C, I, W, GG, IG>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
I: InjectiveMap<C>,
|
|||
W: Window,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
IG: InjectiveMapGadget<C, I, GG>,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
_compressor: PhantomData<I>,
|
|||
_compressor_gadget: PhantomData<IG>,
|
|||
_comm: PhantomData<CommGadget<C, GG, W>>,
|
|||
}
|
|||
|
|||
impl<C, I, GG, IG, W>
|
|||
crate::commitment::CommitmentGadget<PedersenCommCompressor<C, I, W>, ConstraintF<C>>
|
|||
for CommitmentCompressorGadget<C, I, W, GG, IG>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
I: InjectiveMap<C>,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
ConstraintF<C>: PrimeField,
|
|||
IG: InjectiveMapGadget<C, I, GG>,
|
|||
W: Window,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
type OutputVar = IG::OutputVar;
|
|||
type ParametersVar = ParametersVar<C, GG>;
|
|||
type RandomnessVar = RandomnessVar<ConstraintF<C>>;
|
|||
|
|||
fn commit(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF<C>>],
|
|||
r: &Self::RandomnessVar,
|
|||
) -> Result<Self::OutputVar, SynthesisError> {
|
|||
let result = CommGadget::<C, GG, W>::commit(parameters, input, r)?;
|
|||
IG::evaluate(&result)
|
|||
}
|
|||
}
|
@ -1,44 +0,0 @@ |
|||
use crate::Error;
|
|||
use core::marker::PhantomData;
|
|||
use rand::Rng;
|
|||
|
|||
use super::{pedersen, CommitmentScheme};
|
|||
pub use crate::crh::injective_map::InjectiveMap;
|
|||
use algebra_core::ProjectiveCurve;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
pub struct PedersenCommCompressor<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> {
|
|||
_group: PhantomData<C>,
|
|||
_compressor: PhantomData<I>,
|
|||
_comm: pedersen::Commitment<C, W>,
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> CommitmentScheme
|
|||
for PedersenCommCompressor<C, I, W>
|
|||
{
|
|||
type Output = I::Output;
|
|||
type Parameters = pedersen::Parameters<C>;
|
|||
type Randomness = pedersen::Randomness<C>;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
|||
let time = start_timer!(|| format!("PedersenCompressor::Setup"));
|
|||
let params = pedersen::Commitment::<C, W>::setup(rng);
|
|||
end_timer!(time);
|
|||
params
|
|||
}
|
|||
|
|||
fn commit(
|
|||
parameters: &Self::Parameters,
|
|||
input: &[u8],
|
|||
randomness: &Self::Randomness,
|
|||
) -> Result<Self::Output, Error> {
|
|||
let eval_time = start_timer!(|| "PedersenCompressor::Eval");
|
|||
let result = I::injective_map(&pedersen::Commitment::<C, W>::commit(
|
|||
parameters, input, randomness,
|
|||
)?)?;
|
|||
end_timer!(eval_time);
|
|||
Ok(result)
|
|||
}
|
|||
}
|
@ -1,30 +0,0 @@ |
|||
use algebra_core::UniformRand;
|
|||
use core::{fmt::Debug, hash::Hash};
|
|||
use rand::Rng;
|
|||
|
|||
use algebra_core::bytes::ToBytes;
|
|||
|
|||
pub mod blake2s;
|
|||
pub mod injective_map;
|
|||
pub mod pedersen;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub use constraints::*;
|
|||
|
|||
use crate::Error;
|
|||
|
|||
pub trait CommitmentScheme {
|
|||
type Output: ToBytes + Clone + Default + Eq + Hash + Debug;
|
|||
type Parameters: Clone;
|
|||
type Randomness: Clone + ToBytes + Default + Eq + UniformRand + Debug;
|
|||
|
|||
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
|
|||
|
|||
fn commit(
|
|||
parameters: &Self::Parameters,
|
|||
input: &[u8],
|
|||
r: &Self::Randomness,
|
|||
) -> Result<Self::Output, Error>;
|
|||
}
|
@ -1,204 +0,0 @@ |
|||
use crate::{
|
|||
commitment::pedersen::{Commitment, Parameters, Randomness},
|
|||
crh::pedersen::Window,
|
|||
Vec,
|
|||
};
|
|||
use algebra_core::{
|
|||
fields::{Field, PrimeField},
|
|||
to_bytes, ProjectiveCurve, Zero,
|
|||
};
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
|
|||
use core::{borrow::Borrow, marker::PhantomData};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
|
|||
pub struct ParametersVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
|
|||
where
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
params: Parameters<C>,
|
|||
#[doc(hidden)]
|
|||
_group_var: PhantomData<GG>,
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug)]
|
|||
pub struct RandomnessVar<F: Field>(Vec<UInt8<F>>);
|
|||
|
|||
pub struct CommGadget<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>, W: Window>
|
|||
where
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
#[doc(hidden)]
|
|||
_curve: PhantomData<*const C>,
|
|||
#[doc(hidden)]
|
|||
_group_var: PhantomData<*const GG>,
|
|||
#[doc(hidden)]
|
|||
_window: PhantomData<*const W>,
|
|||
}
|
|||
|
|||
impl<C, GG, W> crate::commitment::CommitmentGadget<Commitment<C, W>, ConstraintF<C>>
|
|||
for CommGadget<C, GG, W>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
W: Window,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
ConstraintF<C>: PrimeField,
|
|||
{
|
|||
type OutputVar = GG;
|
|||
type ParametersVar = ParametersVar<C, GG>;
|
|||
type RandomnessVar = RandomnessVar<ConstraintF<C>>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(parameters, r))]
|
|||
fn commit(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF<C>>],
|
|||
r: &Self::RandomnessVar,
|
|||
) -> Result<Self::OutputVar, SynthesisError> {
|
|||
assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS));
|
|||
|
|||
let mut padded_input = input.to_vec();
|
|||
// Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`.
|
|||
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(UInt8::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 commitment output.
|
|||
let input_in_bits: Vec<Boolean<_>> = padded_input
|
|||
.iter()
|
|||
.flat_map(|byte| byte.to_bits_le().unwrap())
|
|||
.collect();
|
|||
let input_in_bits = input_in_bits.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();
|
|||
result.precomputed_base_scalar_mul_le(
|
|||
rand_bits
|
|||
.iter()
|
|||
.zip(¶meters.params.randomness_generator),
|
|||
)?;
|
|||
|
|||
Ok(result)
|
|||
}
|
|||
}
|
|||
|
|||
impl<C, GG> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GG>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
fn new_variable<T: Borrow<Parameters<C>>>(
|
|||
_cs: impl Into<Namespace<ConstraintF<C>>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
_mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let params = f()?.borrow().clone();
|
|||
Ok(ParametersVar {
|
|||
params,
|
|||
_group_var: PhantomData,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<C, F> AllocVar<Randomness<C>, F> for RandomnessVar<F>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
F: PrimeField,
|
|||
{
|
|||
fn new_variable<T: Borrow<Randomness<C>>>(
|
|||
cs: impl Into<Namespace<F>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let r = to_bytes![&f().map(|b| b.borrow().0).unwrap_or(C::ScalarField::zero())].unwrap();
|
|||
match mode {
|
|||
AllocationMode::Constant => Ok(Self(UInt8::constant_vec(&r))),
|
|||
AllocationMode::Input => UInt8::new_input_vec(cs, &r).map(Self),
|
|||
AllocationMode::Witness => UInt8::new_witness_vec(cs, &r).map(Self),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use algebra::{
|
|||
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr},
|
|||
test_rng, UniformRand,
|
|||
};
|
|||
|
|||
use crate::{
|
|||
commitment::{
|
|||
pedersen::{constraints::CommGadget, Commitment, Randomness},
|
|||
CommitmentGadget, CommitmentScheme,
|
|||
},
|
|||
crh::pedersen,
|
|||
};
|
|||
use r1cs_core::ConstraintSystem;
|
|||
use r1cs_std::{ed_on_bls12_381::EdwardsVar, prelude::*};
|
|||
|
|||
#[test]
|
|||
fn commitment_gadget_test() {
|
|||
let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
|
|||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|||
pub(super) struct Window;
|
|||
|
|||
impl pedersen::Window for Window {
|
|||
const WINDOW_SIZE: usize = 4;
|
|||
const NUM_WINDOWS: usize = 8;
|
|||
}
|
|||
|
|||
let input = [1u8; 4];
|
|||
|
|||
let rng = &mut test_rng();
|
|||
|
|||
type TestCOMM = Commitment<JubJub, Window>;
|
|||
type TestCOMMGadget = CommGadget<JubJub, EdwardsVar, Window>;
|
|||
|
|||
let randomness = Randomness(Fr::rand(rng));
|
|||
|
|||
let parameters = Commitment::<JubJub, Window>::setup(rng).unwrap();
|
|||
let primitive_result =
|
|||
Commitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
|
|||
|
|||
let mut input_var = vec![];
|
|||
for input_byte in input.iter() {
|
|||
input_var.push(UInt8::new_witness(cs.clone(), || Ok(*input_byte)).unwrap());
|
|||
}
|
|||
|
|||
let randomness_var =
|
|||
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessVar::new_witness(
|
|||
r1cs_core::ns!(cs, "gadget_randomness"),
|
|||
|| Ok(&randomness),
|
|||
)
|
|||
.unwrap();
|
|||
let parameters_var =
|
|||
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersVar::new_witness(
|
|||
r1cs_core::ns!(cs, "gadget_parameters"),
|
|||
|| Ok(¶meters),
|
|||
)
|
|||
.unwrap();
|
|||
let result_var =
|
|||
TestCOMMGadget::commit(¶meters_var, &input_var, &randomness_var).unwrap();
|
|||
|
|||
let primitive_result = primitive_result;
|
|||
assert_eq!(primitive_result, result_var.value().unwrap());
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
@ -1,125 +0,0 @@ |
|||
use crate::{Error, Vec};
|
|||
use algebra_core::{
|
|||
bytes::ToBytes,
|
|||
io::{Result as IoResult, Write},
|
|||
BitIteratorLE, Field, FpParameters, PrimeField, ProjectiveCurve, ToConstraintField,
|
|||
UniformRand,
|
|||
};
|
|||
|
|||
use core::marker::PhantomData;
|
|||
use rand::Rng;
|
|||
|
|||
use super::CommitmentScheme;
|
|||
|
|||
pub use crate::crh::pedersen::Window;
|
|||
use crate::crh::{pedersen, FixedLengthCRH};
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub struct Parameters<C: ProjectiveCurve> {
|
|||
pub randomness_generator: Vec<C>,
|
|||
pub generators: Vec<Vec<C>>,
|
|||
}
|
|||
|
|||
pub struct Commitment<C: ProjectiveCurve, W: Window> {
|
|||
group: PhantomData<C>,
|
|||
window: PhantomData<W>,
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone, PartialEq, Debug, Eq, Default)]
|
|||
pub struct Randomness<C: ProjectiveCurve>(pub C::ScalarField);
|
|||
|
|||
impl<C: ProjectiveCurve> UniformRand for Randomness<C> {
|
|||
#[inline]
|
|||
fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
|||
Randomness(UniformRand::rand(rng))
|
|||
}
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve> ToBytes for Randomness<C> {
|
|||
fn write<W: Write>(&self, writer: W) -> IoResult<()> {
|
|||
self.0.write(writer)
|
|||
}
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve, W: Window> CommitmentScheme for Commitment<C, W> {
|
|||
type Parameters = Parameters<C>;
|
|||
type Randomness = Randomness<C>;
|
|||
type Output = C::Affine;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
|||
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 = <C::ScalarField as PrimeField>::Params::MODULUS_BITS as usize;
|
|||
let randomness_generator = pedersen::CRH::<C, W>::generator_powers(num_powers, rng);
|
|||
let generators = pedersen::CRH::<C, W>::create_generators(rng);
|
|||
end_timer!(time);
|
|||
|
|||
Ok(Self::Parameters {
|
|||
randomness_generator,
|
|||
generators,
|
|||
})
|
|||
}
|
|||
|
|||
fn commit(
|
|||
parameters: &Self::Parameters,
|
|||
input: &[u8],
|
|||
randomness: &Self::Randomness,
|
|||
) -> Result<Self::Output, Error> {
|
|||
let commit_time = start_timer!(|| "PedersenCOMM::Commit");
|
|||
// If the input is too long, return an error.
|
|||
if input.len() > W::WINDOW_SIZE * W::NUM_WINDOWS {
|
|||
panic!("incorrect input length: {:?}", input.len());
|
|||
}
|
|||
// Pad the input to the necessary length.
|
|||
let mut padded_input = Vec::with_capacity(input.len());
|
|||
let mut input = input;
|
|||
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
|||
let current_length = input.len();
|
|||
padded_input.extend_from_slice(input);
|
|||
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
|
|||
padded_input.push(0u8);
|
|||
}
|
|||
input = padded_input.as_slice();
|
|||
}
|
|||
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::<C, W>::evaluate(&crh_parameters, &input)?.into();
|
|||
let randomize_time = start_timer!(|| "Randomize");
|
|||
|
|||
// Compute h^r.
|
|||
for (bit, power) in BitIteratorLE::new(randomness.0.into_repr())
|
|||
.into_iter()
|
|||
.zip(¶meters.randomness_generator)
|
|||
{
|
|||
if bit {
|
|||
result += power
|
|||
}
|
|||
}
|
|||
end_timer!(randomize_time);
|
|||
end_timer!(commit_time);
|
|||
|
|||
Ok(result.into())
|
|||
}
|
|||
}
|
|||
|
|||
impl<ConstraintF: Field, C: ProjectiveCurve + ToConstraintField<ConstraintF>>
|
|||
ToConstraintField<ConstraintF> for Parameters<C>
|
|||
{
|
|||
#[inline]
|
|||
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
|
|||
Ok(Vec::new())
|
|||
}
|
|||
}
|
@ -1,180 +0,0 @@ |
|||
use core::{borrow::Borrow, marker::PhantomData};
|
|||
|
|||
use crate::{
|
|||
crh::{
|
|||
bowe_hopwood::{Parameters, CHUNK_SIZE, CRH},
|
|||
pedersen::Window,
|
|||
FixedLengthCRHGadget,
|
|||
},
|
|||
Vec,
|
|||
};
|
|||
use algebra_core::{
|
|||
curves::{ModelParameters, TEModelParameters},
|
|||
Field,
|
|||
};
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
use r1cs_std::{
|
|||
alloc::AllocVar, groups::curves::twisted_edwards::AffineVar, prelude::*, uint8::UInt8,
|
|||
};
|
|||
|
|||
use r1cs_std::bits::boolean::Boolean;
|
|||
|
|||
type ConstraintF<P> = <<P as ModelParameters>::BaseField as Field>::BasePrimeField;
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = "P: TEModelParameters, W: Window"))]
|
|||
pub struct ParametersVar<P: TEModelParameters, W: Window> {
|
|||
params: Parameters<P>,
|
|||
#[doc(hidden)]
|
|||
_window: PhantomData<W>,
|
|||
}
|
|||
|
|||
pub struct CRHGadget<P: TEModelParameters, F: FieldVar<P::BaseField, ConstraintF<P>>>
|
|||
where
|
|||
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
|
|||
{
|
|||
#[doc(hidden)]
|
|||
_params: PhantomData<P>,
|
|||
#[doc(hidden)]
|
|||
_base_field: PhantomData<F>,
|
|||
}
|
|||
|
|||
impl<P, F, W> FixedLengthCRHGadget<CRH<P, W>, ConstraintF<P>> for CRHGadget<P, F>
|
|||
where
|
|||
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
|
|||
F: FieldVar<P::BaseField, ConstraintF<P>>,
|
|||
F: TwoBitLookupGadget<ConstraintF<P>, TableConstant = P::BaseField>
|
|||
+ ThreeBitCondNegLookupGadget<ConstraintF<P>, TableConstant = P::BaseField>,
|
|||
P: TEModelParameters,
|
|||
W: Window,
|
|||
{
|
|||
type OutputVar = AffineVar<P, F>;
|
|||
type ParametersVar = ParametersVar<P, W>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(parameters, input))]
|
|||
fn evaluate(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF<P>>],
|
|||
) -> Result<Self::OutputVar, SynthesisError> {
|
|||
// Pad the input if it is not the current length.
|
|||
let mut input_in_bits: Vec<Boolean<_>> = input
|
|||
.iter()
|
|||
.flat_map(|byte| byte.to_bits_le().unwrap())
|
|||
.collect();
|
|||
if (input_in_bits.len()) % CHUNK_SIZE != 0 {
|
|||
let current_length = input_in_bits.len();
|
|||
for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
|
|||
input_in_bits.push(Boolean::constant(false));
|
|||
}
|
|||
}
|
|||
assert!(input_in_bits.len() % CHUNK_SIZE == 0);
|
|||
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
|
|||
for generators in parameters.params.generators.iter() {
|
|||
assert_eq!(generators.len(), W::WINDOW_SIZE);
|
|||
}
|
|||
|
|||
// Allocate new variable for the result.
|
|||
let input_in_bits = input_in_bits
|
|||
.chunks(W::WINDOW_SIZE * CHUNK_SIZE)
|
|||
.map(|x| x.chunks(CHUNK_SIZE).collect::<Vec<_>>())
|
|||
.collect::<Vec<_>>();
|
|||
let result = AffineVar::precomputed_base_3_bit_signed_digit_scalar_mul(
|
|||
¶meters.params.generators,
|
|||
&input_in_bits,
|
|||
)?;
|
|||
|
|||
Ok(result)
|
|||
}
|
|||
}
|
|||
|
|||
impl<P, W> AllocVar<Parameters<P>, ConstraintF<P>> for ParametersVar<P, W>
|
|||
where
|
|||
P: TEModelParameters,
|
|||
W: Window,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(_cs, f))]
|
|||
fn new_variable<T: Borrow<Parameters<P>>>(
|
|||
_cs: impl Into<Namespace<ConstraintF<P>>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
_mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let params = f()?.borrow().clone();
|
|||
Ok(ParametersVar {
|
|||
params,
|
|||
_window: PhantomData,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use rand::Rng;
|
|||
|
|||
use crate::crh::{
|
|||
bowe_hopwood::{constraints::CRHGadget, CRH},
|
|||
pedersen::Window as PedersenWindow,
|
|||
FixedLengthCRH, FixedLengthCRHGadget,
|
|||
};
|
|||
use algebra::{
|
|||
ed_on_bls12_381::{EdwardsParameters, Fq as Fr},
|
|||
test_rng, ProjectiveCurve,
|
|||
};
|
|||
use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
|
|||
use r1cs_std::{alloc::AllocVar, ed_on_bls12_381::FqVar, uint8::UInt8, R1CSVar};
|
|||
|
|||
type TestCRH = CRH<EdwardsParameters, Window>;
|
|||
type TestCRHGadget = CRHGadget<EdwardsParameters, FqVar>;
|
|||
|
|||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|||
pub(super) struct Window;
|
|||
|
|||
impl PedersenWindow for Window {
|
|||
const WINDOW_SIZE: usize = 63;
|
|||
const NUM_WINDOWS: usize = 8;
|
|||
}
|
|||
|
|||
fn generate_input<R: Rng>(
|
|||
cs: ConstraintSystemRef<Fr>,
|
|||
rng: &mut R,
|
|||
) -> ([u8; 189], Vec<UInt8<Fr>>) {
|
|||
let mut input = [1u8; 189];
|
|||
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::<Fr>::new_ref();
|
|||
|
|||
let (input, input_var) = generate_input(cs.clone(), rng);
|
|||
println!("number of constraints for input: {}", cs.num_constraints());
|
|||
|
|||
let parameters = TestCRH::setup(rng).unwrap();
|
|||
let primitive_result = TestCRH::evaluate(¶meters, &input).unwrap();
|
|||
|
|||
let parameters_var =
|
|||
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersVar::new_witness(
|
|||
r1cs_core::ns!(cs, "parameters_var"),
|
|||
|| Ok(¶meters),
|
|||
)
|
|||
.unwrap();
|
|||
println!(
|
|||
"number of constraints for input + params: {}",
|
|||
cs.num_constraints()
|
|||
);
|
|||
|
|||
let result_var = TestCRHGadget::evaluate(¶meters_var, &input_var).unwrap();
|
|||
|
|||
println!("number of constraints total: {}", cs.num_constraints());
|
|||
|
|||
let primitive_result = primitive_result.into_affine();
|
|||
assert_eq!(primitive_result, result_var.value().unwrap().into_affine());
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
@ -1,197 +0,0 @@ |
|||
use crate::{Error, Vec};
|
|||
use core::{
|
|||
fmt::{Debug, Formatter, Result as FmtResult},
|
|||
marker::PhantomData,
|
|||
};
|
|||
use rand::Rng;
|
|||
#[cfg(feature = "parallel")]
|
|||
use rayon::prelude::*;
|
|||
|
|||
use super::pedersen;
|
|||
use crate::crh::FixedLengthCRH;
|
|||
use algebra_core::{
|
|||
biginteger::BigInteger, curves::twisted_edwards_extended::GroupProjective as TEProjective,
|
|||
fields::PrimeField, ProjectiveCurve, TEModelParameters, UniformRand,
|
|||
};
|
|||
use ff_fft::cfg_chunks;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
pub const CHUNK_SIZE: usize = 3;
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = ""), Default(bound = ""))]
|
|||
pub struct Parameters<P: TEModelParameters> {
|
|||
pub generators: Vec<Vec<TEProjective<P>>>,
|
|||
}
|
|||
|
|||
pub struct CRH<P: TEModelParameters, W: pedersen::Window> {
|
|||
group: PhantomData<P>,
|
|||
window: PhantomData<W>,
|
|||
}
|
|||
|
|||
impl<P: TEModelParameters, W: pedersen::Window> CRH<P, W> {
|
|||
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<TEProjective<P>>> {
|
|||
let mut generators = Vec::new();
|
|||
for _ in 0..W::NUM_WINDOWS {
|
|||
let mut generators_for_segment = Vec::new();
|
|||
let mut base = TEProjective::rand(rng);
|
|||
for _ in 0..W::WINDOW_SIZE {
|
|||
generators_for_segment.push(base);
|
|||
for _ in 0..4 {
|
|||
base.double_in_place();
|
|||
}
|
|||
}
|
|||
generators.push(generators_for_segment);
|
|||
}
|
|||
generators
|
|||
}
|
|||
}
|
|||
|
|||
impl<P: TEModelParameters, W: pedersen::Window> FixedLengthCRH for CRH<P, W> {
|
|||
const INPUT_SIZE_BITS: usize = pedersen::CRH::<TEProjective<P>, W>::INPUT_SIZE_BITS;
|
|||
type Output = TEProjective<P>;
|
|||
type Parameters = Parameters<P>;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
|||
fn calculate_num_chunks_in_segment<F: PrimeField>() -> usize {
|
|||
let upper_limit = F::modulus_minus_one_div_two();
|
|||
let mut c = 0;
|
|||
let mut range = F::BigInt::from(2_u64);
|
|||
while range < upper_limit {
|
|||
range.muln(4);
|
|||
c += 1;
|
|||
}
|
|||
|
|||
c
|
|||
}
|
|||
|
|||
let maximum_num_chunks_in_segment = calculate_num_chunks_in_segment::<P::ScalarField>();
|
|||
if W::WINDOW_SIZE > maximum_num_chunks_in_segment {
|
|||
return Err(format!(
|
|||
"Bowe-Hopwood-PedersenCRH hash must have a window size resulting in scalars < (p-1)/2, \
|
|||
maximum segment size is {}",
|
|||
maximum_num_chunks_in_segment
|
|||
)
|
|||
.into());
|
|||
}
|
|||
|
|||
let time = start_timer!(|| format!(
|
|||
"Bowe-Hopwood-PedersenCRH::Setup: {} segments of {} 3-bit chunks; {{0,1}}^{{{}}} -> P",
|
|||
W::NUM_WINDOWS,
|
|||
W::WINDOW_SIZE,
|
|||
W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE
|
|||
));
|
|||
let generators = Self::create_generators(rng);
|
|||
end_timer!(time);
|
|||
Ok(Self::Parameters { generators })
|
|||
}
|
|||
|
|||
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
|
|||
let eval_time = start_timer!(|| "BoweHopwoodPedersenCRH::Eval");
|
|||
|
|||
if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE {
|
|||
panic!(
|
|||
"incorrect input length {:?} for window params {:?}x{:?}x{}",
|
|||
input.len(),
|
|||
W::WINDOW_SIZE,
|
|||
W::NUM_WINDOWS,
|
|||
CHUNK_SIZE,
|
|||
);
|
|||
}
|
|||
|
|||
let mut padded_input = Vec::with_capacity(input.len());
|
|||
let input = pedersen::bytes_to_bits(input);
|
|||
// Pad the input if it is not the current length.
|
|||
padded_input.extend_from_slice(&input);
|
|||
if input.len() % CHUNK_SIZE != 0 {
|
|||
let current_length = input.len();
|
|||
for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
|
|||
padded_input.push(false);
|
|||
}
|
|||
}
|
|||
|
|||
assert_eq!(padded_input.len() % CHUNK_SIZE, 0);
|
|||
|
|||
assert_eq!(
|
|||
parameters.generators.len(),
|
|||
W::NUM_WINDOWS,
|
|||
"Incorrect pp of size {:?} for window params {:?}x{:?}x{}",
|
|||
parameters.generators.len(),
|
|||
W::WINDOW_SIZE,
|
|||
W::NUM_WINDOWS,
|
|||
CHUNK_SIZE,
|
|||
);
|
|||
for generators in parameters.generators.iter() {
|
|||
assert_eq!(generators.len(), W::WINDOW_SIZE);
|
|||
}
|
|||
assert_eq!(CHUNK_SIZE, 3);
|
|||
|
|||
// Compute sum of h_i^{sum of
|
|||
// (1-2*c_{i,j,2})*(1+c_{i,j,0}+2*c_{i,j,1})*2^{4*(j-1)} for all j in segment}
|
|||
// for all i. Described in section 5.4.1.7 in the Zcash protocol
|
|||
// specification.
|
|||
|
|||
let result = cfg_chunks!(padded_input, W::WINDOW_SIZE * CHUNK_SIZE)
|
|||
.zip(¶meters.generators)
|
|||
.map(|(segment_bits, segment_generators)| {
|
|||
cfg_chunks!(segment_bits, CHUNK_SIZE)
|
|||
.zip(segment_generators)
|
|||
.map(|(chunk_bits, generator)| {
|
|||
let mut encoded = generator.clone();
|
|||
if chunk_bits[0] {
|
|||
encoded = encoded + generator;
|
|||
}
|
|||
if chunk_bits[1] {
|
|||
encoded += &generator.double();
|
|||
}
|
|||
if chunk_bits[2] {
|
|||
encoded = -encoded;
|
|||
}
|
|||
encoded
|
|||
})
|
|||
.sum::<TEProjective<P>>()
|
|||
})
|
|||
.sum::<TEProjective<P>>();
|
|||
|
|||
end_timer!(eval_time);
|
|||
|
|||
Ok(result)
|
|||
}
|
|||
}
|
|||
|
|||
impl<P: TEModelParameters> Debug for Parameters<P> {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|||
write!(f, "Bowe-Hopwood-Pedersen Hash Parameters {{\n")?;
|
|||
for (i, g) in self.generators.iter().enumerate() {
|
|||
write!(f, "\t Generator {}: {:?}\n", i, g)?;
|
|||
}
|
|||
write!(f, "}}\n")
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use crate::{
|
|||
crh::{bowe_hopwood::CRH, pedersen::Window},
|
|||
FixedLengthCRH,
|
|||
};
|
|||
use algebra::{ed_on_bls12_381::EdwardsParameters, test_rng};
|
|||
|
|||
#[test]
|
|||
fn test_simple_bh() {
|
|||
#[derive(Clone)]
|
|||
struct TestWindow {}
|
|||
impl Window for TestWindow {
|
|||
const WINDOW_SIZE: usize = 63;
|
|||
const NUM_WINDOWS: usize = 8;
|
|||
}
|
|||
|
|||
let rng = &mut test_rng();
|
|||
let params = <CRH<EdwardsParameters, TestWindow> as FixedLengthCRH>::setup(rng).unwrap();
|
|||
let _ =
|
|||
<CRH<EdwardsParameters, TestWindow> as FixedLengthCRH>::evaluate(¶ms, &[1, 2, 3])
|
|||
.unwrap();
|
|||
}
|
|||
}
|
@ -1,25 +0,0 @@ |
|||
use algebra_core::Field;
|
|||
use core::fmt::Debug;
|
|||
|
|||
use crate::crh::FixedLengthCRH;
|
|||
use r1cs_core::SynthesisError;
|
|||
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
pub trait FixedLengthCRHGadget<H: FixedLengthCRH, ConstraintF: Field>: Sized {
|
|||
type OutputVar: EqGadget<ConstraintF>
|
|||
+ ToBytesGadget<ConstraintF>
|
|||
+ CondSelectGadget<ConstraintF>
|
|||
+ AllocVar<H::Output, ConstraintF>
|
|||
+ R1CSVar<ConstraintF>
|
|||
+ Debug
|
|||
+ Clone
|
|||
+ Sized;
|
|||
|
|||
type ParametersVar: AllocVar<H::Parameters, ConstraintF> + Clone;
|
|||
|
|||
fn evaluate(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF>],
|
|||
) -> Result<Self::OutputVar, SynthesisError>;
|
|||
}
|
@ -1,98 +0,0 @@ |
|||
use core::{fmt::Debug, marker::PhantomData};
|
|||
|
|||
use crate::crh::{
|
|||
injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor},
|
|||
pedersen::{constraints as ped_constraints, Window},
|
|||
FixedLengthCRHGadget,
|
|||
};
|
|||
|
|||
use algebra_core::{
|
|||
curves::{
|
|||
models::{ModelParameters, TEModelParameters},
|
|||
twisted_edwards_extended::GroupProjective as TEProjective,
|
|||
},
|
|||
fields::{Field, PrimeField, SquareRootField},
|
|||
ProjectiveCurve,
|
|||
};
|
|||
use r1cs_core::SynthesisError;
|
|||
use r1cs_std::{
|
|||
fields::fp::FpVar,
|
|||
groups::{curves::twisted_edwards::AffineVar as TEVar, CurveVar},
|
|||
prelude::*,
|
|||
};
|
|||
|
|||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
|||
|
|||
pub trait InjectiveMapGadget<
|
|||
C: ProjectiveCurve,
|
|||
I: InjectiveMap<C>,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
> where
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
type OutputVar: EqGadget<ConstraintF<C>>
|
|||
+ ToBytesGadget<ConstraintF<C>>
|
|||
+ CondSelectGadget<ConstraintF<C>>
|
|||
+ AllocVar<I::Output, ConstraintF<C>>
|
|||
+ R1CSVar<ConstraintF<C>, Value = I::Output>
|
|||
+ Debug
|
|||
+ Clone
|
|||
+ Sized;
|
|||
|
|||
fn evaluate(ge: &GG) -> Result<Self::OutputVar, SynthesisError>;
|
|||
}
|
|||
|
|||
pub struct TECompressorGadget;
|
|||
|
|||
impl<F, P> InjectiveMapGadget<TEProjective<P>, TECompressor, TEVar<P, FpVar<F>>>
|
|||
for TECompressorGadget
|
|||
where
|
|||
F: PrimeField + SquareRootField,
|
|||
P: TEModelParameters + ModelParameters<BaseField = F>,
|
|||
{
|
|||
type OutputVar = FpVar<F>;
|
|||
|
|||
fn evaluate(ge: &TEVar<P, FpVar<F>>) -> Result<Self::OutputVar, SynthesisError> {
|
|||
Ok(ge.x.clone())
|
|||
}
|
|||
}
|
|||
|
|||
pub struct PedersenCRHCompressorGadget<C, I, W, GG, IG>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
I: InjectiveMap<C>,
|
|||
W: Window,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
IG: InjectiveMapGadget<C, I, GG>,
|
|||
{
|
|||
#[doc(hidden)]
|
|||
_compressor: PhantomData<I>,
|
|||
#[doc(hidden)]
|
|||
_compressor_gadget: PhantomData<IG>,
|
|||
#[doc(hidden)]
|
|||
_crh: ped_constraints::CRHGadget<C, GG, W>,
|
|||
}
|
|||
|
|||
impl<C, I, GG, IG, W> FixedLengthCRHGadget<PedersenCRHCompressor<C, I, W>, ConstraintF<C>>
|
|||
for PedersenCRHCompressorGadget<C, I, W, GG, IG>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
I: InjectiveMap<C>,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
IG: InjectiveMapGadget<C, I, GG>,
|
|||
W: Window,
|
|||
{
|
|||
type OutputVar = IG::OutputVar;
|
|||
type ParametersVar = ped_constraints::CRHParametersVar<C, GG>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(parameters, input))]
|
|||
fn evaluate(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF<C>>],
|
|||
) -> Result<Self::OutputVar, SynthesisError> {
|
|||
let result = ped_constraints::CRHGadget::<C, GG, W>::evaluate(parameters, input)?;
|
|||
IG::evaluate(&result)
|
|||
}
|
|||
}
|
@ -1,59 +0,0 @@ |
|||
use crate::{CryptoError, Error};
|
|||
use algebra_core::bytes::ToBytes;
|
|||
use core::{fmt::Debug, hash::Hash, marker::PhantomData};
|
|||
use rand::Rng;
|
|||
|
|||
use super::{pedersen, FixedLengthCRH};
|
|||
use algebra_core::curves::{
|
|||
models::{ModelParameters, TEModelParameters},
|
|||
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
|
|||
ProjectiveCurve,
|
|||
};
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
pub trait InjectiveMap<C: ProjectiveCurve> {
|
|||
type Output: ToBytes + Clone + Eq + Hash + Default + Debug;
|
|||
|
|||
fn injective_map(ge: &C::Affine) -> Result<Self::Output, CryptoError>;
|
|||
}
|
|||
|
|||
pub struct TECompressor;
|
|||
|
|||
impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor {
|
|||
type Output = <P as ModelParameters>::BaseField;
|
|||
|
|||
fn injective_map(ge: &TEAffine<P>) -> Result<Self::Output, CryptoError> {
|
|||
debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve());
|
|||
Ok(ge.x)
|
|||
}
|
|||
}
|
|||
|
|||
pub struct PedersenCRHCompressor<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> {
|
|||
_group: PhantomData<C>,
|
|||
_compressor: PhantomData<I>,
|
|||
_crh: pedersen::CRH<C, W>,
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> FixedLengthCRH
|
|||
for PedersenCRHCompressor<C, I, W>
|
|||
{
|
|||
const INPUT_SIZE_BITS: usize = pedersen::CRH::<C, W>::INPUT_SIZE_BITS;
|
|||
type Output = I::Output;
|
|||
type Parameters = pedersen::Parameters<C>;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
|||
let time = start_timer!(|| format!("PedersenCRHCompressor::Setup"));
|
|||
let params = pedersen::CRH::<C, W>::setup(rng);
|
|||
end_timer!(time);
|
|||
params
|
|||
}
|
|||
|
|||
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
|
|||
let eval_time = start_timer!(|| "PedersenCRHCompressor::Eval");
|
|||
let result = I::injective_map(&pedersen::CRH::<C, W>::evaluate(parameters, input)?)?;
|
|||
end_timer!(eval_time);
|
|||
Ok(result)
|
|||
}
|
|||
}
|
@ -1,24 +0,0 @@ |
|||
use algebra_core::bytes::ToBytes;
|
|||
use core::hash::Hash;
|
|||
use rand::Rng;
|
|||
|
|||
pub mod bowe_hopwood;
|
|||
pub mod injective_map;
|
|||
pub mod pedersen;
|
|||
|
|||
use crate::Error;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub use constraints::*;
|
|||
|
|||
pub trait FixedLengthCRH {
|
|||
const INPUT_SIZE_BITS: usize;
|
|||
|
|||
type Output: ToBytes + Clone + Eq + core::fmt::Debug + Hash + Default;
|
|||
type Parameters: Clone + Default;
|
|||
|
|||
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
|
|||
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error>;
|
|||
}
|
@ -1,152 +0,0 @@ |
|||
use crate::{
|
|||
crh::{
|
|||
pedersen::{Parameters, Window, CRH},
|
|||
FixedLengthCRHGadget,
|
|||
},
|
|||
Vec,
|
|||
};
|
|||
use algebra_core::{Field, ProjectiveCurve};
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use core::{borrow::Borrow, marker::PhantomData};
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
|
|||
pub struct CRHParametersVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
|
|||
where
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
params: Parameters<C>,
|
|||
#[doc(hidden)]
|
|||
_group_g: PhantomData<GG>,
|
|||
}
|
|||
|
|||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
|||
pub struct CRHGadget<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>, W: Window>
|
|||
where
|
|||
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<C, GG, W> FixedLengthCRHGadget<CRH<C, W>, ConstraintF<C>> for CRHGadget<C, GG, W>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
W: Window,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
type OutputVar = GG;
|
|||
type ParametersVar = CRHParametersVar<C, GG>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(parameters, input))]
|
|||
fn evaluate(
|
|||
parameters: &Self::ParametersVar,
|
|||
input: &[UInt8<ConstraintF<C>>],
|
|||
) -> Result<Self::OutputVar, SynthesisError> {
|
|||
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(UInt8::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<Boolean<_>> = 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<C, GG> AllocVar<Parameters<C>, ConstraintF<C>> for CRHParametersVar<C, GG>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GG: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(_cs, f))]
|
|||
fn new_variable<T: Borrow<Parameters<C>>>(
|
|||
_cs: impl Into<Namespace<ConstraintF<C>>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
_mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let params = f()?.borrow().clone();
|
|||
Ok(CRHParametersVar {
|
|||
params,
|
|||
_group_g: PhantomData,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use crate::crh::{pedersen, pedersen::constraints::*, FixedLengthCRH, FixedLengthCRHGadget};
|
|||
use algebra::{
|
|||
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq as Fr},
|
|||
test_rng,
|
|||
};
|
|||
use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
|
|||
use r1cs_std::ed_on_bls12_381::EdwardsVar;
|
|||
use rand::Rng;
|
|||
|
|||
type TestCRH = pedersen::CRH<JubJub, Window>;
|
|||
type TestCRHGadget = CRHGadget<JubJub, EdwardsVar, Window>;
|
|||
|
|||
#[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_input<R: Rng>(
|
|||
cs: ConstraintSystemRef<Fr>,
|
|||
rng: &mut R,
|
|||
) -> ([u8; 128], Vec<UInt8<Fr>>) {
|
|||
let mut input = [1u8; 128];
|
|||
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::<Fr>::new_ref();
|
|||
|
|||
let (input, input_var) = generate_input(cs.clone(), rng);
|
|||
|
|||
let parameters = TestCRH::setup(rng).unwrap();
|
|||
let primitive_result = TestCRH::evaluate(¶meters, &input).unwrap();
|
|||
|
|||
let parameters_var =
|
|||
CRHParametersVar::new_constant(r1cs_core::ns!(cs, "CRH Parameters"), ¶meters)
|
|||
.unwrap();
|
|||
|
|||
let result_var = TestCRHGadget::evaluate(¶meters_var, &input_var).unwrap();
|
|||
|
|||
let primitive_result = primitive_result;
|
|||
assert_eq!(primitive_result, result_var.value().unwrap());
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
@ -1,152 +0,0 @@ |
|||
use crate::{Error, Vec};
|
|||
use core::{
|
|||
fmt::{Debug, Formatter, Result as FmtResult},
|
|||
marker::PhantomData,
|
|||
};
|
|||
use rand::Rng;
|
|||
#[cfg(feature = "parallel")]
|
|||
use rayon::prelude::*;
|
|||
|
|||
use crate::crh::FixedLengthCRH;
|
|||
use algebra_core::{Field, ProjectiveCurve, ToConstraintField};
|
|||
use ff_fft::cfg_chunks;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
pub trait Window: Clone {
|
|||
const WINDOW_SIZE: usize;
|
|||
const NUM_WINDOWS: usize;
|
|||
}
|
|||
|
|||
#[derive(Clone, Default)]
|
|||
pub struct Parameters<C: ProjectiveCurve> {
|
|||
pub generators: Vec<Vec<C>>,
|
|||
}
|
|||
|
|||
pub struct CRH<C: ProjectiveCurve, W: Window> {
|
|||
group: PhantomData<C>,
|
|||
window: PhantomData<W>,
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve, W: Window> CRH<C, W> {
|
|||
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<C>> {
|
|||
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<R: Rng>(num_powers: usize, rng: &mut R) -> Vec<C> {
|
|||
let mut cur_gen_powers = Vec::with_capacity(num_powers);
|
|||
let mut base = C::rand(rng);
|
|||
for _ in 0..num_powers {
|
|||
cur_gen_powers.push(base);
|
|||
base.double_in_place();
|
|||
}
|
|||
cur_gen_powers
|
|||
}
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve, W: Window> FixedLengthCRH for CRH<C, W> {
|
|||
const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS;
|
|||
type Output = C::Affine;
|
|||
type Parameters = Parameters<C>;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
|||
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: &[u8]) -> Result<Self::Output, Error> {
|
|||
let eval_time = start_timer!(|| "PedersenCRH::Eval");
|
|||
|
|||
if (input.len() * 8) > 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() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
|||
let current_length = input.len();
|
|||
padded_input.extend_from_slice(input);
|
|||
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
|
|||
padded_input.push(0u8);
|
|||
}
|
|||
input = padded_input.as_slice();
|
|||
}
|
|||
|
|||
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!(bits, 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;
|
|||
}
|
|||
}
|
|||
encoded
|
|||
})
|
|||
.sum::<C>();
|
|||
|
|||
end_timer!(eval_time);
|
|||
|
|||
Ok(result.into())
|
|||
}
|
|||
}
|
|||
|
|||
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
|||
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<C: ProjectiveCurve> Debug for Parameters<C> {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|||
write!(f, "Pedersen Hash Parameters {{\n")?;
|
|||
for (i, g) in self.generators.iter().enumerate() {
|
|||
write!(f, "\t Generator {}: {:?}\n", i, g)?;
|
|||
}
|
|||
write!(f, "}}\n")
|
|||
}
|
|||
}
|
|||
|
|||
impl<ConstraintF: Field, C: ProjectiveCurve + ToConstraintField<ConstraintF>>
|
|||
ToConstraintField<ConstraintF> for Parameters<C>
|
|||
{
|
|||
#[inline]
|
|||
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
|
|||
Ok(Vec::new())
|
|||
}
|
|||
}
|
@ -1,59 +0,0 @@ |
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
|||
|
|||
#[macro_use]
|
|||
extern crate bench_utils;
|
|||
|
|||
#[macro_use]
|
|||
extern crate derivative;
|
|||
|
|||
#[macro_use]
|
|||
extern crate alloc;
|
|||
|
|||
#[cfg(not(feature = "std"))]
|
|||
pub(crate) use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
|
|||
|
|||
#[cfg(feature = "std")]
|
|||
pub(crate) use std::{borrow::ToOwned, boxed::Box, vec::Vec};
|
|||
|
|||
pub mod commitment;
|
|||
pub mod crh;
|
|||
pub mod merkle_tree;
|
|||
pub mod nizk;
|
|||
pub mod prf;
|
|||
pub mod signature;
|
|||
|
|||
pub use self::{
|
|||
commitment::CommitmentScheme,
|
|||
crh::FixedLengthCRH,
|
|||
merkle_tree::{MerkleTree, Path},
|
|||
nizk::NIZK,
|
|||
prf::PRF,
|
|||
signature::SignatureScheme,
|
|||
};
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub use self::{
|
|||
commitment::CommitmentGadget, crh::FixedLengthCRHGadget, merkle_tree::constraints::PathVar,
|
|||
nizk::NIZKVerifierGadget, prf::PRFGadget, signature::SigRandomizePkGadget,
|
|||
};
|
|||
|
|||
pub type Error = Box<dyn algebra_core::Error>;
|
|||
|
|||
#[derive(Debug)]
|
|||
pub enum CryptoError {
|
|||
IncorrectInputLength(usize),
|
|||
NotPrimeOrder,
|
|||
}
|
|||
|
|||
impl core::fmt::Display for CryptoError {
|
|||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|||
let msg = match self {
|
|||
CryptoError::IncorrectInputLength(len) => format!("input length is wrong: {}", len),
|
|||
CryptoError::NotPrimeOrder => "element is not prime order".to_owned(),
|
|||
};
|
|||
write!(f, "{}", msg)
|
|||
}
|
|||
}
|
|||
|
|||
impl algebra_core::Error for CryptoError {}
|
@ -1,258 +0,0 @@ |
|||
use algebra_core::Field;
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use crate::{
|
|||
crh::{FixedLengthCRH, FixedLengthCRHGadget},
|
|||
merkle_tree::*,
|
|||
};
|
|||
|
|||
use core::borrow::Borrow;
|
|||
|
|||
pub struct PathVar<P, HGadget, ConstraintF>
|
|||
where
|
|||
P: Config,
|
|||
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
|||
ConstraintF: Field,
|
|||
{
|
|||
path: Vec<(HGadget::OutputVar, HGadget::OutputVar)>,
|
|||
}
|
|||
|
|||
impl<P, CRHGadget, ConstraintF> PathVar<P, CRHGadget, ConstraintF>
|
|||
where
|
|||
P: Config,
|
|||
ConstraintF: Field,
|
|||
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
|||
<CRHGadget::OutputVar as R1CSVar<ConstraintF>>::Value: PartialEq,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(self, parameters, root, leaf))]
|
|||
pub fn check_membership(
|
|||
&self,
|
|||
parameters: &CRHGadget::ParametersVar,
|
|||
root: &CRHGadget::OutputVar,
|
|||
leaf: impl ToBytesGadget<ConstraintF>,
|
|||
) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
|||
assert_eq!(self.path.len(), P::HEIGHT - 1);
|
|||
// Check that the hash of the given leaf matches the leaf hash in the membership
|
|||
// proof.
|
|||
let leaf_bits = leaf.to_bytes()?;
|
|||
let leaf_hash = CRHGadget::evaluate(parameters, &leaf_bits)?;
|
|||
let cs = leaf_hash.cs().or(root.cs());
|
|||
|
|||
// Check if leaf is one of the bottom-most siblings.
|
|||
let leaf_is_left = Boolean::new_witness(r1cs_core::ns!(cs, "leaf_is_left"), || {
|
|||
Ok(leaf_hash.value()?.eq(&self.path[0].0.value()?))
|
|||
})?;
|
|||
|
|||
let mut result =
|
|||
leaf_hash.is_eq(&leaf_is_left.select(&self.path[0].0, &self.path[0].1)?)?;
|
|||
|
|||
// Check levels between leaf level and root.
|
|||
let mut previous_hash = leaf_hash;
|
|||
for &(ref left_hash, ref right_hash) in &self.path {
|
|||
// Check if the previous_hash matches the correct current hash.
|
|||
let previous_is_left =
|
|||
Boolean::new_witness(r1cs_core::ns!(cs, "previous_is_left"), || {
|
|||
Ok(previous_hash.value()?.eq(&left_hash.value()?))
|
|||
})?;
|
|||
|
|||
let ns = r1cs_core::ns!(cs, "enforcing that inner hash is correct");
|
|||
let equality_cmp = previous_is_left.select(left_hash, right_hash)?;
|
|||
result = result.and(&previous_hash.is_eq(&equality_cmp)?)?;
|
|||
drop(ns);
|
|||
|
|||
previous_hash =
|
|||
hash_inner_node::<P::H, CRHGadget, ConstraintF>(parameters, left_hash, right_hash)?;
|
|||
}
|
|||
|
|||
result.and(&root.is_eq(&previous_hash)?)
|
|||
}
|
|||
}
|
|||
|
|||
pub(crate) fn hash_inner_node<H, HG, ConstraintF>(
|
|||
parameters: &HG::ParametersVar,
|
|||
left_child: &HG::OutputVar,
|
|||
right_child: &HG::OutputVar,
|
|||
) -> Result<HG::OutputVar, SynthesisError>
|
|||
where
|
|||
ConstraintF: Field,
|
|||
H: FixedLengthCRH,
|
|||
HG: FixedLengthCRHGadget<H, ConstraintF>,
|
|||
{
|
|||
let left_bytes = left_child.to_bytes()?;
|
|||
let right_bytes = right_child.to_bytes()?;
|
|||
let mut bytes = left_bytes;
|
|||
bytes.extend_from_slice(&right_bytes);
|
|||
|
|||
HG::evaluate(parameters, &bytes)
|
|||
}
|
|||
|
|||
impl<P, HGadget, ConstraintF> AllocVar<Path<P>, ConstraintF> for PathVar<P, HGadget, ConstraintF>
|
|||
where
|
|||
P: Config,
|
|||
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
|||
ConstraintF: Field,
|
|||
{
|
|||
fn new_variable<T: Borrow<Path<P>>>(
|
|||
cs: impl Into<Namespace<ConstraintF>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
f().and_then(|val| {
|
|||
let mut path = Vec::new();
|
|||
for &(ref l, ref r) in val.borrow().path.iter() {
|
|||
let l_hash = HGadget::OutputVar::new_variable(
|
|||
r1cs_core::ns!(cs, "l_child"),
|
|||
|| Ok(l),
|
|||
mode,
|
|||
)?;
|
|||
let r_hash = HGadget::OutputVar::new_variable(
|
|||
r1cs_core::ns!(cs, "r_child"),
|
|||
|| Ok(r),
|
|||
mode,
|
|||
)?;
|
|||
path.push((l_hash, r_hash));
|
|||
}
|
|||
Ok(PathVar { path })
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use crate::{
|
|||
crh::{
|
|||
pedersen::{self, constraints::CRHGadget},
|
|||
FixedLengthCRH, FixedLengthCRHGadget,
|
|||
},
|
|||
merkle_tree::*,
|
|||
};
|
|||
use algebra::ed_on_bls12_381::{EdwardsProjective as JubJub, Fq};
|
|||
use r1cs_core::ConstraintSystem;
|
|||
use rand::SeedableRng;
|
|||
use rand_xorshift::XorShiftRng;
|
|||
|
|||
use super::*;
|
|||
use r1cs_std::ed_on_bls12_381::EdwardsVar;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub(super) struct Window4x256;
|
|||
impl pedersen::Window for Window4x256 {
|
|||
const WINDOW_SIZE: usize = 4;
|
|||
const NUM_WINDOWS: usize = 256;
|
|||
}
|
|||
|
|||
type H = pedersen::CRH<JubJub, Window4x256>;
|
|||
type HG = CRHGadget<JubJub, EdwardsVar, Window4x256>;
|
|||
|
|||
struct JubJubMerkleTreeParams;
|
|||
|
|||
impl Config for JubJubMerkleTreeParams {
|
|||
const HEIGHT: usize = 32;
|
|||
type H = H;
|
|||
}
|
|||
|
|||
type JubJubMerkleTree = MerkleTree<JubJubMerkleTreeParams>;
|
|||
|
|||
fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
|
|||
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
|
|||
|
|||
let crh_parameters = H::setup(&mut rng).unwrap();
|
|||
let tree = JubJubMerkleTree::new(crh_parameters.clone(), leaves).unwrap();
|
|||
let root = tree.root();
|
|||
let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
for (i, leaf) in leaves.iter().enumerate() {
|
|||
let proof = tree.generate_proof(i, &leaf).unwrap();
|
|||
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
|||
|
|||
// Allocate Merkle Tree Root
|
|||
let root = <HG as FixedLengthCRHGadget<H, _>>::OutputVar::new_witness(
|
|||
r1cs_core::ns!(cs, "new_digest"),
|
|||
|| {
|
|||
if use_bad_root {
|
|||
Ok(<H as FixedLengthCRH>::Output::default())
|
|||
} else {
|
|||
Ok(root)
|
|||
}
|
|||
},
|
|||
)
|
|||
.unwrap();
|
|||
|
|||
let constraints_from_digest = cs.num_constraints();
|
|||
println!("constraints from digest: {}", constraints_from_digest);
|
|||
|
|||
// Allocate Parameters for CRH
|
|||
let crh_parameters = <HG as FixedLengthCRHGadget<H, Fq>>::ParametersVar::new_constant(
|
|||
r1cs_core::ns!(cs, "new_parameter"),
|
|||
&crh_parameters,
|
|||
)
|
|||
.unwrap();
|
|||
|
|||
let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
|
|||
println!(
|
|||
"constraints from parameters: {}",
|
|||
constraints_from_parameters
|
|||
);
|
|||
|
|||
// Allocate Leaf
|
|||
let leaf_g = UInt8::constant_vec(leaf);
|
|||
|
|||
let constraints_from_leaf =
|
|||
cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
|
|||
println!("constraints from leaf: {}", constraints_from_leaf);
|
|||
|
|||
// Allocate Merkle Tree Path
|
|||
let cw =
|
|||
PathVar::<_, HG, _>::new_witness(r1cs_core::ns!(cs, "new_witness"), || Ok(&proof))
|
|||
.unwrap();
|
|||
for (i, (l, r)) in cw.path.iter().enumerate() {
|
|||
assert_eq!(l.value().unwrap(), proof.path[i].0);
|
|||
assert_eq!(r.value().unwrap(), proof.path[i].1);
|
|||
}
|
|||
|
|||
let constraints_from_path = cs.num_constraints()
|
|||
- constraints_from_parameters
|
|||
- constraints_from_digest
|
|||
- constraints_from_leaf;
|
|||
println!("constraints from path: {}", constraints_from_path);
|
|||
let leaf_g: &[_] = leaf_g.as_slice();
|
|||
cw.check_membership(&crh_parameters, &root, &leaf_g)
|
|||
.unwrap()
|
|||
.enforce_equal(&Boolean::TRUE)
|
|||
.unwrap();
|
|||
let setup_constraints = constraints_from_leaf
|
|||
+ constraints_from_digest
|
|||
+ constraints_from_parameters
|
|||
+ constraints_from_path;
|
|||
println!(
|
|||
"number of constraints: {}",
|
|||
cs.num_constraints() - setup_constraints
|
|||
);
|
|||
}
|
|||
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn good_root_test() {
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..4u8 {
|
|||
let input = [i; 30];
|
|||
leaves.push(input);
|
|||
}
|
|||
generate_merkle_tree(&leaves, false);
|
|||
}
|
|||
|
|||
#[should_panic]
|
|||
#[test]
|
|||
fn bad_root_test() {
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..4u8 {
|
|||
let input = [i; 30];
|
|||
leaves.push(input);
|
|||
}
|
|||
generate_merkle_tree(&leaves, true);
|
|||
}
|
|||
}
|
@ -1,449 +0,0 @@ |
|||
use crate::{crh::FixedLengthCRH, Vec};
|
|||
use algebra_core::bytes::ToBytes;
|
|||
use core::fmt;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
pub trait Config {
|
|||
const HEIGHT: usize;
|
|||
type H: FixedLengthCRH;
|
|||
}
|
|||
|
|||
/// Stores the hashes of a particular path (in order) from leaf to root.
|
|||
/// Our path `is_left_child()` if the boolean in `path` is true.
|
|||
#[derive(Derivative)]
|
|||
#[derivative(
|
|||
Clone(bound = "P: Config"),
|
|||
Debug(bound = "P: Config, <P::H as FixedLengthCRH>::Output: fmt::Debug")
|
|||
)]
|
|||
pub struct Path<P: Config> {
|
|||
pub(crate) path: Vec<(
|
|||
<P::H as FixedLengthCRH>::Output,
|
|||
<P::H as FixedLengthCRH>::Output,
|
|||
)>,
|
|||
}
|
|||
|
|||
pub type Parameters<P> = <<P as Config>::H as FixedLengthCRH>::Parameters;
|
|||
pub type Digest<P> = <<P as Config>::H as FixedLengthCRH>::Output;
|
|||
|
|||
impl<P: Config> Default for Path<P> {
|
|||
fn default() -> Self {
|
|||
let mut path = Vec::with_capacity(P::HEIGHT as usize);
|
|||
for _i in 1..P::HEIGHT as usize {
|
|||
path.push((
|
|||
<P::H as FixedLengthCRH>::Output::default(),
|
|||
<P::H as FixedLengthCRH>::Output::default(),
|
|||
));
|
|||
}
|
|||
Self { path }
|
|||
}
|
|||
}
|
|||
|
|||
impl<P: Config> Path<P> {
|
|||
pub fn verify<L: ToBytes>(
|
|||
&self,
|
|||
parameters: &<P::H as FixedLengthCRH>::Parameters,
|
|||
root_hash: &<P::H as FixedLengthCRH>::Output,
|
|||
leaf: &L,
|
|||
) -> Result<bool, crate::Error> {
|
|||
if self.path.len() != (P::HEIGHT - 1) as usize {
|
|||
return Ok(false);
|
|||
}
|
|||
// Check that the given leaf matches the leaf in the membership proof.
|
|||
let mut buffer = [0u8; 128];
|
|||
|
|||
if !self.path.is_empty() {
|
|||
let claimed_leaf_hash = hash_leaf::<P::H, L>(parameters, leaf, &mut buffer)?;
|
|||
|
|||
// Check if leaf is one of the bottom-most siblings.
|
|||
if claimed_leaf_hash != self.path[0].0 && claimed_leaf_hash != self.path[0].1 {
|
|||
return Ok(false);
|
|||
};
|
|||
|
|||
let mut prev = claimed_leaf_hash;
|
|||
// Check levels between leaf level and root.
|
|||
for &(ref hash, ref sibling_hash) in &self.path {
|
|||
// Check if the previous hash matches the correct current hash.
|
|||
if &prev != hash && &prev != sibling_hash {
|
|||
return Ok(false);
|
|||
};
|
|||
prev = hash_inner_node::<P::H>(parameters, hash, sibling_hash, &mut buffer)?;
|
|||
}
|
|||
|
|||
if root_hash != &prev {
|
|||
return Ok(false);
|
|||
}
|
|||
Ok(true)
|
|||
} else {
|
|||
Ok(false)
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
pub struct MerkleTree<P: Config> {
|
|||
tree: Vec<<P::H as FixedLengthCRH>::Output>,
|
|||
padding_tree: Vec<(
|
|||
<P::H as FixedLengthCRH>::Output,
|
|||
<P::H as FixedLengthCRH>::Output,
|
|||
)>,
|
|||
parameters: <P::H as FixedLengthCRH>::Parameters,
|
|||
root: Option<<P::H as FixedLengthCRH>::Output>,
|
|||
}
|
|||
|
|||
impl<P: Config> MerkleTree<P> {
|
|||
pub const HEIGHT: u8 = P::HEIGHT as u8;
|
|||
|
|||
pub fn blank(parameters: <P::H as FixedLengthCRH>::Parameters) -> Self {
|
|||
MerkleTree {
|
|||
tree: Vec::new(),
|
|||
padding_tree: Vec::new(),
|
|||
root: None,
|
|||
parameters,
|
|||
}
|
|||
}
|
|||
|
|||
pub fn new<L: ToBytes>(
|
|||
parameters: <P::H as FixedLengthCRH>::Parameters,
|
|||
leaves: &[L],
|
|||
) -> Result<Self, crate::Error> {
|
|||
let new_time = start_timer!(|| "MerkleTree::New");
|
|||
|
|||
let last_level_size = leaves.len().next_power_of_two();
|
|||
let tree_size = 2 * last_level_size - 1;
|
|||
let tree_height = tree_height(tree_size);
|
|||
assert!(tree_height as u8 <= Self::HEIGHT);
|
|||
|
|||
// Initialize the merkle tree.
|
|||
let mut tree = Vec::with_capacity(tree_size);
|
|||
let empty_hash = hash_empty::<P::H>(¶meters)?;
|
|||
for _ in 0..tree_size {
|
|||
tree.push(empty_hash.clone());
|
|||
}
|
|||
|
|||
// Compute the starting indices for each level of the tree.
|
|||
let mut index = 0;
|
|||
let mut level_indices = Vec::with_capacity(tree_height);
|
|||
for _ in 0..tree_height {
|
|||
level_indices.push(index);
|
|||
index = left_child(index);
|
|||
}
|
|||
|
|||
// Compute and store the hash values for each leaf.
|
|||
let last_level_index = level_indices.pop().unwrap_or(0);
|
|||
let mut buffer = [0u8; 128];
|
|||
for (i, leaf) in leaves.iter().enumerate() {
|
|||
tree[last_level_index + i] = hash_leaf::<P::H, _>(¶meters, leaf, &mut buffer)?;
|
|||
}
|
|||
|
|||
// Compute the hash values for every node in the tree.
|
|||
let mut upper_bound = last_level_index;
|
|||
let mut buffer = [0u8; 128];
|
|||
level_indices.reverse();
|
|||
for &start_index in &level_indices {
|
|||
// Iterate over the current level.
|
|||
for current_index in start_index..upper_bound {
|
|||
let left_index = left_child(current_index);
|
|||
let right_index = right_child(current_index);
|
|||
|
|||
// Compute Hash(left || right).
|
|||
tree[current_index] = hash_inner_node::<P::H>(
|
|||
¶meters,
|
|||
&tree[left_index],
|
|||
&tree[right_index],
|
|||
&mut buffer,
|
|||
)?;
|
|||
}
|
|||
upper_bound = start_index;
|
|||
}
|
|||
// Finished computing actual tree.
|
|||
// Now, we compute the dummy nodes until we hit our HEIGHT goal.
|
|||
let mut cur_height = tree_height;
|
|||
let mut padding_tree = Vec::new();
|
|||
let mut cur_hash = tree[0].clone();
|
|||
let root_hash = if cur_height < Self::HEIGHT as usize {
|
|||
while cur_height < (Self::HEIGHT - 1) as usize {
|
|||
cur_hash =
|
|||
hash_inner_node::<P::H>(¶meters, &cur_hash, &empty_hash, &mut buffer)?;
|
|||
padding_tree.push((cur_hash.clone(), empty_hash.clone()));
|
|||
cur_height += 1;
|
|||
}
|
|||
hash_inner_node::<P::H>(¶meters, &cur_hash, &empty_hash, &mut buffer)?
|
|||
} else {
|
|||
cur_hash
|
|||
};
|
|||
end_timer!(new_time);
|
|||
|
|||
Ok(MerkleTree {
|
|||
tree,
|
|||
padding_tree,
|
|||
parameters,
|
|||
root: Some(root_hash),
|
|||
})
|
|||
}
|
|||
|
|||
#[inline]
|
|||
pub fn root(&self) -> <P::H as FixedLengthCRH>::Output {
|
|||
self.root.clone().unwrap()
|
|||
}
|
|||
|
|||
pub fn generate_proof<L: ToBytes>(
|
|||
&self,
|
|||
index: usize,
|
|||
leaf: &L,
|
|||
) -> Result<Path<P>, crate::Error> {
|
|||
let prove_time = start_timer!(|| "MerkleTree::GenProof");
|
|||
let mut path = Vec::new();
|
|||
|
|||
let mut buffer = [0u8; 128];
|
|||
let leaf_hash = hash_leaf::<P::H, _>(&self.parameters, leaf, &mut buffer)?;
|
|||
let tree_height = tree_height(self.tree.len());
|
|||
let tree_index = convert_index_to_last_level(index, tree_height);
|
|||
let empty_hash = hash_empty::<P::H>(&self.parameters)?;
|
|||
|
|||
// Check that the given index corresponds to the correct leaf.
|
|||
if leaf_hash != self.tree[tree_index] {
|
|||
return Err(Error::IncorrectLeafIndex(tree_index).into());
|
|||
}
|
|||
|
|||
// Iterate from the leaf up to the root, storing all intermediate hash values.
|
|||
let mut current_node = tree_index;
|
|||
while !is_root(current_node) {
|
|||
let sibling_node = sibling(current_node).unwrap();
|
|||
let (curr_hash, sibling_hash) = (
|
|||
self.tree[current_node].clone(),
|
|||
self.tree[sibling_node].clone(),
|
|||
);
|
|||
if is_left_child(current_node) {
|
|||
path.push((curr_hash, sibling_hash));
|
|||
} else {
|
|||
path.push((sibling_hash, curr_hash));
|
|||
}
|
|||
current_node = parent(current_node).unwrap();
|
|||
}
|
|||
|
|||
// Store the root node. Set boolean as true for consistency with digest
|
|||
// location.
|
|||
assert!(path.len() < Self::HEIGHT as usize);
|
|||
if path.len() != (Self::HEIGHT - 1) as usize {
|
|||
path.push((self.tree[0].clone(), empty_hash));
|
|||
for &(ref hash, ref sibling_hash) in &self.padding_tree {
|
|||
path.push((hash.clone(), sibling_hash.clone()));
|
|||
}
|
|||
}
|
|||
end_timer!(prove_time);
|
|||
if path.len() != (Self::HEIGHT - 1) as usize {
|
|||
return Err(Error::IncorrectPathLength(path.len()).into());
|
|||
} else {
|
|||
Ok(Path { path })
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Debug)]
|
|||
pub enum Error {
|
|||
IncorrectLeafIndex(usize),
|
|||
IncorrectPathLength(usize),
|
|||
}
|
|||
|
|||
impl core::fmt::Display for Error {
|
|||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|||
let msg = match self {
|
|||
Error::IncorrectLeafIndex(index) => format!("incorrect leaf index: {}", index),
|
|||
Error::IncorrectPathLength(len) => format!("incorrect path length: {}", len),
|
|||
};
|
|||
write!(f, "{}", msg)
|
|||
}
|
|||
}
|
|||
|
|||
impl algebra_core::Error for Error {}
|
|||
|
|||
/// Returns the height of the tree, given the size of the tree.
|
|||
#[inline]
|
|||
fn tree_height(tree_size: usize) -> usize {
|
|||
if tree_size == 1 {
|
|||
return 1;
|
|||
}
|
|||
|
|||
algebra_core::log2(tree_size) as usize
|
|||
}
|
|||
|
|||
/// Returns true iff the index represents the root.
|
|||
#[inline]
|
|||
fn is_root(index: usize) -> bool {
|
|||
index == 0
|
|||
}
|
|||
|
|||
/// Returns the index of the left child, given an index.
|
|||
#[inline]
|
|||
fn left_child(index: usize) -> usize {
|
|||
2 * index + 1
|
|||
}
|
|||
|
|||
/// Returns the index of the right child, given an index.
|
|||
#[inline]
|
|||
fn right_child(index: usize) -> usize {
|
|||
2 * index + 2
|
|||
}
|
|||
|
|||
/// Returns the index of the sibling, given an index.
|
|||
#[inline]
|
|||
fn sibling(index: usize) -> Option<usize> {
|
|||
if index == 0 {
|
|||
None
|
|||
} else if is_left_child(index) {
|
|||
Some(index + 1)
|
|||
} else {
|
|||
Some(index - 1)
|
|||
}
|
|||
}
|
|||
|
|||
/// Returns true iff the given index represents a left child.
|
|||
#[inline]
|
|||
fn is_left_child(index: usize) -> bool {
|
|||
index % 2 == 1
|
|||
}
|
|||
|
|||
/// Returns the index of the parent, given an index.
|
|||
#[inline]
|
|||
fn parent(index: usize) -> Option<usize> {
|
|||
if index > 0 {
|
|||
Some((index - 1) >> 1)
|
|||
} else {
|
|||
None
|
|||
}
|
|||
}
|
|||
|
|||
#[inline]
|
|||
fn convert_index_to_last_level(index: usize, tree_height: usize) -> usize {
|
|||
index + (1 << (tree_height - 1)) - 1
|
|||
}
|
|||
|
|||
/// Returns the output hash, given a left and right hash value.
|
|||
pub(crate) fn hash_inner_node<H: FixedLengthCRH>(
|
|||
parameters: &H::Parameters,
|
|||
left: &H::Output,
|
|||
right: &H::Output,
|
|||
buffer: &mut [u8],
|
|||
) -> Result<H::Output, crate::Error> {
|
|||
let bytes = algebra_core::to_bytes![left]?
|
|||
.into_iter()
|
|||
.chain(algebra_core::to_bytes![right]?);
|
|||
buffer.iter_mut().zip(bytes).for_each(|(b, l_b)| *b = l_b);
|
|||
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
|
|||
}
|
|||
|
|||
/// Returns the hash of a leaf.
|
|||
pub(crate) fn hash_leaf<H: FixedLengthCRH, L: ToBytes>(
|
|||
parameters: &H::Parameters,
|
|||
leaf: &L,
|
|||
buffer: &mut [u8],
|
|||
) -> Result<H::Output, crate::Error> {
|
|||
buffer
|
|||
.iter_mut()
|
|||
.zip(&algebra_core::to_bytes![leaf]?)
|
|||
.for_each(|(b, l_b)| *b = *l_b);
|
|||
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
|
|||
}
|
|||
|
|||
pub(crate) fn hash_empty<H: FixedLengthCRH>(
|
|||
parameters: &H::Parameters,
|
|||
) -> Result<H::Output, crate::Error> {
|
|||
let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8];
|
|||
H::evaluate(parameters, &empty_buffer)
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use crate::{
|
|||
crh::{pedersen, *},
|
|||
merkle_tree::*,
|
|||
};
|
|||
use algebra::{ed_on_bls12_381::EdwardsProjective as JubJub, Zero};
|
|||
use rand::SeedableRng;
|
|||
use rand_xorshift::XorShiftRng;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub(super) struct Window4x256;
|
|||
impl pedersen::Window for Window4x256 {
|
|||
const WINDOW_SIZE: usize = 4;
|
|||
const NUM_WINDOWS: usize = 256;
|
|||
}
|
|||
|
|||
type H = pedersen::CRH<JubJub, Window4x256>;
|
|||
|
|||
struct JubJubMerkleTreeParams;
|
|||
|
|||
impl Config for JubJubMerkleTreeParams {
|
|||
const HEIGHT: usize = 8;
|
|||
type H = H;
|
|||
}
|
|||
type JubJubMerkleTree = MerkleTree<JubJubMerkleTreeParams>;
|
|||
|
|||
fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
|
|||
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
|
|||
|
|||
let crh_parameters = H::setup(&mut rng).unwrap();
|
|||
let tree = JubJubMerkleTree::new(crh_parameters.clone(), &leaves).unwrap();
|
|||
let root = tree.root();
|
|||
for (i, leaf) in leaves.iter().enumerate() {
|
|||
let proof = tree.generate_proof(i, &leaf).unwrap();
|
|||
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
|||
}
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn good_root_test() {
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..4u8 {
|
|||
leaves.push([i, i, i, i, i, i, i, i]);
|
|||
}
|
|||
generate_merkle_tree(&leaves);
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..100u8 {
|
|||
leaves.push([i, i, i, i, i, i, i, i]);
|
|||
}
|
|||
generate_merkle_tree(&leaves);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn no_dummy_nodes_test() {
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..(1u8 << JubJubMerkleTree::HEIGHT - 1) {
|
|||
leaves.push([i, i, i, i, i, i, i, i]);
|
|||
}
|
|||
generate_merkle_tree(&leaves);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn single_leaf_test() {
|
|||
generate_merkle_tree(&[[1u8; 8]]);
|
|||
}
|
|||
|
|||
fn bad_merkle_tree_verify<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
|
|||
let mut rng = XorShiftRng::seed_from_u64(13423423u64);
|
|||
|
|||
let crh_parameters = H::setup(&mut rng).unwrap();
|
|||
let tree = JubJubMerkleTree::new(crh_parameters.clone(), &leaves).unwrap();
|
|||
let root = JubJub::zero().into();
|
|||
for (i, leaf) in leaves.iter().enumerate() {
|
|||
let proof = tree.generate_proof(i, &leaf).unwrap();
|
|||
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
|||
}
|
|||
}
|
|||
|
|||
#[should_panic]
|
|||
#[test]
|
|||
fn bad_root_test() {
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..4u8 {
|
|||
leaves.push([i, i, i, i, i, i, i, i]);
|
|||
}
|
|||
generate_merkle_tree(&leaves);
|
|||
let mut leaves = Vec::new();
|
|||
for i in 0..100u8 {
|
|||
leaves.push([i, i, i, i, i, i, i, i]);
|
|||
}
|
|||
bad_merkle_tree_verify(&leaves);
|
|||
}
|
|||
}
|
@ -1,51 +0,0 @@ |
|||
use algebra_core::Field;
|
|||
use core::borrow::Borrow;
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use crate::nizk::NIZK;
|
|||
|
|||
pub trait NIZKVerifierGadget<N: NIZK, ConstraintF: Field> {
|
|||
type PreparedVerificationKeyVar;
|
|||
type VerificationKeyVar: AllocVar<N::VerificationParameters, ConstraintF>
|
|||
+ ToBytesGadget<ConstraintF>;
|
|||
type ProofVar: AllocVar<N::Proof, ConstraintF>;
|
|||
|
|||
/// Optionally allocates `N::Proof` in `cs` without performing
|
|||
/// subgroup checks.
|
|||
///
|
|||
/// The default implementation doesn't omit these checks.
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_proof_unchecked<T: Borrow<N::Proof>>(
|
|||
cs: impl Into<Namespace<ConstraintF>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self::ProofVar, SynthesisError> {
|
|||
Self::ProofVar::new_variable(cs, f, mode)
|
|||
}
|
|||
|
|||
/// Optionally allocates `N::VerificationParameters` in `cs`
|
|||
/// without performing subgroup checks.
|
|||
///
|
|||
/// The default implementation doesn't omit these checks.
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_verification_key_unchecked<T: Borrow<N::VerificationParameters>>(
|
|||
cs: impl Into<Namespace<ConstraintF>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self::VerificationKeyVar, SynthesisError> {
|
|||
Self::VerificationKeyVar::new_variable(cs, f, mode)
|
|||
}
|
|||
|
|||
fn verify<'a, T: 'a + ToBitsGadget<ConstraintF> + ?Sized>(
|
|||
verification_key: &Self::VerificationKeyVar,
|
|||
input: impl IntoIterator<Item = &'a T>,
|
|||
proof: &Self::ProofVar,
|
|||
) -> Result<Boolean<ConstraintF>, SynthesisError>;
|
|||
|
|||
fn verify_prepared<'a, T: 'a + ToBitsGadget<ConstraintF> + ?Sized>(
|
|||
prepared_verification_key: &Self::PreparedVerificationKeyVar,
|
|||
input: impl IntoIterator<Item = &'a T>,
|
|||
proof: &Self::ProofVar,
|
|||
) -> Result<Boolean<ConstraintF>, SynthesisError>;
|
|||
}
|
@ -1,797 +0,0 @@ |
|||
use crate::{
|
|||
nizk::{gm17::Gm17, NIZKVerifierGadget},
|
|||
Vec,
|
|||
};
|
|||
use algebra_core::{AffineCurve, PairingEngine, ToConstraintField};
|
|||
use r1cs_core::{ConstraintSynthesizer, Namespace, SynthesisError};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use core::{borrow::Borrow, marker::PhantomData};
|
|||
use gm17::{PreparedVerifyingKey, Proof, VerifyingKey};
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = "P::G1Var: Clone, P::G2Var: Clone"))]
|
|||
pub struct ProofVar<E: PairingEngine, P: PairingVar<E>> {
|
|||
pub a: P::G1Var,
|
|||
pub b: P::G2Var,
|
|||
pub c: P::G1Var,
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(
|
|||
Clone(bound = "P::G1Var: Clone, P::GTVar: Clone, P::G1PreparedVar: Clone, \
|
|||
P::G2PreparedVar: Clone, ")
|
|||
)]
|
|||
pub struct VerifyingKeyVar<E: PairingEngine, P: PairingVar<E>> {
|
|||
pub h_g2: P::G2Var,
|
|||
pub g_alpha_g1: P::G1Var,
|
|||
pub h_beta_g2: P::G2Var,
|
|||
pub g_gamma_g1: P::G1Var,
|
|||
pub h_gamma_g2: P::G2Var,
|
|||
pub query: Vec<P::G1Var>,
|
|||
}
|
|||
|
|||
impl<E: PairingEngine, P: PairingVar<E>> VerifyingKeyVar<E, P> {
|
|||
#[tracing::instrument(target = "r1cs", skip(self))]
|
|||
pub fn prepare(&self) -> Result<PreparedVerifyingKeyVar<E, P>, SynthesisError> {
|
|||
let g_alpha_pc = P::prepare_g1(&self.g_alpha_g1)?;
|
|||
let h_beta_pc = P::prepare_g2(&self.h_beta_g2)?;
|
|||
let g_gamma_pc = P::prepare_g1(&self.g_gamma_g1)?;
|
|||
let h_gamma_pc = P::prepare_g2(&self.h_gamma_g2)?;
|
|||
let h_pc = P::prepare_g2(&self.h_g2)?;
|
|||
Ok(PreparedVerifyingKeyVar {
|
|||
g_alpha: self.g_alpha_g1.clone(),
|
|||
h_beta: self.h_beta_g2.clone(),
|
|||
g_alpha_pc,
|
|||
h_beta_pc,
|
|||
g_gamma_pc,
|
|||
h_gamma_pc,
|
|||
h_pc,
|
|||
query: self.query.clone(),
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(
|
|||
Clone(bound = "P::G1Var: Clone, P::GTVar: Clone, P::G1PreparedVar: Clone, \
|
|||
P::G2PreparedVar: Clone, ")
|
|||
)]
|
|||
pub struct PreparedVerifyingKeyVar<E: PairingEngine, P: PairingVar<E>> {
|
|||
pub g_alpha: P::G1Var,
|
|||
pub h_beta: P::G2Var,
|
|||
pub g_alpha_pc: P::G1PreparedVar,
|
|||
pub h_beta_pc: P::G2PreparedVar,
|
|||
pub g_gamma_pc: P::G1PreparedVar,
|
|||
pub h_gamma_pc: P::G2PreparedVar,
|
|||
pub h_pc: P::G2PreparedVar,
|
|||
pub query: Vec<P::G1Var>,
|
|||
}
|
|||
|
|||
pub struct Gm17VerifierGadget<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
_pairing_engine: PhantomData<E>,
|
|||
_pairing_gadget: PhantomData<P>,
|
|||
}
|
|||
|
|||
impl<E, P, C, V> NIZKVerifierGadget<Gm17<E, C, V>, E::Fq> for Gm17VerifierGadget<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
C: ConstraintSynthesizer<E::Fr>,
|
|||
V: ToConstraintField<E::Fr>,
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
type PreparedVerificationKeyVar = PreparedVerifyingKeyVar<E, P>;
|
|||
type VerificationKeyVar = VerifyingKeyVar<E, P>;
|
|||
type ProofVar = ProofVar<E, P>;
|
|||
|
|||
/// Allocates `N::Proof` in `cs` without performing
|
|||
/// subgroup checks.
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_proof_unchecked<T: Borrow<Proof<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self::ProofVar, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
f().and_then(|proof| {
|
|||
let proof = proof.borrow();
|
|||
let a = CurveVar::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "Proof.a"),
|
|||
|| Ok(proof.a.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let b = CurveVar::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "Proof.b"),
|
|||
|| Ok(proof.b.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let c = CurveVar::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "Proof.c"),
|
|||
|| Ok(proof.c.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
Ok(ProofVar { a, b, c })
|
|||
})
|
|||
}
|
|||
|
|||
/// Allocates `N::Proof` in `cs` without performing
|
|||
/// subgroup checks.
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_verification_key_unchecked<T: Borrow<VerifyingKey<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self::VerificationKeyVar, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
f().and_then(|vk| {
|
|||
let vk = vk.borrow();
|
|||
let g_alpha_g1 = P::G1Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "g_alpha"),
|
|||
|| Ok(vk.g_alpha_g1.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let h_g2 = P::G2Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "h"),
|
|||
|| Ok(vk.h_g2.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let h_beta_g2 = P::G2Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "h_beta"),
|
|||
|| Ok(vk.h_beta_g2.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let g_gamma_g1 = P::G1Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "g_gamma"),
|
|||
|| Ok(vk.g_gamma_g1.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let h_gamma_g2 = P::G2Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "h_gamma"),
|
|||
|| Ok(vk.h_gamma_g2.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
|
|||
let query = vk
|
|||
.query
|
|||
.iter()
|
|||
.map(|g| {
|
|||
P::G1Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "g"),
|
|||
|| Ok(g.into_projective()),
|
|||
mode,
|
|||
)
|
|||
})
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
|||
Ok(VerifyingKeyVar {
|
|||
g_alpha_g1,
|
|||
h_g2,
|
|||
h_beta_g2,
|
|||
g_gamma_g1,
|
|||
h_gamma_g2,
|
|||
query,
|
|||
})
|
|||
})
|
|||
}
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(vk, input, proof))]
|
|||
fn verify<'a, T: 'a + ToBitsGadget<E::Fq> + ?Sized>(
|
|||
vk: &Self::VerificationKeyVar,
|
|||
input: impl IntoIterator<Item = &'a T>,
|
|||
proof: &Self::ProofVar,
|
|||
) -> Result<Boolean<E::Fq>, SynthesisError> {
|
|||
let pvk = vk.prepare()?;
|
|||
<Self as NIZKVerifierGadget<Gm17<E, C, V>, E::Fq>>::verify_prepared(&pvk, input, proof)
|
|||
}
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(pvk, input, proof))]
|
|||
fn verify_prepared<'a, T: 'a + ToBitsGadget<E::Fq> + ?Sized>(
|
|||
pvk: &Self::PreparedVerificationKeyVar,
|
|||
input: impl IntoIterator<Item = &'a T>,
|
|||
proof: &Self::ProofVar,
|
|||
) -> Result<Boolean<E::Fq>, SynthesisError> {
|
|||
let pvk = pvk.clone();
|
|||
// e(A*G^{alpha}, B*H^{beta}) = e(G^{alpha}, H^{beta}) * e(G^{psi}, H^{gamma}) *
|
|||
// e(C, H) where psi = \sum_{i=0}^l input_i pvk.query[i]
|
|||
let g_psi = {
|
|||
let mut g_psi = pvk.query[0].clone();
|
|||
let mut input_len = 1;
|
|||
let mut input = input.into_iter();
|
|||
for (input, b) in input.by_ref().zip(pvk.query.iter().skip(1)) {
|
|||
let input_bits = input.to_bits_le()?;
|
|||
g_psi += b.scalar_mul_le(input_bits.iter())?;
|
|||
input_len += 1;
|
|||
}
|
|||
// Check that the input and the query in the verification are of the
|
|||
// same length.
|
|||
assert!(input_len == pvk.query.len() && input.next().is_none());
|
|||
g_psi
|
|||
};
|
|||
|
|||
let mut test1_a_g_alpha = proof.a.clone();
|
|||
test1_a_g_alpha += pvk.g_alpha.clone();
|
|||
let mut test1_b_h_beta = proof.b.clone();
|
|||
test1_b_h_beta += pvk.h_beta.clone();
|
|||
|
|||
let test1_exp = {
|
|||
test1_a_g_alpha = test1_a_g_alpha.negate()?;
|
|||
let test1_a_g_alpha_prep = P::prepare_g1(&test1_a_g_alpha)?;
|
|||
let test1_b_h_beta_prep = P::prepare_g2(&test1_b_h_beta)?;
|
|||
|
|||
let g_psi_prep = P::prepare_g1(&g_psi)?;
|
|||
|
|||
let c_prep = P::prepare_g1(&proof.c)?;
|
|||
|
|||
P::miller_loop(
|
|||
&[
|
|||
test1_a_g_alpha_prep,
|
|||
g_psi_prep,
|
|||
c_prep,
|
|||
pvk.g_alpha_pc.clone(),
|
|||
],
|
|||
&[
|
|||
test1_b_h_beta_prep,
|
|||
pvk.h_gamma_pc.clone(),
|
|||
pvk.h_pc.clone(),
|
|||
pvk.h_beta_pc.clone(),
|
|||
],
|
|||
)?
|
|||
};
|
|||
|
|||
let test1 = P::final_exponentiation(&test1_exp).unwrap();
|
|||
|
|||
// e(A, H^{gamma}) = e(G^{gamma}, B)
|
|||
let test2_exp = {
|
|||
let a_prep = P::prepare_g1(&proof.a)?;
|
|||
// pvk.h_gamma_pc
|
|||
//&pvk.g_gamma_pc
|
|||
let proof_b = proof.b.negate()?;
|
|||
let b_prep = P::prepare_g2(&proof_b)?;
|
|||
P::miller_loop(&[a_prep, pvk.g_gamma_pc.clone()], &[pvk.h_gamma_pc, b_prep])?
|
|||
};
|
|||
let test2 = P::final_exponentiation(&test2_exp)?;
|
|||
|
|||
let one = P::GTVar::one();
|
|||
test1.is_eq(&one)?.and(&test2.is_eq(&one)?)
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> AllocVar<PreparedVerifyingKey<E>, E::Fq> for PreparedVerifyingKeyVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
P: PairingVar<E>,
|
|||
P::G1PreparedVar: AllocVar<E::G1Prepared, E::Fq>,
|
|||
P::G2PreparedVar: AllocVar<E::G2Prepared, E::Fq>,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<PreparedVerifyingKey<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
|
|||
f().and_then(|pvk| {
|
|||
let pvk = pvk.borrow();
|
|||
let g_alpha =
|
|||
P::G1Var::new_variable(r1cs_core::ns!(cs, "g_alpha"), || Ok(pvk.g_alpha), mode)?;
|
|||
let h_beta =
|
|||
P::G2Var::new_variable(r1cs_core::ns!(cs, "h_beta"), || Ok(pvk.h_beta), mode)?;
|
|||
let g_alpha_pc = P::G1PreparedVar::new_variable(
|
|||
r1cs_core::ns!(cs, "g_alpha_pc"),
|
|||
|| Ok(pvk.g_alpha.into()),
|
|||
mode,
|
|||
)?;
|
|||
let h_beta_pc = P::G2PreparedVar::new_variable(
|
|||
r1cs_core::ns!(cs, "h_beta_pc"),
|
|||
|| Ok(pvk.h_beta.into()),
|
|||
mode,
|
|||
)?;
|
|||
let g_gamma_pc = P::G1PreparedVar::new_variable(
|
|||
r1cs_core::ns!(cs, "g_gamma_pc"),
|
|||
|| Ok(&pvk.g_gamma_pc),
|
|||
mode,
|
|||
)?;
|
|||
let h_gamma_pc = P::G2PreparedVar::new_variable(
|
|||
r1cs_core::ns!(cs, "h_gamma_pc"),
|
|||
|| Ok(&pvk.h_gamma_pc),
|
|||
mode,
|
|||
)?;
|
|||
let h_pc =
|
|||
P::G2PreparedVar::new_variable(r1cs_core::ns!(cs, "h_pc"), || Ok(&pvk.h_pc), mode)?;
|
|||
let query =
|
|||
Vec::new_variable(r1cs_core::ns!(cs, "query"), || Ok(pvk.query.clone()), mode)?;
|
|||
|
|||
Ok(Self {
|
|||
g_alpha,
|
|||
h_beta,
|
|||
g_alpha_pc,
|
|||
h_beta_pc,
|
|||
g_gamma_pc,
|
|||
h_gamma_pc,
|
|||
h_pc,
|
|||
query,
|
|||
})
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> AllocVar<VerifyingKey<E>, E::Fq> for VerifyingKeyVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<VerifyingKey<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
|
|||
f().and_then(|vk| {
|
|||
let vk = vk.borrow();
|
|||
let g_alpha_g1 =
|
|||
P::G1Var::new_variable(r1cs_core::ns!(cs, "g_alpha"), || Ok(vk.g_alpha_g1), mode)?;
|
|||
let h_g2 = P::G2Var::new_variable(r1cs_core::ns!(cs, "h"), || Ok(vk.h_g2), mode)?;
|
|||
let h_beta_g2 =
|
|||
P::G2Var::new_variable(r1cs_core::ns!(cs, "h_beta"), || Ok(vk.h_beta_g2), mode)?;
|
|||
let g_gamma_g1 =
|
|||
P::G1Var::new_variable(r1cs_core::ns!(cs, "g_gamma"), || Ok(&vk.g_gamma_g1), mode)?;
|
|||
let h_gamma_g2 =
|
|||
P::G2Var::new_variable(r1cs_core::ns!(cs, "h_gamma"), || Ok(&vk.h_gamma_g2), mode)?;
|
|||
let query =
|
|||
Vec::new_variable(r1cs_core::ns!(cs, "query"), || Ok(vk.query.clone()), mode)?;
|
|||
Ok(Self {
|
|||
h_g2,
|
|||
g_alpha_g1,
|
|||
h_beta_g2,
|
|||
g_gamma_g1,
|
|||
h_gamma_g2,
|
|||
query,
|
|||
})
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> AllocVar<Proof<E>, E::Fq> for ProofVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[inline]
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<Proof<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
|
|||
f().and_then(|proof| {
|
|||
let Proof { a, b, c } = proof.borrow().clone();
|
|||
let a = P::G1Var::new_variable(cs.clone(), || Ok(a), mode)?;
|
|||
let b = P::G2Var::new_variable(cs.clone(), || Ok(b), mode)?;
|
|||
let c = P::G1Var::new_variable(r1cs_core::ns!(cs, "c"), || Ok(c), mode)?;
|
|||
Ok(Self { a, b, c })
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> ToBytesGadget<E::Fq> for VerifyingKeyVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[inline]
|
|||
fn to_bytes(&self) -> Result<Vec<UInt8<E::Fq>>, SynthesisError> {
|
|||
let mut bytes = Vec::new();
|
|||
bytes.extend_from_slice(&self.h_g2.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.g_alpha_g1.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.h_beta_g2.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.g_gamma_g1.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.h_gamma_g2.to_bytes()?);
|
|||
for q in &self.query {
|
|||
bytes.extend_from_slice(&q.to_bytes()?);
|
|||
}
|
|||
Ok(bytes)
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use gm17::*;
|
|||
use r1cs_core::{
|
|||
lc, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
|
|||
};
|
|||
|
|||
use super::*;
|
|||
use algebra::{
|
|||
bls12_377::{Bls12_377, Fq, Fr},
|
|||
test_rng, BitIteratorLE, Field, PrimeField,
|
|||
};
|
|||
use r1cs_std::{bls12_377::PairingVar as Bls12_377PairingVar, boolean::Boolean, Assignment};
|
|||
use rand::Rng;
|
|||
|
|||
type TestProofSystem = Gm17<Bls12_377, Bench<Fr>, Fr>;
|
|||
type TestVerifierGadget = Gm17VerifierGadget<Bls12_377, Bls12_377PairingVar>;
|
|||
type TestProofVar = ProofVar<Bls12_377, Bls12_377PairingVar>;
|
|||
type TestVkVar = VerifyingKeyVar<Bls12_377, Bls12_377PairingVar>;
|
|||
|
|||
struct Bench<F: Field> {
|
|||
inputs: Vec<Option<F>>,
|
|||
num_constraints: usize,
|
|||
}
|
|||
|
|||
impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
|
|||
fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
|
|||
assert!(self.inputs.len() >= 2);
|
|||
assert!(self.num_constraints >= self.inputs.len());
|
|||
|
|||
let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
|
|||
for input in self.inputs {
|
|||
let input_var = cs.new_input_variable(|| input.get())?;
|
|||
variables.push((input, input_var));
|
|||
}
|
|||
|
|||
for i in 0..self.num_constraints {
|
|||
let new_entry = {
|
|||
let (input_1_val, input_1_var) = variables[i];
|
|||
let (input_2_val, input_2_var) = variables[i + 1];
|
|||
let result_val = input_1_val
|
|||
.and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
|
|||
let result_var = cs.new_witness_variable(|| {
|
|||
result_val.ok_or(SynthesisError::AssignmentMissing)
|
|||
})?;
|
|||
cs.enforce_constraint(
|
|||
lc!() + input_1_var,
|
|||
lc!() + input_2_var,
|
|||
lc!() + result_var,
|
|||
)
|
|||
.unwrap();
|
|||
(result_val, result_var)
|
|||
};
|
|||
variables.push(new_entry);
|
|||
}
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn gm17_verifier_test() {
|
|||
let num_inputs = 100;
|
|||
let num_constraints = num_inputs;
|
|||
let rng = &mut test_rng();
|
|||
let mut inputs: Vec<Option<Fr>> = Vec::with_capacity(num_inputs);
|
|||
for _ in 0..num_inputs {
|
|||
inputs.push(Some(rng.gen()));
|
|||
}
|
|||
let params = {
|
|||
let c = Bench::<Fr> {
|
|||
inputs: vec![None; num_inputs],
|
|||
num_constraints,
|
|||
};
|
|||
|
|||
generate_random_parameters(c, rng).unwrap()
|
|||
};
|
|||
|
|||
{
|
|||
let proof = {
|
|||
// Create an instance of our circuit (with the
|
|||
// witness)
|
|||
let c = Bench {
|
|||
inputs: inputs.clone(),
|
|||
num_constraints,
|
|||
};
|
|||
// Create a gm17 proof with our parameters.
|
|||
create_random_proof(c, ¶ms, rng).unwrap()
|
|||
};
|
|||
|
|||
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
|
|||
let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
|
|||
let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
|
|||
let mut input_gadgets = Vec::new();
|
|||
|
|||
{
|
|||
for input in inputs.into_iter() {
|
|||
let input_bits: Vec<_> = BitIteratorLE::new(input.into_repr()).collect();
|
|||
let input_bits =
|
|||
Vec::<Boolean<Fq>>::new_input(r1cs_core::ns!(cs, "Input"), || {
|
|||
Ok(input_bits)
|
|||
})
|
|||
.unwrap();
|
|||
input_gadgets.push(input_bits);
|
|||
}
|
|||
}
|
|||
|
|||
let vk_gadget =
|
|||
TestVkVar::new_input(r1cs_core::ns!(cs, "Vk"), || Ok(¶ms.vk)).unwrap();
|
|||
let proof_gadget =
|
|||
TestProofVar::new_witness(r1cs_core::ns!(cs, "Proof"), || Ok(proof.clone()))
|
|||
.unwrap();
|
|||
println!("Time to verify!\n\n\n\n");
|
|||
<TestVerifierGadget as NIZKVerifierGadget<TestProofSystem, Fq>>::verify(
|
|||
&vk_gadget,
|
|||
&input_gadgets,
|
|||
&proof_gadget,
|
|||
)
|
|||
.unwrap()
|
|||
.enforce_equal(&Boolean::TRUE)
|
|||
.unwrap();
|
|||
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!("=========================================================");
|
|||
println!("Unsatisfied constraints:");
|
|||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
|||
println!("=========================================================");
|
|||
}
|
|||
|
|||
// cs.print_named_objects();
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test_recursive {
|
|||
use gm17::*;
|
|||
use r1cs_core::{
|
|||
lc, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
|
|||
};
|
|||
|
|||
use super::*;
|
|||
use algebra::{
|
|||
fields::{FftParameters, FpParameters},
|
|||
mnt4_298::{Fq as MNT4Fq, FqParameters as MNT4FqParameters, Fr as MNT4Fr, MNT4_298},
|
|||
mnt6_298::{Fq as MNT6Fq, FqParameters as MNT6FqParameters, Fr as MNT6Fr, MNT6_298},
|
|||
test_rng, BigInteger, Field, PrimeField,
|
|||
};
|
|||
use r1cs_std::{
|
|||
fields::fp::FpVar, mnt4_298::PairingVar as MNT4_298PairingVar,
|
|||
mnt6_298::PairingVar as MNT6_298PairingVar, uint8::UInt8, Assignment,
|
|||
};
|
|||
use rand::Rng;
|
|||
|
|||
type TestProofSystem1 = Gm17<MNT6_298, Bench<MNT4Fq>, MNT6Fr>;
|
|||
type TestVerifierGadget1 = Gm17VerifierGadget<MNT6_298, MNT6_298PairingVar>;
|
|||
type TestProofVar1 = ProofVar<MNT6_298, MNT6_298PairingVar>;
|
|||
type TestVkVar1 = VerifyingKeyVar<MNT6_298, MNT6_298PairingVar>;
|
|||
|
|||
type TestProofSystem2 = Gm17<MNT4_298, Wrapper, MNT4Fr>;
|
|||
type TestVerifierGadget2 = Gm17VerifierGadget<MNT4_298, MNT4_298PairingVar>;
|
|||
type TestProofVar2 = ProofVar<MNT4_298, MNT4_298PairingVar>;
|
|||
type TestVkVar2 = VerifyingKeyVar<MNT4_298, MNT4_298PairingVar>;
|
|||
|
|||
#[derive(Clone)]
|
|||
struct Bench<F: Field> {
|
|||
inputs: Vec<Option<F>>,
|
|||
num_constraints: usize,
|
|||
}
|
|||
|
|||
impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
|
|||
fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
|
|||
assert!(self.inputs.len() >= 2);
|
|||
assert!(self.num_constraints >= self.inputs.len());
|
|||
|
|||
let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
|
|||
for input in self.inputs {
|
|||
let input_var = cs.new_input_variable(|| input.get())?;
|
|||
variables.push((input, input_var));
|
|||
}
|
|||
|
|||
for i in 0..self.num_constraints {
|
|||
let new_entry = {
|
|||
let (input_1_val, input_1_var) = variables[i];
|
|||
let (input_2_val, input_2_var) = variables[i + 1];
|
|||
let result_val = input_1_val
|
|||
.and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
|
|||
let result_var = cs.new_witness_variable(|| {
|
|||
result_val.ok_or(SynthesisError::AssignmentMissing)
|
|||
})?;
|
|||
cs.enforce_constraint(
|
|||
lc!() + input_1_var,
|
|||
lc!() + input_2_var,
|
|||
lc!() + result_var,
|
|||
)
|
|||
.unwrap();
|
|||
(result_val, result_var)
|
|||
};
|
|||
variables.push(new_entry);
|
|||
}
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
struct Wrapper {
|
|||
inputs: Vec<Option<MNT4Fq>>,
|
|||
params: Parameters<MNT6_298>,
|
|||
proof: Proof<MNT6_298>,
|
|||
}
|
|||
|
|||
impl ConstraintSynthesizer<MNT6Fq> for Wrapper {
|
|||
fn generate_constraints(
|
|||
self,
|
|||
cs: ConstraintSystemRef<MNT6Fq>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
let params = self.params;
|
|||
let proof = self.proof;
|
|||
let inputs: Vec<_> = self
|
|||
.inputs
|
|||
.into_iter()
|
|||
.map(|input| input.unwrap())
|
|||
.collect();
|
|||
let input_gadgets;
|
|||
|
|||
{
|
|||
// Chain all input values in one large byte array.
|
|||
let input_bytes = inputs
|
|||
.clone()
|
|||
.into_iter()
|
|||
.flat_map(|input| {
|
|||
input
|
|||
.into_repr()
|
|||
.as_ref()
|
|||
.iter()
|
|||
.flat_map(|l| l.to_le_bytes().to_vec())
|
|||
.collect::<Vec<_>>()
|
|||
})
|
|||
.collect::<Vec<_>>();
|
|||
|
|||
// Allocate this byte array as input packed into field elements.
|
|||
let input_bytes =
|
|||
UInt8::new_input_vec(r1cs_core::ns!(cs, "Input"), &input_bytes[..])?;
|
|||
// 40 byte
|
|||
let element_size = <MNT4FqParameters as FftParameters>::BigInt::NUM_LIMBS * 8;
|
|||
input_gadgets = input_bytes
|
|||
.chunks(element_size)
|
|||
.map(|chunk| {
|
|||
chunk
|
|||
.iter()
|
|||
.flat_map(|byte| byte.to_bits_le().unwrap())
|
|||
.collect::<Vec<_>>()
|
|||
})
|
|||
.collect::<Vec<_>>();
|
|||
}
|
|||
|
|||
let vk_gadget = TestVkVar1::new_witness(r1cs_core::ns!(cs, "Vk"), || Ok(¶ms.vk))?;
|
|||
let proof_gadget =
|
|||
TestProofVar1::new_witness(r1cs_core::ns!(cs, "Proof"), || Ok(proof.clone()))
|
|||
.unwrap();
|
|||
<TestVerifierGadget1 as NIZKVerifierGadget<TestProofSystem1, MNT6Fq>>::verify(
|
|||
&vk_gadget,
|
|||
&input_gadgets,
|
|||
&proof_gadget,
|
|||
)?
|
|||
.enforce_equal(&Boolean::TRUE)?;
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn gm17_recursive_verifier_test() {
|
|||
let num_inputs = 5;
|
|||
let num_constraints = num_inputs;
|
|||
let rng = &mut test_rng();
|
|||
let mut inputs: Vec<Option<MNT4Fq>> = Vec::with_capacity(num_inputs);
|
|||
for _ in 0..num_inputs {
|
|||
inputs.push(Some(rng.gen()));
|
|||
}
|
|||
|
|||
// Generate inner params and proof.
|
|||
let inner_params = {
|
|||
let c = Bench::<MNT4Fq> {
|
|||
inputs: vec![None; num_inputs],
|
|||
num_constraints,
|
|||
};
|
|||
|
|||
generate_random_parameters(c, rng).unwrap()
|
|||
};
|
|||
|
|||
let inner_proof = {
|
|||
// Create an instance of our circuit (with the
|
|||
// witness)
|
|||
let c = Bench {
|
|||
inputs: inputs.clone(),
|
|||
num_constraints,
|
|||
};
|
|||
// Create a gm17 proof with our parameters.
|
|||
create_random_proof(c, &inner_params, rng).unwrap()
|
|||
};
|
|||
|
|||
// Generate outer params and proof.
|
|||
let params = {
|
|||
let c = Wrapper {
|
|||
inputs: inputs.clone(),
|
|||
params: inner_params.clone(),
|
|||
proof: inner_proof.clone(),
|
|||
};
|
|||
|
|||
generate_random_parameters(c, rng).unwrap()
|
|||
};
|
|||
|
|||
{
|
|||
let proof = {
|
|||
// Create an instance of our circuit (with the
|
|||
// witness)
|
|||
let c = Wrapper {
|
|||
inputs: inputs.clone(),
|
|||
params: inner_params.clone(),
|
|||
proof: inner_proof.clone(),
|
|||
};
|
|||
// Create a gm17 proof with our parameters.
|
|||
create_random_proof(c, ¶ms, rng).unwrap()
|
|||
};
|
|||
|
|||
let cs = ConstraintSystem::<MNT4Fq>::new_ref();
|
|||
|
|||
let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
|
|||
let mut input_gadgets = Vec::new();
|
|||
|
|||
{
|
|||
let bigint_size = <MNT4FqParameters as FftParameters>::BigInt::NUM_LIMBS * 64;
|
|||
let mut input_bits = Vec::new();
|
|||
for input in inputs.into_iter() {
|
|||
let input_gadget =
|
|||
FpVar::new_input(r1cs_core::ns!(cs, "Input"), || Ok(input)).unwrap();
|
|||
let mut fp_bits = input_gadget.to_bits_le().unwrap();
|
|||
|
|||
// Use 320 bits per element.
|
|||
for _ in fp_bits.len()..bigint_size {
|
|||
fp_bits.push(Boolean::constant(false));
|
|||
}
|
|||
input_bits.extend_from_slice(&fp_bits);
|
|||
}
|
|||
|
|||
// Pack input bits into field elements of the underlying circuit.
|
|||
let max_size = 8 * (<MNT6FqParameters as FpParameters>::CAPACITY / 8) as usize;
|
|||
let max_size = max_size as usize;
|
|||
let bigint_size = <MNT6FqParameters as FftParameters>::BigInt::NUM_LIMBS * 64;
|
|||
for chunk in input_bits.chunks(max_size) {
|
|||
let mut chunk = chunk.to_vec();
|
|||
let len = chunk.len();
|
|||
for _ in len..bigint_size {
|
|||
chunk.push(Boolean::constant(false));
|
|||
}
|
|||
input_gadgets.push(chunk);
|
|||
}
|
|||
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
|
|||
}
|
|||
|
|||
let vk_gadget =
|
|||
TestVkVar2::new_input(r1cs_core::ns!(cs, "Vk"), || Ok(¶ms.vk)).unwrap();
|
|||
let proof_gadget =
|
|||
TestProofVar2::new_witness(r1cs_core::ns!(cs, "Proof"), || Ok(proof.clone()))
|
|||
.unwrap();
|
|||
println!("Time to verify!\n\n\n\n");
|
|||
<TestVerifierGadget2 as NIZKVerifierGadget<TestProofSystem2, MNT4Fq>>::verify(
|
|||
&vk_gadget,
|
|||
&input_gadgets,
|
|||
&proof_gadget,
|
|||
)
|
|||
.unwrap()
|
|||
.enforce_equal(&Boolean::TRUE)
|
|||
.unwrap();
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!("=========================================================");
|
|||
println!("Unsatisfied constraints:");
|
|||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
|||
println!("=========================================================");
|
|||
}
|
|||
|
|||
// cs.print_named_objects();
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
|||
}
|
@ -1,87 +0,0 @@ |
|||
use crate::Error;
|
|||
use algebra_core::PairingEngine;
|
|||
use gm17::{
|
|||
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
|
|||
Parameters, PreparedVerifyingKey, Proof, VerifyingKey,
|
|||
};
|
|||
use r1cs_core::ConstraintSynthesizer;
|
|||
use rand::Rng;
|
|||
|
|||
use algebra_core::ToConstraintField;
|
|||
use core::marker::PhantomData;
|
|||
|
|||
use super::NIZK;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
/// Note: V should serialize its contents to `Vec<E::Fr>` in the same order as
|
|||
/// during the constraint generation.
|
|||
pub struct Gm17<
|
|||
E: PairingEngine,
|
|||
C: ConstraintSynthesizer<E::Fr>,
|
|||
V: ToConstraintField<E::Fr> + ?Sized,
|
|||
> {
|
|||
#[doc(hidden)]
|
|||
_engine: PhantomData<E>,
|
|||
#[doc(hidden)]
|
|||
_circuit: PhantomData<C>,
|
|||
#[doc(hidden)]
|
|||
_verifier_input: PhantomData<V>,
|
|||
}
|
|||
|
|||
impl<E: PairingEngine, C: ConstraintSynthesizer<E::Fr>, V: ToConstraintField<E::Fr> + ?Sized> NIZK
|
|||
for Gm17<E, C, V>
|
|||
{
|
|||
type Circuit = C;
|
|||
type AssignedCircuit = C;
|
|||
type VerifierInput = V;
|
|||
type ProvingParameters = Parameters<E>;
|
|||
type VerificationParameters = VerifyingKey<E>;
|
|||
type PreparedVerificationParameters = PreparedVerifyingKey<E>;
|
|||
type Proof = Proof<E>;
|
|||
|
|||
fn setup<R: Rng>(
|
|||
circuit: Self::Circuit,
|
|||
rng: &mut R,
|
|||
) -> Result<
|
|||
(
|
|||
Self::ProvingParameters,
|
|||
Self::PreparedVerificationParameters,
|
|||
),
|
|||
Error,
|
|||
> {
|
|||
let nizk_time = start_timer!(|| "{Groth-Maller 2017}::Setup");
|
|||
let pp = generate_random_parameters::<E, Self::Circuit, R>(circuit, rng)?;
|
|||
let vk = prepare_verifying_key(&pp.vk);
|
|||
end_timer!(nizk_time);
|
|||
Ok((pp, vk))
|
|||
}
|
|||
|
|||
fn prove<R: Rng>(
|
|||
pp: &Self::ProvingParameters,
|
|||
input_and_witness: Self::AssignedCircuit,
|
|||
rng: &mut R,
|
|||
) -> Result<Self::Proof, Error> {
|
|||
let proof_time = start_timer!(|| "{Groth-Maller 2017}::Prove");
|
|||
let result = create_random_proof::<E, _, _>(input_and_witness, pp, rng)?;
|
|||
end_timer!(proof_time);
|
|||
Ok(result)
|
|||
}
|
|||
|
|||
fn verify(
|
|||
vk: &Self::PreparedVerificationParameters,
|
|||
input: &Self::VerifierInput,
|
|||
proof: &Self::Proof,
|
|||
) -> Result<bool, Error> {
|
|||
let verify_time = start_timer!(|| "{Groth-Maller 2017}::Verify");
|
|||
let conversion_time = start_timer!(|| "Convert input to E::Fr");
|
|||
let input = input.to_field_elements()?;
|
|||
end_timer!(conversion_time);
|
|||
let verification = start_timer!(|| format!("Verify proof w/ input len: {}", input.len()));
|
|||
let result = verify_proof(&vk, proof, &input)?;
|
|||
end_timer!(verification);
|
|||
end_timer!(verify_time);
|
|||
Ok(result)
|
|||
}
|
|||
}
|
@ -1,746 +0,0 @@ |
|||
use crate::{
|
|||
nizk::{groth16::Groth16, NIZKVerifierGadget},
|
|||
Vec,
|
|||
};
|
|||
use algebra_core::{AffineCurve, PairingEngine, ToConstraintField};
|
|||
use r1cs_core::{ConstraintSynthesizer, Namespace, SynthesisError};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use core::{borrow::Borrow, marker::PhantomData};
|
|||
use groth16::{PreparedVerifyingKey, Proof, VerifyingKey};
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = "P::G1Var: Clone, P::G2Var: Clone"))]
|
|||
pub struct ProofVar<E: PairingEngine, P: PairingVar<E>> {
|
|||
pub a: P::G1Var,
|
|||
pub b: P::G2Var,
|
|||
pub c: P::G1Var,
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(
|
|||
Clone(bound = "P::G1Var: Clone, P::GTVar: Clone, P::G1PreparedVar: Clone, \
|
|||
P::G2PreparedVar: Clone, ")
|
|||
)]
|
|||
pub struct VerifyingKeyVar<E: PairingEngine, P: PairingVar<E>> {
|
|||
pub alpha_g1: P::G1Var,
|
|||
pub beta_g2: P::G2Var,
|
|||
pub gamma_g2: P::G2Var,
|
|||
pub delta_g2: P::G2Var,
|
|||
pub gamma_abc_g1: Vec<P::G1Var>,
|
|||
}
|
|||
|
|||
impl<E: PairingEngine, P: PairingVar<E>> VerifyingKeyVar<E, P> {
|
|||
pub fn prepare(&self) -> Result<PreparedVerifyingKeyVar<E, P>, SynthesisError> {
|
|||
let alpha_g1_pc = P::prepare_g1(&self.alpha_g1)?;
|
|||
let beta_g2_pc = P::prepare_g2(&self.beta_g2)?;
|
|||
|
|||
let alpha_g1_beta_g2 = P::pairing(alpha_g1_pc, beta_g2_pc)?;
|
|||
let gamma_g2_neg_pc = P::prepare_g2(&self.gamma_g2.negate()?)?;
|
|||
let delta_g2_neg_pc = P::prepare_g2(&self.delta_g2.negate()?)?;
|
|||
|
|||
Ok(PreparedVerifyingKeyVar {
|
|||
alpha_g1_beta_g2,
|
|||
gamma_g2_neg_pc,
|
|||
delta_g2_neg_pc,
|
|||
gamma_abc_g1: self.gamma_abc_g1.clone(),
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(
|
|||
Clone(bound = "P::G1Var: Clone, P::GTVar: Clone, P::G1PreparedVar: Clone, \
|
|||
P::G2PreparedVar: Clone, ")
|
|||
)]
|
|||
pub struct PreparedVerifyingKeyVar<E: PairingEngine, P: PairingVar<E>> {
|
|||
pub alpha_g1_beta_g2: P::GTVar,
|
|||
pub gamma_g2_neg_pc: P::G2PreparedVar,
|
|||
pub delta_g2_neg_pc: P::G2PreparedVar,
|
|||
pub gamma_abc_g1: Vec<P::G1Var>,
|
|||
}
|
|||
|
|||
pub struct Groth16VerifierGadget<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
_pairing_engine: PhantomData<E>,
|
|||
_pairing_gadget: PhantomData<P>,
|
|||
}
|
|||
|
|||
impl<E, P, C, V> NIZKVerifierGadget<Groth16<E, C, V>, E::Fq> for Groth16VerifierGadget<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
C: ConstraintSynthesizer<E::Fr>,
|
|||
V: ToConstraintField<E::Fr>,
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
type PreparedVerificationKeyVar = PreparedVerifyingKeyVar<E, P>;
|
|||
type VerificationKeyVar = VerifyingKeyVar<E, P>;
|
|||
type ProofVar = ProofVar<E, P>;
|
|||
|
|||
/// Allocates `N::Proof` in `cs` without performing
|
|||
/// subgroup checks.
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_proof_unchecked<T: Borrow<Proof<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self::ProofVar, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
f().and_then(|proof| {
|
|||
let proof = proof.borrow();
|
|||
let a = CurveVar::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "Proof.a"),
|
|||
|| Ok(proof.a.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let b = CurveVar::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "Proof.b"),
|
|||
|| Ok(proof.b.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let c = CurveVar::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "Proof.c"),
|
|||
|| Ok(proof.c.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
Ok(ProofVar { a, b, c })
|
|||
})
|
|||
}
|
|||
|
|||
/// Allocates `N::Proof` in `cs` without performing
|
|||
/// subgroup checks.
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_verification_key_unchecked<T: Borrow<VerifyingKey<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self::VerificationKeyVar, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
f().and_then(|vk| {
|
|||
let vk = vk.borrow();
|
|||
let alpha_g1 = P::G1Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "alpha_g1"),
|
|||
|| Ok(vk.alpha_g1.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let beta_g2 = P::G2Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "beta_g2"),
|
|||
|| Ok(vk.beta_g2.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let gamma_g2 = P::G2Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "gamma_g2"),
|
|||
|| Ok(vk.gamma_g2.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
let delta_g2 = P::G2Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "delta_g2"),
|
|||
|| Ok(vk.delta_g2.into_projective()),
|
|||
mode,
|
|||
)?;
|
|||
|
|||
let gamma_abc_g1 = vk
|
|||
.gamma_abc_g1
|
|||
.iter()
|
|||
.map(|g| {
|
|||
P::G1Var::new_variable_omit_prime_order_check(
|
|||
r1cs_core::ns!(cs, "g"),
|
|||
|| Ok(g.into_projective()),
|
|||
mode,
|
|||
)
|
|||
})
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
|||
Ok(VerifyingKeyVar {
|
|||
alpha_g1,
|
|||
beta_g2,
|
|||
gamma_g2,
|
|||
delta_g2,
|
|||
gamma_abc_g1,
|
|||
})
|
|||
})
|
|||
}
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(vk, input, proof))]
|
|||
fn verify<'a, T: 'a + ToBitsGadget<E::Fq> + ?Sized>(
|
|||
vk: &Self::VerificationKeyVar,
|
|||
input: impl IntoIterator<Item = &'a T>,
|
|||
proof: &Self::ProofVar,
|
|||
) -> Result<Boolean<E::Fq>, SynthesisError> {
|
|||
let pvk = vk.prepare()?;
|
|||
<Self as NIZKVerifierGadget<Groth16<E, C, V>, E::Fq>>::verify_prepared(&pvk, input, proof)
|
|||
}
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(pvk, public_inputs, proof))]
|
|||
fn verify_prepared<'a, T: 'a + ToBitsGadget<E::Fq> + ?Sized>(
|
|||
pvk: &Self::PreparedVerificationKeyVar,
|
|||
public_inputs: impl IntoIterator<Item = &'a T>,
|
|||
proof: &Self::ProofVar,
|
|||
) -> Result<Boolean<E::Fq>, SynthesisError> {
|
|||
let pvk = pvk.clone();
|
|||
|
|||
let g_ic = {
|
|||
let mut g_ic: P::G1Var = pvk.gamma_abc_g1[0].clone();
|
|||
let mut input_len = 1;
|
|||
let mut public_inputs = public_inputs.into_iter();
|
|||
for (input, b) in public_inputs.by_ref().zip(pvk.gamma_abc_g1.iter().skip(1)) {
|
|||
let encoded_input_i: P::G1Var = b.scalar_mul_le(input.to_bits_le()?.iter())?;
|
|||
g_ic += encoded_input_i;
|
|||
input_len += 1;
|
|||
}
|
|||
// Check that the input and the query in the verification are of the
|
|||
// same length.
|
|||
assert!(input_len == pvk.gamma_abc_g1.len() && public_inputs.next().is_none());
|
|||
g_ic
|
|||
};
|
|||
|
|||
let test_exp = {
|
|||
let proof_a_prep = P::prepare_g1(&proof.a)?;
|
|||
let proof_b_prep = P::prepare_g2(&proof.b)?;
|
|||
let proof_c_prep = P::prepare_g1(&proof.c)?;
|
|||
|
|||
let g_ic_prep = P::prepare_g1(&g_ic)?;
|
|||
|
|||
P::miller_loop(
|
|||
&[proof_a_prep, g_ic_prep, proof_c_prep],
|
|||
&[
|
|||
proof_b_prep,
|
|||
pvk.gamma_g2_neg_pc.clone(),
|
|||
pvk.delta_g2_neg_pc.clone(),
|
|||
],
|
|||
)?
|
|||
};
|
|||
|
|||
let test = P::final_exponentiation(&test_exp)?;
|
|||
test.is_eq(&pvk.alpha_g1_beta_g2)
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> AllocVar<PreparedVerifyingKey<E>, E::Fq> for PreparedVerifyingKeyVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<PreparedVerifyingKey<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
|
|||
f().and_then(|pvk| {
|
|||
let pvk = pvk.borrow();
|
|||
let alpha_g1_beta_g2 = P::GTVar::new_variable(
|
|||
r1cs_core::ns!(cs, "alpha_g1_beta_g2"),
|
|||
|| Ok(pvk.alpha_g1_beta_g2),
|
|||
mode,
|
|||
)?;
|
|||
|
|||
let gamma_g2_neg_pc = P::G2PreparedVar::new_variable(
|
|||
r1cs_core::ns!(cs, "gamma_g2_neg_pc"),
|
|||
|| Ok(pvk.gamma_g2_neg_pc.clone()),
|
|||
mode,
|
|||
)?;
|
|||
|
|||
let delta_g2_neg_pc = P::G2PreparedVar::new_variable(
|
|||
r1cs_core::ns!(cs, "delta_g2_neg_pc"),
|
|||
|| Ok(pvk.delta_g2_neg_pc.clone()),
|
|||
mode,
|
|||
)?;
|
|||
|
|||
let gamma_abc_g1 = Vec::new_variable(
|
|||
r1cs_core::ns!(cs, "gamma_abc_g1"),
|
|||
|| Ok(pvk.gamma_abc_g1.clone()),
|
|||
mode,
|
|||
)?;
|
|||
|
|||
Ok(Self {
|
|||
alpha_g1_beta_g2,
|
|||
gamma_g2_neg_pc,
|
|||
delta_g2_neg_pc,
|
|||
gamma_abc_g1,
|
|||
})
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> AllocVar<VerifyingKey<E>, E::Fq> for VerifyingKeyVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<VerifyingKey<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
|
|||
f().and_then(|vk| {
|
|||
let VerifyingKey {
|
|||
alpha_g1,
|
|||
beta_g2,
|
|||
gamma_g2,
|
|||
delta_g2,
|
|||
gamma_abc_g1,
|
|||
} = vk.borrow().clone();
|
|||
let alpha_g1 =
|
|||
P::G1Var::new_variable(r1cs_core::ns!(cs, "alpha_g1"), || Ok(alpha_g1), mode)?;
|
|||
let beta_g2 =
|
|||
P::G2Var::new_variable(r1cs_core::ns!(cs, "beta_g2"), || Ok(beta_g2), mode)?;
|
|||
let gamma_g2 =
|
|||
P::G2Var::new_variable(r1cs_core::ns!(cs, "gamma_g2"), || Ok(gamma_g2), mode)?;
|
|||
let delta_g2 =
|
|||
P::G2Var::new_variable(r1cs_core::ns!(cs, "delta_g2"), || Ok(delta_g2), mode)?;
|
|||
|
|||
let gamma_abc_g1 = Vec::new_variable(cs.clone(), || Ok(gamma_abc_g1), mode)?;
|
|||
Ok(Self {
|
|||
alpha_g1,
|
|||
beta_g2,
|
|||
gamma_g2,
|
|||
delta_g2,
|
|||
gamma_abc_g1,
|
|||
})
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> AllocVar<Proof<E>, E::Fq> for ProofVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<Proof<E>>>(
|
|||
cs: impl Into<Namespace<E::Fq>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
|
|||
f().and_then(|proof| {
|
|||
let Proof { a, b, c } = proof.borrow().clone();
|
|||
let a = P::G1Var::new_variable(r1cs_core::ns!(cs, "a"), || Ok(a), mode)?;
|
|||
let b = P::G2Var::new_variable(r1cs_core::ns!(cs, "b"), || Ok(b), mode)?;
|
|||
let c = P::G1Var::new_variable(r1cs_core::ns!(cs, "c"), || Ok(c), mode)?;
|
|||
Ok(Self { a, b, c })
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<E, P> ToBytesGadget<E::Fq> for VerifyingKeyVar<E, P>
|
|||
where
|
|||
E: PairingEngine,
|
|||
P: PairingVar<E>,
|
|||
{
|
|||
#[inline]
|
|||
#[tracing::instrument(target = "r1cs", skip(self))]
|
|||
fn to_bytes(&self) -> Result<Vec<UInt8<E::Fq>>, SynthesisError> {
|
|||
let mut bytes = Vec::new();
|
|||
bytes.extend_from_slice(&self.alpha_g1.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.beta_g2.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.gamma_g2.to_bytes()?);
|
|||
bytes.extend_from_slice(&self.delta_g2.to_bytes()?);
|
|||
for g in &self.gamma_abc_g1 {
|
|||
bytes.extend_from_slice(&g.to_bytes()?);
|
|||
}
|
|||
Ok(bytes)
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use groth16::*;
|
|||
use r1cs_core::{
|
|||
lc, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
|
|||
};
|
|||
|
|||
use super::*;
|
|||
use algebra::{
|
|||
bls12_377::{Bls12_377, Fq, Fr},
|
|||
test_rng, BitIteratorLE, Field, PrimeField,
|
|||
};
|
|||
use r1cs_std::{bls12_377::PairingVar as Bls12_377PairingVar, boolean::Boolean, Assignment};
|
|||
use rand::Rng;
|
|||
|
|||
type TestProofSystem = Groth16<Bls12_377, Bench<Fr>, Fr>;
|
|||
type TestVerifierGadget = Groth16VerifierGadget<Bls12_377, Bls12_377PairingVar>;
|
|||
type TestProofVar = ProofVar<Bls12_377, Bls12_377PairingVar>;
|
|||
type TestVkVar = VerifyingKeyVar<Bls12_377, Bls12_377PairingVar>;
|
|||
|
|||
struct Bench<F: Field> {
|
|||
inputs: Vec<Option<F>>,
|
|||
num_constraints: usize,
|
|||
}
|
|||
|
|||
impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
|
|||
fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
|
|||
assert!(self.inputs.len() >= 2);
|
|||
assert!(self.num_constraints >= self.inputs.len());
|
|||
|
|||
let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
|
|||
for input in self.inputs {
|
|||
let input_var = cs.new_input_variable(|| input.get())?;
|
|||
variables.push((input, input_var));
|
|||
}
|
|||
|
|||
for i in 0..self.num_constraints {
|
|||
let new_entry = {
|
|||
let (input_1_val, input_1_var) = variables[i];
|
|||
let (input_2_val, input_2_var) = variables[i + 1];
|
|||
let result_val = input_1_val
|
|||
.and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
|
|||
let result_var = cs.new_witness_variable(|| {
|
|||
result_val.ok_or(SynthesisError::AssignmentMissing)
|
|||
})?;
|
|||
cs.enforce_constraint(
|
|||
lc!() + input_1_var,
|
|||
lc!() + input_2_var,
|
|||
lc!() + result_var,
|
|||
)
|
|||
.unwrap();
|
|||
(result_val, result_var)
|
|||
};
|
|||
variables.push(new_entry);
|
|||
}
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn groth16_verifier_test() {
|
|||
let num_inputs = 100;
|
|||
let num_constraints = num_inputs;
|
|||
let rng = &mut test_rng();
|
|||
let mut inputs: Vec<Option<Fr>> = Vec::with_capacity(num_inputs);
|
|||
for _ in 0..num_inputs {
|
|||
inputs.push(Some(rng.gen()));
|
|||
}
|
|||
let params = {
|
|||
let c = Bench::<Fr> {
|
|||
inputs: vec![None; num_inputs],
|
|||
num_constraints,
|
|||
};
|
|||
|
|||
generate_random_parameters(c, rng).unwrap()
|
|||
};
|
|||
|
|||
{
|
|||
let proof = {
|
|||
// Create an instance of our circuit (with the
|
|||
// witness)
|
|||
let c = Bench {
|
|||
inputs: inputs.clone(),
|
|||
num_constraints,
|
|||
};
|
|||
// Create a groth16 proof with our parameters.
|
|||
create_random_proof(c, ¶ms, rng).unwrap()
|
|||
};
|
|||
|
|||
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
|
|||
let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
|
|||
let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
|
|||
let mut input_gadgets = Vec::new();
|
|||
|
|||
{
|
|||
for input in inputs.into_iter() {
|
|||
let input_bits = BitIteratorLE::new(input.into_repr()).collect::<Vec<_>>();
|
|||
|
|||
let input_bits =
|
|||
Vec::<Boolean<Fq>>::new_input(r1cs_core::ns!(cs, "Input"), || {
|
|||
Ok(input_bits)
|
|||
})
|
|||
.unwrap();
|
|||
input_gadgets.push(input_bits);
|
|||
}
|
|||
}
|
|||
|
|||
let vk_gadget =
|
|||
TestVkVar::new_input(r1cs_core::ns!(cs, "Vk"), || Ok(¶ms.vk)).unwrap();
|
|||
let proof_gadget =
|
|||
TestProofVar::new_witness(r1cs_core::ns!(cs, "Proof"), || Ok(proof.clone()))
|
|||
.unwrap();
|
|||
println!("Time to verify!\n\n\n\n");
|
|||
<TestVerifierGadget as NIZKVerifierGadget<TestProofSystem, Fq>>::verify(
|
|||
&vk_gadget,
|
|||
&input_gadgets,
|
|||
&proof_gadget,
|
|||
)
|
|||
.unwrap()
|
|||
.enforce_equal(&Boolean::TRUE)
|
|||
.unwrap();
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!("=========================================================");
|
|||
println!("Unsatisfied constraints:");
|
|||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
|||
println!("=========================================================");
|
|||
}
|
|||
|
|||
// cs.print_named_objects();
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test_recursive {
|
|||
use groth16::*;
|
|||
use r1cs_core::{
|
|||
lc, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
|
|||
};
|
|||
|
|||
use super::*;
|
|||
use algebra::{
|
|||
fields::{FftParameters, FpParameters},
|
|||
mnt4_298::{Fq as MNT4Fq, FqParameters as MNT4FqParameters, Fr as MNT4Fr, MNT4_298},
|
|||
mnt6_298::{Fq as MNT6Fq, FqParameters as MNT6FqParameters, Fr as MNT6Fr, MNT6_298},
|
|||
test_rng, BigInteger, Field, PrimeField,
|
|||
};
|
|||
use r1cs_std::{
|
|||
fields::fp::FpVar, mnt4_298::PairingVar as MNT4_298PairingVar,
|
|||
mnt6_298::PairingVar as MNT6_298PairingVar, uint8::UInt8, Assignment,
|
|||
};
|
|||
use rand::Rng;
|
|||
|
|||
type TestProofSystem1 = Groth16<MNT6_298, Bench<MNT4Fq>, MNT6Fr>;
|
|||
type TestVerifierGadget1 = Groth16VerifierGadget<MNT6_298, MNT6_298PairingVar>;
|
|||
type TestProofVar1 = ProofVar<MNT6_298, MNT6_298PairingVar>;
|
|||
type TestVkVar1 = VerifyingKeyVar<MNT6_298, MNT6_298PairingVar>;
|
|||
|
|||
type TestProofSystem2 = Groth16<MNT4_298, Wrapper, MNT4Fr>;
|
|||
type TestVerifierGadget2 = Groth16VerifierGadget<MNT4_298, MNT4_298PairingVar>;
|
|||
type TestProofVar2 = ProofVar<MNT4_298, MNT4_298PairingVar>;
|
|||
type TestVkVar2 = VerifyingKeyVar<MNT4_298, MNT4_298PairingVar>;
|
|||
|
|||
#[derive(Clone)]
|
|||
struct Bench<F: Field> {
|
|||
inputs: Vec<Option<F>>,
|
|||
num_constraints: usize,
|
|||
}
|
|||
|
|||
impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
|
|||
fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
|
|||
assert!(self.inputs.len() >= 2);
|
|||
assert!(self.num_constraints >= self.inputs.len());
|
|||
|
|||
let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
|
|||
for input in self.inputs {
|
|||
let input_var = cs.new_input_variable(|| input.get())?;
|
|||
variables.push((input, input_var));
|
|||
}
|
|||
|
|||
for i in 0..self.num_constraints {
|
|||
let new_entry = {
|
|||
let (input_1_val, input_1_var) = variables[i];
|
|||
let (input_2_val, input_2_var) = variables[i + 1];
|
|||
let result_val = input_1_val
|
|||
.and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
|
|||
let result_var = cs.new_witness_variable(|| {
|
|||
result_val.ok_or(SynthesisError::AssignmentMissing)
|
|||
})?;
|
|||
cs.enforce_constraint(
|
|||
lc!() + input_1_var,
|
|||
lc!() + input_2_var,
|
|||
lc!() + result_var,
|
|||
)
|
|||
.unwrap();
|
|||
(result_val, result_var)
|
|||
};
|
|||
variables.push(new_entry);
|
|||
}
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
struct Wrapper {
|
|||
inputs: Vec<Option<MNT4Fq>>,
|
|||
params: Parameters<MNT6_298>,
|
|||
proof: Proof<MNT6_298>,
|
|||
}
|
|||
|
|||
impl ConstraintSynthesizer<MNT6Fq> for Wrapper {
|
|||
fn generate_constraints(
|
|||
self,
|
|||
cs: ConstraintSystemRef<MNT6Fq>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
let params = self.params;
|
|||
let proof = self.proof;
|
|||
let inputs: Vec<_> = self
|
|||
.inputs
|
|||
.into_iter()
|
|||
.map(|input| input.unwrap())
|
|||
.collect();
|
|||
let input_gadgets;
|
|||
|
|||
{
|
|||
// Chain all input values in one large byte array.
|
|||
let input_bytes = inputs
|
|||
.clone()
|
|||
.into_iter()
|
|||
.flat_map(|input| {
|
|||
input
|
|||
.into_repr()
|
|||
.as_ref()
|
|||
.iter()
|
|||
.flat_map(|l| l.to_le_bytes().to_vec())
|
|||
.collect::<Vec<_>>()
|
|||
})
|
|||
.collect::<Vec<_>>();
|
|||
|
|||
// Allocate this byte array as input packed into field elements.
|
|||
let input_bytes =
|
|||
UInt8::new_input_vec(r1cs_core::ns!(cs, "Input"), &input_bytes[..])?;
|
|||
// 40 byte
|
|||
let element_size = <MNT4FqParameters as FftParameters>::BigInt::NUM_LIMBS * 8;
|
|||
input_gadgets = input_bytes
|
|||
.chunks(element_size)
|
|||
.map(|chunk| {
|
|||
chunk
|
|||
.iter()
|
|||
.flat_map(|byte| byte.to_bits_le().unwrap())
|
|||
.collect::<Vec<_>>()
|
|||
})
|
|||
.collect::<Vec<_>>();
|
|||
}
|
|||
|
|||
let vk_gadget = TestVkVar1::new_witness(r1cs_core::ns!(cs, "Vk"), || Ok(¶ms.vk))?;
|
|||
let proof_gadget =
|
|||
TestProofVar1::new_witness(r1cs_core::ns!(cs, "Proof"), || Ok(proof.clone()))
|
|||
.unwrap();
|
|||
<TestVerifierGadget1 as NIZKVerifierGadget<TestProofSystem1, MNT6Fq>>::verify(
|
|||
&vk_gadget,
|
|||
&input_gadgets,
|
|||
&proof_gadget,
|
|||
)?
|
|||
.enforce_equal(&Boolean::TRUE)?;
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn groth16_recursive_verifier_test() {
|
|||
let num_inputs = 5;
|
|||
let num_constraints = num_inputs;
|
|||
let rng = &mut test_rng();
|
|||
let mut inputs: Vec<Option<MNT4Fq>> = Vec::with_capacity(num_inputs);
|
|||
for _ in 0..num_inputs {
|
|||
inputs.push(Some(rng.gen()));
|
|||
}
|
|||
|
|||
// Generate inner params and proof.
|
|||
let inner_params = {
|
|||
let c = Bench::<MNT4Fq> {
|
|||
inputs: vec![None; num_inputs],
|
|||
num_constraints,
|
|||
};
|
|||
|
|||
generate_random_parameters(c, rng).unwrap()
|
|||
};
|
|||
|
|||
let inner_proof = {
|
|||
// Create an instance of our circuit (with the
|
|||
// witness)
|
|||
let c = Bench {
|
|||
inputs: inputs.clone(),
|
|||
num_constraints,
|
|||
};
|
|||
// Create a groth16 proof with our parameters.
|
|||
create_random_proof(c, &inner_params, rng).unwrap()
|
|||
};
|
|||
|
|||
// Generate outer params and proof.
|
|||
let params = {
|
|||
let c = Wrapper {
|
|||
inputs: inputs.clone(),
|
|||
params: inner_params.clone(),
|
|||
proof: inner_proof.clone(),
|
|||
};
|
|||
|
|||
generate_random_parameters(c, rng).unwrap()
|
|||
};
|
|||
|
|||
{
|
|||
let proof = {
|
|||
// Create an instance of our circuit (with the
|
|||
// witness)
|
|||
let c = Wrapper {
|
|||
inputs: inputs.clone(),
|
|||
params: inner_params.clone(),
|
|||
proof: inner_proof.clone(),
|
|||
};
|
|||
// Create a groth16 proof with our parameters.
|
|||
create_random_proof(c, ¶ms, rng).unwrap()
|
|||
};
|
|||
|
|||
let cs = ConstraintSystem::<MNT4Fq>::new_ref();
|
|||
|
|||
let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
|
|||
let mut input_gadgets = Vec::new();
|
|||
|
|||
{
|
|||
let bigint_size = <MNT4FqParameters as FftParameters>::BigInt::NUM_LIMBS * 64;
|
|||
let mut input_bits = Vec::new();
|
|||
for input in inputs.into_iter() {
|
|||
let input_gadget =
|
|||
FpVar::new_input(r1cs_core::ns!(cs, "Input"), || Ok(input)).unwrap();
|
|||
let mut fp_bits = input_gadget.to_bits_le().unwrap();
|
|||
|
|||
// Use 320 bits per element.
|
|||
for _ in fp_bits.len()..bigint_size {
|
|||
fp_bits.push(Boolean::constant(false));
|
|||
}
|
|||
input_bits.extend_from_slice(&fp_bits);
|
|||
}
|
|||
|
|||
// Pack input bits into field elements of the underlying circuit.
|
|||
let max_size = 8 * (<MNT6FqParameters as FpParameters>::CAPACITY / 8) as usize;
|
|||
let max_size = max_size as usize;
|
|||
let bigint_size = <MNT6FqParameters as FftParameters>::BigInt::NUM_LIMBS * 64;
|
|||
for chunk in input_bits.chunks(max_size) {
|
|||
let mut chunk = chunk.to_vec();
|
|||
let len = chunk.len();
|
|||
for _ in len..bigint_size {
|
|||
chunk.push(Boolean::constant(false));
|
|||
}
|
|||
input_gadgets.push(chunk);
|
|||
}
|
|||
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
|
|||
}
|
|||
|
|||
let vk_gadget =
|
|||
TestVkVar2::new_input(r1cs_core::ns!(cs, "Vk"), || Ok(¶ms.vk)).unwrap();
|
|||
let proof_gadget =
|
|||
TestProofVar2::new_witness(r1cs_core::ns!(cs, "Proof"), || Ok(proof.clone()))
|
|||
.unwrap();
|
|||
println!("Time to verify!\n\n\n\n");
|
|||
<TestVerifierGadget2 as NIZKVerifierGadget<TestProofSystem2, MNT4Fq>>::verify(
|
|||
&vk_gadget,
|
|||
&input_gadgets,
|
|||
&proof_gadget,
|
|||
)
|
|||
.unwrap()
|
|||
.enforce_equal(&Boolean::TRUE)
|
|||
.unwrap();
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!("=========================================================");
|
|||
println!("Unsatisfied constraints:");
|
|||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
|||
println!("=========================================================");
|
|||
}
|
|||
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
}
|
|||
}
|
@ -1,87 +0,0 @@ |
|||
use crate::Error;
|
|||
use algebra_core::PairingEngine;
|
|||
use groth16::{
|
|||
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
|
|||
Parameters, PreparedVerifyingKey, Proof, VerifyingKey,
|
|||
};
|
|||
use r1cs_core::ConstraintSynthesizer;
|
|||
use rand::Rng;
|
|||
|
|||
use algebra_core::ToConstraintField;
|
|||
use core::marker::PhantomData;
|
|||
|
|||
use super::NIZK;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
/// Note: V should serialize its contents to `Vec<E::Fr>` in the same order as
|
|||
/// during the constraint generation.
|
|||
pub struct Groth16<
|
|||
E: PairingEngine,
|
|||
C: ConstraintSynthesizer<E::Fr>,
|
|||
V: ToConstraintField<E::Fr> + ?Sized,
|
|||
> {
|
|||
#[doc(hidden)]
|
|||
_engine: PhantomData<E>,
|
|||
#[doc(hidden)]
|
|||
_circuit: PhantomData<C>,
|
|||
#[doc(hidden)]
|
|||
_verifier_input: PhantomData<V>,
|
|||
}
|
|||
|
|||
impl<E: PairingEngine, C: ConstraintSynthesizer<E::Fr>, V: ToConstraintField<E::Fr> + ?Sized> NIZK
|
|||
for Groth16<E, C, V>
|
|||
{
|
|||
type Circuit = C;
|
|||
type AssignedCircuit = C;
|
|||
type VerifierInput = V;
|
|||
type ProvingParameters = Parameters<E>;
|
|||
type VerificationParameters = VerifyingKey<E>;
|
|||
type PreparedVerificationParameters = PreparedVerifyingKey<E>;
|
|||
type Proof = Proof<E>;
|
|||
|
|||
fn setup<R: Rng>(
|
|||
circuit: Self::Circuit,
|
|||
rng: &mut R,
|
|||
) -> Result<
|
|||
(
|
|||
Self::ProvingParameters,
|
|||
Self::PreparedVerificationParameters,
|
|||
),
|
|||
Error,
|
|||
> {
|
|||
let nizk_time = start_timer!(|| "{Groth 2016}::Setup");
|
|||
let pp = generate_random_parameters::<E, Self::Circuit, R>(circuit, rng)?;
|
|||
let vk = prepare_verifying_key(&pp.vk);
|
|||
end_timer!(nizk_time);
|
|||
Ok((pp, vk))
|
|||
}
|
|||
|
|||
fn prove<R: Rng>(
|
|||
pp: &Self::ProvingParameters,
|
|||
input_and_witness: Self::AssignedCircuit,
|
|||
rng: &mut R,
|
|||
) -> Result<Self::Proof, Error> {
|
|||
let proof_time = start_timer!(|| "{Groth 2016}::Prove");
|
|||
let result = create_random_proof::<E, _, _>(input_and_witness, pp, rng)?;
|
|||
end_timer!(proof_time);
|
|||
Ok(result)
|
|||
}
|
|||
|
|||
fn verify(
|
|||
vk: &Self::PreparedVerificationParameters,
|
|||
input: &Self::VerifierInput,
|
|||
proof: &Self::Proof,
|
|||
) -> Result<bool, Error> {
|
|||
let verify_time = start_timer!(|| "{Groth 2016}::Verify");
|
|||
let conversion_time = start_timer!(|| "Convert input to E::Fr");
|
|||
let input = input.to_field_elements()?;
|
|||
end_timer!(conversion_time);
|
|||
let verification = start_timer!(|| format!("Verify proof w/ input len: {}", input.len()));
|
|||
let result = verify_proof(&vk, proof, &input)?;
|
|||
end_timer!(verification);
|
|||
end_timer!(verify_time);
|
|||
Ok(result)
|
|||
}
|
|||
}
|
@ -1,115 +0,0 @@ |
|||
use algebra_core::bytes::ToBytes;
|
|||
use rand::Rng;
|
|||
|
|||
#[cfg(feature = "gm17")]
|
|||
pub mod gm17;
|
|||
#[cfg(feature = "gm17")]
|
|||
pub use self::gm17::Gm17;
|
|||
|
|||
#[cfg(feature = "groth16")]
|
|||
pub mod groth16;
|
|||
#[cfg(feature = "groth16")]
|
|||
pub use self::groth16::Groth16;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub use constraints::*;
|
|||
|
|||
use crate::Error;
|
|||
|
|||
pub trait NIZK {
|
|||
type Circuit;
|
|||
type AssignedCircuit;
|
|||
type VerifierInput: ?Sized;
|
|||
type ProvingParameters: Clone;
|
|||
type VerificationParameters: Clone + Default + From<Self::PreparedVerificationParameters>;
|
|||
type PreparedVerificationParameters: Clone + Default + From<Self::VerificationParameters>;
|
|||
type Proof: ToBytes + Clone + Default;
|
|||
|
|||
fn setup<R: Rng>(
|
|||
circuit: Self::Circuit,
|
|||
rng: &mut R,
|
|||
) -> Result<
|
|||
(
|
|||
Self::ProvingParameters,
|
|||
Self::PreparedVerificationParameters,
|
|||
),
|
|||
Error,
|
|||
>;
|
|||
|
|||
fn prove<R: Rng>(
|
|||
parameter: &Self::ProvingParameters,
|
|||
input_and_witness: Self::AssignedCircuit,
|
|||
rng: &mut R,
|
|||
) -> Result<Self::Proof, Error>;
|
|||
|
|||
fn verify(
|
|||
verifier_key: &Self::PreparedVerificationParameters,
|
|||
input: &Self::VerifierInput,
|
|||
proof: &Self::Proof,
|
|||
) -> Result<bool, Error>;
|
|||
}
|
|||
|
|||
#[cfg(all(feature = "gm17", test))]
|
|||
mod test {
|
|||
use algebra::test_rng;
|
|||
use core::ops::AddAssign;
|
|||
|
|||
#[test]
|
|||
fn test_gm17() {
|
|||
use crate::nizk::{gm17::Gm17, NIZK};
|
|||
use algebra::{
|
|||
bls12_377::{Bls12_377, Fr},
|
|||
One,
|
|||
};
|
|||
use r1cs_core::{lc, ConstraintSynthesizer, ConstraintSystemRef, SynthesisError, Variable};
|
|||
|
|||
#[derive(Copy, Clone)]
|
|||
struct R1CSCircuit {
|
|||
x: Option<Fr>,
|
|||
sum: Option<Fr>,
|
|||
w: Option<Fr>,
|
|||
}
|
|||
|
|||
impl R1CSCircuit {
|
|||
pub(super) fn new(x: Fr, sum: Fr, w: Fr) -> Self {
|
|||
Self {
|
|||
x: Some(x),
|
|||
sum: Some(sum),
|
|||
w: Some(w),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl ConstraintSynthesizer<Fr> for R1CSCircuit {
|
|||
fn generate_constraints(
|
|||
self,
|
|||
cs: ConstraintSystemRef<Fr>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
let input = cs.new_input_variable(|| Ok(self.x.unwrap()))?;
|
|||
let sum = cs.new_input_variable(|| Ok(self.sum.unwrap()))?;
|
|||
let witness = cs.new_witness_variable(|| Ok(self.w.unwrap()))?;
|
|||
|
|||
cs.enforce_constraint(lc!() + sum, lc!() + Variable::One, lc!() + input + witness)?;
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
let mut sum = Fr::one();
|
|||
sum.add_assign(&Fr::one());
|
|||
let circuit = R1CSCircuit::new(Fr::one(), sum, Fr::one());
|
|||
|
|||
let rng = &mut test_rng();
|
|||
|
|||
let parameters = Gm17::<Bls12_377, R1CSCircuit, [Fr]>::setup(circuit, rng).unwrap();
|
|||
|
|||
let proof =
|
|||
Gm17::<Bls12_377, R1CSCircuit, [Fr]>::prove(¶meters.0, circuit, rng).unwrap();
|
|||
|
|||
let result =
|
|||
Gm17::<Bls12_377, R1CSCircuit, [Fr]>::verify(¶meters.1, &[Fr::one(), sum], &proof)
|
|||
.unwrap();
|
|||
assert!(result);
|
|||
}
|
|||
}
|
@ -1,537 +0,0 @@ |
|||
use algebra_core::PrimeField;
|
|||
use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError};
|
|||
|
|||
use crate::{prf::PRFGadget, Vec};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use core::borrow::Borrow;
|
|||
|
|||
// 2.1. Parameters
|
|||
// The following table summarizes various parameters and their ranges:
|
|||
// | BLAKE2b | BLAKE2s |
|
|||
// --------------+------------------+------------------+
|
|||
// Bits in word | w = 64 | w = 32 |
|
|||
// Rounds in F | r = 12 | r = 10 |
|
|||
// Block bytes | bb = 128 | bb = 64 |
|
|||
// Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
|
|||
// Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
|
|||
// Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
|
|||
// --------------+------------------+------------------+
|
|||
// G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
|
|||
// constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
|
|||
// --------------+------------------+------------------+
|
|||
//
|
|||
|
|||
const R1: usize = 16;
|
|||
const R2: usize = 12;
|
|||
const R3: usize = 8;
|
|||
const R4: usize = 7;
|
|||
|
|||
// Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
|||
// ----------+-------------------------------------------------+
|
|||
// SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
|||
// SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
|
|||
// SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
|
|||
// SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
|
|||
// SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
|
|||
// SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
|
|||
// SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
|
|||
// SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
|
|||
// SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
|
|||
// SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
|
|||
// ----------+-------------------------------------------------+
|
|||
//
|
|||
|
|||
const SIGMA: [[usize; 16]; 10] = [
|
|||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|||
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
|||
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
|||
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
|||
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
|||
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
|||
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
|||
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
|||
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
|||
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
|
|||
];
|
|||
|
|||
// 3.1. Mixing Function G
|
|||
// The G primitive function mixes two input words, "x" and "y", into
|
|||
// four words indexed by "a", "b", "c", and "d" in the working vector
|
|||
// v[0..15]. The full modified vector is returned. The rotation
|
|||
// constants (R1, R2, R3, R4) are given in Section 2.1.
|
|||
// FUNCTION G( v[0..15], a, b, c, d, x, y )
|
|||
// |
|
|||
// | v[a] := (v[a] + v[b] + x) mod 2**w
|
|||
// | v[d] := (v[d] ^ v[a]) >>> R1
|
|||
// | v[c] := (v[c] + v[d]) mod 2**w
|
|||
// | v[b] := (v[b] ^ v[c]) >>> R2
|
|||
// | v[a] := (v[a] + v[b] + y) mod 2**w
|
|||
// | v[d] := (v[d] ^ v[a]) >>> R3
|
|||
// | v[c] := (v[c] + v[d]) mod 2**w
|
|||
// | v[b] := (v[b] ^ v[c]) >>> R4
|
|||
// |
|
|||
// | RETURN v[0..15]
|
|||
// |
|
|||
// END FUNCTION.
|
|||
//
|
|||
|
|||
fn mixing_g<ConstraintF: PrimeField>(
|
|||
v: &mut [UInt32<ConstraintF>],
|
|||
a: usize,
|
|||
b: usize,
|
|||
c: usize,
|
|||
d: usize,
|
|||
x: &UInt32<ConstraintF>,
|
|||
y: &UInt32<ConstraintF>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
v[a] = UInt32::addmany(&[v[a].clone(), v[b].clone(), x.clone()])?;
|
|||
v[d] = v[d].xor(&v[a])?.rotr(R1);
|
|||
v[c] = UInt32::addmany(&[v[c].clone(), v[d].clone()])?;
|
|||
v[b] = v[b].xor(&v[c])?.rotr(R2);
|
|||
v[a] = UInt32::addmany(&[v[a].clone(), v[b].clone(), y.clone()])?;
|
|||
v[d] = v[d].xor(&v[a])?.rotr(R3);
|
|||
v[c] = UInt32::addmany(&[v[c].clone(), v[d].clone()])?;
|
|||
v[b] = v[b].xor(&v[c])?.rotr(R4);
|
|||
|
|||
Ok(())
|
|||
}
|
|||
|
|||
// 3.2. Compression Function F
|
|||
// Compression function F takes as an argument the state vector "h",
|
|||
// message block vector "m" (last block is padded with zeros to full
|
|||
// block size, if required), 2w-bit offset counter "t", and final block
|
|||
// indicator flag "f". Local vector v[0..15] is used in processing. F
|
|||
// returns a new state vector. The number of rounds, "r", is 12 for
|
|||
// BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
|
|||
// FUNCTION F( h[0..7], m[0..15], t, f )
|
|||
// |
|
|||
// | // Initialize local work vector v[0..15]
|
|||
// | v[0..7] := h[0..7] // First half from state.
|
|||
// | v[8..15] := IV[0..7] // Second half from IV.
|
|||
// |
|
|||
// | v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
|
|||
// | v[13] := v[13] ^ (t >> w) // High word.
|
|||
// |
|
|||
// | IF f = TRUE THEN // last block flag?
|
|||
// | | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
|
|||
// | END IF.
|
|||
// |
|
|||
// | // Cryptographic mixing
|
|||
// | FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
|
|||
// | |
|
|||
// | | // Message word selection permutation for this round.
|
|||
// | | s[0..15] := SIGMA[i mod 10][0..15]
|
|||
// | |
|
|||
// | | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
|
|||
// | | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
|
|||
// | | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
|
|||
// | | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
|
|||
// | |
|
|||
// | | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
|
|||
// | | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
|
|||
// | | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
|
|||
// | | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
|
|||
// | |
|
|||
// | END FOR
|
|||
// |
|
|||
// | FOR i = 0 TO 7 DO // XOR the two halves.
|
|||
// | | h[i] := h[i] ^ v[i] ^ v[i + 8]
|
|||
// | END FOR.
|
|||
// |
|
|||
// | RETURN h[0..7] // New state.
|
|||
// |
|
|||
// END FUNCTION.
|
|||
//
|
|||
|
|||
fn blake2s_compression<ConstraintF: PrimeField>(
|
|||
h: &mut [UInt32<ConstraintF>],
|
|||
m: &[UInt32<ConstraintF>],
|
|||
t: u64,
|
|||
f: bool,
|
|||
) -> Result<(), SynthesisError> {
|
|||
assert_eq!(h.len(), 8);
|
|||
assert_eq!(m.len(), 16);
|
|||
|
|||
// static const uint32_t blake2s_iv[8] =
|
|||
// {
|
|||
// 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
|||
// 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
|||
// };
|
|||
//
|
|||
|
|||
let mut v = Vec::with_capacity(16);
|
|||
v.extend_from_slice(h);
|
|||
v.push(UInt32::constant(0x6A09E667));
|
|||
v.push(UInt32::constant(0xBB67AE85));
|
|||
v.push(UInt32::constant(0x3C6EF372));
|
|||
v.push(UInt32::constant(0xA54FF53A));
|
|||
v.push(UInt32::constant(0x510E527F));
|
|||
v.push(UInt32::constant(0x9B05688C));
|
|||
v.push(UInt32::constant(0x1F83D9AB));
|
|||
v.push(UInt32::constant(0x5BE0CD19));
|
|||
|
|||
assert_eq!(v.len(), 16);
|
|||
|
|||
v[12] = v[12].xor(&UInt32::constant(t as u32))?;
|
|||
v[13] = v[13].xor(&UInt32::constant((t >> 32) as u32))?;
|
|||
|
|||
if f {
|
|||
v[14] = v[14].xor(&UInt32::constant(u32::max_value()))?;
|
|||
}
|
|||
|
|||
for i in 0..10 {
|
|||
let s = SIGMA[i % 10];
|
|||
|
|||
mixing_g(&mut v, 0, 4, 8, 12, &m[s[0]], &m[s[1]])?;
|
|||
mixing_g(&mut v, 1, 5, 9, 13, &m[s[2]], &m[s[3]])?;
|
|||
mixing_g(&mut v, 2, 6, 10, 14, &m[s[4]], &m[s[5]])?;
|
|||
mixing_g(&mut v, 3, 7, 11, 15, &m[s[6]], &m[s[7]])?;
|
|||
mixing_g(&mut v, 0, 5, 10, 15, &m[s[8]], &m[s[9]])?;
|
|||
mixing_g(&mut v, 1, 6, 11, 12, &m[s[10]], &m[s[11]])?;
|
|||
mixing_g(&mut v, 2, 7, 8, 13, &m[s[12]], &m[s[13]])?;
|
|||
mixing_g(&mut v, 3, 4, 9, 14, &m[s[14]], &m[s[15]])?;
|
|||
}
|
|||
|
|||
for i in 0..8 {
|
|||
h[i] = h[i].xor(&v[i])?;
|
|||
h[i] = h[i].xor(&v[i + 8])?;
|
|||
}
|
|||
|
|||
Ok(())
|
|||
}
|
|||
|
|||
// FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
|||
// |
|
|||
// | h[0..7] := IV[0..7] // Initialization Vector.
|
|||
// |
|
|||
// | // Parameter block p[0]
|
|||
// | h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
|||
// |
|
|||
// | // Process padded key and data blocks
|
|||
// | IF dd > 1 THEN
|
|||
// | | FOR i = 0 TO dd - 2 DO
|
|||
// | | | h := F( h, d[i], (i + 1) * bb, FALSE )
|
|||
// | | END FOR.
|
|||
// | END IF.
|
|||
// |
|
|||
// | // Final block.
|
|||
// | IF kk = 0 THEN
|
|||
// | | h := F( h, d[dd - 1], ll, TRUE )
|
|||
// | ELSE
|
|||
// | | h := F( h, d[dd - 1], ll + bb, TRUE )
|
|||
// | END IF.
|
|||
// |
|
|||
// | RETURN first "nn" bytes from little-endian word array h[].
|
|||
// |
|
|||
// END FUNCTION.
|
|||
//
|
|||
|
|||
pub fn evaluate_blake2s<ConstraintF: PrimeField>(
|
|||
input: &[Boolean<ConstraintF>],
|
|||
) -> Result<Vec<UInt32<ConstraintF>>, SynthesisError> {
|
|||
assert!(input.len() % 8 == 0);
|
|||
let mut parameters = [0; 8];
|
|||
parameters[0] = 0x01010000 ^ 32;
|
|||
evaluate_blake2s_with_parameters(input, ¶meters)
|
|||
}
|
|||
|
|||
pub fn evaluate_blake2s_with_parameters<F: PrimeField>(
|
|||
input: &[Boolean<F>],
|
|||
parameters: &[u32; 8],
|
|||
) -> Result<Vec<UInt32<F>>, SynthesisError> {
|
|||
assert!(input.len() % 8 == 0);
|
|||
|
|||
let mut h = Vec::with_capacity(8);
|
|||
h.push(UInt32::constant(0x6A09E667).xor(&UInt32::constant(parameters[0]))?);
|
|||
h.push(UInt32::constant(0xBB67AE85).xor(&UInt32::constant(parameters[1]))?);
|
|||
h.push(UInt32::constant(0x3C6EF372).xor(&UInt32::constant(parameters[2]))?);
|
|||
h.push(UInt32::constant(0xA54FF53A).xor(&UInt32::constant(parameters[3]))?);
|
|||
h.push(UInt32::constant(0x510E527F).xor(&UInt32::constant(parameters[4]))?);
|
|||
h.push(UInt32::constant(0x9B05688C).xor(&UInt32::constant(parameters[5]))?);
|
|||
h.push(UInt32::constant(0x1F83D9AB).xor(&UInt32::constant(parameters[6]))?);
|
|||
h.push(UInt32::constant(0x5BE0CD19).xor(&UInt32::constant(parameters[7]))?);
|
|||
|
|||
let mut blocks: Vec<Vec<UInt32<F>>> = vec![];
|
|||
|
|||
for block in input.chunks(512) {
|
|||
let mut this_block = Vec::with_capacity(16);
|
|||
for word in block.chunks(32) {
|
|||
let mut tmp = word.to_vec();
|
|||
while tmp.len() < 32 {
|
|||
tmp.push(Boolean::constant(false));
|
|||
}
|
|||
this_block.push(UInt32::from_bits_le(&tmp));
|
|||
}
|
|||
while this_block.len() < 16 {
|
|||
this_block.push(UInt32::constant(0));
|
|||
}
|
|||
blocks.push(this_block);
|
|||
}
|
|||
|
|||
if blocks.is_empty() {
|
|||
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
|||
}
|
|||
|
|||
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
|
|||
blake2s_compression(&mut h, block, ((i as u64) + 1) * 64, false)?;
|
|||
}
|
|||
|
|||
blake2s_compression(
|
|||
&mut h,
|
|||
&blocks[blocks.len() - 1],
|
|||
(input.len() / 8) as u64,
|
|||
true,
|
|||
)?;
|
|||
|
|||
Ok(h)
|
|||
}
|
|||
|
|||
use crate::prf::Blake2s;
|
|||
|
|||
pub struct Blake2sGadget;
|
|||
#[derive(Clone, Debug)]
|
|||
pub struct OutputVar<ConstraintF: PrimeField>(pub Vec<UInt8<ConstraintF>>);
|
|||
|
|||
impl<ConstraintF: PrimeField> EqGadget<ConstraintF> for OutputVar<ConstraintF> {
|
|||
#[tracing::instrument(target = "r1cs")]
|
|||
fn is_eq(&self, other: &Self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
|||
self.0.is_eq(&other.0)
|
|||
}
|
|||
|
|||
/// If `should_enforce == true`, enforce that `self` and `other` are equal; else,
|
|||
/// enforce a vacuously true statement.
|
|||
#[tracing::instrument(target = "r1cs")]
|
|||
fn conditional_enforce_equal(
|
|||
&self,
|
|||
other: &Self,
|
|||
should_enforce: &Boolean<ConstraintF>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
self.0.conditional_enforce_equal(&other.0, should_enforce)
|
|||
}
|
|||
|
|||
/// If `should_enforce == true`, enforce that `self` and `other` are not equal; else,
|
|||
/// enforce a vacuously true statement.
|
|||
#[tracing::instrument(target = "r1cs")]
|
|||
fn conditional_enforce_not_equal(
|
|||
&self,
|
|||
other: &Self,
|
|||
should_enforce: &Boolean<ConstraintF>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
self.0
|
|||
.as_slice()
|
|||
.conditional_enforce_not_equal(other.0.as_slice(), should_enforce)
|
|||
}
|
|||
}
|
|||
|
|||
impl<ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for OutputVar<ConstraintF> {
|
|||
#[inline]
|
|||
fn to_bytes(&self) -> Result<Vec<UInt8<ConstraintF>>, SynthesisError> {
|
|||
Ok(self.0.clone())
|
|||
}
|
|||
}
|
|||
|
|||
impl<ConstraintF: PrimeField> AllocVar<[u8; 32], ConstraintF> for OutputVar<ConstraintF> {
|
|||
#[tracing::instrument(target = "r1cs", skip(cs, f))]
|
|||
fn new_variable<T: Borrow<[u8; 32]>>(
|
|||
cs: impl Into<Namespace<ConstraintF>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let bytes = f().map(|b| *b.borrow()).unwrap_or([0u8; 32]);
|
|||
match mode {
|
|||
AllocationMode::Constant => Ok(Self(UInt8::constant_vec(&bytes))),
|
|||
AllocationMode::Input => UInt8::new_input_vec(cs, &bytes).map(Self),
|
|||
AllocationMode::Witness => UInt8::new_witness_vec(cs, &bytes).map(Self),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl<F: PrimeField> R1CSVar<F> for OutputVar<F> {
|
|||
type Value = [u8; 32];
|
|||
|
|||
fn cs(&self) -> ConstraintSystemRef<F> {
|
|||
self.0.cs()
|
|||
}
|
|||
|
|||
fn value(&self) -> Result<Self::Value, SynthesisError> {
|
|||
let mut value = [0u8; 32];
|
|||
for (val_i, self_i) in value.iter_mut().zip(&self.0) {
|
|||
*val_i = self_i.value()?;
|
|||
}
|
|||
Ok(value)
|
|||
}
|
|||
}
|
|||
|
|||
impl<F: PrimeField> PRFGadget<Blake2s, F> for Blake2sGadget {
|
|||
type OutputVar = OutputVar<F>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(cs))]
|
|||
fn new_seed(cs: impl Into<Namespace<F>>, seed: &[u8; 32]) -> Vec<UInt8<F>> {
|
|||
let ns = cs.into();
|
|||
let cs = ns.cs();
|
|||
UInt8::new_witness_vec(r1cs_core::ns!(cs, "New Blake2s seed"), seed).unwrap()
|
|||
}
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(seed, input))]
|
|||
fn evaluate(seed: &[UInt8<F>], input: &[UInt8<F>]) -> Result<Self::OutputVar, SynthesisError> {
|
|||
assert_eq!(seed.len(), 32);
|
|||
let input: Vec<_> = seed
|
|||
.iter()
|
|||
.chain(input)
|
|||
.flat_map(|b| b.to_bits_le().unwrap())
|
|||
.collect();
|
|||
let result: Vec<_> = evaluate_blake2s(&input)?
|
|||
.into_iter()
|
|||
.flat_map(|int| int.to_bytes().unwrap())
|
|||
.collect();
|
|||
Ok(OutputVar(result))
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use algebra::ed_on_bls12_381::Fq as Fr;
|
|||
use rand::{Rng, SeedableRng};
|
|||
use rand_xorshift::XorShiftRng;
|
|||
|
|||
use crate::prf::blake2s::{constraints::evaluate_blake2s, Blake2s as B2SPRF};
|
|||
use blake2::VarBlake2s;
|
|||
use r1cs_core::ConstraintSystem;
|
|||
|
|||
use super::Blake2sGadget;
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
#[test]
|
|||
fn test_blake2s_constraints() {
|
|||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|||
let input_bits: Vec<_> = (0..512)
|
|||
.map(|_| Boolean::new_witness(r1cs_core::ns!(cs, "input bit"), || Ok(true)).unwrap())
|
|||
.collect();
|
|||
evaluate_blake2s(&input_bits).unwrap();
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
assert_eq!(cs.num_constraints(), 21792);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_blake2s_prf() {
|
|||
use crate::prf::{PRFGadget, PRF};
|
|||
use rand::Rng;
|
|||
|
|||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|||
|
|||
let mut seed = [0u8; 32];
|
|||
rng.fill(&mut seed);
|
|||
|
|||
let mut input = [0u8; 32];
|
|||
rng.fill(&mut input);
|
|||
|
|||
let seed_var = Blake2sGadget::new_seed(cs.clone(), &seed);
|
|||
let input_var =
|
|||
UInt8::new_witness_vec(r1cs_core::ns!(cs, "declare_input"), &input).unwrap();
|
|||
let out = B2SPRF::evaluate(&seed, &input).unwrap();
|
|||
let actual_out_var = <Blake2sGadget as PRFGadget<_, Fr>>::OutputVar::new_witness(
|
|||
r1cs_core::ns!(cs, "declare_output"),
|
|||
|| Ok(out),
|
|||
)
|
|||
.unwrap();
|
|||
|
|||
let output_var = Blake2sGadget::evaluate(&seed_var, &input_var).unwrap();
|
|||
output_var.enforce_equal(&actual_out_var).unwrap();
|
|||
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!(
|
|||
"which is unsatisfied: {:?}",
|
|||
cs.which_is_unsatisfied().unwrap()
|
|||
);
|
|||
}
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_blake2s_precomp_constraints() {
|
|||
// Test that 512 fixed leading bits (constants)
|
|||
// doesn't result in more constraints.
|
|||
|
|||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|||
let input_bits: Vec<_> = (0..512)
|
|||
.map(|_| Boolean::constant(rng.gen()))
|
|||
.chain((0..512).map(|_| {
|
|||
Boolean::new_witness(r1cs_core::ns!(cs, "input bit"), || Ok(true)).unwrap()
|
|||
}))
|
|||
.collect();
|
|||
evaluate_blake2s(&input_bits).unwrap();
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
assert_eq!(cs.num_constraints(), 21792);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_blake2s_constant_constraints() {
|
|||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|||
let input_bits: Vec<_> = (0..512)
|
|||
.map(|_| Boolean::<Fr>::constant(rng.gen()))
|
|||
.collect();
|
|||
evaluate_blake2s(&input_bits).unwrap();
|
|||
assert_eq!(cs.num_constraints(), 0);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_blake2s() {
|
|||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|||
|
|||
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
|
|||
use digest::*;
|
|||
let mut h = VarBlake2s::new_keyed(&[], 32);
|
|||
|
|||
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
|
|||
|
|||
h.input(&data);
|
|||
|
|||
let mut hash_result = Vec::with_capacity(h.output_size());
|
|||
h.variable_result(|res| hash_result.extend_from_slice(res));
|
|||
|
|||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|||
|
|||
let mut input_bits = vec![];
|
|||
|
|||
for input_byte in data.into_iter() {
|
|||
for bit_i in 0..8 {
|
|||
let cs = r1cs_core::ns!(cs, "input bit");
|
|||
|
|||
input_bits.push(
|
|||
Boolean::new_witness(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8))
|
|||
.unwrap(),
|
|||
);
|
|||
}
|
|||
}
|
|||
|
|||
let r = evaluate_blake2s(&input_bits).unwrap();
|
|||
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
|
|||
let mut s = hash_result
|
|||
.iter()
|
|||
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
|||
|
|||
for chunk in r {
|
|||
for b in chunk.to_bits_le() {
|
|||
match b {
|
|||
Boolean::Is(b) => {
|
|||
assert!(s.next().unwrap() == b.value().unwrap());
|
|||
}
|
|||
Boolean::Not(b) => {
|
|||
assert!(s.next().unwrap() != b.value().unwrap());
|
|||
}
|
|||
Boolean::Constant(b) => {
|
|||
assert!(input_len == 0);
|
|||
assert!(s.next().unwrap() == b);
|
|||
}
|
|||
}
|
|||
}
|
|||
}
|
|||
}
|
|||
}
|
|||
}
|
@ -1,92 +0,0 @@ |
|||
use alloc::vec::Vec;
|
|||
use blake2::{Blake2s as B2s, VarBlake2s};
|
|||
use digest::Digest;
|
|||
|
|||
use super::PRF;
|
|||
use crate::CryptoError;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub struct Blake2s;
|
|||
|
|||
impl PRF for Blake2s {
|
|||
type Input = [u8; 32];
|
|||
type Output = [u8; 32];
|
|||
type Seed = [u8; 32];
|
|||
|
|||
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError> {
|
|||
let eval_time = start_timer!(|| "Blake2s::Eval");
|
|||
let mut h = B2s::new();
|
|||
h.input(seed.as_ref());
|
|||
h.input(input.as_ref());
|
|||
let mut result = [0u8; 32];
|
|||
result.copy_from_slice(&h.result());
|
|||
end_timer!(eval_time);
|
|||
Ok(result)
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone)]
|
|||
pub struct Blake2sWithParameterBlock {
|
|||
pub digest_length: u8,
|
|||
pub key_length: u8,
|
|||
pub fan_out: u8,
|
|||
pub depth: u8,
|
|||
pub leaf_length: u32,
|
|||
pub node_offset: u32,
|
|||
pub xof_digest_length: u16,
|
|||
pub node_depth: u8,
|
|||
pub inner_length: u8,
|
|||
pub salt: [u8; 8],
|
|||
pub personalization: [u8; 8],
|
|||
}
|
|||
|
|||
impl Blake2sWithParameterBlock {
|
|||
pub fn parameters(&self) -> [u32; 8] {
|
|||
let mut parameters = [0; 8];
|
|||
parameters[0] = u32::from_le_bytes([
|
|||
self.digest_length,
|
|||
self.key_length,
|
|||
self.fan_out,
|
|||
self.depth,
|
|||
]);
|
|||
parameters[1] = self.leaf_length;
|
|||
parameters[2] = self.node_offset;
|
|||
parameters[3] = u32::from_le_bytes([
|
|||
self.xof_digest_length as u8,
|
|||
(self.xof_digest_length >> 8) as u8,
|
|||
self.node_depth,
|
|||
self.inner_length,
|
|||
]);
|
|||
|
|||
let mut salt_bytes_1 = [0; 4];
|
|||
let mut salt_bytes_2 = [0; 4];
|
|||
let mut personalization_bytes_1 = [0; 4];
|
|||
let mut personalization_bytes_2 = [0; 4];
|
|||
for i in 0..4 {
|
|||
salt_bytes_1[i] = self.salt[i];
|
|||
salt_bytes_2[i] = self.salt[4 + i];
|
|||
personalization_bytes_1[i] = self.personalization[i];
|
|||
personalization_bytes_2[i] = self.personalization[4 + i];
|
|||
}
|
|||
parameters[4] = u32::from_le_bytes(salt_bytes_1);
|
|||
parameters[5] = u32::from_le_bytes(salt_bytes_2);
|
|||
parameters[6] = u32::from_le_bytes(personalization_bytes_1);
|
|||
parameters[7] = u32::from_le_bytes(personalization_bytes_2);
|
|||
|
|||
parameters
|
|||
}
|
|||
|
|||
pub fn evaluate(&self, input: &[u8]) -> Vec<u8> {
|
|||
use digest::*;
|
|||
let eval_time = start_timer!(|| "Blake2sWithParameterBlock::Eval");
|
|||
let mut h = VarBlake2s::with_parameter_block(&self.parameters());
|
|||
h.input(input.as_ref());
|
|||
end_timer!(eval_time);
|
|||
let mut buf = Vec::with_capacity(h.output_size());
|
|||
h.variable_result(|res| buf.extend_from_slice(res));
|
|||
buf
|
|||
}
|
|||
}
|
@ -1,20 +0,0 @@ |
|||
use algebra_core::Field;
|
|||
use core::fmt::Debug;
|
|||
|
|||
use crate::{prf::PRF, Vec};
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
pub trait PRFGadget<P: PRF, F: Field> {
|
|||
type OutputVar: EqGadget<F>
|
|||
+ ToBytesGadget<F>
|
|||
+ AllocVar<P::Output, F>
|
|||
+ R1CSVar<F, Value = P::Output>
|
|||
+ Clone
|
|||
+ Debug;
|
|||
|
|||
fn new_seed(cs: impl Into<Namespace<F>>, seed: &P::Seed) -> Vec<UInt8<F>>;
|
|||
|
|||
fn evaluate(seed: &[UInt8<F>], input: &[UInt8<F>]) -> Result<Self::OutputVar, SynthesisError>;
|
|||
}
|
@ -1,20 +0,0 @@ |
|||
use algebra_core::bytes::{FromBytes, ToBytes};
|
|||
use core::{fmt::Debug, hash::Hash};
|
|||
|
|||
use crate::CryptoError;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub use constraints::*;
|
|||
|
|||
pub mod blake2s;
|
|||
pub use self::blake2s::*;
|
|||
|
|||
pub trait PRF {
|
|||
type Input: FromBytes + Default;
|
|||
type Output: ToBytes + Eq + Clone + Debug + Default + Hash;
|
|||
type Seed: FromBytes + ToBytes + Clone + Default + Debug;
|
|||
|
|||
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError>;
|
|||
}
|
@ -1,20 +0,0 @@ |
|||
use algebra_core::Field;
|
|||
use r1cs_core::SynthesisError;
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use crate::signature::SignatureScheme;
|
|||
|
|||
pub trait SigRandomizePkGadget<S: SignatureScheme, ConstraintF: Field> {
|
|||
type ParametersVar: AllocVar<S::Parameters, ConstraintF> + Clone;
|
|||
|
|||
type PublicKeyVar: ToBytesGadget<ConstraintF>
|
|||
+ EqGadget<ConstraintF>
|
|||
+ AllocVar<S::PublicKey, ConstraintF>
|
|||
+ Clone;
|
|||
|
|||
fn randomize(
|
|||
parameters: &Self::ParametersVar,
|
|||
public_key: &Self::PublicKeyVar,
|
|||
randomness: &[UInt8<ConstraintF>],
|
|||
) -> Result<Self::PublicKeyVar, SynthesisError>;
|
|||
}
|
@ -1,104 +0,0 @@ |
|||
use crate::Error;
|
|||
use algebra_core::bytes::ToBytes;
|
|||
use core::hash::Hash;
|
|||
use rand::Rng;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub use constraints::*;
|
|||
|
|||
pub mod schnorr;
|
|||
|
|||
pub trait SignatureScheme {
|
|||
type Parameters: Clone + Send + Sync;
|
|||
type PublicKey: ToBytes + Hash + Eq + Clone + Default + Send + Sync;
|
|||
type SecretKey: ToBytes + Clone + Default;
|
|||
type Signature: Clone + Default + Send + Sync;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error>;
|
|||
|
|||
fn keygen<R: Rng>(
|
|||
pp: &Self::Parameters,
|
|||
rng: &mut R,
|
|||
) -> Result<(Self::PublicKey, Self::SecretKey), Error>;
|
|||
|
|||
fn sign<R: Rng>(
|
|||
pp: &Self::Parameters,
|
|||
sk: &Self::SecretKey,
|
|||
message: &[u8],
|
|||
rng: &mut R,
|
|||
) -> Result<Self::Signature, Error>;
|
|||
|
|||
fn verify(
|
|||
pp: &Self::Parameters,
|
|||
pk: &Self::PublicKey,
|
|||
message: &[u8],
|
|||
signature: &Self::Signature,
|
|||
) -> Result<bool, Error>;
|
|||
|
|||
fn randomize_public_key(
|
|||
pp: &Self::Parameters,
|
|||
public_key: &Self::PublicKey,
|
|||
randomness: &[u8],
|
|||
) -> Result<Self::PublicKey, Error>;
|
|||
|
|||
fn randomize_signature(
|
|||
pp: &Self::Parameters,
|
|||
signature: &Self::Signature,
|
|||
randomness: &[u8],
|
|||
) -> Result<Self::Signature, Error>;
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod test {
|
|||
use crate::signature::{schnorr, *};
|
|||
use algebra::{
|
|||
ed_on_bls12_381::EdwardsProjective as JubJub, groups::Group, test_rng, to_bytes,
|
|||
UniformRand,
|
|||
};
|
|||
use blake2::Blake2s;
|
|||
|
|||
fn sign_and_verify<S: SignatureScheme>(message: &[u8]) {
|
|||
let rng = &mut test_rng();
|
|||
let parameters = S::setup::<_>(rng).unwrap();
|
|||
let (pk, sk) = S::keygen(¶meters, rng).unwrap();
|
|||
let sig = S::sign(¶meters, &sk, &message, rng).unwrap();
|
|||
assert!(S::verify(¶meters, &pk, &message, &sig).unwrap());
|
|||
}
|
|||
|
|||
fn failed_verification<S: SignatureScheme>(message: &[u8], bad_message: &[u8]) {
|
|||
let rng = &mut test_rng();
|
|||
let parameters = S::setup::<_>(rng).unwrap();
|
|||
let (pk, sk) = S::keygen(¶meters, rng).unwrap();
|
|||
let sig = S::sign(¶meters, &sk, message, rng).unwrap();
|
|||
assert!(!S::verify(¶meters, &pk, bad_message, &sig).unwrap());
|
|||
}
|
|||
|
|||
fn randomize_and_verify<S: SignatureScheme>(message: &[u8], randomness: &[u8]) {
|
|||
let rng = &mut test_rng();
|
|||
let parameters = S::setup::<_>(rng).unwrap();
|
|||
let (pk, sk) = S::keygen(¶meters, rng).unwrap();
|
|||
let sig = S::sign(¶meters, &sk, message, rng).unwrap();
|
|||
assert!(S::verify(¶meters, &pk, message, &sig).unwrap());
|
|||
let randomized_pk = S::randomize_public_key(¶meters, &pk, randomness).unwrap();
|
|||
let randomized_sig = S::randomize_signature(¶meters, &sig, randomness).unwrap();
|
|||
assert!(S::verify(¶meters, &randomized_pk, &message, &randomized_sig).unwrap());
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn schnorr_signature_test() {
|
|||
let message = "Hi, I am a Schnorr signature!";
|
|||
let rng = &mut test_rng();
|
|||
sign_and_verify::<schnorr::Schnorr<JubJub, Blake2s>>(message.as_bytes());
|
|||
failed_verification::<schnorr::Schnorr<JubJub, Blake2s>>(
|
|||
message.as_bytes(),
|
|||
"Bad message".as_bytes(),
|
|||
);
|
|||
let random_scalar = to_bytes!(<JubJub as Group>::ScalarField::rand(rng)).unwrap();
|
|||
randomize_and_verify::<schnorr::Schnorr<JubJub, Blake2s>>(
|
|||
message.as_bytes(),
|
|||
&random_scalar.as_slice(),
|
|||
);
|
|||
}
|
|||
}
|
@ -1,158 +0,0 @@ |
|||
use crate::Vec;
|
|||
use algebra_core::{Field, ProjectiveCurve};
|
|||
use r1cs_core::{Namespace, SynthesisError};
|
|||
use r1cs_std::prelude::*;
|
|||
|
|||
use crate::signature::SigRandomizePkGadget;
|
|||
|
|||
use core::{borrow::Borrow, marker::PhantomData};
|
|||
|
|||
use crate::signature::schnorr::{Parameters, PublicKey, Schnorr};
|
|||
use digest::Digest;
|
|||
|
|||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
|||
|
|||
#[derive(Clone)]
|
|||
pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
|||
where
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
generator: GC,
|
|||
_curve: PhantomData<C>,
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(
|
|||
Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
|
|||
Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
|
|||
)]
|
|||
pub struct PublicKeyVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
|||
where
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
pub_key: GC,
|
|||
#[doc(hidden)]
|
|||
_group: PhantomData<*const C>,
|
|||
}
|
|||
|
|||
pub struct SchnorrRandomizePkGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
|||
where
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
#[doc(hidden)]
|
|||
_group: PhantomData<*const C>,
|
|||
#[doc(hidden)]
|
|||
_group_gadget: PhantomData<*const GC>,
|
|||
}
|
|||
|
|||
impl<C, GC, D> SigRandomizePkGadget<Schnorr<C, D>, ConstraintF<C>>
|
|||
for SchnorrRandomizePkGadget<C, GC>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GC: CurveVar<C, ConstraintF<C>>,
|
|||
D: Digest + Send + Sync,
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
type ParametersVar = ParametersVar<C, GC>;
|
|||
type PublicKeyVar = PublicKeyVar<C, GC>;
|
|||
|
|||
#[tracing::instrument(target = "r1cs", skip(parameters, public_key, randomness))]
|
|||
fn randomize(
|
|||
parameters: &Self::ParametersVar,
|
|||
public_key: &Self::PublicKeyVar,
|
|||
randomness: &[UInt8<ConstraintF<C>>],
|
|||
) -> Result<Self::PublicKeyVar, SynthesisError> {
|
|||
let base = parameters.generator.clone();
|
|||
let randomness = randomness
|
|||
.iter()
|
|||
.flat_map(|b| b.to_bits_le().unwrap())
|
|||
.collect::<Vec<_>>();
|
|||
let rand_pk = &public_key.pub_key + &base.scalar_mul_le(randomness.iter())?;
|
|||
Ok(PublicKeyVar {
|
|||
pub_key: rand_pk,
|
|||
_group: PhantomData,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<C, GC, D> AllocVar<Parameters<C, D>, ConstraintF<C>> for ParametersVar<C, GC>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GC: CurveVar<C, ConstraintF<C>>,
|
|||
D: Digest,
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
fn new_variable<T: Borrow<Parameters<C, D>>>(
|
|||
cs: impl Into<Namespace<ConstraintF<C>>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let generator = GC::new_variable(cs, || f().map(|g| g.borrow().generator), mode)?;
|
|||
Ok(Self {
|
|||
generator,
|
|||
_curve: PhantomData,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<C, GC> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GC>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GC: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
fn new_variable<T: Borrow<PublicKey<C>>>(
|
|||
cs: impl Into<Namespace<ConstraintF<C>>>,
|
|||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|||
mode: AllocationMode,
|
|||
) -> Result<Self, SynthesisError> {
|
|||
let pub_key = GC::new_variable(cs, f, mode)?;
|
|||
Ok(Self {
|
|||
pub_key,
|
|||
_group: PhantomData,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl<C, GC> EqGadget<ConstraintF<C>> for PublicKeyVar<C, GC>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GC: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
#[inline]
|
|||
fn is_eq(&self, other: &Self) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
|
|||
self.pub_key.is_eq(&other.pub_key)
|
|||
}
|
|||
|
|||
#[inline]
|
|||
fn conditional_enforce_equal(
|
|||
&self,
|
|||
other: &Self,
|
|||
condition: &Boolean<ConstraintF<C>>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
self.pub_key
|
|||
.conditional_enforce_equal(&other.pub_key, condition)
|
|||
}
|
|||
|
|||
#[inline]
|
|||
fn conditional_enforce_not_equal(
|
|||
&self,
|
|||
other: &Self,
|
|||
condition: &Boolean<ConstraintF<C>>,
|
|||
) -> Result<(), SynthesisError> {
|
|||
self.pub_key
|
|||
.conditional_enforce_not_equal(&other.pub_key, condition)
|
|||
}
|
|||
}
|
|||
|
|||
impl<C, GC> ToBytesGadget<ConstraintF<C>> for PublicKeyVar<C, GC>
|
|||
where
|
|||
C: ProjectiveCurve,
|
|||
GC: CurveVar<C, ConstraintF<C>>,
|
|||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|||
{
|
|||
fn to_bytes(&self) -> Result<Vec<UInt8<ConstraintF<C>>>, SynthesisError> {
|
|||
self.pub_key.to_bytes()
|
|||
}
|
|||
}
|
@ -1,229 +0,0 @@ |
|||
use crate::{Error, SignatureScheme, Vec};
|
|||
use algebra_core::{
|
|||
bytes::ToBytes,
|
|||
fields::{Field, PrimeField},
|
|||
io::{Result as IoResult, Write},
|
|||
to_bytes, AffineCurve, One, ProjectiveCurve, ToConstraintField, UniformRand, Zero,
|
|||
};
|
|||
use core::{hash::Hash, marker::PhantomData};
|
|||
use digest::Digest;
|
|||
use rand::Rng;
|
|||
|
|||
#[cfg(feature = "r1cs")]
|
|||
pub mod constraints;
|
|||
|
|||
pub struct Schnorr<C: ProjectiveCurve, D: Digest> {
|
|||
_group: PhantomData<C>,
|
|||
_hash: PhantomData<D>,
|
|||
}
|
|||
|
|||
#[derive(Derivative)]
|
|||
#[derivative(Clone(bound = "C: ProjectiveCurve, H: Digest"), Debug)]
|
|||
pub struct Parameters<C: ProjectiveCurve, H: Digest> {
|
|||
_hash: PhantomData<H>,
|
|||
pub generator: C::Affine,
|
|||
pub salt: [u8; 32],
|
|||
}
|
|||
|
|||
pub type PublicKey<C> = <C as ProjectiveCurve>::Affine;
|
|||
|
|||
#[derive(Clone, Default, Debug)]
|
|||
pub struct SecretKey<C: ProjectiveCurve>(pub C::ScalarField);
|
|||
|
|||
impl<C: ProjectiveCurve> ToBytes for SecretKey<C> {
|
|||
#[inline]
|
|||
fn write<W: Write>(&self, writer: W) -> IoResult<()> {
|
|||
self.0.write(writer)
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone, Default, Debug)]
|
|||
pub struct Signature<C: ProjectiveCurve> {
|
|||
pub prover_response: C::ScalarField,
|
|||
pub verifier_challenge: C::ScalarField,
|
|||
}
|
|||
|
|||
impl<C: ProjectiveCurve + Hash, D: Digest + Send + Sync> SignatureScheme for Schnorr<C, D>
|
|||
where
|
|||
C::ScalarField: PrimeField,
|
|||
{
|
|||
type Parameters = Parameters<C, D>;
|
|||
type PublicKey = PublicKey<C>;
|
|||
type SecretKey = SecretKey<C>;
|
|||
type Signature = Signature<C>;
|
|||
|
|||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
|||
let setup_time = start_timer!(|| "SchnorrSig::Setup");
|
|||
|
|||
let mut salt = [0u8; 32];
|
|||
rng.fill_bytes(&mut salt);
|
|||
let generator = C::rand(rng).into();
|
|||
|
|||
end_timer!(setup_time);
|
|||
Ok(Parameters {
|
|||
_hash: PhantomData,
|
|||
generator,
|
|||
salt,
|
|||
})
|
|||
}
|
|||
|
|||
fn keygen<R: Rng>(
|
|||
parameters: &Self::Parameters,
|
|||
rng: &mut R,
|
|||
) -> Result<(Self::PublicKey, Self::SecretKey), Error> {
|
|||
let keygen_time = start_timer!(|| "SchnorrSig::KeyGen");
|
|||
|
|||
let secret_key = C::ScalarField::rand(rng);
|
|||
let public_key = parameters.generator.mul(secret_key).into();
|
|||
|
|||
end_timer!(keygen_time);
|
|||
Ok((public_key, SecretKey(secret_key)))
|
|||
}
|
|||
|
|||
fn sign<R: Rng>(
|
|||
parameters: &Self::Parameters,
|
|||
sk: &Self::SecretKey,
|
|||
message: &[u8],
|
|||
rng: &mut R,
|
|||
) -> Result<Self::Signature, Error> {
|
|||
let sign_time = start_timer!(|| "SchnorrSig::Sign");
|
|||
// (k, e);
|
|||
let (random_scalar, verifier_challenge) = loop {
|
|||
// Sample a random scalar `k` from the prime scalar field.
|
|||
let random_scalar: C::ScalarField = C::ScalarField::rand(rng);
|
|||
// Commit to the random scalar via r := k · G.
|
|||
// This is the prover's first msg in the Sigma protocol.
|
|||
let prover_commitment = parameters.generator.mul(random_scalar).into_affine();
|
|||
|
|||
// Hash everything to get verifier challenge.
|
|||
let mut hash_input = Vec::new();
|
|||
hash_input.extend_from_slice(¶meters.salt);
|
|||
hash_input.extend_from_slice(&to_bytes![prover_commitment]?);
|
|||
hash_input.extend_from_slice(message);
|
|||
|
|||
// Compute the supposed verifier response: e := H(salt || r || msg);
|
|||
if let Some(verifier_challenge) =
|
|||
C::ScalarField::from_random_bytes(&D::digest(&hash_input))
|
|||
{
|
|||
break (random_scalar, verifier_challenge);
|
|||
};
|
|||
};
|
|||
|
|||
// k - xe;
|
|||
let prover_response = random_scalar - &(verifier_challenge * &sk.0);
|
|||
let signature = Signature {
|
|||
prover_response,
|
|||
verifier_challenge,
|
|||
};
|
|||
|
|||
end_timer!(sign_time);
|
|||
Ok(signature)
|
|||
}
|
|||
|
|||
fn verify(
|
|||
parameters: &Self::Parameters,
|
|||
pk: &Self::PublicKey,
|
|||
message: &[u8],
|
|||
signature: &Self::Signature,
|
|||
) -> Result<bool, Error> {
|
|||
let verify_time = start_timer!(|| "SchnorrSig::Verify");
|
|||
|
|||
let Signature {
|
|||
prover_response,
|
|||
verifier_challenge,
|
|||
} = signature;
|
|||
let mut claimed_prover_commitment = parameters.generator.mul(*prover_response);
|
|||
let public_key_times_verifier_challenge = pk.mul(*verifier_challenge);
|
|||
claimed_prover_commitment += &public_key_times_verifier_challenge;
|
|||
let claimed_prover_commitment = claimed_prover_commitment.into_affine();
|
|||
|
|||
let mut hash_input = Vec::new();
|
|||
hash_input.extend_from_slice(¶meters.salt);
|
|||
hash_input.extend_from_slice(&to_bytes![claimed_prover_commitment]?);
|
|||
hash_input.extend_from_slice(&message);
|
|||
|
|||
let obtained_verifier_challenge = if let Some(obtained_verifier_challenge) =
|
|||
C::ScalarField::from_random_bytes(&D::digest(&hash_input))
|
|||
{
|
|||
obtained_verifier_challenge
|
|||
} else {
|
|||
return Ok(false);
|
|||
};
|
|||
end_timer!(verify_time);
|
|||
Ok(verifier_challenge == &obtained_verifier_challenge)
|
|||
}
|
|||
|
|||
fn randomize_public_key(
|
|||
parameters: &Self::Parameters,
|
|||
public_key: &Self::PublicKey,
|
|||
randomness: &[u8],
|
|||
) -> Result<Self::PublicKey, Error> {
|
|||
let rand_pk_time = start_timer!(|| "SchnorrSig::RandomizePubKey");
|
|||
|
|||
let randomized_pk = *public_key;
|
|||
let base = parameters.generator;
|
|||
let mut encoded = C::zero();
|
|||
for bit in bytes_to_bits(randomness)
|
|||
.into_iter()
|
|||
.rev()
|
|||
.skip_while(|b| !b)
|
|||
{
|
|||
encoded.double_in_place();
|
|||
if bit {
|
|||
encoded.add_assign_mixed(&base)
|
|||
}
|
|||
}
|
|||
encoded.add_assign_mixed(&randomized_pk);
|
|||
|
|||
end_timer!(rand_pk_time);
|
|||
|
|||
Ok(encoded.into())
|
|||
}
|
|||
|
|||
fn randomize_signature(
|
|||
_parameter: &Self::Parameters,
|
|||
signature: &Self::Signature,
|
|||
randomness: &[u8],
|
|||
) -> Result<Self::Signature, Error> {
|
|||
let rand_signature_time = start_timer!(|| "SchnorrSig::RandomizeSig");
|
|||
let Signature {
|
|||
prover_response,
|
|||
verifier_challenge,
|
|||
} = signature;
|
|||
let mut base = C::ScalarField::one();
|
|||
let mut multiplier = C::ScalarField::zero();
|
|||
for bit in bytes_to_bits(randomness) {
|
|||
if bit {
|
|||
multiplier += &base;
|
|||
}
|
|||
base.double_in_place();
|
|||
}
|
|||
|
|||
let new_sig = Signature {
|
|||
prover_response: *prover_response - &(*verifier_challenge * &multiplier),
|
|||
verifier_challenge: *verifier_challenge,
|
|||
};
|
|||
end_timer!(rand_signature_time);
|
|||
Ok(new_sig)
|
|||
}
|
|||
}
|
|||
|
|||
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
|||
let mut bits = Vec::with_capacity(bytes.len() * 8);
|
|||
for byte in bytes {
|
|||
for i in 0..8 {
|
|||
let bit = (*byte >> (8 - i - 1)) & 1;
|
|||
bits.push(bit == 1);
|
|||
}
|
|||
}
|
|||
bits
|
|||
}
|
|||
|
|||
impl<ConstraintF: Field, C: ProjectiveCurve + ToConstraintField<ConstraintF>, D: Digest>
|
|||
ToConstraintField<ConstraintF> for Parameters<C, D>
|
|||
{
|
|||
#[inline]
|
|||
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
|
|||
self.generator.into_projective().to_field_elements()
|
|||
}
|
|||
}
|
@ -1,58 +0,0 @@ |
|||
[package] |
|||
name = "r1cs-std" |
|||
version = "0.1.1-alpha.0" |
|||
authors = [ |
|||
"Sean Bowe", |
|||
"Alessandro Chiesa", |
|||
"Matthew Green", |
|||
"Ian Miers", |
|||
"Pratyush Mishra", |
|||
"Howard Wu" |
|||
] |
|||
description = "A standard library for constraint system gadgets" |
|||
homepage = "https://libzexe.org" |
|||
repository = "https://github.com/scipr/zexe" |
|||
documentation = "https://docs.rs/r1cs-std/" |
|||
keywords = ["zero knowledge", "cryptography", "zkSNARK", "SNARK"] |
|||
categories = ["cryptography"] |
|||
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] |
|||
license = "MIT/Apache-2.0" |
|||
edition = "2018" |
|||
|
|||
################################# Dependencies ################################ |
|||
|
|||
[dependencies] |
|||
algebra = { git = "https://github.com/scipr-lab/zexe", default-features = false } |
|||
r1cs-core = { git = "https://github.com/scipr-lab/zexe", default-features = false } |
|||
derivative = { version = "2", features = ["use_core"] } |
|||
tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } |
|||
|
|||
[dev-dependencies] |
|||
rand = { version = "0.7", default-features = false } |
|||
rand_xorshift = { version = "0.2" } |
|||
# Currently this means that all downstream users of `r1cs-std` will be using |
|||
# `algebra` with the `bls12_381` feature. This is because of a cargo bug. |
|||
algebra = { git = "https://github.com/scipr-lab/zexe", default-features = false, features = [ "bls12_381" ] } |
|||
|
|||
[features] |
|||
default = ["std"] |
|||
full = [ |
|||
"bls12_377", "ed_on_bn254", "ed_on_bls12_381", "ed_on_bls12_377", "ed_on_cp6_782", |
|||
"ed_on_bw6_761", "ed_on_mnt4_298", "ed_on_mnt4_753", "mnt4_298", "mnt4_753", "mnt6_298", "mnt6_753" |
|||
] |
|||
|
|||
bls12_377 = [ "algebra/bls12_377" ] |
|||
ed_on_bn254 = [ "algebra/ed_on_bn254" ] |
|||
ed_on_bls12_381 = [ "algebra/ed_on_bls12_381" ] |
|||
ed_on_bls12_377 = [ "algebra/ed_on_bls12_377" ] |
|||
ed_on_cp6_782 = [ "algebra/ed_on_cp6_782" ] |
|||
ed_on_bw6_761 = [ "algebra/ed_on_bw6_761", "algebra/ed_on_cp6_782" ] |
|||
ed_on_mnt4_298 = [ "algebra/ed_on_mnt4_298" ] |
|||
ed_on_mnt4_753 = [ "algebra/ed_on_mnt4_753" ] |
|||
mnt4_298 = [ "algebra/mnt4_298" ] |
|||
mnt4_753 = [ "algebra/mnt4_753" ] |
|||
mnt6_298 = [ "algebra/mnt6_298" ] |
|||
mnt6_753 = [ "algebra/mnt6_753" ] |
|||
|
|||
std = [ "algebra/std", "r1cs-core/std" ] |
|||
parallel = [ "std", "algebra/parallel" ] |
@ -1 +0,0 @@ |
|||
../LICENSE-APACHE |
@ -1 +0,0 @@ |
|||
../LICENSE-MIT |
@ -1,422 +0,0 @@ |
|||
use algebra::{prelude::*, BitIteratorBE};
|
|||
use core::{
|
|||
fmt::Debug,
|
|||
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
|
|||
};
|
|||
use r1cs_core::SynthesisError;
|
|||
|
|||
use crate::{prelude::*, Assignment};
|
|||
|
|||
/// This module contains a generic implementation of cubic extension field variables.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::CubicExtField`.
|
|||
pub mod cubic_extension;
|
|||
/// This module contains a generic implementation of quadratic extension field variables.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::QuadExtField`.
|
|||
pub mod quadratic_extension;
|
|||
|
|||
/// This module contains a generic implementation of prime field variables.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::Fp*`.
|
|||
pub mod fp;
|
|||
|
|||
/// This module contains a generic implementation of the degree-12 tower extension field.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::Fp12`
|
|||
pub mod fp12;
|
|||
/// This module contains a generic implementation of the degree-2 tower extension field.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::Fp2`
|
|||
pub mod fp2;
|
|||
/// This module contains a generic implementation of the degree-3 tower extension field.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::Fp3`
|
|||
pub mod fp3;
|
|||
/// This module contains a generic implementation of the degree-4 tower extension field.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::Fp4`
|
|||
pub mod fp4;
|
|||
/// This module contains a generic implementation of the degree-6 tower extension field.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::fp6_2over3::Fp6`
|
|||
pub mod fp6_2over3;
|
|||
/// This module contains a generic implementation of the degree-6 tower extension field.
|
|||
/// That is, it implements the R1CS equivalent of `algebra_core::fp6_3over2::Fp6`
|
|||
pub mod fp6_3over2;
|
|||
|
|||
/// This trait is a hack used to work around the lack of implied bounds.
|
|||
pub trait FieldOpsBounds<'a, F, T: 'a>: |
|||
Sized
|
|||
+ Add<&'a T, Output = T>
|
|||
+ Sub<&'a T, Output = T>
|
|||
+ Mul<&'a T, Output = T>
|
|||
+ Add<T, Output = T>
|
|||
+ Sub<T, Output = T>
|
|||
+ Mul<T, Output = T>
|
|||
+ Add<F, Output = T>
|
|||
+ Sub<F, Output = T>
|
|||
+ Mul<F, Output = T>
|
|||
{
|
|||
}
|
|||
|
|||
/// A variable representing a field. Corresponds to the native type `F`.
|
|||
pub trait FieldVar<F: Field, ConstraintF: Field>: |
|||
'static
|
|||
+ Clone
|
|||
+ From<Boolean<ConstraintF>>
|
|||
+ R1CSVar<ConstraintF, Value = F>
|
|||
+ EqGadget<ConstraintF>
|
|||
+ ToBitsGadget<ConstraintF>
|
|||
+ AllocVar<F, ConstraintF>
|
|||
+ ToBytesGadget<ConstraintF>
|
|||
+ CondSelectGadget<ConstraintF>
|
|||
+ for<'a> FieldOpsBounds<'a, F, Self>
|
|||
+ for<'a> AddAssign<&'a Self>
|
|||
+ for<'a> SubAssign<&'a Self>
|
|||
+ for<'a> MulAssign<&'a Self>
|
|||
+ AddAssign<Self>
|
|||
+ SubAssign<Self>
|
|||
+ MulAssign<Self>
|
|||
+ AddAssign<F>
|
|||
+ SubAssign<F>
|
|||
+ MulAssign<F>
|
|||
+ Debug
|
|||
{
|
|||
/// Returns the constant `F::zero()`.
|
|||
fn zero() -> Self;
|
|||
|
|||
/// Returns a `Boolean` representing whether `self == Self::zero()`.
|
|||
fn is_zero(&self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
|||
self.is_eq(&Self::zero())
|
|||
}
|
|||
|
|||
/// Returns the constant `F::one()`.
|
|||
fn one() -> Self;
|
|||
|
|||
/// Returns a `Boolean` representing whether `self == Self::one()`.
|
|||
fn is_one(&self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
|||
self.is_eq(&Self::one())
|
|||
}
|
|||
|
|||
/// Returns a constant with value `v`.
|
|||
///
|
|||
/// This *should not* allocate any variables.
|
|||
fn constant(v: F) -> Self;
|
|||
|
|||
/// Computes `self + self`.
|
|||
fn double(&self) -> Result<Self, SynthesisError> {
|
|||
Ok(self.clone() + self)
|
|||
}
|
|||
|
|||
/// Sets `self = self + self`.
|
|||
fn double_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
|||
*self += self.double()?;
|
|||
Ok(self)
|
|||
}
|
|||
|
|||
/// Coputes `-self`.
|
|||
fn negate(&self) -> Result<Self, SynthesisError>;
|
|||
|
|||
/// Sets `self = -self`.
|
|||
#[inline]
|
|||
fn negate_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
|||
*self = self.negate()?;
|
|||
Ok(self)
|
|||
}
|
|||
|
|||
/// Computes `self * self`.
|
|||
///
|
|||
/// A default implementation is provided which just invokes the underlying
|
|||
/// multiplication routine. However, this method should be specialized
|
|||
/// for extension fields, where faster algorithms exist for squaring.
|
|||
fn square(&self) -> Result<Self, SynthesisError> {
|
|||
Ok(self.clone() * self)
|
|||
}
|
|||
|
|||
/// Sets `self = self.square()`.
|
|||
fn square_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
|||
*self = self.square()?;
|
|||
Ok(self)
|
|||
}
|
|||
|
|||
/// Enforces that `self * other == result`.
|
|||
fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> {
|
|||
let actual_result = self.clone() * other;
|
|||
result.enforce_equal(&actual_result)
|
|||
}
|
|||
|
|||
/// Enforces that `self * self == result`.
|
|||
fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> {
|
|||
let actual_result = self.square()?;
|
|||
result.enforce_equal(&actual_result)
|
|||
}
|
|||
|
|||
/// Computes `result` such that `self * result == Self::one()`.
|
|||
fn inverse(&self) -> Result<Self, SynthesisError>;
|
|||
|
|||
/// Returns `(self / denominator)`. but requires fewer constraints than
|
|||
/// `self * denominator.inverse()`.
|
|||
/// It is up to the caller to ensure that denominator is non-zero,
|
|||
/// since in that case the result is unconstrained.
|
|||
fn mul_by_inverse(&self, denominator: &Self) -> Result<Self, SynthesisError> {
|
|||
let result = Self::new_witness(self.cs(), || {
|
|||
let denominator_inv_native = denominator.value()?.inverse().get()?;
|
|||
let result = self.value()? * &denominator_inv_native;
|
|||
Ok(result)
|
|||
})?;
|
|||
result.mul_equals(&denominator, &self)?;
|
|||
|
|||
Ok(result)
|
|||
}
|
|||
|
|||
/// Computes the frobenius map over `self`.
|
|||
fn frobenius_map(&self, power: usize) -> Result<Self, SynthesisError>;
|
|||
|
|||
/// Sets `self = self.frobenius_map()`.
|
|||
fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> {
|
|||
*self = self.frobenius_map(power)?;
|
|||
Ok(self)
|
|||
}
|
|||
|
|||
/// Comptues `self^bits`, where `bits` is a *little-endian* bit-wise decomposition
|
|||
/// of the exponent.
|
|||
fn pow_le(&self, bits: &[Boolean<ConstraintF>]) -> Result<Self, SynthesisError> {
|
|||
let mut res = Self::one();
|
|||
let mut power = self.clone();
|
|||
for bit in bits {
|
|||
let tmp = res.clone() * &power;
|
|||
res = bit.select(&tmp, &res)?;
|
|||
power.square_in_place()?;
|
|||
}
|
|||
Ok(res)
|
|||
}
|
|||
|
|||
/// Computes `self^S`, where S is interpreted as an little-endian u64-decomposition of
|
|||
/// an integer.
|
|||
fn pow_by_constant<S: AsRef<[u64]>>(&self, exp: S) -> Result<Self, SynthesisError> {
|
|||
let mut res = Self::one();
|
|||
for i in BitIteratorBE::without_leading_zeros(exp) {
|
|||
res.square_in_place()?;
|
|||
if i {
|
|||
res *= self;
|
|||
}
|
|||
}
|
|||
Ok(res)
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
pub(crate) mod tests {
|
|||
use rand::{self, SeedableRng};
|
|||
use rand_xorshift::XorShiftRng;
|
|||
|
|||
use crate::{fields::*, Vec};
|
|||
use algebra::{test_rng, BitIteratorLE, Field, UniformRand};
|
|||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
|||
|
|||
#[allow(dead_code)]
|
|||
pub(crate) fn field_test<F, ConstraintF, AF>() -> Result<(), SynthesisError>
|
|||
where
|
|||
F: Field,
|
|||
ConstraintF: Field,
|
|||
AF: FieldVar<F, ConstraintF>,
|
|||
AF: TwoBitLookupGadget<ConstraintF, TableConstant = F>,
|
|||
for<'a> &'a AF: FieldOpsBounds<'a, F, AF>,
|
|||
{
|
|||
let cs = ConstraintSystem::<ConstraintF>::new_ref();
|
|||
|
|||
let mut rng = test_rng();
|
|||
let a_native = F::rand(&mut rng);
|
|||
let b_native = F::rand(&mut rng);
|
|||
let a = AF::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
let b = AF::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
let b_const = AF::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
|
|||
let zero = AF::zero();
|
|||
let zero_native = zero.value()?;
|
|||
zero.enforce_equal(&zero)?;
|
|||
|
|||
let one = AF::one();
|
|||
let one_native = one.value()?;
|
|||
one.enforce_equal(&one)?;
|
|||
|
|||
one.enforce_not_equal(&zero)?;
|
|||
|
|||
let one_dup = &zero + &one;
|
|||
one_dup.enforce_equal(&one)?;
|
|||
|
|||
let two = &one + &one;
|
|||
two.enforce_equal(&two)?;
|
|||
two.enforce_equal(&one.double()?)?;
|
|||
two.enforce_not_equal(&one)?;
|
|||
two.enforce_not_equal(&zero)?;
|
|||
|
|||
// a + 0 = a
|
|||
let a_plus_zero = &a + &zero;
|
|||
assert_eq!(a_plus_zero.value()?, a_native);
|
|||
a_plus_zero.enforce_equal(&a)?;
|
|||
a_plus_zero.enforce_not_equal(&a.double()?)?;
|
|||
|
|||
// a - 0 = a
|
|||
let a_minus_zero = &a - &zero;
|
|||
assert_eq!(a_minus_zero.value()?, a_native);
|
|||
a_minus_zero.enforce_equal(&a)?;
|
|||
|
|||
// a - a = 0
|
|||
let a_minus_a = &a - &a;
|
|||
assert_eq!(a_minus_a.value()?, zero_native);
|
|||
a_minus_a.enforce_equal(&zero)?;
|
|||
|
|||
// a + b = b + a
|
|||
let a_b = &a + &b;
|
|||
let b_a = &b + &a;
|
|||
assert_eq!(a_b.value()?, a_native + &b_native);
|
|||
a_b.enforce_equal(&b_a)?;
|
|||
|
|||
// (a + b) + a = a + (b + a)
|
|||
let ab_a = &a_b + &a;
|
|||
let a_ba = &a + &b_a;
|
|||
assert_eq!(ab_a.value()?, a_native + &b_native + &a_native);
|
|||
ab_a.enforce_equal(&a_ba)?;
|
|||
|
|||
let b_times_a_plus_b = &a_b * &b;
|
|||
let b_times_b_plus_a = &b_a * &b;
|
|||
assert_eq!(
|
|||
b_times_a_plus_b.value()?,
|
|||
b_native * &(b_native + &a_native)
|
|||
);
|
|||
assert_eq!(
|
|||
b_times_a_plus_b.value()?,
|
|||
(b_native + &a_native) * &b_native
|
|||
);
|
|||
assert_eq!(
|
|||
b_times_a_plus_b.value()?,
|
|||
(a_native + &b_native) * &b_native
|
|||
);
|
|||
b_times_b_plus_a.enforce_equal(&b_times_a_plus_b)?;
|
|||
|
|||
// a * 1 = a
|
|||
assert_eq!((&a * &one).value()?, a_native * &one_native);
|
|||
|
|||
// a * b = b * a
|
|||
let ab = &a * &b;
|
|||
let ba = &b * &a;
|
|||
assert_eq!(ab.value()?, ba.value()?);
|
|||
assert_eq!(ab.value()?, a_native * &b_native);
|
|||
|
|||
let ab_const = &a * &b_const;
|
|||
let b_const_a = &b_const * &a;
|
|||
assert_eq!(ab_const.value()?, b_const_a.value()?);
|
|||
assert_eq!(ab_const.value()?, ab.value()?);
|
|||
assert_eq!(ab_const.value()?, a_native * &b_native);
|
|||
|
|||
// (a * b) * a = a * (b * a)
|
|||
let ab_a = &ab * &a;
|
|||
let a_ba = &a * &ba;
|
|||
assert_eq!(ab_a.value()?, a_ba.value()?);
|
|||
assert_eq!(ab_a.value()?, a_native * &b_native * &a_native);
|
|||
|
|||
let aa = &a * &a;
|
|||
let a_squared = a.square()?;
|
|||
a_squared.enforce_equal(&aa)?;
|
|||
assert_eq!(aa.value()?, a_squared.value()?);
|
|||
assert_eq!(aa.value()?, a_native.square());
|
|||
|
|||
let aa = &a * a.value()?;
|
|||
a_squared.enforce_equal(&aa)?;
|
|||
assert_eq!(aa.value()?, a_squared.value()?);
|
|||
assert_eq!(aa.value()?, a_native.square());
|
|||
|
|||
let a_b2 = &a + b_native;
|
|||
a_b.enforce_equal(&a_b2)?;
|
|||
assert_eq!(a_b.value()?, a_b2.value()?);
|
|||
|
|||
let a_inv = a.inverse()?;
|
|||
a_inv.mul_equals(&a, &one)?;
|
|||
assert_eq!(a_inv.value()?, a.value()?.inverse().unwrap());
|
|||
assert_eq!(a_inv.value()?, a_native.inverse().unwrap());
|
|||
|
|||
let a_b_inv = a.mul_by_inverse(&b)?;
|
|||
a_b_inv.mul_equals(&b, &a)?;
|
|||
assert_eq!(a_b_inv.value()?, a_native * b_native.inverse().unwrap());
|
|||
|
|||
// a * a * a = a^3
|
|||
let bits = BitIteratorLE::without_trailing_zeros([3u64])
|
|||
.map(Boolean::constant)
|
|||
.collect::<Vec<_>>();
|
|||
assert_eq!(a_native.pow([0x3]), a.pow_le(&bits)?.value()?);
|
|||
|
|||
// a * a * a = a^3
|
|||
assert_eq!(a_native.pow([0x3]), a.pow_by_constant(&[0x3])?.value()?);
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
|
|||
// a * a * a = a^3
|
|||
let mut constants = [F::zero(); 4];
|
|||
for c in &mut constants {
|
|||
*c = UniformRand::rand(&mut test_rng());
|
|||
}
|
|||
let bits = [
|
|||
Boolean::<ConstraintF>::constant(false),
|
|||
Boolean::constant(true),
|
|||
];
|
|||
let lookup_result = AF::two_bit_lookup(&bits, constants.as_ref())?;
|
|||
assert_eq!(lookup_result.value()?, constants[2]);
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
|
|||
let f = F::from(1u128 << 64);
|
|||
let f_bits = algebra::BitIteratorLE::new(&[0u64, 1u64]).collect::<Vec<_>>();
|
|||
let fv = AF::new_witness(r1cs_core::ns!(cs, "alloc u128"), || Ok(f))?;
|
|||
assert_eq!(fv.to_bits_le()?.value().unwrap()[..128], f_bits[..128]);
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
|
|||
let r_native: F = UniformRand::rand(&mut test_rng());
|
|||
|
|||
let r = AF::new_witness(r1cs_core::ns!(cs, "r_native"), || Ok(r_native)).unwrap();
|
|||
let _ = r.to_non_unique_bits_le()?;
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
let _ = r.to_bits_le()?;
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
|
|||
let bytes = r.to_non_unique_bytes()?;
|
|||
assert_eq!(
|
|||
algebra::to_bytes!(r_native).unwrap(),
|
|||
bytes.value().unwrap()
|
|||
);
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
let bytes = r.to_bytes()?;
|
|||
assert_eq!(
|
|||
algebra::to_bytes!(r_native).unwrap(),
|
|||
bytes.value().unwrap()
|
|||
);
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
|
|||
let ab_false = &a + (AF::from(Boolean::Constant(false)) * b_native);
|
|||
assert_eq!(ab_false.value()?, a_native);
|
|||
let ab_true = &a + (AF::from(Boolean::Constant(true)) * b_native);
|
|||
assert_eq!(ab_true.value()?, a_native + &b_native);
|
|||
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
|||
}
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
Ok(())
|
|||
}
|
|||
|
|||
#[allow(dead_code)]
|
|||
pub(crate) fn frobenius_tests<F: Field, ConstraintF, AF>(
|
|||
maxpower: usize,
|
|||
) -> Result<(), SynthesisError>
|
|||
where
|
|||
F: Field,
|
|||
ConstraintF: Field,
|
|||
AF: FieldVar<F, ConstraintF>,
|
|||
for<'a> &'a AF: FieldOpsBounds<'a, F, AF>,
|
|||
{
|
|||
let cs = ConstraintSystem::<ConstraintF>::new_ref();
|
|||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
|||
for i in 0..=maxpower {
|
|||
let mut a = F::rand(&mut rng);
|
|||
let mut a_gadget = AF::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a))?;
|
|||
a_gadget.frobenius_map_in_place(i)?;
|
|||
a.frobenius_map(i);
|
|||
|
|||
assert_eq!(a_gadget.value()?, a);
|
|||
}
|
|||
|
|||
assert!(cs.is_satisfied().unwrap());
|
|||
Ok(())
|
|||
}
|
|||
}
|
@ -1,29 +0,0 @@ |
|||
use crate::groups::bls12;
|
|||
use algebra::bls12_377::Parameters;
|
|||
|
|||
/// An element of G1 in the BLS12-377 bilinear group.
|
|||
pub type G1Var = bls12::G1Var<Parameters>;
|
|||
/// An element of G2 in the BLS12-377 bilinear group.
|
|||
pub type G2Var = bls12::G2Var<Parameters>;
|
|||
|
|||
/// Represents the cached precomputation that can be performed on a G1 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G1PreparedVar = bls12::G1PreparedVar<Parameters>;
|
|||
/// Represents the cached precomputation that can be performed on a G2 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G2PreparedVar = bls12::G2PreparedVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
use algebra::curves::models::bls12::Bls12Parameters;
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as Bls12Parameters>::G1Parameters,
|
|||
G1Var,
|
|||
>()
|
|||
.unwrap();
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as Bls12Parameters>::G2Parameters,
|
|||
G2Var,
|
|||
>()
|
|||
.unwrap();
|
|||
}
|
@ -1,32 +0,0 @@ |
|||
use algebra::bls12_377::{Fq, Fq12Parameters, Fq2Parameters, Fq6Parameters};
|
|||
|
|||
use crate::fields::{fp::FpVar, fp12::Fp12Var, fp2::Fp2Var, fp6_3over2::Fp6Var};
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq2`.
|
|||
pub type Fq2Var = Fp2Var<Fq2Parameters>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq6`.
|
|||
pub type Fq6Var = Fp6Var<Fq6Parameters>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq12`.
|
|||
pub type Fq12Var = Fp12Var<Fq12Parameters>;
|
|||
|
|||
#[test]
|
|||
fn bls12_377_field_test() {
|
|||
use super::*;
|
|||
use crate::fields::tests::*;
|
|||
use algebra::bls12_377::{Fq, Fq12, Fq2, Fq6};
|
|||
|
|||
field_test::<_, _, FqVar>().unwrap();
|
|||
frobenius_tests::<Fq, _, FqVar>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq2Var>().unwrap();
|
|||
frobenius_tests::<Fq2, _, Fq2Var>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq6Var>().unwrap();
|
|||
frobenius_tests::<Fq6, _, Fq6Var>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq12Var>().unwrap();
|
|||
frobenius_tests::<Fq12, _, Fq12Var>(13).unwrap();
|
|||
}
|
@ -1,157 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::bls12_377`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::bls12_377::{Fq, Fq2, Fq6, Fq12}`,
|
|||
//! group variables for `algebra::bls12_377::{G1, G2}`, and implements constraint
|
|||
//! generation for computing `Bls12_377::pairing`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::bls12_377::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, bls12_377::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::bls12_377::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `G1Var` and `G2Var`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, bls12_377::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::bls12_377::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `G1` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G1Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `G1`.
|
|||
//! let zero = G1Var::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! Finally, one can check pairing computations as well:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, PairingEngine, bls12_377::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::bls12_377::{self, *};
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate random `G1` and `G2` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G2Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let pairing_result_native = Bls12_377::pairing(a_native, b_native);
|
|||
//!
|
|||
//! // Prepare `a` and `b` for pairing.
|
|||
//! let a_prep = bls12_377::PairingVar::prepare_g1(&a)?;
|
|||
//! let b_prep = bls12_377::PairingVar::prepare_g2(&b)?;
|
|||
//! let pairing_result = bls12_377::PairingVar::pairing(a_prep, b_prep)?;
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!(pairing_result.value()?, pairing_result_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! let a_prep_const = bls12_377::PairingVar::prepare_g1(&a_const)?;
|
|||
//! let b_prep_const = bls12_377::PairingVar::prepare_g2(&b_const)?;
|
|||
//! let pairing_result_const = bls12_377::PairingVar::pairing(a_prep_const, b_prep_const)?;
|
|||
//! println!("Done here 3");
|
|||
//!
|
|||
//! pairing_result.enforce_equal(&pairing_result_const)?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
mod pairing;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
|||
pub use pairing::*;
|
@ -1,9 +0,0 @@ |
|||
use algebra::bls12_377::Parameters;
|
|||
|
|||
/// Specifies the constraints for computing a pairing in the BLS12-377 bilinear group.
|
|||
pub type PairingVar = crate::pairing::bls12::PairingVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::pairing::tests::bilinearity_test::<algebra::Bls12_377, PairingVar>().unwrap()
|
|||
}
|
@ -1,12 +0,0 @@ |
|||
use crate::groups::curves::twisted_edwards::AffineVar;
|
|||
use algebra::ed_on_bls12_377::*;
|
|||
|
|||
use crate::ed_on_bls12_377::FqVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_377::EdwardsAffine`.
|
|||
pub type EdwardsVar = AffineVar<EdwardsParameters, FqVar>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::groups::curves::twisted_edwards::test::<EdwardsParameters, EdwardsVar>().unwrap();
|
|||
}
|
@ -1,10 +0,0 @@ |
|||
use crate::fields::fp::FpVar;
|
|||
use algebra::ed_on_bls12_377::fq::Fq;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_377::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::fields::tests::field_test::<_, _, FqVar>().unwrap();
|
|||
}
|
@ -1,107 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_bls12_377`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_bls12_377::Fq`,
|
|||
//! and group variables for `algebra::ed_on_bls12_377::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_bls12_377::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_bls12_377::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_bls12_377::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_bls12_377::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_bls12_377::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
@ -1,12 +0,0 @@ |
|||
use crate::groups::curves::twisted_edwards::AffineVar;
|
|||
use algebra::ed_on_bls12_381::*;
|
|||
|
|||
use crate::ed_on_bls12_381::FqVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_381::EdwardsAffine`.
|
|||
pub type EdwardsVar = AffineVar<EdwardsParameters, FqVar>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::groups::curves::twisted_edwards::test::<_, EdwardsVar>().unwrap();
|
|||
}
|
@ -1,9 +0,0 @@ |
|||
use crate::fields::fp::FpVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_381::Fq`.
|
|||
pub type FqVar = FpVar<algebra::ed_on_bls12_381::Fq>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::fields::tests::field_test::<_, _, FqVar>().unwrap();
|
|||
}
|
@ -1,107 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_bls12_381`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_bls12_381::Fq`,
|
|||
//! and group variables for `algebra::ed_on_bls12_381::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_bls12_381::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_bls12_381::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_bls12_381::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_bls12_381::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_bls12_381::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `Edwards`.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
@ -1,12 +0,0 @@ |
|||
use crate::groups::curves::twisted_edwards::AffineVar;
|
|||
use algebra::ed_on_bn254::*;
|
|||
|
|||
use crate::ed_on_bn254::FqVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_bn254::EdwardsAffine`.
|
|||
pub type EdwardsVar = AffineVar<EdwardsParameters, FqVar>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::groups::curves::twisted_edwards::test::<_, EdwardsVar>().unwrap();
|
|||
}
|
@ -1,9 +0,0 @@ |
|||
use crate::fields::fp::FpVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_bn254::Fq`.
|
|||
pub type FqVar = FpVar<algebra::ed_on_bn254::Fq>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::fields::tests::field_test::<_, _, FqVar>().unwrap();
|
|||
}
|
@ -1,107 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_bn254`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_bn254::Fq`,
|
|||
//! and group variables for `algebra::ed_on_bn254::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_bn254::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_bn254::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_bn254::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_bn254::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_bn254::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `Edwards`.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
@ -1,103 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_bw6_761`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_bw6_761::Fq`,
|
|||
//! and group variables for `algebra::ed_on_bw6_761::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_bw6_761::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_bw6_761::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_bw6_761::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_bw6_761::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_bw6_761::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `Edwards`.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
pub use crate::instantiated::ed_on_cp6_782::*;
|
@ -1,12 +0,0 @@ |
|||
use crate::groups::curves::twisted_edwards::AffineVar;
|
|||
use algebra::ed_on_cp6_782::*;
|
|||
|
|||
use crate::instantiated::ed_on_cp6_782::FqVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_cp6_782::EdwardsAffine`.
|
|||
pub type EdwardsVar = AffineVar<EdwardsParameters, FqVar>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::groups::curves::twisted_edwards::test::<EdwardsParameters, EdwardsVar>().unwrap();
|
|||
}
|
@ -1,10 +0,0 @@ |
|||
use crate::fields::fp::FpVar;
|
|||
use algebra::ed_on_cp6_782::fq::Fq;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_cp6_782::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::fields::tests::field_test::<_, _, FqVar>().unwrap();
|
|||
}
|
@ -1,108 +0,0 @@ |
|||
#![allow(unreachable_pub)]
|
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_cp6_782`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_cp6_782::Fq`,
|
|||
//! and group variables for `algebra::ed_on_cp6_782::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_cp6_782::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_cp6_782::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_cp6_782::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_cp6_782::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_cp6_782::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `Edwards`.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
@ -1,12 +0,0 @@ |
|||
use crate::groups::curves::twisted_edwards::AffineVar;
|
|||
use algebra::ed_on_mnt4_298::*;
|
|||
|
|||
use crate::instantiated::ed_on_mnt4_298::fields::FqVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_298::EdwardsAffine`.
|
|||
pub type EdwardsVar = AffineVar<EdwardsParameters, FqVar>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::groups::curves::twisted_edwards::test::<EdwardsParameters, EdwardsVar>().unwrap();
|
|||
}
|
@ -1,10 +0,0 @@ |
|||
use crate::fields::fp::FpVar;
|
|||
use algebra::ed_on_mnt4_298::fq::Fq;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_298::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::fields::tests::field_test::<_, _, FqVar>().unwrap();
|
|||
}
|
@ -1,107 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_mnt4_298`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_mnt4_298::Fq`,
|
|||
//! and group variables for `algebra::ed_on_mnt4_298::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_mnt4_298::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_mnt4_298::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_mnt4_298::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_mnt4_298::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_mnt4_298::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `Edwards`.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
@ -1,12 +0,0 @@ |
|||
use crate::groups::curves::twisted_edwards::AffineVar;
|
|||
use algebra::ed_on_mnt4_753::*;
|
|||
|
|||
use crate::instantiated::ed_on_mnt4_753::fields::FqVar;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_753::EdwardsAffine`.
|
|||
pub type EdwardsVar = AffineVar<EdwardsParameters, FqVar>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::groups::curves::twisted_edwards::test::<EdwardsParameters, EdwardsVar>().unwrap();
|
|||
}
|
@ -1,10 +0,0 @@ |
|||
use crate::fields::fp::FpVar;
|
|||
use algebra::ed_on_mnt4_753::fq::Fq;
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_753::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::fields::tests::field_test::<_, _, FqVar>().unwrap();
|
|||
}
|
@ -1,107 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::ed_on_mnt4_753`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::ed_on_mnt4_753::Fq`,
|
|||
//! and group variables for `algebra::ed_on_mnt4_753::GroupProjective`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::ed_on_mnt4_753::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, ed_on_mnt4_753::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::ed_on_mnt4_753::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `EdwardsVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, ed_on_mnt4_753::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::ed_on_mnt4_753::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Edwards` elements.
|
|||
//! let a_native = EdwardsProjective::rand(&mut rng);
|
|||
//! let b_native = EdwardsProjective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `Edwards`.
|
|||
//! let zero = EdwardsVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
@ -1,29 +0,0 @@ |
|||
use crate::groups::mnt4;
|
|||
use algebra::mnt4_298::Parameters;
|
|||
|
|||
/// An element of G1 in the MNT4-298 bilinear group.
|
|||
pub type G1Var = mnt4::G1Var<Parameters>;
|
|||
/// An element of G2 in the MNT4-298 bilinear group.
|
|||
pub type G2Var = mnt4::G2Var<Parameters>;
|
|||
|
|||
/// Represents the cached precomputation that can be performed on a G1 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G1PreparedVar = mnt4::G1PreparedVar<Parameters>;
|
|||
/// Represents the cached precomputation that can be performed on a G2 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G2PreparedVar = mnt4::G2PreparedVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
use algebra::curves::models::mnt4::MNT4Parameters;
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT4Parameters>::G1Parameters,
|
|||
G1Var,
|
|||
>()
|
|||
.unwrap();
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT4Parameters>::G2Parameters,
|
|||
G2Var,
|
|||
>()
|
|||
.unwrap();
|
|||
}
|
@ -1,26 +0,0 @@ |
|||
use algebra::mnt4_298::{Fq, Fq2Parameters, Fq4Parameters};
|
|||
|
|||
use crate::fields::{fp::FpVar, fp2::Fp2Var, fp4::Fp4Var};
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq2`.
|
|||
pub type Fq2Var = Fp2Var<Fq2Parameters>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq4`.
|
|||
pub type Fq4Var = Fp4Var<Fq4Parameters>;
|
|||
|
|||
#[test]
|
|||
fn mnt4_298_field_gadgets_test() {
|
|||
use super::*;
|
|||
use crate::fields::tests::*;
|
|||
use algebra::mnt4_298::{Fq, Fq2, Fq4};
|
|||
|
|||
field_test::<_, _, FqVar>().unwrap();
|
|||
frobenius_tests::<Fq, _, FqVar>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq2Var>().unwrap();
|
|||
frobenius_tests::<Fq2, _, Fq2Var>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq4Var>().unwrap();
|
|||
frobenius_tests::<Fq4, _, Fq4Var>(13).unwrap();
|
|||
}
|
@ -1,157 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::mnt4_298`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::mnt4_298::{Fq, Fq2, Fq4}`,
|
|||
//! group variables for `algebra::mnt4_298::{G1, G2}`, and implements constraint
|
|||
//! generation for computing `MNT4_298::pairing`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::mnt4_298::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, mnt4_298::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::mnt4_298::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `G1Var` and `G2Var`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, mnt4_298::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt4_298::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `G1` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G1Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `G1`.
|
|||
//! let zero = G1Var::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! Finally, one can check pairing computations as well:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, PairingEngine, mnt4_298::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt4_298::{self, *};
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate random `G1` and `G2` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G2Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let pairing_result_native = MNT4_298::pairing(a_native, b_native);
|
|||
//!
|
|||
//! // Prepare `a` and `b` for pairing.
|
|||
//! let a_prep = mnt4_298::PairingVar::prepare_g1(&a)?;
|
|||
//! let b_prep = mnt4_298::PairingVar::prepare_g2(&b)?;
|
|||
//! let pairing_result = mnt4_298::PairingVar::pairing(a_prep, b_prep)?;
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!(pairing_result.value()?, pairing_result_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! let a_prep_const = mnt4_298::PairingVar::prepare_g1(&a_const)?;
|
|||
//! let b_prep_const = mnt4_298::PairingVar::prepare_g2(&b_const)?;
|
|||
//! let pairing_result_const = mnt4_298::PairingVar::pairing(a_prep_const, b_prep_const)?;
|
|||
//! println!("Done here 3");
|
|||
//!
|
|||
//! pairing_result.enforce_equal(&pairing_result_const)?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
mod pairing;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
|||
pub use pairing::*;
|
@ -1,9 +0,0 @@ |
|||
use algebra::mnt4_298::Parameters;
|
|||
|
|||
/// Specifies the constraints for computing a pairing in the MNT4-298 bilinear group.
|
|||
pub type PairingVar = crate::pairing::mnt4::PairingVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::pairing::tests::bilinearity_test::<algebra::MNT4_298, PairingVar>().unwrap()
|
|||
}
|
@ -1,29 +0,0 @@ |
|||
use crate::groups::mnt4;
|
|||
use algebra::mnt4_753::Parameters;
|
|||
|
|||
/// An element of G1 in the MNT4-753 bilinear group.
|
|||
pub type G1Var = mnt4::G1Var<Parameters>;
|
|||
/// An element of G2 in the MNT4-753 bilinear group.
|
|||
pub type G2Var = mnt4::G2Var<Parameters>;
|
|||
|
|||
/// Represents the cached precomputation that can be performed on a G1 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G1PreparedVar = mnt4::G1PreparedVar<Parameters>;
|
|||
/// Represents the cached precomputation that can be performed on a G2 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G2PreparedVar = mnt4::G2PreparedVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
use algebra::curves::models::mnt4::MNT4Parameters;
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT4Parameters>::G1Parameters,
|
|||
G1Var,
|
|||
>()
|
|||
.unwrap();
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT4Parameters>::G2Parameters,
|
|||
G2Var,
|
|||
>()
|
|||
.unwrap();
|
|||
}
|
@ -1,26 +0,0 @@ |
|||
use algebra::mnt4_753::{Fq, Fq2Parameters, Fq4Parameters};
|
|||
|
|||
use crate::fields::{fp::FpVar, fp2::Fp2Var, fp4::Fp4Var};
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq2`.
|
|||
pub type Fq2Var = Fp2Var<Fq2Parameters>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq4`.
|
|||
pub type Fq4Var = Fp4Var<Fq4Parameters>;
|
|||
|
|||
#[test]
|
|||
fn mnt4_753_field_gadgets_test() {
|
|||
use super::*;
|
|||
use crate::fields::tests::*;
|
|||
use algebra::mnt4_753::{Fq, Fq2, Fq4};
|
|||
|
|||
field_test::<_, _, FqVar>().unwrap();
|
|||
frobenius_tests::<Fq, _, FqVar>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq2Var>().unwrap();
|
|||
frobenius_tests::<Fq2, _, Fq2Var>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq4Var>().unwrap();
|
|||
frobenius_tests::<Fq4, _, Fq4Var>(13).unwrap();
|
|||
}
|
@ -1,157 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::mnt4_753`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::mnt4_753::{Fq, Fq2, Fq4}`,
|
|||
//! group variables for `algebra::mnt4_753::{G1, G2}`, and implements constraint
|
|||
//! generation for computing `MNT4_753::pairing`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::mnt4_753::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, mnt4_753::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::mnt4_753::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `G1Var` and `G2Var`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, mnt4_753::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt4_753::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `G1` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G1Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `G1`.
|
|||
//! let zero = G1Var::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! Finally, one can check pairing computations as well:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, PairingEngine, mnt4_753::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt4_753::{self, *};
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate random `G1` and `G2` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G2Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let pairing_result_native = MNT4_753::pairing(a_native, b_native);
|
|||
//!
|
|||
//! // Prepare `a` and `b` for pairing.
|
|||
//! let a_prep = mnt4_753::PairingVar::prepare_g1(&a)?;
|
|||
//! let b_prep = mnt4_753::PairingVar::prepare_g2(&b)?;
|
|||
//! let pairing_result = mnt4_753::PairingVar::pairing(a_prep, b_prep)?;
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!(pairing_result.value()?, pairing_result_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! let a_prep_const = mnt4_753::PairingVar::prepare_g1(&a_const)?;
|
|||
//! let b_prep_const = mnt4_753::PairingVar::prepare_g2(&b_const)?;
|
|||
//! let pairing_result_const = mnt4_753::PairingVar::pairing(a_prep_const, b_prep_const)?;
|
|||
//! println!("Done here 3");
|
|||
//!
|
|||
//! pairing_result.enforce_equal(&pairing_result_const)?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
mod pairing;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
|||
pub use pairing::*;
|
@ -1,9 +0,0 @@ |
|||
use algebra::mnt4_753::Parameters;
|
|||
|
|||
/// Specifies the constraints for computing a pairing in the MNT4-753 bilinear group.
|
|||
pub type PairingVar = crate::pairing::mnt4::PairingVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::pairing::tests::bilinearity_test::<algebra::MNT4_753, PairingVar>().unwrap()
|
|||
}
|
@ -1,29 +0,0 @@ |
|||
use crate::groups::mnt6;
|
|||
use algebra::mnt6_298::Parameters;
|
|||
|
|||
/// An element of G1 in the MNT6-298 bilinear group.
|
|||
pub type G1Var = mnt6::G1Var<Parameters>;
|
|||
/// An element of G2 in the MNT6-298 bilinear group.
|
|||
pub type G2Var = mnt6::G2Var<Parameters>;
|
|||
|
|||
/// Represents the cached precomputation that can be performed on a G1 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G1PreparedVar = mnt6::G1PreparedVar<Parameters>;
|
|||
/// Represents the cached precomputation that can be performed on a G2 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G2PreparedVar = mnt6::G2PreparedVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
use algebra::curves::models::mnt6::MNT6Parameters;
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT6Parameters>::G1Parameters,
|
|||
G1Var,
|
|||
>()
|
|||
.unwrap();
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT6Parameters>::G2Parameters,
|
|||
G2Var,
|
|||
>()
|
|||
.unwrap();
|
|||
}
|
@ -1,26 +0,0 @@ |
|||
use algebra::mnt6_298::{Fq, Fq3Parameters, Fq6Parameters};
|
|||
|
|||
use crate::fields::{fp::FpVar, fp3::Fp3Var, fp6_2over3::Fp6Var};
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq3`.
|
|||
pub type Fq3Var = Fp3Var<Fq3Parameters>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq6`.
|
|||
pub type Fq6Var = Fp6Var<Fq6Parameters>;
|
|||
|
|||
#[test]
|
|||
fn mnt6_298_field_gadgets_test() {
|
|||
use super::*;
|
|||
use crate::fields::tests::*;
|
|||
use algebra::mnt6_298::{Fq, Fq3, Fq6};
|
|||
|
|||
field_test::<_, _, FqVar>().unwrap();
|
|||
frobenius_tests::<Fq, _, FqVar>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq3Var>().unwrap();
|
|||
frobenius_tests::<Fq3, _, Fq3Var>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq6Var>().unwrap();
|
|||
frobenius_tests::<Fq6, _, Fq6Var>(13).unwrap();
|
|||
}
|
@ -1,157 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::mnt6_298`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::mnt6_298::{Fq, Fq3, Fq6}`,
|
|||
//! group variables for `algebra::mnt6_298::{G1, G2}`, and implements constraint
|
|||
//! generation for computing `MNT6_298::pairing`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::mnt6_298::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, mnt6_298::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::mnt6_298::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `G1Var` and `G2Var`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, mnt6_298::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt6_298::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `G1` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G1Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `G1`.
|
|||
//! let zero = G1Var::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! Finally, one can check pairing computations as well:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, PairingEngine, mnt6_298::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt6_298::{self, *};
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate random `G1` and `G2` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G2Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let pairing_result_native = MNT6_298::pairing(a_native, b_native);
|
|||
//!
|
|||
//! // Prepare `a` and `b` for pairing.
|
|||
//! let a_prep = mnt6_298::PairingVar::prepare_g1(&a)?;
|
|||
//! let b_prep = mnt6_298::PairingVar::prepare_g2(&b)?;
|
|||
//! let pairing_result = mnt6_298::PairingVar::pairing(a_prep, b_prep)?;
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!(pairing_result.value()?, pairing_result_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! let a_prep_const = mnt6_298::PairingVar::prepare_g1(&a_const)?;
|
|||
//! let b_prep_const = mnt6_298::PairingVar::prepare_g2(&b_const)?;
|
|||
//! let pairing_result_const = mnt6_298::PairingVar::pairing(a_prep_const, b_prep_const)?;
|
|||
//! println!("Done here 3");
|
|||
//!
|
|||
//! pairing_result.enforce_equal(&pairing_result_const)?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
mod pairing;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
|||
pub use pairing::*;
|
@ -1,9 +0,0 @@ |
|||
use algebra::mnt6_298::Parameters;
|
|||
|
|||
/// Specifies the constraints for computing a pairing in the MNT6-298 bilinear group.
|
|||
pub type PairingVar = crate::pairing::mnt6::PairingVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::pairing::tests::bilinearity_test::<algebra::MNT6_298, PairingVar>().unwrap()
|
|||
}
|
@ -1,29 +0,0 @@ |
|||
use crate::groups::mnt6;
|
|||
use algebra::mnt6_753::Parameters;
|
|||
|
|||
/// An element of G1 in the MNT6-753 bilinear group.
|
|||
pub type G1Var = mnt6::G1Var<Parameters>;
|
|||
/// An element of G2 in the MNT6-753 bilinear group.
|
|||
pub type G2Var = mnt6::G2Var<Parameters>;
|
|||
|
|||
/// Represents the cached precomputation that can be performed on a G1 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G1PreparedVar = mnt6::G1PreparedVar<Parameters>;
|
|||
/// Represents the cached precomputation that can be performed on a G2 element
|
|||
/// which enables speeding up pairing computation.
|
|||
pub type G2PreparedVar = mnt6::G2PreparedVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
use algebra::curves::models::mnt6::MNT6Parameters;
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT6Parameters>::G1Parameters,
|
|||
G1Var,
|
|||
>()
|
|||
.unwrap();
|
|||
crate::groups::curves::short_weierstrass::test::<
|
|||
<Parameters as MNT6Parameters>::G2Parameters,
|
|||
G2Var,
|
|||
>()
|
|||
.unwrap();
|
|||
}
|
@ -1,26 +0,0 @@ |
|||
use algebra::mnt6_753::{Fq, Fq3Parameters, Fq6Parameters};
|
|||
|
|||
use crate::fields::{fp::FpVar, fp3::Fp3Var, fp6_2over3::Fp6Var};
|
|||
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq`.
|
|||
pub type FqVar = FpVar<Fq>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq3`.
|
|||
pub type Fq3Var = Fp3Var<Fq3Parameters>;
|
|||
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq6`.
|
|||
pub type Fq6Var = Fp6Var<Fq6Parameters>;
|
|||
|
|||
#[test]
|
|||
fn mnt6_753_field_gadgets_test() {
|
|||
use super::*;
|
|||
use crate::fields::tests::*;
|
|||
use algebra::mnt6_753::{Fq, Fq3, Fq6};
|
|||
|
|||
field_test::<_, _, FqVar>().unwrap();
|
|||
frobenius_tests::<Fq, _, FqVar>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq3Var>().unwrap();
|
|||
frobenius_tests::<Fq3, _, Fq3Var>(13).unwrap();
|
|||
|
|||
field_test::<_, _, Fq6Var>().unwrap();
|
|||
frobenius_tests::<Fq6, _, Fq6Var>(13).unwrap();
|
|||
}
|
@ -1,157 +0,0 @@ |
|||
//! This module implements the R1CS equivalent of `algebra::mnt6_753`.
|
|||
//!
|
|||
//! It implements field variables for `algebra::mnt6_753::{Fq, Fq3, Fq6}`,
|
|||
//! group variables for `algebra::mnt6_753::{G1, G2}`, and implements constraint
|
|||
//! generation for computing `MNT6_753::pairing`.
|
|||
//!
|
|||
//! The field underlying these constraints is `algebra::mnt6_753::Fq`.
|
|||
//!
|
|||
//! # Examples
|
|||
//!
|
|||
//! One can perform standard algebraic operations on `FqVar`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! use algebra::{UniformRand, mnt6_753::*};
|
|||
//! use r1cs_core::*;
|
|||
//! use r1cs_std::prelude::*;
|
|||
//! use r1cs_std::mnt6_753::*;
|
|||
//!
|
|||
//! let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! // This rng is just for test purposes; do not use it
|
|||
//! // in real applications.
|
|||
//! let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `Fq` elements.
|
|||
//! let a_native = Fq::rand(&mut rng);
|
|||
//! let b_native = Fq::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?;
|
|||
//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let one = FqVar::one();
|
|||
//! let zero = FqVar::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two = &one + &one + &zero;
|
|||
//! two.enforce_equal(&one.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that the value of &a * &b is correct.
|
|||
//! assert_eq!((&a * &b).value()?, a_native * &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! One can also perform standard algebraic operations on `G1Var` and `G2Var`:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, mnt6_753::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt6_753::*;
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate some random `G1` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G1Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! // This returns the identity of `G1`.
|
|||
//! let zero = G1Var::zero();
|
|||
//!
|
|||
//! // Sanity check one + one = two
|
|||
//! let two_a = &a + &a + &zero;
|
|||
//! two_a.enforce_equal(&a.double()?)?;
|
|||
//!
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!((&a + &b).value()?, a_native + &b_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! (&a + &b).enforce_equal(&(&a_const + &b_const))?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
//!
|
|||
//! Finally, one can check pairing computations as well:
|
|||
//!
|
|||
//! ```
|
|||
//! # fn main() -> Result<(), r1cs_core::SynthesisError> {
|
|||
//! # use algebra::{UniformRand, PairingEngine, mnt6_753::*};
|
|||
//! # use r1cs_core::*;
|
|||
//! # use r1cs_std::prelude::*;
|
|||
//! # use r1cs_std::mnt6_753::{self, *};
|
|||
//!
|
|||
//! # let cs = ConstraintSystem::<Fq>::new_ref();
|
|||
//! # let mut rng = algebra::test_rng();
|
|||
//!
|
|||
//! // Generate random `G1` and `G2` elements.
|
|||
//! let a_native = G1Projective::rand(&mut rng);
|
|||
//! let b_native = G2Projective::rand(&mut rng);
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as witness variables in `cs`.
|
|||
//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?;
|
|||
//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?;
|
|||
//!
|
|||
//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any
|
|||
//! // constraints or variables.
|
|||
//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?;
|
|||
//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?;
|
|||
//!
|
|||
//! let pairing_result_native = MNT6_753::pairing(a_native, b_native);
|
|||
//!
|
|||
//! // Prepare `a` and `b` for pairing.
|
|||
//! let a_prep = mnt6_753::PairingVar::prepare_g1(&a)?;
|
|||
//! let b_prep = mnt6_753::PairingVar::prepare_g2(&b)?;
|
|||
//! let pairing_result = mnt6_753::PairingVar::pairing(a_prep, b_prep)?;
|
|||
//!
|
|||
//! // Check that the value of &a + &b is correct.
|
|||
//! assert_eq!(pairing_result.value()?, pairing_result_native);
|
|||
//!
|
|||
//! // Check that operations on variables and constants are equivalent.
|
|||
//! let a_prep_const = mnt6_753::PairingVar::prepare_g1(&a_const)?;
|
|||
//! let b_prep_const = mnt6_753::PairingVar::prepare_g2(&b_const)?;
|
|||
//! let pairing_result_const = mnt6_753::PairingVar::pairing(a_prep_const, b_prep_const)?;
|
|||
//! println!("Done here 3");
|
|||
//!
|
|||
//! pairing_result.enforce_equal(&pairing_result_const)?;
|
|||
//! assert!(cs.is_satisfied()?);
|
|||
//! # Ok(())
|
|||
//! # }
|
|||
//! ```
|
|||
|
|||
mod curves;
|
|||
mod fields;
|
|||
mod pairing;
|
|||
|
|||
pub use curves::*;
|
|||
pub use fields::*;
|
|||
pub use pairing::*;
|
@ -1,9 +0,0 @@ |
|||
use algebra::mnt6_753::Parameters;
|
|||
|
|||
/// Specifies the constraints for computing a pairing in the MNT6-753 bilinear group.
|
|||
pub type PairingVar = crate::pairing::mnt6::PairingVar<Parameters>;
|
|||
|
|||
#[test]
|
|||
fn test() {
|
|||
crate::pairing::tests::bilinearity_test::<algebra::MNT6_753, PairingVar>().unwrap()
|
|||
}
|
@ -1,38 +0,0 @@ |
|||
#[cfg(feature = "bls12_377")]
|
|||
pub mod bls12_377;
|
|||
|
|||
#[cfg(feature = "ed_on_bls12_377")]
|
|||
pub mod ed_on_bls12_377;
|
|||
|
|||
#[cfg(feature = "ed_on_cp6_782")]
|
|||
pub mod ed_on_cp6_782;
|
|||
|
|||
#[cfg(all(not(feature = "ed_on_cp6_782"), feature = "ed_on_bw6_761"))]
|
|||
pub(crate) mod ed_on_cp6_782;
|
|||
|
|||
#[cfg(feature = "ed_on_bw6_761")]
|
|||
pub mod ed_on_bw6_761;
|
|||
|
|||
#[cfg(feature = "ed_on_bn254")]
|
|||
pub mod ed_on_bn254;
|
|||
|
|||
#[cfg(feature = "ed_on_bls12_381")]
|
|||
pub mod ed_on_bls12_381;
|
|||
|
|||
#[cfg(feature = "ed_on_mnt4_298")]
|
|||
pub mod ed_on_mnt4_298;
|
|||
|
|||
#[cfg(feature = "ed_on_mnt4_753")]
|
|||
pub mod ed_on_mnt4_753;
|
|||
|
|||
#[cfg(feature = "mnt4_298")]
|
|||
pub mod mnt4_298;
|
|||
|
|||
#[cfg(feature = "mnt4_753")]
|
|||
pub mod mnt4_753;
|
|||
|
|||
#[cfg(feature = "mnt6_298")]
|
|||
pub mod mnt6_298;
|
|||
|
|||
#[cfg(feature = "mnt6_753")]
|
|||
pub mod mnt6_753;
|
@ -1,187 +0,0 @@ |
|||
//! This crate implements common "gadgets" that make
|
|||
//! programming rank-1 constraint systems easier.
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
|||
#![deny(unused_import_braces, unused_qualifications, trivial_casts)]
|
|||
#![deny(trivial_numeric_casts, variant_size_differences, unreachable_pub)]
|
|||
#![deny(non_shorthand_field_patterns, unused_attributes, unused_imports)]
|
|||
#![deny(unused_extern_crates, renamed_and_removed_lints, unused_allocation)]
|
|||
#![deny(unused_comparisons, bare_trait_objects, const_err, unused_must_use)]
|
|||
#![deny(unused_mut, unused_unsafe, private_in_public, unsafe_code)]
|
|||
#![deny(missing_docs)]
|
|||
#![forbid(unsafe_code)]
|
|||
|
|||
#[doc(hidden)]
|
|||
#[cfg(all(test, not(feature = "std")))]
|
|||
#[macro_use]
|
|||
extern crate std;
|
|||
|
|||
#[doc(hidden)]
|
|||
#[cfg(not(feature = "std"))]
|
|||
extern crate alloc as ralloc;
|
|||
|
|||
#[doc(hidden)]
|
|||
#[macro_use]
|
|||
extern crate algebra;
|
|||
|
|||
#[doc(hidden)]
|
|||
#[macro_use]
|
|||
extern crate derivative;
|
|||
|
|||
/// Some utility macros for making downstream impls easier.
|
|||
#[macro_use]
|
|||
pub mod macros;
|
|||
|
|||
#[cfg(not(feature = "std"))]
|
|||
use ralloc::vec::Vec;
|
|||
|
|||
#[cfg(feature = "std")]
|
|||
use std::vec::Vec;
|
|||
|
|||
use algebra::prelude::Field;
|
|||
|
|||
/// This module implements gadgets related to bit manipulation, such as `Boolean` and `UInt`s.
|
|||
pub mod bits;
|
|||
pub use self::bits::*;
|
|||
|
|||
/// This module implements gadgets related to field arithmetic.
|
|||
pub mod fields;
|
|||
|
|||
/// This module implements gadgets related to group arithmetic, and specifically elliptic curve arithmetic.
|
|||
pub mod groups;
|
|||
|
|||
mod instantiated;
|
|||
|
|||
#[cfg(feature = "bls12_377")]
|
|||
pub use instantiated::bls12_377;
|
|||
|
|||
#[cfg(feature = "ed_on_bn254")]
|
|||
pub use instantiated::ed_on_bn254;
|
|||
|
|||
#[cfg(feature = "ed_on_bls12_377")]
|
|||
pub use instantiated::ed_on_bls12_377;
|
|||
|
|||
#[cfg(feature = "ed_on_mnt4_298")]
|
|||
pub use instantiated::ed_on_mnt4_298;
|
|||
|
|||
#[cfg(feature = "ed_on_mnt4_753")]
|
|||
pub use instantiated::ed_on_mnt4_753;
|
|||
|
|||
#[cfg(feature = "ed_on_cp6_782")]
|
|||
pub use instantiated::ed_on_cp6_782;
|
|||
|
|||
#[cfg(feature = "ed_on_bw6_761")]
|
|||
pub use instantiated::ed_on_bw6_761;
|
|||
|
|||
#[cfg(feature = "ed_on_bls12_381")]
|
|||
pub use instantiated::ed_on_bls12_381;
|
|||
|
|||
#[cfg(feature = "mnt4_298")]
|
|||
pub use instantiated::mnt4_298;
|
|||
|
|||
#[cfg(feature = "mnt4_753")]
|
|||
pub use instantiated::mnt4_753;
|
|||
|
|||
#[cfg(feature = "mnt6_298")]
|
|||
pub use instantiated::mnt6_298;
|
|||
|
|||
#[cfg(feature = "mnt6_753")]
|
|||
pub use instantiated::mnt6_753;
|
|||
|
|||
/// This module implements gadgets related to computing pairings in bilinear groups.
|
|||
pub mod pairing;
|
|||
|
|||
/// This module describes a trait for allocating new variables in a constraint system.
|
|||
pub mod alloc;
|
|||
/// This module describes a trait for checking equality of variables.
|
|||
pub mod eq;
|
|||
/// This module describes traits for conditionally selecting a variable from a list of variables.
|
|||
pub mod select;
|
|||
|
|||
#[allow(missing_docs)]
|
|||
pub mod prelude {
|
|||
pub use crate::{
|
|||
alloc::*,
|
|||
bits::{boolean::Boolean, uint32::UInt32, uint8::UInt8, ToBitsGadget, ToBytesGadget},
|
|||
eq::*,
|
|||
fields::{FieldOpsBounds, FieldVar},
|
|||
groups::{CurveVar, GroupOpsBounds},
|
|||
instantiated::*,
|
|||
pairing::PairingVar,
|
|||
select::*,
|
|||
R1CSVar,
|
|||
};
|
|||
}
|
|||
|
|||
/// This trait describes some core functionality that is common to high-level variables,
|
|||
/// such as `Boolean`s, `FieldVar`s, `GroupVar`s, etc.
|
|||
pub trait R1CSVar<F: Field> {
|
|||
/// The type of the "native" value that `Self` represents in the constraint system.
|
|||
type Value: core::fmt::Debug + Eq + Clone;
|
|||
|
|||
/// Returns the underlying `ConstraintSystemRef`.
|
|||
///
|
|||
/// If `self` is a constant value, then this *must* return `r1cs_core::ConstraintSystemRef::None`.
|
|||
fn cs(&self) -> r1cs_core::ConstraintSystemRef<F>;
|
|||
|
|||
/// Returns `true` if `self` is a circuit-generation-time constant.
|
|||
fn is_constant(&self) -> bool {
|
|||
self.cs().is_none()
|
|||
}
|
|||
|
|||
/// Returns the value that is assigned to `self` in the underlying
|
|||
/// `ConstraintSystem`.
|
|||
fn value(&self) -> Result<Self::Value, r1cs_core::SynthesisError>;
|
|||
}
|
|||
|
|||
impl<F: Field, T: R1CSVar<F>> R1CSVar<F> for [T] {
|
|||
type Value = Vec<T::Value>;
|
|||
|
|||
fn cs(&self) -> r1cs_core::ConstraintSystemRef<F> {
|
|||
let mut result = r1cs_core::ConstraintSystemRef::None;
|
|||
for var in self {
|
|||
result = var.cs().or(result);
|
|||
}
|
|||
result
|
|||
}
|
|||
|
|||
fn value(&self) -> Result<Self::Value, r1cs_core::SynthesisError> {
|
|||
let mut result = Vec::new();
|
|||
for var in self {
|
|||
result.push(var.value()?);
|
|||
}
|
|||
Ok(result)
|
|||
}
|
|||
}
|
|||
|
|||
impl<'a, F: Field, T: 'a + R1CSVar<F>> R1CSVar<F> for &'a T {
|
|||
type Value = T::Value;
|
|||
|
|||
fn cs(&self) -> r1cs_core::ConstraintSystemRef<F> {
|
|||
(*self).cs()
|
|||
}
|
|||
|
|||
fn value(&self) -> Result<Self::Value, r1cs_core::SynthesisError> {
|
|||
(*self).value()
|
|||
}
|
|||
}
|
|||
|
|||
/// A utility trait to convert `Self` to `Result<T, SynthesisErrorA`.>
|
|||
pub trait Assignment<T> {
|
|||
/// Converts `self` to `Result`.
|
|||
fn get(self) -> Result<T, r1cs_core::SynthesisError>;
|
|||
}
|
|||
|
|||
impl<T> Assignment<T> for Option<T> {
|
|||
fn get(self) -> Result<T, r1cs_core::SynthesisError> {
|
|||
self.ok_or(r1cs_core::SynthesisError::AssignmentMissing)
|
|||
}
|
|||
}
|
|||
|
|||
/// Specifies how to convert a variable of type `Self` to variables of
|
|||
/// type `FpVar<ConstraintF>`
|
|||
pub trait ToConstraintFieldGadget<ConstraintF: algebra::PrimeField> {
|
|||
/// Converts `self` to `FpVar<ConstraintF>` variables.
|
|||
fn to_constraint_field(
|
|||
&self,
|
|||
) -> Result<Vec<crate::fields::fp::FpVar<ConstraintF>>, r1cs_core::SynthesisError>;
|
|||
}
|
@ -1,183 +0,0 @@ |
|||
use crate::prelude::*;
|
|||
use algebra::{Field, PairingEngine};
|
|||
use core::fmt::Debug;
|
|||
use r1cs_core::SynthesisError;
|
|||
|
|||
/// This module implements pairings for BLS12 bilinear groups.
|
|||
pub mod bls12;
|
|||
/// This module implements pairings for MNT4 bilinear groups.
|
|||
pub mod mnt4;
|
|||
/// This module implements pairings for MNT6 bilinear groups.
|
|||
pub mod mnt6;
|
|||
|
|||
/// Specifies the constraints for computing a pairing in the yybilinear group `E`.
|
|||
pub trait PairingVar<E: PairingEngine, ConstraintF: Field = <E as PairingEngine>::Fq> {
|
|||
/// An variable representing an element of `G1`.
|
|||
/// This is the R1CS equivalent of `E::G1Projective`.
|
|||
type G1Var: CurveVar<E::G1Projective, ConstraintF>
|
|||
+ AllocVar<E::G1Projective, ConstraintF>
|
|||
+ AllocVar<E::G1Affine, ConstraintF>;
|
|||
|
|||
/// An variable representing an element of `G2`.
|
|||
/// This is the R1CS equivalent of `E::G2Projective`.
|
|||
type G2Var: CurveVar<E::G2Projective, ConstraintF>
|
|||
+ AllocVar<E::G2Projective, ConstraintF>
|
|||
+ AllocVar<E::G2Affine, ConstraintF>;
|
|||
|
|||
/// An variable representing an element of `GT`.
|
|||
/// This is the R1CS equivalent of `E::GT`.
|
|||
type GTVar: FieldVar<E::Fqk, ConstraintF>;
|
|||
|
|||
/// An variable representing cached precomputation that can speed up pairings computations.
|
|||
/// This is the R1CS equivalent of `E::G1Prepared`.
|
|||
type G1PreparedVar: ToBytesGadget<ConstraintF>
|
|||
+ AllocVar<E::G1Prepared, ConstraintF>
|
|||
+ Clone
|
|||
+ Debug;
|
|||
/// An variable representing cached precomputation that can speed up pairings computations.
|
|||
/// This is the R1CS equivalent of `E::G2Prepared`.
|
|||
type G2PreparedVar: ToBytesGadget<ConstraintF>
|
|||
+ AllocVar<E::G2Prepared, ConstraintF>
|
|||
+ Clone
|
|||
+ Debug;
|
|||
|
|||
/// Computes a multi-miller loop between elements
|
|||
/// of `p` and `q`.
|
|||
fn miller_loop(
|
|||
p: &[Self::G1PreparedVar],
|
|||
q: &[Self::G2PreparedVar],
|
|||
) -> Result<Self::GTVar, SynthesisError>;
|
|||
|
|||
/// Computes a final exponentiation over `p`.
|
|||
fn final_exponentiation(p: &Self::GTVar) -> Result<Self::GTVar, SynthesisError>;
|
|||
|
|||
/// Computes a pairing over `p` and `q`.
|
|||
#[tracing::instrument(target = "r1cs")]
|
|||
fn pairing(
|
|||
p: Self::G1PreparedVar,
|
|||
q: Self::G2PreparedVar,
|
|||
) -> Result<Self::GTVar, SynthesisError> {
|
|||
let tmp = Self::miller_loop(&[p], &[q])?;
|
|||
Self::final_exponentiation(&tmp)
|
|||
}
|
|||
|
|||
/// Computes a product of pairings over the elements in `p` and `q`.
|
|||
#[must_use]
|
|||
#[tracing::instrument(target = "r1cs")]
|
|||
fn product_of_pairings(
|
|||
p: &[Self::G1PreparedVar],
|
|||
q: &[Self::G2PreparedVar],
|
|||
) -> Result<Self::GTVar, SynthesisError> {
|
|||
let miller_result = Self::miller_loop(p, q)?;
|
|||
Self::final_exponentiation(&miller_result)
|
|||
}
|
|||
|
|||
/// Performs the precomputation to generate `Self::G1PreparedVar`.
|
|||
fn prepare_g1(q: &Self::G1Var) -> Result<Self::G1PreparedVar, SynthesisError>;
|
|||
|
|||
/// Performs the precomputation to generate `Self::G2PreparedVar`.
|
|||
fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError>;
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
pub(crate) mod tests {
|
|||
use crate::{prelude::*, Vec};
|
|||
use algebra::{
|
|||
test_rng, BitIteratorLE, Field, PairingEngine, PrimeField, ProjectiveCurve, UniformRand,
|
|||
};
|
|||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
|||
|
|||
#[allow(dead_code)]
|
|||
pub(crate) fn bilinearity_test<E: PairingEngine, P: PairingVar<E>>(
|
|||
) -> Result<(), SynthesisError>
|
|||
where
|
|||
for<'a> &'a P::G1Var: GroupOpsBounds<'a, E::G1Projective, P::G1Var>,
|
|||
for<'a> &'a P::G2Var: GroupOpsBounds<'a, E::G2Projective, P::G2Var>,
|
|||
for<'a> &'a P::GTVar: FieldOpsBounds<'a, E::Fqk, P::GTVar>,
|
|||
{
|
|||
let cs = ConstraintSystem::<E::Fq>::new_ref();
|
|||
|
|||
let mut rng = test_rng();
|
|||
let a = E::G1Projective::rand(&mut rng);
|
|||
let b = E::G2Projective::rand(&mut rng);
|
|||
let s = E::Fr::rand(&mut rng);
|
|||
|
|||
let mut sa = a;
|
|||
sa *= s;
|
|||
let mut sb = b;
|
|||
sb *= s;
|
|||
|
|||
let a_g = P::G1Var::new_witness(cs.clone(), || Ok(a.into_affine()))?;
|
|||
let b_g = P::G2Var::new_witness(cs.clone(), || Ok(b.into_affine()))?;
|
|||
let sa_g = P::G1Var::new_witness(cs.clone(), || Ok(sa.into_affine()))?;
|
|||
let sb_g = P::G2Var::new_witness(cs.clone(), || Ok(sb.into_affine()))?;
|
|||
|
|||
let mut preparation_num_constraints = cs.num_constraints();
|
|||
let a_prep_g = P::prepare_g1(&a_g)?;
|
|||
let b_prep_g = P::prepare_g2(&b_g)?;
|
|||
preparation_num_constraints = cs.num_constraints() - preparation_num_constraints;
|
|||
println!(
|
|||
"Preparation num constraints: {}",
|
|||
preparation_num_constraints
|
|||
);
|
|||
|
|||
let sa_prep_g = P::prepare_g1(&sa_g)?;
|
|||
let sb_prep_g = P::prepare_g2(&sb_g)?;
|
|||
|
|||
let (ans1_g, ans1_n) = {
|
|||
let ml_constraints = cs.num_constraints();
|
|||
let ml_g = P::miller_loop(&[sa_prep_g], &[b_prep_g.clone()])?;
|
|||
println!(
|
|||
"ML num constraints: {}",
|
|||
cs.num_constraints() - ml_constraints
|
|||
);
|
|||
let fe_constraints = cs.num_constraints();
|
|||
let ans_g = P::final_exponentiation(&ml_g)?;
|
|||
println!(
|
|||
"FE num constraints: {}",
|
|||
cs.num_constraints() - fe_constraints
|
|||
);
|
|||
let ans_n = E::pairing(sa, b);
|
|||
(ans_g, ans_n)
|
|||
};
|
|||
|
|||
let (ans2_g, ans2_n) = {
|
|||
let ans_g = P::pairing(a_prep_g.clone(), sb_prep_g)?;
|
|||
let ans_n = E::pairing(a, sb);
|
|||
(ans_g, ans_n)
|
|||
};
|
|||
|
|||
let (ans3_g, ans3_n) = {
|
|||
let s_iter = BitIteratorLE::without_trailing_zeros(s.into_repr())
|
|||
.map(Boolean::constant)
|
|||
.collect::<Vec<_>>();
|
|||
|
|||
let mut ans_g = P::pairing(a_prep_g, b_prep_g)?;
|
|||
let mut ans_n = E::pairing(a, b);
|
|||
ans_n = ans_n.pow(s.into_repr());
|
|||
ans_g = ans_g.pow_le(&s_iter)?;
|
|||
|
|||
(ans_g, ans_n)
|
|||
};
|
|||
|
|||
ans1_g.enforce_equal(&ans2_g)?;
|
|||
ans2_g.enforce_equal(&ans3_g)?;
|
|||
|
|||
assert_eq!(ans1_g.value()?, ans1_n, "Failed native test 1");
|
|||
assert_eq!(ans2_g.value()?, ans2_n, "Failed native test 2");
|
|||
assert_eq!(ans3_g.value()?, ans3_n, "Failed native test 3");
|
|||
|
|||
assert_eq!(ans1_n, ans2_n, "Failed ans1_native == ans2_native");
|
|||
assert_eq!(ans2_n, ans3_n, "Failed ans2_native == ans3_native");
|
|||
assert_eq!(ans1_g.value()?, ans3_g.value()?, "Failed ans1 == ans3");
|
|||
assert_eq!(ans1_g.value()?, ans2_g.value()?, "Failed ans1 == ans2");
|
|||
assert_eq!(ans2_g.value()?, ans3_g.value()?, "Failed ans2 == ans3");
|
|||
|
|||
if !cs.is_satisfied().unwrap() {
|
|||
println!("Unsatisfied: {:?}", cs.which_is_unsatisfied());
|
|||
}
|
|||
|
|||
assert!(cs.is_satisfied().unwrap(), "cs is not satisfied");
|
|||
Ok(())
|
|||
}
|
|||
}
|