Browse Source

Update `crypto-primitives` and their contraints.

master
Pratyush Mishra 4 years ago
parent
commit
3bb3697c13
35 changed files with 1771 additions and 3117 deletions
  1. +1
    -7
      cp-benches/Cargo.toml
  2. +6
    -10
      cp-benches/benches/crypto_primitives/comm.rs
  3. +4
    -4
      cp-benches/benches/crypto_primitives/crh.rs
  4. +0
    -121
      cp-benches/benches/crypto_primitives/nizk.rs
  5. +76
    -167
      cp-benches/benches/crypto_primitives/signature.rs
  6. +2
    -2
      crypto-primitives/Cargo.toml
  7. +67
    -138
      crypto-primitives/src/commitment/blake2s/constraints.rs
  8. +4
    -4
      crypto-primitives/src/commitment/blake2s/mod.rs
  9. +12
    -12
      crypto-primitives/src/commitment/constraints.rs
  10. +38
    -41
      crypto-primitives/src/commitment/injective_map/constraints.rs
  11. +11
    -14
      crypto-primitives/src/commitment/injective_map/mod.rs
  12. +94
    -172
      crypto-primitives/src/commitment/pedersen/constraints.rs
  13. +27
    -37
      crypto-primitives/src/commitment/pedersen/mod.rs
  14. +72
    -117
      crypto-primitives/src/crh/bowe_hopwood/constraints.rs
  15. +33
    -34
      crypto-primitives/src/crh/bowe_hopwood/mod.rs
  16. +10
    -10
      crypto-primitives/src/crh/constraints.rs
  17. +52
    -85
      crypto-primitives/src/crh/injective_map/constraints.rs
  18. +18
    -33
      crypto-primitives/src/crh/injective_map/mod.rs
  19. +2
    -1
      crypto-primitives/src/crh/mod.rs
  20. +70
    -131
      crypto-primitives/src/crh/pedersen/constraints.rs
  21. +22
    -22
      crypto-primitives/src/crh/pedersen/mod.rs
  22. +4
    -4
      crypto-primitives/src/lib.rs
  23. +93
    -159
      crypto-primitives/src/merkle_tree/constraints.rs
  24. +43
    -47
      crypto-primitives/src/merkle_tree/mod.rs
  25. +48
    -36
      crypto-primitives/src/nizk/constraints.rs
  26. +335
    -603
      crypto-primitives/src/nizk/gm17/constraints.rs
  27. +304
    -530
      crypto-primitives/src/nizk/groth16/constraints.rs
  28. +13
    -13
      crypto-primitives/src/nizk/mod.rs
  29. +144
    -312
      crypto-primitives/src/prf/blake2s/constraints.rs
  30. +8
    -11
      crypto-primitives/src/prf/constraints.rs
  31. +1
    -1
      crypto-primitives/src/prf/mod.rs
  32. +9
    -10
      crypto-primitives/src/signature/constraints.rs
  33. +5
    -5
      crypto-primitives/src/signature/mod.rs
  34. +87
    -172
      crypto-primitives/src/signature/schnorr/constraints.rs
  35. +56
    -52
      crypto-primitives/src/signature/schnorr/mod.rs

+ 1
- 7
cp-benches/Cargo.toml

@ -49,10 +49,4 @@ harness = false
[[bench]] [[bench]]
name = "schnorr_sig" name = "schnorr_sig"
path = "benches/crypto_primitives/signature.rs" path = "benches/crypto_primitives/signature.rs"
harness = false
[[bench]]
name = "gm17"
path = "benches/crypto_primitives/nizk.rs"
required-features = ["r1cs", "gm17"]
harness = false
harness = false

+ 6
- 10
cp-benches/benches/crypto_primitives/comm.rs

@ -10,7 +10,7 @@ use crypto_primitives::commitment::{pedersen::*, CommitmentScheme};
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct CommWindow; pub struct CommWindow;
impl PedersenWindow for CommWindow {
impl Window for CommWindow {
const WINDOW_SIZE: usize = 250; const WINDOW_SIZE: usize = 250;
const NUM_WINDOWS: usize = 8; const NUM_WINDOWS: usize = 8;
} }
@ -19,25 +19,21 @@ fn pedersen_comm_setup(c: &mut Criterion) {
c.bench_function("Pedersen Commitment Setup", move |b| { c.bench_function("Pedersen Commitment Setup", move |b| {
b.iter(|| { b.iter(|| {
let mut rng = &mut rand::thread_rng(); let mut rng = &mut rand::thread_rng();
PedersenCommitment::<Edwards, CommWindow>::setup(&mut rng).unwrap()
Commitment::<Edwards, CommWindow>::setup(&mut rng).unwrap()
}) })
}); });
} }
fn pedersen_comm_eval(c: &mut Criterion) { fn pedersen_comm_eval(c: &mut Criterion) {
let mut rng = &mut rand::thread_rng(); let mut rng = &mut rand::thread_rng();
let parameters = PedersenCommitment::<Edwards, CommWindow>::setup(&mut rng).unwrap();
let parameters = Commitment::<Edwards, CommWindow>::setup(&mut rng).unwrap();
let input = vec![5u8; 128]; let input = vec![5u8; 128];
c.bench_function("Pedersen Commitment Eval", move |b| { c.bench_function("Pedersen Commitment Eval", move |b| {
b.iter(|| { b.iter(|| {
let rng = &mut rand::thread_rng(); let rng = &mut rand::thread_rng();
let commitment_randomness = PedersenRandomness::rand(rng);
PedersenCommitment::<Edwards, CommWindow>::commit(
&parameters,
&input,
&commitment_randomness,
)
.unwrap();
let commitment_randomness = Randomness::rand(rng);
Commitment::<Edwards, CommWindow>::commit(&parameters, &input, &commitment_randomness)
.unwrap();
}) })
}); });
} }

+ 4
- 4
cp-benches/benches/crypto_primitives/crh.rs

@ -10,7 +10,7 @@ use crypto_primitives::crh::{pedersen::*, FixedLengthCRH};
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct HashWindow; pub struct HashWindow;
impl PedersenWindow for HashWindow {
impl Window for HashWindow {
const WINDOW_SIZE: usize = 250; const WINDOW_SIZE: usize = 250;
const NUM_WINDOWS: usize = 8; const NUM_WINDOWS: usize = 8;
} }
@ -19,18 +19,18 @@ fn pedersen_crh_setup(c: &mut Criterion) {
c.bench_function("Pedersen CRH Setup", move |b| { c.bench_function("Pedersen CRH Setup", move |b| {
b.iter(|| { b.iter(|| {
let mut rng = &mut rand::thread_rng(); let mut rng = &mut rand::thread_rng();
PedersenCRH::<Edwards, HashWindow>::setup(&mut rng).unwrap()
CRH::<Edwards, HashWindow>::setup(&mut rng).unwrap()
}) })
}); });
} }
fn pedersen_crh_eval(c: &mut Criterion) { fn pedersen_crh_eval(c: &mut Criterion) {
let mut rng = &mut rand::thread_rng(); let mut rng = &mut rand::thread_rng();
let parameters = PedersenCRH::<Edwards, HashWindow>::setup(&mut rng).unwrap();
let parameters = CRH::<Edwards, HashWindow>::setup(&mut rng).unwrap();
let input = vec![5u8; 128]; let input = vec![5u8; 128];
c.bench_function("Pedersen CRH Eval", move |b| { c.bench_function("Pedersen CRH Eval", move |b| {
b.iter(|| { b.iter(|| {
PedersenCRH::<Edwards, HashWindow>::evaluate(&parameters, &input).unwrap();
CRH::<Edwards, HashWindow>::evaluate(&parameters, &input).unwrap();
}) })
}); });
} }

+ 0
- 121
cp-benches/benches/crypto_primitives/nizk.rs

@ -1,121 +0,0 @@
#[macro_use]
extern crate criterion;
use algebra::{curves::bls12_377::Bls12_377, fields::bls12_377::Fr, Field};
use crypto_primitives::nizk::*;
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
use criterion::Criterion;
use rand::{thread_rng, Rng};
type TestProofSystem = Gm17<Bls12_377, Bench<Fr>, Fr>;
struct Bench<F: Field> {
inputs: Vec<Option<F>>,
num_constraints: usize,
}
impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
) -> 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 (i, input) in self.inputs.into_iter().enumerate() {
let input_var = cs.alloc_input(
|| format!("Input {}", i),
|| input.ok_or(SynthesisError::AssignmentMissing),
)?;
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.alloc(
|| format!("Result {}", i),
|| result_val.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| format!("Enforce constraint {}", i),
|lc| lc + input_1_var,
|lc| lc + input_2_var,
|lc| lc + result_var,
);
(result_val, result_var)
};
variables.push(new_entry);
}
Ok(())
}
}
fn gm17_setup(c: &mut Criterion) {
let num_inputs = 100;
let num_constraints = num_inputs;
let rng = &mut thread_rng();
let mut inputs: Vec<Option<Fr>> = Vec::with_capacity(num_inputs);
for _ in 0..num_inputs {
inputs.push(Some(rng.gen()));
}
c.bench_function("gm17_setup", move |b| {
b.iter(|| {
TestProofSystem::setup(
Bench::<Fr> {
inputs: vec![None; num_inputs],
num_constraints,
},
rng,
)
.unwrap()
})
});
}
fn gm17_prove(c: &mut Criterion) {
let num_inputs = 100;
let num_constraints = num_inputs;
let rng = &mut thread_rng();
let mut inputs: Vec<Option<Fr>> = Vec::with_capacity(num_inputs);
for _ in 0..num_inputs {
inputs.push(Some(rng.gen()));
}
let params = TestProofSystem::setup(
Bench::<Fr> {
inputs: vec![None; num_inputs],
num_constraints,
},
rng,
)
.unwrap();
c.bench_function("gm17_prove", move |b| {
b.iter(|| {
TestProofSystem::prove(
&params.0,
Bench {
inputs: inputs.clone(),
num_constraints,
},
rng,
)
.unwrap()
})
});
}
criterion_group! {
name = nizk_eval;
config = Criterion::default().sample_size(10);
targets = gm17_setup, gm17_prove
}
criterion_main!(nizk_eval);

+ 76
- 167
cp-benches/benches/crypto_primitives/signature.rs

@ -1,180 +1,89 @@
#[macro_use] #[macro_use]
extern crate criterion; extern crate criterion;
mod affine {
use algebra::ed_on_bls12_377::EdwardsAffine as Edwards;
use blake2::Blake2s;
use criterion::Criterion;
use crypto_primitives::signature::{schnorr::*, SignatureScheme};
use rand::{self, Rng};
type SchnorrEdwards = SchnorrSignature<Edwards, Blake2s>;
fn schnorr_signature_setup(c: &mut Criterion) {
c.bench_function("SchnorrEdwardsAffine: 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("SchnorrEdwardsAffine: KeyGen", move |b| {
b.iter(|| {
let mut rng = &mut rand::thread_rng();
SchnorrEdwards::keygen(&parameters, &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(&parameters, &mut rng).unwrap();
let message = [100u8; 128];
c.bench_function("SchnorrEdwardsAffine: Sign", move |b| {
b.iter(|| {
let mut rng = &mut rand::thread_rng();
SchnorrEdwards::sign(&parameters, &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(&parameters, &mut rng).unwrap();
let message = [100u8; 128];
let signature = SchnorrEdwards::sign(&parameters, &sk, &message, &mut rng).unwrap();
c.bench_function("SchnorrEdwardsAffine: Verify", move |b| {
b.iter(|| SchnorrEdwards::verify(&parameters, &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(&parameters, &mut rng).unwrap();
let randomness: [u8; 32] = rng.gen();
c.bench_function("SchnorrEdwardsAffine: Randomize PubKey", move |b| {
b.iter(|| SchnorrEdwards::randomize_public_key(&parameters, &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(&parameters, &mut rng).unwrap();
let randomness: [u8; 32] = rng.gen();
let message = [100u8; 128];
let signature = SchnorrEdwards::sign(&parameters, &sk, &message, &mut rng).unwrap();
c.bench_function("SchnorrEdwardsAffine: Randomize Signature", move |b| {
b.iter(|| {
SchnorrEdwards::randomize_signature(&parameters, &signature, &randomness).unwrap()
})
});
}
criterion_group! {
name = schnorr_sig_affine;
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
}
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()
})
});
} }
mod projective {
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 = SchnorrSignature<Edwards, Blake2s>;
fn schnorr_signature_setup(c: &mut Criterion) {
c.bench_function("SchnorrEdwardsProjective: 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("SchnorrEdwardsProjective: KeyGen", move |b| {
b.iter(|| {
let mut rng = &mut rand::thread_rng();
SchnorrEdwards::keygen(&parameters, &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(&parameters, &mut rng).unwrap();
let message = [100u8; 128];
fn schnorr_signature_keygen(c: &mut Criterion) {
let mut rng = &mut rand::thread_rng();
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
c.bench_function("SchnorrEdwardsProjective: Sign", move |b| {
b.iter(|| {
let mut rng = &mut rand::thread_rng();
SchnorrEdwards::sign(&parameters, &sk, &message, &mut rng).unwrap()
})
});
}
c.bench_function("SchnorrEdwards: KeyGen", move |b| {
b.iter(|| {
let mut rng = &mut rand::thread_rng();
SchnorrEdwards::keygen(&parameters, &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(&parameters, &mut rng).unwrap();
let message = [100u8; 128];
let signature = SchnorrEdwards::sign(&parameters, &sk, &message, &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(&parameters, &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(&parameters, &sk, &message, &mut rng).unwrap()
})
});
}
c.bench_function("SchnorrEdwardsProjective: Verify", move |b| {
b.iter(|| SchnorrEdwards::verify(&parameters, &pk, &message, &signature).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(&parameters, &mut rng).unwrap();
let message = [100u8; 128];
let signature = SchnorrEdwards::sign(&parameters, &sk, &message, &mut rng).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(&parameters, &mut rng).unwrap();
let randomness: [u8; 32] = rng.gen();
c.bench_function("SchnorrEdwards: Verify", move |b| {
b.iter(|| SchnorrEdwards::verify(&parameters, &pk, &message, &signature).unwrap())
});
}
c.bench_function("SchnorrEdwardsProjective: Randomize PubKey", move |b| {
b.iter(|| SchnorrEdwards::randomize_public_key(&parameters, &pk, &randomness).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(&parameters, &mut rng).unwrap();
let randomness: [u8; 32] = rng.gen();
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(&parameters, &mut rng).unwrap();
let randomness: [u8; 32] = rng.gen();
let message = [100u8; 128];
let signature = SchnorrEdwards::sign(&parameters, &sk, &message, &mut rng).unwrap();
c.bench_function("SchnorrEdwards: Randomize PubKey", move |b| {
b.iter(|| SchnorrEdwards::randomize_public_key(&parameters, &pk, &randomness).unwrap())
});
}
c.bench_function("SchnorrEdwardsProjective: Randomize Signature", move |b| {
b.iter(|| {
SchnorrEdwards::randomize_signature(&parameters, &signature, &randomness).unwrap()
})
});
}
criterion_group! {
name = schnorr_sig_projective;
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
}
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(&parameters, &mut rng).unwrap();
let randomness: [u8; 32] = rng.gen();
let message = [100u8; 128];
let signature = SchnorrEdwards::sign(&parameters, &sk, &message, &mut rng).unwrap();
c.bench_function("SchnorrEdwards: Randomize Signature", move |b| {
b.iter(|| {
SchnorrEdwards::randomize_signature(&parameters, &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
} }
use crate::{affine::schnorr_sig_affine, projective::schnorr_sig_projective};
criterion_main!(schnorr_sig_affine, schnorr_sig_projective);
criterion_main!(schnorr_sig);

+ 2
- 2
crypto-primitives/Cargo.toml

@ -40,9 +40,9 @@ rayon = { version = "1.0", optional = true }
derivative = { version = "2.0", features = ["use_core"] } derivative = { version = "2.0", features = ["use_core"] }
[features] [features]
default = ["std"]
default = ["std", "r1cs"]
r1cs = ["r1cs-core", "r1cs-std"] r1cs = ["r1cs-core", "r1cs-std"]
std = ["r1cs", "algebra-core/std", "r1cs-core/std", "r1cs-std/std"]
std = [ "algebra-core/std", "r1cs-core/std", "r1cs-std/std"]
parallel = ["std", "rayon", "gm17/parallel", "groth16/parallel", "ff-fft/parallel"] parallel = ["std", "rayon", "gm17/parallel", "groth16/parallel", "ff-fft/parallel"]
[dev-dependencies] [dev-dependencies]

+ 67
- 138
crypto-primitives/src/commitment/blake2s/constraints.rs

@ -1,9 +1,10 @@
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::{Namespace, SynthesisError};
use crate::{ use crate::{
commitment::blake2s::Blake2sCommitment,
prf::blake2s::constraints::{blake2s_gadget, Blake2sOutputGadget},
CommitmentGadget, Vec,
commitment::blake2s,
commitment::CommitmentGadget,
prf::blake2s::constraints::{evaluate_blake2s, OutputVar},
Vec,
}; };
use algebra_core::{Field, PrimeField}; use algebra_core::{Field, PrimeField};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
@ -11,126 +12,58 @@ use r1cs_std::prelude::*;
use core::borrow::Borrow; use core::borrow::Borrow;
#[derive(Clone)] #[derive(Clone)]
pub struct Blake2sParametersGadget;
pub struct ParametersVar;
#[derive(Clone)] #[derive(Clone)]
pub struct Blake2sRandomnessGadget(pub Vec<UInt8>);
pub struct Blake2sCommitmentGadget;
impl<ConstraintF: PrimeField> CommitmentGadget<Blake2sCommitment, ConstraintF>
for Blake2sCommitmentGadget
{
type OutputGadget = Blake2sOutputGadget;
type ParametersGadget = Blake2sParametersGadget;
type RandomnessGadget = Blake2sRandomnessGadget;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
_: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError> {
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>;
fn commit(
_: &Self::ParametersVar,
input: &[UInt8<F>],
r: &Self::RandomnessVar,
) -> Result<Self::OutputVar, SynthesisError> {
let mut input_bits = Vec::with_capacity(512); let mut input_bits = Vec::with_capacity(512);
for byte in input.iter().chain(r.0.iter()) { for byte in input.iter().chain(r.0.iter()) {
input_bits.extend_from_slice(&byte.into_bits_le()); input_bits.extend_from_slice(&byte.into_bits_le());
} }
let mut result = Vec::new(); let mut result = Vec::new();
for (i, int) in blake2s_gadget(cs.ns(|| "Blake2s Eval"), &input_bits)?
.into_iter()
.enumerate()
{
let chunk = int.to_bytes(&mut cs.ns(|| format!("Result ToBytes {}", i)))?;
for int in evaluate_blake2s(&input_bits)?.into_iter() {
let chunk = int.to_bytes()?;
result.extend_from_slice(&chunk); result.extend_from_slice(&chunk);
} }
Ok(Blake2sOutputGadget(result))
Ok(OutputVar(result))
} }
} }
impl<ConstraintF: Field> AllocGadget<(), ConstraintF> for Blake2sParametersGadget {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<()>,
{
Self::alloc(cs, || Ok(val))
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(_: CS, _: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<()>,
{
Ok(Blake2sParametersGadget)
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
_: CS,
_: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<()>,
{
Ok(Blake2sParametersGadget)
impl<ConstraintF: Field> AllocVar<(), ConstraintF> for ParametersVar {
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> AllocGadget<[u8; 32], ConstraintF> for Blake2sRandomnessGadget {
#[inline]
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<[u8; 32]>,
{
let mut bytes = vec![];
for (i, b) in val.borrow().iter().enumerate() {
bytes.push(UInt8::alloc_constant(cs.ns(|| format!("value {}", i)), b)?)
impl<ConstraintF: PrimeField> AllocVar<[u8; 32], ConstraintF> for RandomnessVar<ConstraintF> {
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),
} }
Ok(Blake2sRandomnessGadget(bytes))
}
#[inline]
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_vec(cs, &value)?;
Ok(Blake2sRandomnessGadget(bytes))
}
#[inline]
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_input_vec(cs, &value)?;
Ok(Blake2sRandomnessGadget(bytes))
} }
} }
@ -138,64 +71,60 @@ impl AllocGadget<[u8; 32], ConstraintF> for Blake2sRand
mod test { mod test {
use crate::{ use crate::{
commitment::blake2s::{ commitment::blake2s::{
constraints::{Blake2sCommitmentGadget, Blake2sRandomnessGadget},
Blake2sCommitment,
constraints::{CommGadget, RandomnessVar},
Commitment,
}, },
*,
commitment::{CommitmentGadget, CommitmentScheme},
}; };
use algebra::{ed_on_bls12_381::Fq as Fr, test_rng}; use algebra::{ed_on_bls12_381::Fq as Fr, test_rng};
use r1cs_core::ConstraintSystem; use r1cs_core::ConstraintSystem;
use r1cs_std::{prelude::*, test_constraint_system::TestConstraintSystem};
use r1cs_std::prelude::*;
use rand::Rng; use rand::Rng;
#[test] #[test]
fn commitment_gadget_test() { fn commitment_gadget_test() {
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let input = [1u8; 32]; let input = [1u8; 32];
let rng = &mut test_rng(); let rng = &mut test_rng();
type TestCOMM = Blake2sCommitment;
type TestCOMMGadget = Blake2sCommitmentGadget;
type TestCOMM = Commitment;
type TestCOMMGadget = CommGadget;
let mut randomness = [0u8; 32]; let mut randomness = [0u8; 32];
rng.fill(&mut randomness); rng.fill(&mut randomness);
let parameters = (); let parameters = ();
let primitive_result = Blake2sCommitment::commit(&parameters, &input, &randomness).unwrap();
let primitive_result = Commitment::commit(&parameters, &input, &randomness).unwrap();
let mut input_bytes = vec![];
for (byte_i, input_byte) in input.iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
let mut input_var = vec![];
for byte in &input {
input_var.push(UInt8::new_witness(cs.clone(), || Ok(*byte)).unwrap());
} }
let mut randomness_bytes = vec![];
for (byte_i, random_byte) in randomness.iter().enumerate() {
let cs = cs.ns(|| format!("randomness_byte_gadget_{}", byte_i));
randomness_bytes.push(UInt8::alloc(cs, || Ok(*random_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_bytes = Blake2sRandomnessGadget(randomness_bytes);
let randomness_var = RandomnessVar(randomness_var);
let gadget_parameters =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
let parameters_var =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::ParametersVar::new_witness(
cs.ns("gadget_parameters"),
|| Ok(&parameters), || Ok(&parameters),
) )
.unwrap(); .unwrap();
let gadget_result =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::check_commitment_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
&randomness_bytes,
)
.unwrap();
let result_var = <TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::commit(
&parameters_var,
&input_var,
&randomness_var,
)
.unwrap();
for i in 0..32 { for i in 0..32 {
assert_eq!(primitive_result[i], gadget_result.0[i].get_value().unwrap());
assert_eq!(primitive_result[i], result_var.0[i].value().unwrap());
} }
assert!(cs.is_satisfied());
assert!(cs.is_satisfied().unwrap());
} }
} }

+ 4
- 4
crypto-primitives/src/commitment/blake2s/mod.rs

@ -4,12 +4,12 @@ use blake2::Blake2s as b2s;
use digest::Digest; use digest::Digest;
use rand::Rng; use rand::Rng;
pub struct Blake2sCommitment;
pub struct Commitment;
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
impl CommitmentScheme for Blake2sCommitment {
impl CommitmentScheme for Commitment {
type Parameters = (); type Parameters = ();
type Randomness = [u8; 32]; type Randomness = [u8; 32];
type Output = [u8; 32]; type Output = [u8; 32];
@ -21,11 +21,11 @@ impl CommitmentScheme for Blake2sCommitment {
fn commit( fn commit(
_: &Self::Parameters, _: &Self::Parameters,
input: &[u8], input: &[u8],
randomness: &Self::Randomness,
r: &Self::Randomness,
) -> Result<Self::Output, Error> { ) -> Result<Self::Output, Error> {
let mut h = b2s::new(); let mut h = b2s::new();
h.input(input); h.input(input);
h.input(randomness.as_ref());
h.input(r.as_ref());
let mut result = [0u8; 32]; let mut result = [0u8; 32];
result.copy_from_slice(&h.result()); result.copy_from_slice(&h.result());
Ok(result) Ok(result)

+ 12
- 12
crypto-primitives/src/commitment/constraints.rs

@ -1,23 +1,23 @@
use crate::CommitmentScheme;
use crate::commitment::CommitmentScheme;
use algebra_core::Field; use algebra_core::Field;
use core::fmt::Debug; use core::fmt::Debug;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::SynthesisError;
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
pub trait CommitmentGadget<C: CommitmentScheme, ConstraintF: Field> { pub trait CommitmentGadget<C: CommitmentScheme, ConstraintF: Field> {
type OutputGadget: EqGadget<ConstraintF>
type OutputVar: EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF> + ToBytesGadget<ConstraintF>
+ AllocGadget<C::Output, ConstraintF>
+ AllocVar<C::Output, ConstraintF>
+ R1CSVar<ConstraintF>
+ Clone + Clone
+ Sized + Sized
+ Debug; + Debug;
type ParametersGadget: AllocGadget<C::Parameters, ConstraintF> + Clone;
type RandomnessGadget: AllocGadget<C::Randomness, ConstraintF> + Clone;
type ParametersVar: AllocVar<C::Parameters, ConstraintF> + Clone;
type RandomnessVar: AllocVar<C::Randomness, ConstraintF> + Clone;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError>;
fn commit(
parameters: &Self::ParametersVar,
input: &[UInt8<ConstraintF>],
r: &Self::RandomnessVar,
) -> Result<Self::OutputVar, SynthesisError>;
} }

+ 38
- 41
crypto-primitives/src/commitment/injective_map/constraints.rs

@ -1,62 +1,59 @@
use algebra_core::{Field, PrimeField};
use crate::commitment::{ use crate::commitment::{
injective_map::{InjectiveMap, PedersenCommCompressor}, injective_map::{InjectiveMap, PedersenCommCompressor},
pedersen::{ pedersen::{
constraints::{
PedersenCommitmentGadget, PedersenCommitmentGadgetParameters, PedersenRandomnessGadget,
},
PedersenWindow,
constraints::{CommGadget, ParametersVar, RandomnessVar},
Window,
}, },
CommitmentGadget,
}; };
pub use crate::crh::injective_map::constraints::InjectiveMapGadget; pub use crate::crh::injective_map::constraints::InjectiveMapGadget;
use algebra_core::groups::Group;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::{groups::GroupGadget, uint8::UInt8};
use algebra_core::{Field, PrimeField, ProjectiveCurve};
use r1cs_core::SynthesisError;
use r1cs_std::{
groups::{CurveVar, GroupOpsBounds},
uint8::UInt8,
};
use core::marker::PhantomData; use core::marker::PhantomData;
pub struct PedersenCommitmentCompressorGadget<G, I, ConstraintF, GG, IG>
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
pub struct CommitmentCompressorGadget<C, I, W, GG, IG>
where where
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
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: PhantomData<I>,
_compressor_gadget: PhantomData<IG>, _compressor_gadget: PhantomData<IG>,
_crh: PedersenCommitmentGadget<G, ConstraintF, GG>,
_comm: PhantomData<CommGadget<C, GG, W>>,
} }
impl<G, I, ConstraintF, GG, IG, W> CommitmentGadget<PedersenCommCompressor<G, I, W>, ConstraintF>
for PedersenCommitmentCompressorGadget<G, I, ConstraintF, GG, IG>
impl<C, I, GG, IG, W>
crate::commitment::CommitmentGadget<PedersenCommCompressor<C, I, W>, ConstraintF<C>>
for CommitmentCompressorGadget<C, I, W, GG, IG>
where where
G: Group,
I: InjectiveMap<G>,
ConstraintF: PrimeField,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
W: PedersenWindow,
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 OutputGadget = IG::OutputGadget;
type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
type RandomnessGadget = PedersenRandomnessGadget;
type OutputVar = IG::OutputVar;
type ParametersVar = ParametersVar<C, GG>;
type RandomnessVar = RandomnessVar<ConstraintF<C>>;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError> {
let result = PedersenCommitmentGadget::<G, ConstraintF, GG>::check_commitment_gadget(
cs.ns(|| "PedersenComm"),
parameters,
input,
r,
)?;
IG::evaluate_map(cs.ns(|| "InjectiveMap"), &result)
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)
} }
} }

+ 11
- 14
crypto-primitives/src/commitment/injective_map/mod.rs

@ -2,32 +2,29 @@ use crate::Error;
use core::marker::PhantomData; use core::marker::PhantomData;
use rand::Rng; use rand::Rng;
use super::{
pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness, PedersenWindow},
CommitmentScheme,
};
use super::{pedersen, CommitmentScheme};
pub use crate::crh::injective_map::InjectiveMap; pub use crate::crh::injective_map::InjectiveMap;
use algebra_core::groups::Group;
use algebra_core::ProjectiveCurve;
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
pub struct PedersenCommCompressor<G: Group, I: InjectiveMap<G>, W: PedersenWindow> {
_group: PhantomData<G>,
pub struct PedersenCommCompressor<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> {
_group: PhantomData<C>,
_compressor: PhantomData<I>, _compressor: PhantomData<I>,
_comm: PedersenCommitment<G, W>,
_comm: pedersen::Commitment<C, W>,
} }
impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> CommitmentScheme
for PedersenCommCompressor<G, I, W>
impl<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> CommitmentScheme
for PedersenCommCompressor<C, I, W>
{ {
type Output = I::Output; type Output = I::Output;
type Parameters = PedersenParameters<G>;
type Randomness = PedersenRandomness<G>;
type Parameters = pedersen::Parameters<C>;
type Randomness = pedersen::Randomness<C>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> { fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!("PedersenCompressor::Setup")); let time = start_timer!(|| format!("PedersenCompressor::Setup"));
let params = PedersenCommitment::<G, W>::setup(rng);
let params = pedersen::Commitment::<C, W>::setup(rng);
end_timer!(time); end_timer!(time);
params params
} }
@ -38,7 +35,7 @@ impl, W: PedersenWindow> CommitmentScheme
randomness: &Self::Randomness, randomness: &Self::Randomness,
) -> Result<Self::Output, Error> { ) -> Result<Self::Output, Error> {
let eval_time = start_timer!(|| "PedersenCompressor::Eval"); let eval_time = start_timer!(|| "PedersenCompressor::Eval");
let result = I::injective_map(&PedersenCommitment::<G, W>::commit(
let result = I::injective_map(&pedersen::Commitment::<C, W>::commit(
parameters, input, randomness, parameters, input, randomness,
)?)?; )?)?;
end_timer!(eval_time); end_timer!(eval_time);

+ 94
- 172
crypto-primitives/src/commitment/pedersen/constraints.rs

@ -1,57 +1,63 @@
use crate::{ use crate::{
commitment::pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness},
crh::pedersen::PedersenWindow,
commitment::pedersen::{Commitment, Parameters, Randomness},
crh::pedersen::Window,
Vec, Vec,
}; };
use algebra_core::{ use algebra_core::{
fields::{Field, PrimeField}, fields::{Field, PrimeField},
to_bytes, Group, ToBytes,
to_bytes, ProjectiveCurve, ToBytes, Zero,
}; };
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::{Namespace, SynthesisError};
use crate::commitment::CommitmentGadget;
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Clone(bound = "G: Group, W: PedersenWindow, ConstraintF: Field"))]
pub struct PedersenCommitmentGadgetParameters<G: Group, W: PedersenWindow, ConstraintF: Field> {
params: PedersenParameters<G>,
#[doc(hidden)]
_group: PhantomData<G>,
#[doc(hidden)]
_engine: PhantomData<ConstraintF>,
#[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)] #[doc(hidden)]
_window: PhantomData<W>,
_group_var: PhantomData<GG>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PedersenRandomnessGadget(Vec<UInt8>);
pub struct PedersenCommitmentGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>(
#[doc(hidden)] PhantomData<*const G>,
#[doc(hidden)] PhantomData<*const GG>,
PhantomData<ConstraintF>,
);
pub struct RandomnessVar<F: Field>(Vec<UInt8<F>>);
impl<ConstraintF, G, GG, W> CommitmentGadget<PedersenCommitment<G, W>, ConstraintF>
for PedersenCommitmentGadget<G, ConstraintF, GG>
pub struct CommGadget<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>, W: Window>
where where
ConstraintF: PrimeField,
G: Group,
GG: GroupGadget<G, ConstraintF>,
W: PedersenWindow,
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
{ {
type OutputGadget = GG;
type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
type RandomnessGadget = PedersenRandomnessGadget;
#[doc(hidden)]
_curve: PhantomData<*const C>,
#[doc(hidden)]
_group_var: PhantomData<*const GG>,
#[doc(hidden)]
_window: PhantomData<*const W>,
}
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError> {
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>>;
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)); assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS));
let mut padded_input = input.to_vec(); let mut padded_input = input.to_vec();
@ -72,16 +78,12 @@ where
.flat_map(|byte| byte.into_bits_le()) .flat_map(|byte| byte.into_bits_le())
.collect(); .collect();
let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
let mut result = GG::precomputed_base_multiscalar_mul(
cs.ns(|| "multiexp"),
&parameters.params.generators,
input_in_bits,
)?;
let mut result =
GG::precomputed_base_multiscalar_mul(&parameters.params.generators, input_in_bits)?;
// Compute h^r // Compute h^r
let rand_bits: Vec<_> = r.0.iter().flat_map(|byte| byte.into_bits_le()).collect(); let rand_bits: Vec<_> = r.0.iter().flat_map(|byte| byte.into_bits_le()).collect();
result.precomputed_base_scalar_mul( result.precomputed_base_scalar_mul(
cs.ns(|| "Randomizer"),
rand_bits rand_bits
.iter() .iter()
.zip(&parameters.params.randomness_generator), .zip(&parameters.params.randomness_generator),
@ -91,109 +93,41 @@ where
} }
} }
impl<G, W, ConstraintF> AllocGadget<PedersenParameters<G>, ConstraintF>
for PedersenCommitmentGadgetParameters<G, W, ConstraintF>
impl<C, GG> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GG>
where where
G: Group,
W: PedersenWindow,
ConstraintF: PrimeField,
C: ProjectiveCurve,
GG: CurveVar<C, ConstraintF<C>>,
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<PedersenParameters<G>>,
{
let parameters = val.borrow().clone();
Ok(PedersenCommitmentGadgetParameters {
params: parameters,
_group: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let temp = value_gen()?;
Self::alloc_constant(cs, temp)
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let temp = value_gen()?;
let parameters = temp.borrow().clone();
Ok(PedersenCommitmentGadgetParameters {
params: parameters,
_group: PhantomData,
_engine: PhantomData,
_window: PhantomData,
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<G, ConstraintF> AllocGadget<PedersenRandomness<G>, ConstraintF> for PedersenRandomnessGadget
impl<C, F> AllocVar<Randomness<C>, F> for RandomnessVar<F>
where where
G: Group,
ConstraintF: PrimeField,
C: ProjectiveCurve,
F: PrimeField,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<PedersenRandomness<G>>,
{
let mut result_bytes = vec![];
for (i, byte) in to_bytes![val.borrow().0].unwrap().into_iter().enumerate() {
let cur = UInt8::alloc_constant(cs.ns(|| format!("byte {}", i)), byte)?;
result_bytes.push(cur);
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),
} }
Ok(PedersenRandomnessGadget(result_bytes))
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenRandomness<G>>,
{
let temp = value_gen()?;
let randomness = to_bytes![temp.borrow().0].unwrap();
Ok(PedersenRandomnessGadget(UInt8::alloc_vec(cs, &randomness)?))
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenRandomness<G>>,
{
let temp = value_gen()?;
let randomness = to_bytes![temp.borrow().0].unwrap();
Ok(PedersenRandomnessGadget(UInt8::alloc_input_vec(
cs,
&randomness,
)?))
} }
} }
@ -201,31 +135,27 @@ where
mod test { mod test {
use algebra::{ use algebra::{
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr}, ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr},
test_rng, ProjectiveCurve, UniformRand,
test_rng, UniformRand,
}; };
use crate::{ use crate::{
commitment::{ commitment::{
pedersen::{
constraints::PedersenCommitmentGadget, PedersenCommitment, PedersenRandomness,
},
pedersen::{constraints::CommGadget, Commitment, Randomness},
CommitmentGadget, CommitmentScheme, CommitmentGadget, CommitmentScheme,
}, },
crh::pedersen::PedersenWindow,
crh::pedersen,
}; };
use r1cs_core::ConstraintSystem; use r1cs_core::ConstraintSystem;
use r1cs_std::{
ed_on_bls12_381::EdwardsGadget, prelude::*, test_constraint_system::TestConstraintSystem,
};
use r1cs_std::{ed_on_bls12_381::EdwardsVar, prelude::*};
#[test] #[test]
fn commitment_gadget_test() { fn commitment_gadget_test() {
let mut cs = TestConstraintSystem::<Fq>::new();
let cs = ConstraintSystem::<Fq>::new_ref();
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct Window; pub(super) struct Window;
impl PedersenWindow for Window {
impl pedersen::Window for Window {
const WINDOW_SIZE: usize = 4; const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 8; const NUM_WINDOWS: usize = 8;
} }
@ -234,45 +164,37 @@ mod test {
let rng = &mut test_rng(); let rng = &mut test_rng();
type TestCOMM = PedersenCommitment<JubJub, Window>;
type TestCOMMGadget = PedersenCommitmentGadget<JubJub, Fq, EdwardsGadget>;
type TestCOMM = Commitment<JubJub, Window>;
type TestCOMMGadget = CommGadget<JubJub, EdwardsVar, Window>;
let randomness = PedersenRandomness(Fr::rand(rng));
let randomness = Randomness(Fr::rand(rng));
let parameters = PedersenCommitment::<JubJub, Window>::setup(rng).unwrap();
let parameters = Commitment::<JubJub, Window>::setup(rng).unwrap();
let primitive_result = let primitive_result =
PedersenCommitment::<JubJub, Window>::commit(&parameters, &input, &randomness).unwrap();
Commitment::<JubJub, Window>::commit(&parameters, &input, &randomness).unwrap();
let mut input_bytes = vec![];
for (byte_i, input_byte) in input.iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).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 =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessGadget::alloc(
&mut cs.ns(|| "gadget_randomness"),
let randomness_var =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessVar::new_witness(
cs.ns("gadget_randomness"),
|| Ok(&randomness), || Ok(&randomness),
) )
.unwrap(); .unwrap();
let gadget_parameters =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
let parameters_var =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersVar::new_witness(
cs.ns("gadget_parameters"),
|| Ok(&parameters), || Ok(&parameters),
) )
.unwrap(); .unwrap();
let gadget_result =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::check_commitment_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
&randomness,
)
.unwrap();
let result_var =
TestCOMMGadget::commit(&parameters_var, &input_var, &randomness_var).unwrap();
let primitive_result = primitive_result.into_affine();
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
assert!(cs.is_satisfied());
let primitive_result = primitive_result;
assert_eq!(primitive_result, result_var.value().unwrap());
assert!(cs.is_satisfied().unwrap());
} }
} }

+ 27
- 37
crypto-primitives/src/commitment/pedersen/mod.rs

@ -1,9 +1,8 @@
use crate::{Error, Vec}; use crate::{Error, Vec};
use algebra_core::{ use algebra_core::{
bytes::ToBytes, bytes::ToBytes,
groups::Group,
io::{Result as IoResult, Write}, io::{Result as IoResult, Write},
BitIterator, Field, FpParameters, PrimeField, ToConstraintField, UniformRand,
BitIterator, Field, FpParameters, PrimeField, ProjectiveCurve, ToConstraintField, UniformRand,
}; };
use core::marker::PhantomData; use core::marker::PhantomData;
@ -11,64 +10,55 @@ use rand::Rng;
use super::CommitmentScheme; use super::CommitmentScheme;
pub use crate::crh::pedersen::PedersenWindow;
use crate::crh::{
pedersen::{PedersenCRH, PedersenParameters as PedersenCRHParameters},
FixedLengthCRH,
};
pub use crate::crh::pedersen::Window;
use crate::crh::{pedersen, FixedLengthCRH};
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
#[derive(Clone)] #[derive(Clone)]
pub struct PedersenParameters<G: Group> {
pub randomness_generator: Vec<G>,
pub generators: Vec<Vec<G>>,
pub struct Parameters<C: ProjectiveCurve> {
pub randomness_generator: Vec<C>,
pub generators: Vec<Vec<C>>,
} }
pub struct PedersenCommitment<G: Group, W: PedersenWindow> {
group: PhantomData<G>,
pub struct Commitment<C: ProjectiveCurve, W: Window> {
group: PhantomData<C>,
window: PhantomData<W>, window: PhantomData<W>,
} }
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(
Clone(bound = "G: Group"),
PartialEq(bound = "G: Group"),
Debug(bound = "G: Group"),
Eq(bound = "G: Group"),
Default(bound = "G: Group")
)]
pub struct PedersenRandomness<G: Group>(pub G::ScalarField);
impl<G: Group> UniformRand for PedersenRandomness<G> {
#[derivative(Clone, PartialEq, Debug, Eq, Default)]
pub struct Randomness<C: ProjectiveCurve>(pub C::ScalarField);
impl<C: ProjectiveCurve> UniformRand for Randomness<C> {
#[inline] #[inline]
fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self { fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
PedersenRandomness(UniformRand::rand(rng))
Randomness(UniformRand::rand(rng))
} }
} }
impl<G: Group> ToBytes for PedersenRandomness<G> {
impl<C: ProjectiveCurve> ToBytes for Randomness<C> {
fn write<W: Write>(&self, writer: W) -> IoResult<()> { fn write<W: Write>(&self, writer: W) -> IoResult<()> {
self.0.write(writer) self.0.write(writer)
} }
} }
impl<G: Group, W: PedersenWindow> CommitmentScheme for PedersenCommitment<G, W> {
type Parameters = PedersenParameters<G>;
type Randomness = PedersenRandomness<G>;
type Output = G;
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> { fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!( let time = start_timer!(|| format!(
"PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
"PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> C",
W::NUM_WINDOWS, W::NUM_WINDOWS,
W::WINDOW_SIZE, W::WINDOW_SIZE,
W::NUM_WINDOWS * W::WINDOW_SIZE W::NUM_WINDOWS * W::WINDOW_SIZE
)); ));
let num_powers = <G::ScalarField as PrimeField>::Params::MODULUS_BITS as usize;
let randomness_generator = PedersenCRH::<_, W>::generator_powers(num_powers, rng);
let generators = PedersenCRH::<_, W>::create_generators(rng);
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); end_timer!(time);
Ok(Self::Parameters { Ok(Self::Parameters {
@ -102,10 +92,10 @@ impl CommitmentScheme for PedersenCommitment
// Invoke Pedersen CRH here, to prevent code duplication. // Invoke Pedersen CRH here, to prevent code duplication.
let crh_parameters = PedersenCRHParameters {
let crh_parameters = pedersen::Parameters {
generators: parameters.generators.clone(), generators: parameters.generators.clone(),
}; };
let mut result = PedersenCRH::<_, W>::evaluate(&crh_parameters, &input)?;
let mut result: C = pedersen::CRH::<C, W>::evaluate(&crh_parameters, &input)?.into();
let randomize_time = start_timer!(|| "Randomize"); let randomize_time = start_timer!(|| "Randomize");
// Compute h^r. // Compute h^r.
@ -122,12 +112,12 @@ impl CommitmentScheme for PedersenCommitment
end_timer!(randomize_time); end_timer!(randomize_time);
end_timer!(commit_time); end_timer!(commit_time);
Ok(result)
Ok(result.into())
} }
} }
impl<ConstraintF: Field, G: Group + ToConstraintField<ConstraintF>> ToConstraintField<ConstraintF>
for PedersenParameters<G>
impl<ConstraintF: Field, C: ProjectiveCurve + ToConstraintField<ConstraintF>>
ToConstraintField<ConstraintF> for Parameters<C>
{ {
#[inline] #[inline]
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> { fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {

+ 72
- 117
crypto-primitives/src/crh/bowe_hopwood/constraints.rs

@ -1,61 +1,60 @@
use core::{borrow::Borrow, hash::Hash, marker::PhantomData};
use core::{borrow::Borrow, marker::PhantomData};
use crate::{ use crate::{
crh::{ crh::{
bowe_hopwood::{BoweHopwoodPedersenCRH, BoweHopwoodPedersenParameters, CHUNK_SIZE},
pedersen::PedersenWindow,
bowe_hopwood::{Parameters, CHUNK_SIZE, CRH},
pedersen::Window,
FixedLengthCRHGadget, FixedLengthCRHGadget,
}, },
Vec, Vec,
}; };
use algebra_core::{groups::Group, Field};
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::{alloc::AllocGadget, groups::GroupGadget, uint8::UInt8};
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; use r1cs_std::bits::boolean::Boolean;
type ConstraintF<P> = <<P as ModelParameters>::BaseField as Field>::BasePrimeField;
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Clone(
bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"
))]
pub struct BoweHopwoodPedersenCRHGadgetParameters<
G: Group,
W: PedersenWindow,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
> {
params: BoweHopwoodPedersenParameters<G>,
_group_g: PhantomData<GG>,
_engine: PhantomData<ConstraintF>,
#[derivative(Clone(bound = "P: TEModelParameters, W: Window"))]
pub struct ParametersVar<P: TEModelParameters, W: Window> {
params: Parameters<P>,
#[doc(hidden)]
_window: PhantomData<W>, _window: PhantomData<W>,
} }
pub struct BoweHopwoodPedersenCRHGadget<
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
> {
_group: PhantomData<*const G>,
_group_gadget: PhantomData<*const GG>,
_engine: PhantomData<ConstraintF>,
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<ConstraintF, G, GG, W> FixedLengthCRHGadget<BoweHopwoodPedersenCRH<G, W>, ConstraintF>
for BoweHopwoodPedersenCRHGadget<G, ConstraintF, GG>
impl<P, F, W> FixedLengthCRHGadget<CRH<P, W>, ConstraintF<P>> for CRHGadget<P, F>
where where
ConstraintF: Field,
G: Group + Hash,
GG: GroupGadget<G, ConstraintF>,
W: PedersenWindow,
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 OutputGadget = GG;
type ParametersGadget = BoweHopwoodPedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
type OutputVar = AffineVar<P, F>;
type ParametersVar = ParametersVar<P, W>;
fn evaluate(
parameters: &Self::ParametersVar,
input: &[UInt8<ConstraintF<P>>],
) -> Result<Self::OutputVar, SynthesisError> {
// Pad the input if it is not the current length. // Pad the input if it is not the current length.
let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect(); let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect();
if (input_in_bits.len()) % CHUNK_SIZE != 0 { if (input_in_bits.len()) % CHUNK_SIZE != 0 {
@ -75,8 +74,7 @@ where
.chunks(W::WINDOW_SIZE * CHUNK_SIZE) .chunks(W::WINDOW_SIZE * CHUNK_SIZE)
.map(|x| x.chunks(CHUNK_SIZE).collect::<Vec<_>>()) .map(|x| x.chunks(CHUNK_SIZE).collect::<Vec<_>>())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let result = GG::precomputed_base_3_bit_signed_digit_scalar_mul(
cs,
let result = AffineVar::precomputed_base_3_bit_signed_digit_scalar_mul(
&parameters.params.generators, &parameters.params.generators,
&input_in_bits, &input_in_bits,
)?; )?;
@ -85,51 +83,19 @@ where
} }
} }
impl<G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
AllocGadget<BoweHopwoodPedersenParameters<G>, ConstraintF>
for BoweHopwoodPedersenCRHGadgetParameters<G, W, ConstraintF, GG>
impl<P, W> AllocVar<Parameters<P>, ConstraintF<P>> for ParametersVar<P, W>
where
P: TEModelParameters,
W: Window,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<BoweHopwoodPedersenParameters<G>>,
{
let params = val.borrow().clone();
Ok(BoweHopwoodPedersenCRHGadgetParameters {
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, params,
_group_g: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<BoweHopwoodPedersenParameters<G>>,
{
let params = value_gen()?.borrow().clone();
Self::alloc_constant(cs, params)
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<BoweHopwoodPedersenParameters<G>>,
{
let params = value_gen()?.borrow().clone();
Ok(BoweHopwoodPedersenCRHGadgetParameters {
params,
_group_g: PhantomData,
_engine: PhantomData,
_window: PhantomData, _window: PhantomData,
}) })
} }
@ -140,22 +106,19 @@ mod test {
use rand::Rng; use rand::Rng;
use crate::crh::{ use crate::crh::{
bowe_hopwood::{constraints::BoweHopwoodPedersenCRHGadget, BoweHopwoodPedersenCRH},
pedersen::PedersenWindow,
bowe_hopwood::{constraints::CRHGadget, CRH},
pedersen::Window as PedersenWindow,
FixedLengthCRH, FixedLengthCRHGadget, FixedLengthCRH, FixedLengthCRHGadget,
}; };
use algebra::{ use algebra::{
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq as Fr},
ed_on_bls12_381::{EdwardsParameters, Fq as Fr},
test_rng, ProjectiveCurve, test_rng, ProjectiveCurve,
}; };
use r1cs_core::ConstraintSystem;
use r1cs_std::{
alloc::AllocGadget, ed_on_bls12_381::EdwardsGadget,
test_constraint_system::TestConstraintSystem, uint8::UInt8,
};
use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
use r1cs_std::{alloc::AllocVar, ed_on_bls12_381::FqVar, uint8::UInt8, R1CSVar};
type TestCRH = BoweHopwoodPedersenCRH<JubJub, Window>;
type TestCRHGadget = BoweHopwoodPedersenCRHGadget<JubJub, Fr, EdwardsGadget>;
type TestCRH = CRH<EdwardsParameters, Window>;
type TestCRHGadget = CRHGadget<EdwardsParameters, FqVar>;
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct Window; pub(super) struct Window;
@ -165,35 +128,34 @@ mod test {
const NUM_WINDOWS: usize = 8; const NUM_WINDOWS: usize = 8;
} }
fn generate_input<CS: ConstraintSystem<Fr>, R: Rng>(
mut cs: CS,
fn generate_input<R: Rng>(
cs: ConstraintSystemRef<Fr>,
rng: &mut R, rng: &mut R,
) -> ([u8; 189], Vec<UInt8>) {
) -> ([u8; 189], Vec<UInt8<Fr>>) {
let mut input = [1u8; 189]; let mut input = [1u8; 189];
rng.fill_bytes(&mut input); rng.fill_bytes(&mut input);
let mut input_bytes = vec![]; let mut input_bytes = vec![];
for (byte_i, input_byte) in input.iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
for byte in input.iter() {
input_bytes.push(UInt8::new_witness(cs.clone(), || Ok(byte)).unwrap());
} }
(input, input_bytes) (input, input_bytes)
} }
#[test] #[test]
fn crh_primitive_gadget_test() {
fn test_native_equality() {
let rng = &mut test_rng(); let rng = &mut test_rng();
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let (input, input_bytes) = generate_input(&mut cs, rng);
let (input, input_var) = generate_input(cs.clone(), rng);
println!("number of constraints for input: {}", cs.num_constraints()); println!("number of constraints for input: {}", cs.num_constraints());
let parameters = TestCRH::setup(rng).unwrap(); let parameters = TestCRH::setup(rng).unwrap();
let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap(); let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap();
let gadget_parameters =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
let parameters_var =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersVar::new_witness(
cs.ns("parameters_var"),
|| Ok(&parameters), || Ok(&parameters),
) )
.unwrap(); .unwrap();
@ -202,19 +164,12 @@ mod test {
cs.num_constraints() cs.num_constraints()
); );
let gadget_result =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::check_evaluation_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
)
.unwrap();
let result_var = TestCRHGadget::evaluate(&parameters_var, &input_var).unwrap();
println!("number of constraints total: {}", cs.num_constraints()); println!("number of constraints total: {}", cs.num_constraints());
let primitive_result = primitive_result.into_affine(); let primitive_result = primitive_result.into_affine();
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
assert!(cs.is_satisfied());
assert_eq!(primitive_result, result_var.value().unwrap().into_affine());
assert!(cs.is_satisfied().unwrap());
} }
} }

+ 33
- 34
crypto-primitives/src/crh/bowe_hopwood/mod.rs

@ -7,9 +7,12 @@ use rand::Rng;
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
use rayon::prelude::*; use rayon::prelude::*;
use super::pedersen::{bytes_to_bits, PedersenCRH, PedersenWindow};
use super::pedersen;
use crate::crh::FixedLengthCRH; use crate::crh::FixedLengthCRH;
use algebra_core::{biginteger::BigInteger, fields::PrimeField, groups::Group};
use algebra_core::{
biginteger::BigInteger, curves::twisted_edwards_extended::GroupProjective as TEProjective,
fields::PrimeField, ProjectiveCurve, TEModelParameters, UniformRand,
};
use ff_fft::cfg_chunks; use ff_fft::cfg_chunks;
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
@ -17,22 +20,23 @@ pub mod constraints;
pub const CHUNK_SIZE: usize = 3; pub const CHUNK_SIZE: usize = 3;
#[derive(Clone, Default)]
pub struct BoweHopwoodPedersenParameters<G: Group> {
pub generators: Vec<Vec<G>>,
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Default(bound = ""))]
pub struct Parameters<P: TEModelParameters> {
pub generators: Vec<Vec<TEProjective<P>>>,
} }
pub struct BoweHopwoodPedersenCRH<G: Group, W: PedersenWindow> {
group: PhantomData<G>,
pub struct CRH<P: TEModelParameters, W: pedersen::Window> {
group: PhantomData<P>,
window: PhantomData<W>, window: PhantomData<W>,
} }
impl<G: Group, W: PedersenWindow> BoweHopwoodPedersenCRH<G, W> {
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<G>> {
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(); let mut generators = Vec::new();
for _ in 0..W::NUM_WINDOWS { for _ in 0..W::NUM_WINDOWS {
let mut generators_for_segment = Vec::new(); let mut generators_for_segment = Vec::new();
let mut base = G::rand(rng);
let mut base = TEProjective::rand(rng);
for _ in 0..W::WINDOW_SIZE { for _ in 0..W::WINDOW_SIZE {
generators_for_segment.push(base); generators_for_segment.push(base);
for _ in 0..4 { for _ in 0..4 {
@ -45,10 +49,10 @@ impl BoweHopwoodPedersenCRH {
} }
} }
impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W> {
const INPUT_SIZE_BITS: usize = PedersenCRH::<G, W>::INPUT_SIZE_BITS;
type Output = G;
type Parameters = BoweHopwoodPedersenParameters<G>;
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 setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
fn calculate_num_chunks_in_segment<F: PrimeField>() -> usize { fn calculate_num_chunks_in_segment<F: PrimeField>() -> usize {
@ -63,10 +67,10 @@ impl FixedLengthCRH for BoweHopwoodPedersenCRH
c c
} }
let maximum_num_chunks_in_segment = calculate_num_chunks_in_segment::<G::ScalarField>();
let maximum_num_chunks_in_segment = calculate_num_chunks_in_segment::<P::ScalarField>();
if W::WINDOW_SIZE > maximum_num_chunks_in_segment { if W::WINDOW_SIZE > maximum_num_chunks_in_segment {
return Err(format!( return Err(format!(
"Bowe-Hopwood hash must have a window size resulting in scalars < (p-1)/2, \
"Bowe-Hopwood-PedersenCRH hash must have a window size resulting in scalars < (p-1)/2, \
maximum segment size is {}", maximum segment size is {}",
maximum_num_chunks_in_segment maximum_num_chunks_in_segment
) )
@ -74,7 +78,7 @@ impl FixedLengthCRH for BoweHopwoodPedersenCRH
} }
let time = start_timer!(|| format!( let time = start_timer!(|| format!(
"BoweHopwoodPedersenCRH::Setup: {} segments of {} 3-bit chunks; {{0,1}}^{{{}}} -> G",
"Bowe-Hopwood-PedersenCRH::Setup: {} segments of {} 3-bit chunks; {{0,1}}^{{{}}} -> P",
W::NUM_WINDOWS, W::NUM_WINDOWS,
W::WINDOW_SIZE, W::WINDOW_SIZE,
W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE
@ -98,7 +102,7 @@ impl FixedLengthCRH for BoweHopwoodPedersenCRH
} }
let mut padded_input = Vec::with_capacity(input.len()); let mut padded_input = Vec::with_capacity(input.len());
let input = bytes_to_bits(input);
let input = pedersen::bytes_to_bits(input);
// Pad the input if it is not the current length. // Pad the input if it is not the current length.
padded_input.extend_from_slice(&input); padded_input.extend_from_slice(&input);
if input.len() % CHUNK_SIZE != 0 { if input.len() % CHUNK_SIZE != 0 {
@ -143,13 +147,13 @@ impl FixedLengthCRH for BoweHopwoodPedersenCRH
encoded += &generator.double(); encoded += &generator.double();
} }
if chunk_bits[2] { if chunk_bits[2] {
encoded = encoded.neg();
encoded = -encoded;
} }
encoded encoded
}) })
.sum::<G>()
.sum::<TEProjective<P>>()
}) })
.sum::<G>();
.sum::<TEProjective<P>>();
end_timer!(eval_time); end_timer!(eval_time);
@ -157,9 +161,9 @@ impl FixedLengthCRH for BoweHopwoodPedersenCRH
} }
} }
impl<G: Group> Debug for BoweHopwoodPedersenParameters<G> {
impl<P: TEModelParameters> Debug for Parameters<P> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "Bowe-Hopwood Pedersen Hash Parameters {{\n")?;
write!(f, "Bowe-Hopwood-Pedersen Hash Parameters {{\n")?;
for (i, g) in self.generators.iter().enumerate() { for (i, g) in self.generators.iter().enumerate() {
write!(f, "\t Generator {}: {:?}\n", i, g)?; write!(f, "\t Generator {}: {:?}\n", i, g)?;
} }
@ -170,28 +174,23 @@ impl Debug for BoweHopwoodPedersenParameters {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
crh::{bowe_hopwood::BoweHopwoodPedersenCRH, pedersen::PedersenWindow},
crh::{bowe_hopwood::CRH, pedersen::Window},
FixedLengthCRH, FixedLengthCRH,
}; };
use algebra::{ed_on_bls12_381::EdwardsProjective, test_rng};
use algebra::{ed_on_bls12_381::EdwardsParameters, test_rng};
#[test] #[test]
fn test_simple_bh() { fn test_simple_bh() {
#[derive(Clone)] #[derive(Clone)]
struct TestWindow {} struct TestWindow {}
impl PedersenWindow for TestWindow {
impl Window for TestWindow {
const WINDOW_SIZE: usize = 63; const WINDOW_SIZE: usize = 63;
const NUM_WINDOWS: usize = 8; const NUM_WINDOWS: usize = 8;
} }
let rng = &mut test_rng(); let rng = &mut test_rng();
let params =
<BoweHopwoodPedersenCRH<EdwardsProjective, TestWindow> as FixedLengthCRH>::setup(rng)
.unwrap();
<BoweHopwoodPedersenCRH<EdwardsProjective, TestWindow> as FixedLengthCRH>::evaluate(
&params,
&[1, 2, 3],
)
.unwrap();
let params = <CRH<EdwardsParameters, TestWindow> as FixedLengthCRH>::setup(rng).unwrap();
<CRH<EdwardsParameters, TestWindow> as FixedLengthCRH>::evaluate(&params, &[1, 2, 3])
.unwrap();
} }
} }

+ 10
- 10
crypto-primitives/src/crh/constraints.rs

@ -2,24 +2,24 @@ use algebra_core::Field;
use core::fmt::Debug; use core::fmt::Debug;
use crate::crh::FixedLengthCRH; use crate::crh::FixedLengthCRH;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::SynthesisError;
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
pub trait FixedLengthCRHGadget<H: FixedLengthCRH, ConstraintF: Field>: Sized { pub trait FixedLengthCRHGadget<H: FixedLengthCRH, ConstraintF: Field>: Sized {
type OutputGadget: ConditionalEqGadget<ConstraintF>
+ EqGadget<ConstraintF>
type OutputVar: EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF> + ToBytesGadget<ConstraintF>
+ CondSelectGadget<ConstraintF> + CondSelectGadget<ConstraintF>
+ AllocGadget<H::Output, ConstraintF>
+ AllocVar<H::Output, ConstraintF>
+ R1CSVar<ConstraintF>
+ Debug + Debug
+ Clone + Clone
+ Sized; + Sized;
type ParametersGadget: AllocGadget<H::Parameters, ConstraintF> + Clone;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError>;
type ParametersVar: AllocVar<H::Parameters, ConstraintF> + Clone;
fn evaluate(
parameters: &Self::ParametersVar,
input: &[UInt8<ConstraintF>],
) -> Result<Self::OutputVar, SynthesisError>;
} }

+ 52
- 85
crypto-primitives/src/crh/injective_map/constraints.rs

@ -2,129 +2,96 @@ use core::{fmt::Debug, marker::PhantomData};
use crate::crh::{ use crate::crh::{
injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor}, injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor},
pedersen::{
constraints::{PedersenCRHGadget, PedersenCRHGadgetParameters},
PedersenWindow,
},
pedersen::{constraints as ped_constraints, Window},
FixedLengthCRHGadget, FixedLengthCRHGadget,
}; };
use algebra_core::{ use algebra_core::{
curves::{ curves::{
models::{ModelParameters, TEModelParameters}, models::{ModelParameters, TEModelParameters},
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
twisted_edwards_extended::GroupProjective as TEProjective,
}, },
fields::{Field, PrimeField, SquareRootField}, fields::{Field, PrimeField, SquareRootField},
groups::Group,
ProjectiveCurve,
}; };
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::SynthesisError;
use r1cs_std::{ use r1cs_std::{
fields::fp::FpGadget,
groups::{curves::twisted_edwards::AffineGadget as TwistedEdwardsGadget, GroupGadget},
fields::fp::FpVar,
groups::{curves::twisted_edwards::AffineVar as TEVar, CurveVar},
prelude::*, prelude::*,
}; };
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
pub trait InjectiveMapGadget< pub trait InjectiveMapGadget<
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
>
C: ProjectiveCurve,
I: InjectiveMap<C>,
GG: CurveVar<C, ConstraintF<C>>,
> where
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
{ {
type OutputGadget: EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF>
+ CondSelectGadget<ConstraintF>
+ AllocGadget<I::Output, ConstraintF>
type OutputVar: EqGadget<ConstraintF<C>>
+ ToBytesGadget<ConstraintF<C>>
+ CondSelectGadget<ConstraintF<C>>
+ AllocVar<I::Output, ConstraintF<C>>
+ R1CSVar<ConstraintF<C>, Value = I::Output>
+ Debug + Debug
+ Clone + Clone
+ Sized; + Sized;
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
ge: &GG,
) -> Result<Self::OutputGadget, SynthesisError>;
fn evaluate(ge: &GG) -> Result<Self::OutputVar, SynthesisError>;
} }
pub struct TECompressorGadget; pub struct TECompressorGadget;
impl<ConstraintF, P>
InjectiveMapGadget<
TEAffine<P>,
TECompressor,
ConstraintF,
TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
> for TECompressorGadget
where
ConstraintF: PrimeField + SquareRootField,
P: TEModelParameters + ModelParameters<BaseField = ConstraintF>,
{
type OutputGadget = FpGadget<ConstraintF>;
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
ge: &TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
) -> Result<Self::OutputGadget, SynthesisError> {
Ok(ge.x.clone())
}
}
impl<ConstraintF, P>
InjectiveMapGadget<
TEProjective<P>,
TECompressor,
ConstraintF,
TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
> for TECompressorGadget
impl<F, P> InjectiveMapGadget<TEProjective<P>, TECompressor, TEVar<P, FpVar<F>>>
for TECompressorGadget
where where
ConstraintF: PrimeField + SquareRootField,
P: TEModelParameters + ModelParameters<BaseField = ConstraintF>,
F: PrimeField + SquareRootField,
P: TEModelParameters + ModelParameters<BaseField = F>,
{ {
type OutputGadget = FpGadget<ConstraintF>;
type OutputVar = FpVar<F>;
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
ge: &TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
) -> Result<Self::OutputGadget, SynthesisError> {
fn evaluate(ge: &TEVar<P, FpVar<F>>) -> Result<Self::OutputVar, SynthesisError> {
Ok(ge.x.clone()) Ok(ge.x.clone())
} }
} }
pub struct PedersenCRHCompressorGadget<G, I, ConstraintF, GG, IG>
pub struct PedersenCRHCompressorGadget<C, I, W, GG, IG>
where where
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
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>, _compressor: PhantomData<I>,
#[doc(hidden)]
_compressor_gadget: PhantomData<IG>, _compressor_gadget: PhantomData<IG>,
_crh: PedersenCRHGadget<G, ConstraintF, GG>,
#[doc(hidden)]
_crh: ped_constraints::CRHGadget<C, GG, W>,
} }
impl<G, I, ConstraintF, GG, IG, W> FixedLengthCRHGadget<PedersenCRHCompressor<G, I, W>, ConstraintF>
for PedersenCRHCompressorGadget<G, I, ConstraintF, GG, IG>
impl<C, I, GG, IG, W> FixedLengthCRHGadget<PedersenCRHCompressor<C, I, W>, ConstraintF<C>>
for PedersenCRHCompressorGadget<C, I, W, GG, IG>
where where
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
W: PedersenWindow,
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 OutputGadget = IG::OutputGadget;
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
type OutputVar = IG::OutputVar;
type ParametersVar = ped_constraints::CRHParametersVar<C, GG>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
let result = PedersenCRHGadget::<G, ConstraintF, GG>::check_evaluation_gadget(
cs.ns(|| "PedCRH"),
parameters,
input,
)?;
IG::evaluate_map(cs.ns(|| "InjectiveMap"), &result)
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)
} }
} }

+ 18
- 33
crypto-primitives/src/crh/injective_map/mod.rs

@ -3,71 +3,56 @@ use algebra_core::bytes::ToBytes;
use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use core::{fmt::Debug, hash::Hash, marker::PhantomData};
use rand::Rng; use rand::Rng;
use super::{
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
FixedLengthCRH,
};
use algebra_core::{
curves::{
models::{ModelParameters, TEModelParameters},
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
ProjectiveCurve,
},
groups::Group,
use super::{pedersen, FixedLengthCRH};
use algebra_core::curves::{
models::{ModelParameters, TEModelParameters},
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
ProjectiveCurve,
}; };
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
pub trait InjectiveMap<G: Group> {
pub trait InjectiveMap<C: ProjectiveCurve> {
type Output: ToBytes + Clone + Eq + Hash + Default + Debug; type Output: ToBytes + Clone + Eq + Hash + Default + Debug;
fn injective_map(ge: &G) -> Result<Self::Output, CryptoError>;
fn injective_map(ge: &C::Affine) -> Result<Self::Output, CryptoError>;
} }
pub struct TECompressor; pub struct TECompressor;
impl<P: TEModelParameters> InjectiveMap<TEAffine<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)
}
}
impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor { impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor {
type Output = <P as ModelParameters>::BaseField; type Output = <P as ModelParameters>::BaseField;
fn injective_map(ge: &TEProjective<P>) -> Result<Self::Output, CryptoError> {
let ge = ge.into_affine();
fn injective_map(ge: &TEAffine<P>) -> Result<Self::Output, CryptoError> {
debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve()); debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve());
Ok(ge.x) Ok(ge.x)
} }
} }
pub struct PedersenCRHCompressor<G: Group, I: InjectiveMap<G>, W: PedersenWindow> {
_group: PhantomData<G>,
pub struct PedersenCRHCompressor<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> {
_group: PhantomData<C>,
_compressor: PhantomData<I>, _compressor: PhantomData<I>,
_crh: PedersenCRH<G, W>,
_crh: pedersen::CRH<C, W>,
} }
impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> FixedLengthCRH
for PedersenCRHCompressor<G, I, W>
impl<C: ProjectiveCurve, I: InjectiveMap<C>, W: pedersen::Window> FixedLengthCRH
for PedersenCRHCompressor<C, I, W>
{ {
const INPUT_SIZE_BITS: usize = PedersenCRH::<G, W>::INPUT_SIZE_BITS;
const INPUT_SIZE_BITS: usize = pedersen::CRH::<C, W>::INPUT_SIZE_BITS;
type Output = I::Output; type Output = I::Output;
type Parameters = PedersenParameters<G>;
type Parameters = pedersen::Parameters<C>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> { fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!("PedersenCRHCompressor::Setup")); let time = start_timer!(|| format!("PedersenCRHCompressor::Setup"));
let params = PedersenCRH::<G, W>::setup(rng);
let params = pedersen::CRH::<C, W>::setup(rng);
end_timer!(time); end_timer!(time);
params params
} }
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> { fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
let eval_time = start_timer!(|| "PedersenCRHCompressor::Eval"); let eval_time = start_timer!(|| "PedersenCRHCompressor::Eval");
let result = I::injective_map(&PedersenCRH::<G, W>::evaluate(parameters, input)?)?;
let result = I::injective_map(&pedersen::CRH::<C, W>::evaluate(parameters, input)?)?;
end_timer!(eval_time); end_timer!(eval_time);
Ok(result) Ok(result)
} }

+ 2
- 1
crypto-primitives/src/crh/mod.rs

@ -15,7 +15,8 @@ pub use constraints::*;
pub trait FixedLengthCRH { pub trait FixedLengthCRH {
const INPUT_SIZE_BITS: usize; const INPUT_SIZE_BITS: usize;
type Output: ToBytes + Clone + Eq + Hash + Default;
type Output: ToBytes + Clone + Eq + core::fmt::Debug + Hash + Default;
type Parameters: Clone + Default; type Parameters: Clone + Default;
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>; fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;

+ 70
- 131
crypto-primitives/src/crh/pedersen/constraints.rs

@ -1,57 +1,54 @@
use crate::{ use crate::{
crh::{ crh::{
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
pedersen::{Parameters, Window, CRH},
FixedLengthCRHGadget, FixedLengthCRHGadget,
}, },
Vec, Vec,
}; };
use algebra_core::{Field, Group};
use r1cs_core::{ConstraintSystem, SynthesisError};
use algebra_core::{Field, ProjectiveCurve};
use r1cs_core::{Namespace, SynthesisError};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Clone(
bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"
))]
pub struct PedersenCRHGadgetParameters<
G: Group,
W: PedersenWindow,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
> {
params: PedersenParameters<G>,
#[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>, _group_g: PhantomData<GG>,
_engine: PhantomData<ConstraintF>,
_window: PhantomData<W>,
} }
pub struct PedersenCRHGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
#[doc(hideen)]
_group: PhantomData<*const G>,
#[doc(hideen)]
_group_gadget: PhantomData<*const GG>,
#[doc(hideen)]
_engine: PhantomData<ConstraintF>,
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<ConstraintF, G, GG, W> FixedLengthCRHGadget<PedersenCRH<G, W>, ConstraintF>
for PedersenCRHGadget<G, ConstraintF, GG>
impl<C, GG, W> FixedLengthCRHGadget<CRH<C, W>, ConstraintF<C>> for CRHGadget<C, GG, W>
where where
ConstraintF: Field,
G: Group,
GG: GroupGadget<G, ConstraintF>,
W: PedersenWindow,
C: ProjectiveCurve,
GG: CurveVar<C, ConstraintF<C>>,
W: Window,
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
{ {
type OutputGadget = GG;
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
type OutputVar = GG;
type ParametersVar = CRHParametersVar<C, GG>;
fn evaluate(
parameters: &Self::ParametersVar,
input: &[UInt8<ConstraintF<C>>],
) -> Result<Self::OutputVar, SynthesisError> {
let mut padded_input = input.to_vec(); let mut padded_input = input.to_vec();
// Pad the input if it is not the current length. // Pad the input if it is not the current length.
if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS { if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS {
@ -64,145 +61,87 @@ where
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS); assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
// Allocate new variable for the result. // Allocate new variable for the result.
let input_in_bits: Vec<_> = padded_input
.iter()
.flat_map(|byte| byte.into_bits_le())
.collect();
let input_in_bits: Vec<_> = padded_input.iter().flat_map(|b| b.into_bits_le()).collect();
let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
let result = let result =
GG::precomputed_base_multiscalar_mul(cs, &parameters.params.generators, input_in_bits)?;
GG::precomputed_base_multiscalar_mul(&parameters.params.generators, input_in_bits)?;
Ok(result) Ok(result)
} }
} }
impl<G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
AllocGadget<PedersenParameters<G>, ConstraintF>
for PedersenCRHGadgetParameters<G, W, ConstraintF, GG>
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>,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<PedersenParameters<G>>,
{
let params = val.borrow().clone();
Ok(PedersenCRHGadgetParameters {
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, params,
_group_g: PhantomData, _group_g: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let params = value_gen()?.borrow().clone();
Self::alloc_constant(cs, params)
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let params = value_gen()?.borrow().clone();
Ok(PedersenCRHGadgetParameters {
params,
_group_g: PhantomData,
_engine: PhantomData,
_window: PhantomData,
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::crh::{
pedersen::{constraints::PedersenCRHGadget, PedersenCRH, PedersenWindow},
FixedLengthCRH, FixedLengthCRHGadget,
};
use crate::crh::{pedersen, pedersen::constraints::*, FixedLengthCRH, FixedLengthCRHGadget};
use algebra::{ use algebra::{
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq as Fr}, ed_on_bls12_381::{EdwardsProjective as JubJub, Fq as Fr},
test_rng, ProjectiveCurve,
};
use r1cs_core::ConstraintSystem;
use r1cs_std::{
ed_on_bls12_381::EdwardsGadget, prelude::*, test_constraint_system::TestConstraintSystem,
test_rng,
}; };
use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
use r1cs_std::ed_on_bls12_381::EdwardsVar;
use rand::Rng; use rand::Rng;
type TestCRH = PedersenCRH<JubJub, Window>;
type TestCRHGadget = PedersenCRHGadget<JubJub, Fr, EdwardsGadget>;
type TestCRH = pedersen::CRH<JubJub, Window>;
type TestCRHGadget = CRHGadget<JubJub, EdwardsVar, Window>;
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct Window; pub(super) struct Window;
impl PedersenWindow for Window {
impl pedersen::Window for Window {
const WINDOW_SIZE: usize = 128; const WINDOW_SIZE: usize = 128;
const NUM_WINDOWS: usize = 8; const NUM_WINDOWS: usize = 8;
} }
fn generate_input<CS: ConstraintSystem<Fr>, R: Rng>(
mut cs: CS,
fn generate_input<R: Rng>(
cs: ConstraintSystemRef<Fr>,
rng: &mut R, rng: &mut R,
) -> ([u8; 128], Vec<UInt8>) {
) -> ([u8; 128], Vec<UInt8<Fr>>) {
let mut input = [1u8; 128]; let mut input = [1u8; 128];
rng.fill_bytes(&mut input); rng.fill_bytes(&mut input);
let mut input_bytes = vec![]; let mut input_bytes = vec![];
for (byte_i, input_byte) in input.iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
for byte in input.iter() {
input_bytes.push(UInt8::new_witness(cs.clone(), || Ok(byte)).unwrap());
} }
(input, input_bytes) (input, input_bytes)
} }
#[test] #[test]
fn crh_primitive_gadget_test() {
fn test_native_equality() {
let rng = &mut test_rng(); let rng = &mut test_rng();
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let (input, input_bytes) = generate_input(&mut cs, rng);
println!("number of constraints for input: {}", cs.num_constraints());
let (input, input_var) = generate_input(cs.clone(), rng);
let parameters = TestCRH::setup(rng).unwrap(); let parameters = TestCRH::setup(rng).unwrap();
let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap(); let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap();
let gadget_parameters =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
|| Ok(&parameters),
)
.unwrap();
println!(
"number of constraints for input + params: {}",
cs.num_constraints()
);
let gadget_result =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::check_evaluation_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
)
.unwrap();
println!("number of constraints total: {}", cs.num_constraints());
let primitive_result = primitive_result.into_affine();
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
assert!(cs.is_satisfied());
let parameters_var =
CRHParametersVar::new_constant(cs.ns("CRH Parameters"), &parameters).unwrap();
let result_var = TestCRHGadget::evaluate(&parameters_var, &input_var).unwrap();
let primitive_result = primitive_result;
assert_eq!(primitive_result, result_var.value().unwrap());
assert!(cs.is_satisfied().unwrap());
} }
} }

+ 22
- 22
crypto-primitives/src/crh/pedersen/mod.rs

@ -8,29 +8,29 @@ use rand::Rng;
use rayon::prelude::*; use rayon::prelude::*;
use crate::crh::FixedLengthCRH; use crate::crh::FixedLengthCRH;
use algebra_core::{groups::Group, Field, ToConstraintField};
use algebra_core::{Field, ProjectiveCurve, ToConstraintField};
use ff_fft::cfg_chunks; use ff_fft::cfg_chunks;
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
pub trait PedersenWindow: Clone {
pub trait Window: Clone {
const WINDOW_SIZE: usize; const WINDOW_SIZE: usize;
const NUM_WINDOWS: usize; const NUM_WINDOWS: usize;
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct PedersenParameters<G: Group> {
pub generators: Vec<Vec<G>>,
pub struct Parameters<C: ProjectiveCurve> {
pub generators: Vec<Vec<C>>,
} }
pub struct PedersenCRH<G: Group, W: PedersenWindow> {
group: PhantomData<G>,
pub struct CRH<C: ProjectiveCurve, W: Window> {
group: PhantomData<C>,
window: PhantomData<W>, window: PhantomData<W>,
} }
impl<G: Group, W: PedersenWindow> PedersenCRH<G, W> {
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<G>> {
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(); let mut generators_powers = Vec::new();
for _ in 0..W::NUM_WINDOWS { for _ in 0..W::NUM_WINDOWS {
generators_powers.push(Self::generator_powers(W::WINDOW_SIZE, rng)); generators_powers.push(Self::generator_powers(W::WINDOW_SIZE, rng));
@ -38,9 +38,9 @@ impl PedersenCRH {
generators_powers generators_powers
} }
pub fn generator_powers<R: Rng>(num_powers: usize, rng: &mut R) -> Vec<G> {
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 cur_gen_powers = Vec::with_capacity(num_powers);
let mut base = G::rand(rng);
let mut base = C::rand(rng);
for _ in 0..num_powers { for _ in 0..num_powers {
cur_gen_powers.push(base); cur_gen_powers.push(base);
base.double_in_place(); base.double_in_place();
@ -49,14 +49,14 @@ impl PedersenCRH {
} }
} }
impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
impl<C: ProjectiveCurve, W: Window> FixedLengthCRH for CRH<C, W> {
const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS; const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS;
type Output = G;
type Parameters = PedersenParameters<G>;
type Output = C::Affine;
type Parameters = Parameters<C>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> { fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!( let time = start_timer!(|| format!(
"PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
"PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> C",
W::NUM_WINDOWS, W::NUM_WINDOWS,
W::WINDOW_SIZE, W::WINDOW_SIZE,
W::NUM_WINDOWS * W::WINDOW_SIZE W::NUM_WINDOWS * W::WINDOW_SIZE
@ -71,7 +71,7 @@ impl FixedLengthCRH for PedersenCRH {
if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS { if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS {
panic!( panic!(
"incorrect input length {:?} for window params {:?}x{:?}",
"incorrect input length {:?} for window params {:?}{:?}",
input.len(), input.len(),
W::WINDOW_SIZE, W::WINDOW_SIZE,
W::NUM_WINDOWS W::NUM_WINDOWS
@ -93,7 +93,7 @@ impl FixedLengthCRH for PedersenCRH {
assert_eq!( assert_eq!(
parameters.generators.len(), parameters.generators.len(),
W::NUM_WINDOWS, W::NUM_WINDOWS,
"Incorrect pp of size {:?}x{:?} for window params {:?}x{:?}",
"Incorrect pp of size {:?}✕{:?} for window params {:?}✕{:?}",
parameters.generators[0].len(), parameters.generators[0].len(),
parameters.generators.len(), parameters.generators.len(),
W::WINDOW_SIZE, W::WINDOW_SIZE,
@ -105,7 +105,7 @@ impl FixedLengthCRH for PedersenCRH {
let result = cfg_chunks!(bits, W::WINDOW_SIZE) let result = cfg_chunks!(bits, W::WINDOW_SIZE)
.zip(&parameters.generators) .zip(&parameters.generators)
.map(|(bits, generator_powers)| { .map(|(bits, generator_powers)| {
let mut encoded = G::zero();
let mut encoded = C::zero();
for (bit, base) in bits.iter().zip(generator_powers.iter()) { for (bit, base) in bits.iter().zip(generator_powers.iter()) {
if *bit { if *bit {
encoded += base; encoded += base;
@ -113,11 +113,11 @@ impl FixedLengthCRH for PedersenCRH {
} }
encoded encoded
}) })
.sum::<G>();
.sum::<C>();
end_timer!(eval_time); end_timer!(eval_time);
Ok(result)
Ok(result.into())
} }
} }
@ -132,7 +132,7 @@ pub fn bytes_to_bits(bytes: &[u8]) -> Vec {
bits bits
} }
impl<G: Group> Debug for PedersenParameters<G> {
impl<C: ProjectiveCurve> Debug for Parameters<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "Pedersen Hash Parameters {{\n")?; write!(f, "Pedersen Hash Parameters {{\n")?;
for (i, g) in self.generators.iter().enumerate() { for (i, g) in self.generators.iter().enumerate() {
@ -142,8 +142,8 @@ impl Debug for PedersenParameters {
} }
} }
impl<ConstraintF: Field, G: Group + ToConstraintField<ConstraintF>> ToConstraintField<ConstraintF>
for PedersenParameters<G>
impl<ConstraintF: Field, C: ProjectiveCurve + ToConstraintField<ConstraintF>>
ToConstraintField<ConstraintF> for Parameters<C>
{ {
#[inline] #[inline]
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> { fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {

+ 4
- 4
crypto-primitives/src/lib.rs

@ -25,17 +25,17 @@ pub mod signature;
pub use self::{ pub use self::{
commitment::CommitmentScheme, commitment::CommitmentScheme,
crh::FixedLengthCRH, crh::FixedLengthCRH,
merkle_tree::{MerkleHashTree, MerkleTreePath},
merkle_tree::{MerkleTree, Path},
nizk::NIZK, nizk::NIZK,
prf::PRF, prf::PRF,
signature::SignatureScheme, signature::SignatureScheme,
}; };
#[cfg(feature = "r1cs")]
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub use self::{ pub use self::{
commitment::CommitmentGadget, crh::FixedLengthCRHGadget,
merkle_tree::constraints::MerkleTreePathGadget, nizk::NIZKVerifierGadget, prf::PRFGadget,
signature::SigRandomizePkGadget,
commitment::CommitmentGadget, crh::FixedLengthCRHGadget, merkle_tree::constraints::PathVar,
nizk::NIZKVerifierGadget, prf::PRFGadget, signature::SigRandomizePkGadget,
}; };
pub type Error = Box<dyn algebra_core::Error>; pub type Error = Box<dyn algebra_core::Error>;

+ 93
- 159
crypto-primitives/src/merkle_tree/constraints.rs

@ -1,6 +1,6 @@
use algebra_core::Field; use algebra_core::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::{boolean::AllocatedBit, prelude::*};
use r1cs_core::{Namespace, SynthesisError};
use r1cs_std::prelude::*;
use crate::{ use crate::{
crh::{FixedLengthCRH, FixedLengthCRHGadget}, crh::{FixedLengthCRH, FixedLengthCRHGadget},
@ -9,58 +9,56 @@ use crate::{
use core::borrow::Borrow; use core::borrow::Borrow;
pub struct MerkleTreePathGadget<P, HGadget, ConstraintF>
pub struct PathVar<P, HGadget, ConstraintF>
where where
P: MerkleTreeConfig,
P: Config,
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>, HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
ConstraintF: Field, ConstraintF: Field,
{ {
path: Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
path: Vec<(HGadget::OutputVar, HGadget::OutputVar)>,
} }
impl<P, CRHGadget, ConstraintF> MerkleTreePathGadget<P, CRHGadget, ConstraintF>
impl<P, CRHGadget, ConstraintF> PathVar<P, CRHGadget, ConstraintF>
where where
P: MerkleTreeConfig,
P: Config,
ConstraintF: Field, ConstraintF: Field,
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>, CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
<CRHGadget::OutputVar as R1CSVar<ConstraintF>>::Value: PartialEq,
{ {
pub fn check_membership<CS: ConstraintSystem<ConstraintF>>(
pub fn check_membership(
&self, &self,
cs: CS,
parameters: &CRHGadget::ParametersGadget,
root: &CRHGadget::OutputGadget,
parameters: &CRHGadget::ParametersVar,
root: &CRHGadget::OutputVar,
leaf: impl ToBytesGadget<ConstraintF>, leaf: impl ToBytesGadget<ConstraintF>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
self.conditionally_check_membership(cs, parameters, root, leaf, &Boolean::Constant(true))
self.conditionally_check_membership(parameters, root, leaf, &Boolean::Constant(true))
} }
pub fn conditionally_check_membership<CS: ConstraintSystem<ConstraintF>>(
pub fn conditionally_check_membership(
&self, &self,
mut cs: CS,
parameters: &CRHGadget::ParametersGadget,
root: &CRHGadget::OutputGadget,
parameters: &CRHGadget::ParametersVar,
root: &CRHGadget::OutputVar,
leaf: impl ToBytesGadget<ConstraintF>, leaf: impl ToBytesGadget<ConstraintF>,
should_enforce: &Boolean,
should_enforce: &Boolean<ConstraintF>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
assert_eq!(self.path.len(), P::HEIGHT - 1); assert_eq!(self.path.len(), P::HEIGHT - 1);
// Check that the hash of the given leaf matches the leaf hash in the membership // Check that the hash of the given leaf matches the leaf hash in the membership
// proof. // proof.
let leaf_bits = leaf.to_bytes(&mut cs.ns(|| "leaf_to_bytes"))?;
let leaf_hash = CRHGadget::check_evaluation_gadget(
cs.ns(|| "check_evaluation_gadget"),
parameters,
&leaf_bits,
)?;
let leaf_bits = leaf.to_bytes()?;
let leaf_hash = CRHGadget::evaluate(parameters, &leaf_bits)?;
let cs = leaf_hash
.cs()
.or(root.cs())
.or(should_enforce.cs())
.unwrap();
// Check if leaf is one of the bottom-most siblings. // Check if leaf is one of the bottom-most siblings.
let leaf_is_left = AllocatedBit::alloc(&mut cs.ns(|| "leaf_is_left"), || {
Ok(leaf_hash == self.path[0].0)
})?
.into();
CRHGadget::OutputGadget::conditional_enforce_equal_or(
&mut cs.ns(|| "check_leaf_is_left"),
let leaf_is_left = Boolean::new_witness(cs.ns("leaf_is_left"), || {
Ok(leaf_hash.value()?.eq(&self.path[0].0.value()?))
})?;
leaf_hash.conditional_enforce_equal_or(
&leaf_is_left, &leaf_is_left,
&leaf_hash,
&self.path[0].0, &self.path[0].0,
&self.path[0].1, &self.path[0].1,
should_enforce, should_enforce,
@ -68,133 +66,74 @@ where
// Check levels between leaf level and root. // Check levels between leaf level and root.
let mut previous_hash = leaf_hash; let mut previous_hash = leaf_hash;
for (i, &(ref left_hash, ref right_hash)) in self.path.iter().enumerate() {
let mut i = 0;
for &(ref left_hash, ref right_hash) in &self.path {
// Check if the previous_hash matches the correct current hash. // Check if the previous_hash matches the correct current hash.
let previous_is_left =
AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
Ok(&previous_hash == left_hash)
})?
.into();
CRHGadget::OutputGadget::conditional_enforce_equal_or(
&mut cs.ns(|| format!("check_equals_which_{}", i)),
let previous_is_left = Boolean::new_witness(cs.ns("previous_is_left"), || {
Ok(previous_hash.value()?.eq(&left_hash.value()?))
})?;
let ns = cs.ns(format!(
"enforcing that inner hash is correct at i-th level{}",
i
));
previous_hash.conditional_enforce_equal_or(
&previous_is_left, &previous_is_left,
&previous_hash,
left_hash, left_hash,
right_hash, right_hash,
should_enforce, should_enforce,
)?; )?;
drop(ns);
previous_hash = hash_inner_node_gadget::<P::H, CRHGadget, ConstraintF, _>(
&mut cs.ns(|| format!("hash_inner_node_{}", i)),
parameters,
left_hash,
right_hash,
)?;
previous_hash =
hash_inner_node::<P::H, CRHGadget, ConstraintF>(parameters, left_hash, right_hash)?;
i += 1;
} }
root.conditional_enforce_equal(
&mut cs.ns(|| "root_is_last"),
&previous_hash,
should_enforce,
)
root.conditional_enforce_equal(&previous_hash, should_enforce)
} }
} }
pub(crate) fn hash_inner_node_gadget<H, HG, ConstraintF, CS>(
mut cs: CS,
parameters: &HG::ParametersGadget,
left_child: &HG::OutputGadget,
right_child: &HG::OutputGadget,
) -> Result<HG::OutputGadget, SynthesisError>
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 where
ConstraintF: Field, ConstraintF: Field,
CS: ConstraintSystem<ConstraintF>,
H: FixedLengthCRH, H: FixedLengthCRH,
HG: FixedLengthCRHGadget<H, ConstraintF>, HG: FixedLengthCRHGadget<H, ConstraintF>,
{ {
let left_bytes = left_child.to_bytes(&mut cs.ns(|| "left_to_bytes"))?;
let right_bytes = right_child.to_bytes(&mut cs.ns(|| "right_to_bytes"))?;
let left_bytes = left_child.to_bytes()?;
let right_bytes = right_child.to_bytes()?;
let mut bytes = left_bytes; let mut bytes = left_bytes;
bytes.extend_from_slice(&right_bytes); bytes.extend_from_slice(&right_bytes);
HG::check_evaluation_gadget(cs, parameters, &bytes)
HG::evaluate(parameters, &bytes)
} }
impl<P, HGadget, ConstraintF> AllocGadget<MerkleTreePath<P>, ConstraintF>
for MerkleTreePathGadget<P, HGadget, ConstraintF>
impl<P, HGadget, ConstraintF> AllocVar<Path<P>, ConstraintF> for PathVar<P, HGadget, ConstraintF>
where where
P: MerkleTreeConfig,
P: Config,
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>, HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
ConstraintF: Field, ConstraintF: Field,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<MerkleTreePath<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in val.borrow().path.iter().enumerate() {
let l_hash = HGadget::OutputGadget::alloc_constant(
&mut cs.ns(|| format!("l_child_{}", i)),
l.clone(),
)?;
let r_hash = HGadget::OutputGadget::alloc_constant(
&mut cs.ns(|| format!("r_child_{}", i)),
r.clone(),
)?;
path.push((l_hash, r_hash));
}
Ok(MerkleTreePathGadget { path })
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<MerkleTreePath<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
let l_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("l_child_{}", i)), || {
Ok(l.clone())
})?;
let r_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("r_child_{}", i)), || {
Ok(r.clone())
})?;
path.push((l_hash, r_hash));
}
Ok(MerkleTreePathGadget { path })
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<MerkleTreePath<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
let l_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("l_child_{}", i)),
|| Ok(l.clone()),
)?;
let r_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("r_child_{}", i)),
|| Ok(r.clone()),
)?;
path.push((l_hash, r_hash));
}
Ok(MerkleTreePathGadget { path })
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(cs.ns("l_child"), || Ok(l), mode)?;
let r_hash = HGadget::OutputVar::new_variable(cs.ns("r_child"), || Ok(r), mode)?;
path.push((l_hash, r_hash));
}
Ok(PathVar { path })
})
} }
} }
@ -202,37 +141,37 @@ where
mod test { mod test {
use crate::{ use crate::{
crh::{ crh::{
pedersen::{constraints::PedersenCRHGadget, PedersenCRH, PedersenWindow},
pedersen::{self, constraints::CRHGadget},
FixedLengthCRH, FixedLengthCRHGadget, FixedLengthCRH, FixedLengthCRHGadget,
}, },
merkle_tree::*, merkle_tree::*,
}; };
use algebra::ed_on_bls12_381::{EdwardsAffine as JubJub, Fq};
use algebra::ed_on_bls12_381::{EdwardsProjective as JubJub, Fq};
use r1cs_core::ConstraintSystem; use r1cs_core::ConstraintSystem;
use rand::SeedableRng; use rand::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use super::*; use super::*;
use r1cs_std::{ed_on_bls12_381::EdwardsGadget, test_constraint_system::TestConstraintSystem};
use r1cs_std::ed_on_bls12_381::EdwardsVar;
#[derive(Clone)] #[derive(Clone)]
pub(super) struct Window4x256; pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
impl pedersen::Window for Window4x256 {
const WINDOW_SIZE: usize = 4; const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256; const NUM_WINDOWS: usize = 256;
} }
type H = PedersenCRH<JubJub, Window4x256>;
type HG = PedersenCRHGadget<JubJub, Fq, EdwardsGadget>;
type H = pedersen::CRH<JubJub, Window4x256>;
type HG = CRHGadget<JubJub, EdwardsVar, Window4x256>;
struct JubJubMerkleTreeParams; struct JubJubMerkleTreeParams;
impl MerkleTreeConfig for JubJubMerkleTreeParams {
impl Config for JubJubMerkleTreeParams {
const HEIGHT: usize = 32; const HEIGHT: usize = 32;
type H = H; type H = H;
} }
type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
type JubJubMerkleTree = MerkleTree<JubJubMerkleTreeParams>;
fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () { fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
let mut rng = XorShiftRng::seed_from_u64(9174123u64); let mut rng = XorShiftRng::seed_from_u64(9174123u64);
@ -242,13 +181,13 @@ mod test {
let root = tree.root(); let root = tree.root();
let mut satisfied = true; let mut satisfied = true;
for (i, leaf) in leaves.iter().enumerate() { for (i, leaf) in leaves.iter().enumerate() {
let mut cs = TestConstraintSystem::<Fq>::new();
let cs = ConstraintSystem::<Fq>::new_ref();
let proof = tree.generate_proof(i, &leaf).unwrap(); let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap()); assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
// Allocate Merkle Tree Root // Allocate Merkle Tree Root
let root = <HG as FixedLengthCRHGadget<H, _>>::OutputGadget::alloc(
&mut cs.ns(|| format!("new_digest_{}", i)),
let root = <HG as FixedLengthCRHGadget<H, _>>::OutputVar::new_witness(
cs.ns("new_digest"),
|| { || {
if use_bad_root { if use_bad_root {
Ok(<H as FixedLengthCRH>::Output::default()) Ok(<H as FixedLengthCRH>::Output::default())
@ -263,9 +202,9 @@ mod test {
println!("constraints from digest: {}", constraints_from_digest); println!("constraints from digest: {}", constraints_from_digest);
// Allocate Parameters for CRH // Allocate Parameters for CRH
let crh_parameters = <HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| format!("new_parameters_{}", i)),
|| Ok(crh_parameters.clone()),
let crh_parameters = <HG as FixedLengthCRHGadget<H, Fq>>::ParametersVar::new_constant(
cs.ns("new_parameter"),
&crh_parameters,
) )
.unwrap(); .unwrap();
@ -283,26 +222,21 @@ mod test {
println!("constraints from leaf: {}", constraints_from_leaf); println!("constraints from leaf: {}", constraints_from_leaf);
// Allocate Merkle Tree Path // Allocate Merkle Tree Path
let cw = MerkleTreePathGadget::<_, HG, _>::alloc(
&mut cs.ns(|| format!("new_witness_{}", i)),
|| Ok(proof),
)
.unwrap();
let cw = PathVar::<_, HG, _>::new_witness(cs.ns("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() let constraints_from_path = cs.num_constraints()
- constraints_from_parameters - constraints_from_parameters
- constraints_from_digest - constraints_from_digest
- constraints_from_leaf; - constraints_from_leaf;
println!("constraints from path: {}", constraints_from_path); println!("constraints from path: {}", constraints_from_path);
let leaf_g: &[UInt8] = leaf_g.as_slice();
cw.check_membership(
&mut cs.ns(|| format!("new_witness_check_{}", i)),
&crh_parameters,
&root,
&leaf_g,
)
.unwrap();
if !cs.is_satisfied() {
let leaf_g: &[_] = leaf_g.as_slice();
cw.check_membership(&crh_parameters, &root, &leaf_g)
.unwrap();
if !cs.is_satisfied().unwrap() {
satisfied = false; satisfied = false;
println!( println!(
"Unsatisfied constraint: {}", "Unsatisfied constraint: {}",

+ 43
- 47
crypto-primitives/src/merkle_tree/mod.rs

@ -1,11 +1,11 @@
use crate::{crh::FixedLengthCRH, Error, Vec};
use algebra_core::{bytes::ToBytes, io::Cursor};
use crate::{crh::FixedLengthCRH, Vec};
use algebra_core::bytes::ToBytes;
use core::fmt; use core::fmt;
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
pub trait MerkleTreeConfig {
pub trait Config {
const HEIGHT: usize; const HEIGHT: usize;
type H: FixedLengthCRH; type H: FixedLengthCRH;
} }
@ -14,20 +14,20 @@ pub trait MerkleTreeConfig {
/// Our path `is_left_child()` if the boolean in `path` is true. /// Our path `is_left_child()` if the boolean in `path` is true.
#[derive(Derivative)] #[derive(Derivative)]
#[derivative( #[derivative(
Clone(bound = "P: MerkleTreeConfig"),
Debug(bound = "P: MerkleTreeConfig, <P::H as FixedLengthCRH>::Output: fmt::Debug")
Clone(bound = "P: Config"),
Debug(bound = "P: Config, <P::H as FixedLengthCRH>::Output: fmt::Debug")
)] )]
pub struct MerkleTreePath<P: MerkleTreeConfig> {
pub struct Path<P: Config> {
pub(crate) path: Vec<( pub(crate) path: Vec<(
<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output,
<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output,
)>, )>,
} }
pub type MerkleTreeParams<P> = <<P as MerkleTreeConfig>::H as FixedLengthCRH>::Parameters;
pub type MerkleTreeDigest<P> = <<P as MerkleTreeConfig>::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: MerkleTreeConfig> Default for MerkleTreePath<P> {
impl<P: Config> Default for Path<P> {
fn default() -> Self { fn default() -> Self {
let mut path = Vec::with_capacity(P::HEIGHT as usize); let mut path = Vec::with_capacity(P::HEIGHT as usize);
for _i in 1..P::HEIGHT as usize { for _i in 1..P::HEIGHT as usize {
@ -40,13 +40,13 @@ impl Default for MerkleTreePath

{

} }
} }
impl<P: MerkleTreeConfig> MerkleTreePath<P> {
impl<P: Config> Path<P> {
pub fn verify<L: ToBytes>( pub fn verify<L: ToBytes>(
&self, &self,
parameters: &<P::H as FixedLengthCRH>::Parameters, parameters: &<P::H as FixedLengthCRH>::Parameters,
root_hash: &<P::H as FixedLengthCRH>::Output, root_hash: &<P::H as FixedLengthCRH>::Output,
leaf: &L, leaf: &L,
) -> Result<bool, Error> {
) -> Result<bool, crate::Error> {
if self.path.len() != (P::HEIGHT - 1) as usize { if self.path.len() != (P::HEIGHT - 1) as usize {
return Ok(false); return Ok(false);
} }
@ -81,7 +81,7 @@ impl MerkleTreePath

{

} }
} }
pub struct MerkleHashTree<P: MerkleTreeConfig> {
pub struct MerkleTree<P: Config> {
tree: Vec<<P::H as FixedLengthCRH>::Output>, tree: Vec<<P::H as FixedLengthCRH>::Output>,
padding_tree: Vec<( padding_tree: Vec<(
<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output,
@ -91,11 +91,11 @@ pub struct MerkleHashTree {
root: Option<<P::H as FixedLengthCRH>::Output>, root: Option<<P::H as FixedLengthCRH>::Output>,
} }
impl<P: MerkleTreeConfig> MerkleHashTree<P> {
impl<P: Config> MerkleTree<P> {
pub const HEIGHT: u8 = P::HEIGHT as u8; pub const HEIGHT: u8 = P::HEIGHT as u8;
pub fn blank(parameters: <P::H as FixedLengthCRH>::Parameters) -> Self { pub fn blank(parameters: <P::H as FixedLengthCRH>::Parameters) -> Self {
MerkleHashTree {
MerkleTree {
tree: Vec::new(), tree: Vec::new(),
padding_tree: Vec::new(), padding_tree: Vec::new(),
root: None, root: None,
@ -106,7 +106,7 @@ impl MerkleHashTree

{

pub fn new<L: ToBytes>( pub fn new<L: ToBytes>(
parameters: <P::H as FixedLengthCRH>::Parameters, parameters: <P::H as FixedLengthCRH>::Parameters,
leaves: &[L], leaves: &[L],
) -> Result<Self, Error> {
) -> Result<Self, crate::Error> {
let new_time = start_timer!(|| "MerkleTree::New"); let new_time = start_timer!(|| "MerkleTree::New");
let last_level_size = leaves.len().next_power_of_two(); let last_level_size = leaves.len().next_power_of_two();
@ -174,7 +174,7 @@ impl MerkleHashTree

{

}; };
end_timer!(new_time); end_timer!(new_time);
Ok(MerkleHashTree {
Ok(MerkleTree {
tree, tree,
padding_tree, padding_tree,
parameters, parameters,
@ -191,7 +191,7 @@ impl MerkleHashTree

{

&self, &self,
index: usize, index: usize,
leaf: &L, leaf: &L,
) -> Result<MerkleTreePath<P>, Error> {
) -> Result<Path<P>, crate::Error> {
let prove_time = start_timer!(|| "MerkleTree::GenProof"); let prove_time = start_timer!(|| "MerkleTree::GenProof");
let mut path = Vec::new(); let mut path = Vec::new();
@ -203,7 +203,7 @@ impl MerkleHashTree

{

// Check that the given index corresponds to the correct leaf. // Check that the given index corresponds to the correct leaf.
if leaf_hash != self.tree[tree_index] { if leaf_hash != self.tree[tree_index] {
return Err(MerkleTreeError::IncorrectLeafIndex(tree_index).into());
return Err(Error::IncorrectLeafIndex(tree_index).into());
} }
// Iterate from the leaf up to the root, storing all intermediate hash values. // Iterate from the leaf up to the root, storing all intermediate hash values.
@ -233,32 +233,30 @@ impl MerkleHashTree

{

} }
end_timer!(prove_time); end_timer!(prove_time);
if path.len() != (Self::HEIGHT - 1) as usize { if path.len() != (Self::HEIGHT - 1) as usize {
return Err(MerkleTreeError::IncorrectPathLength(path.len()).into());
return Err(Error::IncorrectPathLength(path.len()).into());
} else { } else {
Ok(MerkleTreePath { path })
Ok(Path { path })
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum MerkleTreeError {
pub enum Error {
IncorrectLeafIndex(usize), IncorrectLeafIndex(usize),
IncorrectPathLength(usize), IncorrectPathLength(usize),
} }
impl core::fmt::Display for MerkleTreeError {
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let msg = match self { let msg = match self {
MerkleTreeError::IncorrectLeafIndex(index) => {
format!("incorrect leaf index: {}", index)
}
MerkleTreeError::IncorrectPathLength(len) => format!("incorrect path length: {}", len),
Error::IncorrectLeafIndex(index) => format!("incorrect leaf index: {}", index),
Error::IncorrectPathLength(len) => format!("incorrect path length: {}", len),
}; };
write!(f, "{}", msg) write!(f, "{}", msg)
} }
} }
impl algebra_core::Error for MerkleTreeError {}
impl algebra_core::Error for Error {}
/// Returns the log2 value of the given number. /// Returns the log2 value of the given number.
#[inline] #[inline]
@ -333,14 +331,11 @@ pub(crate) fn hash_inner_node(
left: &H::Output, left: &H::Output,
right: &H::Output, right: &H::Output,
buffer: &mut [u8], buffer: &mut [u8],
) -> Result<H::Output, Error> {
let mut writer = Cursor::new(&mut *buffer);
// Construct left input.
left.write(&mut writer)?;
// Construct right input.
right.write(&mut writer)?;
) -> 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)]) H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
} }
@ -349,16 +344,17 @@ pub(crate) fn hash_leaf(
parameters: &H::Parameters, parameters: &H::Parameters,
leaf: &L, leaf: &L,
buffer: &mut [u8], buffer: &mut [u8],
) -> Result<H::Output, Error> {
let mut writer = Cursor::new(&mut *buffer);
leaf.write(&mut writer)?;
) -> 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)]) H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
} }
pub(crate) fn hash_empty<H: FixedLengthCRH>( pub(crate) fn hash_empty<H: FixedLengthCRH>(
parameters: &H::Parameters, parameters: &H::Parameters,
) -> Result<H::Output, Error> {
) -> Result<H::Output, crate::Error> {
let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8]; let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8];
H::evaluate(parameters, &empty_buffer) H::evaluate(parameters, &empty_buffer)
} }
@ -366,29 +362,29 @@ pub(crate) fn hash_empty(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
crh::{pedersen::*, *},
crh::{pedersen, *},
merkle_tree::*, merkle_tree::*,
}; };
use algebra::{ed_on_bls12_381::EdwardsAffine as JubJub, Zero};
use algebra::{ed_on_bls12_381::EdwardsProjective as JubJub, Zero};
use rand::SeedableRng; use rand::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
#[derive(Clone)] #[derive(Clone)]
pub(super) struct Window4x256; pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
impl pedersen::Window for Window4x256 {
const WINDOW_SIZE: usize = 4; const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256; const NUM_WINDOWS: usize = 256;
} }
type H = PedersenCRH<JubJub, Window4x256>;
type H = pedersen::CRH<JubJub, Window4x256>;
struct JubJubMerkleTreeParams; struct JubJubMerkleTreeParams;
impl MerkleTreeConfig for JubJubMerkleTreeParams {
impl Config for JubJubMerkleTreeParams {
const HEIGHT: usize = 8; const HEIGHT: usize = 8;
type H = H; type H = H;
} }
type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
type JubJubMerkleTree = MerkleTree<JubJubMerkleTreeParams>;
fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () { fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
let mut rng = XorShiftRng::seed_from_u64(9174123u64); let mut rng = XorShiftRng::seed_from_u64(9174123u64);
@ -435,7 +431,7 @@ mod test {
let crh_parameters = H::setup(&mut rng).unwrap(); let crh_parameters = H::setup(&mut rng).unwrap();
let tree = JubJubMerkleTree::new(crh_parameters.clone(), &leaves).unwrap(); let tree = JubJubMerkleTree::new(crh_parameters.clone(), &leaves).unwrap();
let root = JubJub::zero();
let root = JubJub::zero().into();
for (i, leaf) in leaves.iter().enumerate() { for (i, leaf) in leaves.iter().enumerate() {
let proof = tree.generate_proof(i, &leaf).unwrap(); let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap()); assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());

+ 48
- 36
crypto-primitives/src/nizk/constraints.rs

@ -1,47 +1,59 @@
use algebra_core::Field; use algebra_core::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use core::borrow::Borrow;
use r1cs_core::{Namespace, SynthesisError};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
use crate::nizk::NIZK; use crate::nizk::NIZK;
pub trait NIZKVerifierGadget<N: NIZK, ConstraintF: Field> { pub trait NIZKVerifierGadget<N: NIZK, ConstraintF: Field> {
type PreparedVerificationKeyGadget;
type VerificationKeyGadget: AllocGadget<N::VerificationParameters, ConstraintF>
type PreparedVerificationKeyVar;
type VerificationKeyVar: AllocVar<N::VerificationParameters, ConstraintF>
+ ToBytesGadget<ConstraintF>; + ToBytesGadget<ConstraintF>;
type ProofGadget: AllocGadget<N::Proof, ConstraintF>;
type ProofVar: AllocVar<N::Proof, ConstraintF>;
fn check_verify<'a, CS, I, T>(
cs: CS,
verification_key: &Self::VerificationKeyGadget,
input: I,
proof: &Self::ProofGadget,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Iterator<Item = &'a T>,
T: 'a + ToBitsGadget<ConstraintF> + ?Sized;
/// Optionally allocates `N::Proof` in `cs` without performing
/// subgroup checks.
///
/// The default implementation doesn't omit these checks.
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)
}
fn conditional_check_verify<'a, CS, I, T>(
cs: CS,
verification_key: &Self::VerificationKeyGadget,
input: I,
proof: &Self::ProofGadget,
condition: &Boolean,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Iterator<Item = &'a T>,
T: 'a + ToBitsGadget<ConstraintF> + ?Sized;
/// Optionally allocates `N::VerificationParameters` in `cs`
/// without performing subgroup checks.
///
/// The default implementation doesn't omit these checks.
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 conditional_check_verify_prepared<'a, CS, I, T>(
cs: CS,
prepared_verification_key: &Self::PreparedVerificationKeyGadget,
input: I,
proof: &Self::ProofGadget,
condition: &Boolean,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Iterator<Item = &'a T>,
T: 'a + ToBitsGadget<ConstraintF> + ?Sized;
fn verify<'a, T: 'a + ToBitsGadget<ConstraintF> + ?Sized>(
verification_key: &Self::VerificationKeyVar,
input: impl Iterator<Item = &'a T>,
proof: &Self::ProofVar,
) -> Result<(), SynthesisError> {
Self::conditional_verify(verification_key, input, proof, &Boolean::constant(true))
}
fn conditional_verify<'a, T: 'a + ToBitsGadget<ConstraintF> + ?Sized>(
verification_key: &Self::VerificationKeyVar,
input: impl Iterator<Item = &'a T>,
proof: &Self::ProofVar,
condition: &Boolean<ConstraintF>,
) -> Result<(), SynthesisError>;
fn conditional_verify_prepared<'a, T: 'a + ToBitsGadget<ConstraintF> + ?Sized>(
prepared_verification_key: &Self::PreparedVerificationKeyVar,
input: impl Iterator<Item = &'a T>,
proof: &Self::ProofVar,
condition: &Boolean<ConstraintF>,
) -> Result<(), SynthesisError>;
} }

+ 335
- 603
crypto-primitives/src/nizk/gm17/constraints.rs
File diff suppressed because it is too large
View File


+ 304
- 530
crypto-primitives/src/nizk/groth16/constraints.rs
File diff suppressed because it is too large
View File


+ 13
- 13
crypto-primitives/src/nizk/mod.rs

@ -63,7 +63,7 @@ mod test {
bls12_377::{Bls12_377, Fr}, bls12_377::{Bls12_377, Fr},
One, One,
}; };
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
use r1cs_core::{lc, ConstraintSynthesizer, ConstraintSystemRef, SynthesisError, Variable};
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct R1CSCircuit { struct R1CSCircuit {
@ -83,20 +83,20 @@ mod test {
} }
impl ConstraintSynthesizer<Fr> for R1CSCircuit { impl ConstraintSynthesizer<Fr> for R1CSCircuit {
fn generate_constraints<CS: ConstraintSystem<Fr>>(
fn generate_constraints(
self, self,
cs: &mut CS,
cs: ConstraintSystemRef<Fr>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
let input = cs.alloc_input(|| "x", || Ok(self.x.unwrap()))?;
let sum = cs.alloc_input(|| "sum", || Ok(self.sum.unwrap()))?;
let witness = cs.alloc(|| "w", || Ok(self.w.unwrap()))?;
cs.enforce(
|| "check_one",
|lc| lc + sum,
|lc| lc + CS::one(),
|lc| lc + input + witness,
);
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_named_constraint(
"enforce sum",
lc!() + sum,
lc!() + Variable::One,
lc!() + input + witness,
)?;
Ok(()) Ok(())
} }
} }

+ 144
- 312
crypto-primitives/src/prf/blake2s/constraints.rs

@ -1,5 +1,5 @@
use algebra_core::PrimeField; use algebra_core::PrimeField;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError};
use crate::{prf::PRFGadget, Vec}; use crate::{prf::PRFGadget, Vec};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
@ -76,30 +76,23 @@ const SIGMA: [[usize; 16]; 10] = [
// END FUNCTION. // END FUNCTION.
// //
fn mixing_g<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
v: &mut [UInt32],
fn mixing_g<ConstraintF: PrimeField>(
v: &mut [UInt32<ConstraintF>],
a: usize, a: usize,
b: usize, b: usize,
c: usize, c: usize,
d: usize, d: usize,
x: &UInt32,
y: &UInt32,
x: &UInt32<ConstraintF>,
y: &UInt32<ConstraintF>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
v[a] = UInt32::addmany(
cs.ns(|| "mixing step 1"),
&[v[a].clone(), v[b].clone(), x.clone()],
)?;
v[d] = v[d].xor(cs.ns(|| "mixing step 2"), &v[a])?.rotr(R1);
v[c] = UInt32::addmany(cs.ns(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs.ns(|| "mixing step 4"), &v[c])?.rotr(R2);
v[a] = UInt32::addmany(
cs.ns(|| "mixing step 5"),
&[v[a].clone(), v[b].clone(), y.clone()],
)?;
v[d] = v[d].xor(cs.ns(|| "mixing step 6"), &v[a])?.rotr(R3);
v[c] = UInt32::addmany(cs.ns(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs.ns(|| "mixing step 8"), &v[c])?.rotr(R4);
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(()) Ok(())
} }
@ -107,7 +100,7 @@ fn mixing_g>(
// 3.2. Compression Function F // 3.2. Compression Function F
// Compression function F takes as an argument the state vector "h", // Compression function F takes as an argument the state vector "h",
// message block vector "m" (last block is padded with zeros to full // message block vector "m" (last block is padded with zeros to full
// block size, if required), 2w-bit_gadget offset counter "t", and final block
// 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 // 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 // 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. // BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
@ -151,10 +144,9 @@ fn mixing_g>(
// END FUNCTION. // END FUNCTION.
// //
fn blake2s_compression<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
h: &mut [UInt32],
m: &[UInt32],
fn blake2s_compression<ConstraintF: PrimeField>(
h: &mut [UInt32<ConstraintF>],
m: &[UInt32<ConstraintF>],
t: u64, t: u64,
f: bool, f: bool,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
@ -181,106 +173,29 @@ fn blake2s_compression
assert_eq!(v.len(), 16); assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs.ns(|| "first xor"), &UInt32::constant(t as u32))?;
v[13] = v[13].xor(cs.ns(|| "second xor"), &UInt32::constant((t >> 32) as u32))?;
v[12] = v[12].xor(&UInt32::constant(t as u32))?;
v[13] = v[13].xor(&UInt32::constant((t >> 32) as u32))?;
if f { if f {
v[14] = v[14].xor(cs.ns(|| "third xor"), &UInt32::constant(u32::max_value()))?;
v[14] = v[14].xor(&UInt32::constant(u32::max_value()))?;
} }
for i in 0..10 { for i in 0..10 {
let mut cs = cs.ns(|| format!("round {}", i));
let s = SIGMA[i % 10]; let s = SIGMA[i % 10];
mixing_g(
cs.ns(|| "mixing invocation 1"),
&mut v,
0,
4,
8,
12,
&m[s[0]],
&m[s[1]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 2"),
&mut v,
1,
5,
9,
13,
&m[s[2]],
&m[s[3]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 3"),
&mut v,
2,
6,
10,
14,
&m[s[4]],
&m[s[5]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 4"),
&mut v,
3,
7,
11,
15,
&m[s[6]],
&m[s[7]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 5"),
&mut v,
0,
5,
10,
15,
&m[s[8]],
&m[s[9]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 6"),
&mut v,
1,
6,
11,
12,
&m[s[10]],
&m[s[11]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 7"),
&mut v,
2,
7,
8,
13,
&m[s[12]],
&m[s[13]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 8"),
&mut v,
3,
4,
9,
14,
&m[s[14]],
&m[s[15]],
)?;
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 { for i in 0..8 {
let mut cs = cs.ns(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
h[i] = h[i].xor(cs.ns(|| "first xor"), &v[i])?;
h[i] = h[i].xor(cs.ns(|| "second xor"), &v[i + 8])?;
h[i] = h[i].xor(&v[i])?;
h[i] = h[i].xor(&v[i + 8])?;
} }
Ok(()) Ok(())
@ -312,53 +227,32 @@ fn blake2s_compression
// END FUNCTION. // END FUNCTION.
// //
pub fn blake2s_gadget<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
input: &[Boolean],
) -> Result<Vec<UInt32>, SynthesisError> {
pub fn evaluate_blake2s<ConstraintF: PrimeField>(
input: &[Boolean<ConstraintF>],
) -> Result<Vec<UInt32<ConstraintF>>, SynthesisError> {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
let mut parameters = [0; 8]; let mut parameters = [0; 8];
parameters[0] = 0x01010000 ^ 32; parameters[0] = 0x01010000 ^ 32;
blake2s_gadget_with_parameters(cs, input, &parameters)
evaluate_blake2s_with_parameters(input, &parameters)
} }
pub fn blake2s_gadget_with_parameters<
ConstraintF: PrimeField,
CS: ConstraintSystem<ConstraintF>,
>(
mut cs: CS,
input: &[Boolean],
pub fn evaluate_blake2s_with_parameters<F: PrimeField>(
input: &[Boolean<F>],
parameters: &[u32; 8], parameters: &[u32; 8],
) -> Result<Vec<UInt32>, SynthesisError> {
) -> Result<Vec<UInt32<F>>, SynthesisError> {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
let mut h = Vec::with_capacity(8); let mut h = Vec::with_capacity(8);
h.push(
UInt32::constant(0x6A09E667).xor(cs.ns(|| "xor h[0]"), &UInt32::constant(parameters[0]))?,
);
h.push(
UInt32::constant(0xBB67AE85).xor(cs.ns(|| "xor h[1]"), &UInt32::constant(parameters[1]))?,
);
h.push(
UInt32::constant(0x3C6EF372).xor(cs.ns(|| "xor h[2]"), &UInt32::constant(parameters[2]))?,
);
h.push(
UInt32::constant(0xA54FF53A).xor(cs.ns(|| "xor h[3]"), &UInt32::constant(parameters[3]))?,
);
h.push(
UInt32::constant(0x510E527F).xor(cs.ns(|| "xor h[4]"), &UInt32::constant(parameters[4]))?,
);
h.push(
UInt32::constant(0x9B05688C).xor(cs.ns(|| "xor h[5]"), &UInt32::constant(parameters[5]))?,
);
h.push(
UInt32::constant(0x1F83D9AB).xor(cs.ns(|| "xor h[6]"), &UInt32::constant(parameters[6]))?,
);
h.push(
UInt32::constant(0x5BE0CD19).xor(cs.ns(|| "xor h[7]"), &UInt32::constant(parameters[7]))?,
);
let mut blocks: Vec<Vec<UInt32>> = vec![];
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) { for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16); let mut this_block = Vec::with_capacity(16);
@ -380,22 +274,15 @@ pub fn blake2s_gadget_with_parameters<
} }
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() { for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
let cs = cs.ns(|| format!("block {}", i));
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
blake2s_compression(&mut h, block, ((i as u64) + 1) * 64, false)?;
} }
{
let cs = cs.ns(|| "final block");
blake2s_compression(
cs,
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
}
blake2s_compression(
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
Ok(h) Ok(h)
} }
@ -404,134 +291,93 @@ use crate::prf::Blake2s;
pub struct Blake2sGadget; pub struct Blake2sGadget;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Blake2sOutputGadget(pub Vec<UInt8>);
pub struct OutputVar<ConstraintF: PrimeField>(pub Vec<UInt8<ConstraintF>>);
impl PartialEq for Blake2sOutputGadget {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
impl<ConstraintF: PrimeField> EqGadget<ConstraintF> for OutputVar<ConstraintF> {
fn is_eq(&self, other: &Self) -> Result<Boolean<ConstraintF>, SynthesisError> {
self.0.is_eq(&other.0)
} }
}
impl Eq for Blake2sOutputGadget {}
impl<ConstraintF: PrimeField> EqGadget<ConstraintF> for Blake2sOutputGadget {}
impl<ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Blake2sOutputGadget {
#[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
/// If `should_enforce == true`, enforce that `self` and `other` are equal; else,
/// enforce a vacuously true statement.
fn conditional_enforce_equal(
&self, &self,
mut cs: CS,
other: &Self, other: &Self,
condition: &Boolean,
should_enforce: &Boolean<ConstraintF>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
for (i, (a, b)) in self.0.iter().zip(other.0.iter()).enumerate() {
a.conditional_enforce_equal(
&mut cs.ns(|| format!("blake2s_equal_{}", i)),
b,
condition,
)?;
}
Ok(())
self.0.conditional_enforce_equal(&other.0, should_enforce)
} }
fn cost() -> usize {
32 * <UInt8 as ConditionalEqGadget<ConstraintF>>::cost()
/// If `should_enforce == true`, enforce that `self` and `other` are not equal; else,
/// enforce a vacuously true statement.
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 Blake2sOutputGadget {
impl<ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for OutputVar<ConstraintF> {
#[inline] #[inline]
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
&self,
_cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
fn to_bytes(&self) -> Result<Vec<UInt8<ConstraintF>>, SynthesisError> {
Ok(self.0.clone()) Ok(self.0.clone())
} }
} }
impl<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sOutputGadget {
#[inline]
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<[u8; 32]>,
{
let mut bytes = vec![];
for (i, b) in val.borrow().iter().enumerate() {
bytes.push(UInt8::alloc_constant(cs.ns(|| format!("value {}", i)), b)?)
impl<ConstraintF: PrimeField> AllocVar<[u8; 32], ConstraintF> for OutputVar<ConstraintF> {
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),
} }
Ok(Blake2sOutputGadget(bytes))
} }
}
#[inline]
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_vec(cs, &value)?;
Ok(Blake2sOutputGadget(bytes))
impl<F: PrimeField> R1CSVar<F> for OutputVar<F> {
type Value = [u8; 32];
fn cs(&self) -> Option<ConstraintSystemRef<F>> {
self.0.cs()
} }
#[inline]
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_input_vec(cs, &value)?;
Ok(Blake2sOutputGadget(bytes))
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<ConstraintF: PrimeField> PRFGadget<Blake2s, ConstraintF> for Blake2sGadget {
type OutputGadget = Blake2sOutputGadget;
impl<F: PrimeField> PRFGadget<Blake2s, F> for Blake2sGadget {
type OutputVar = OutputVar<F>;
fn new_seed<CS: ConstraintSystem<ConstraintF>>(mut cs: CS, seed: &[u8; 32]) -> Vec<UInt8> {
UInt8::alloc_vec(&mut cs.ns(|| "alloc_seed"), seed).unwrap()
fn new_seed(cs: ConstraintSystemRef<F>, seed: &[u8; 32]) -> Vec<UInt8<F>> {
UInt8::new_witness_vec(cs.ns("New Blake2s seed"), seed).unwrap()
} }
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
seed: &[UInt8],
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
fn evaluate(seed: &[UInt8<F>], input: &[UInt8<F>]) -> Result<Self::OutputVar, SynthesisError> {
assert_eq!(seed.len(), 32); assert_eq!(seed.len(), 32);
// assert_eq!(input.len(), 32);
let mut gadget_input = Vec::with_capacity(512);
for byte in seed.iter().chain(input) {
gadget_input.extend_from_slice(&byte.into_bits_le());
}
let mut result = Vec::new();
for (i, int) in blake2s_gadget(cs.ns(|| "Blake2s Eval"), &gadget_input)?
let input: Vec<_> = seed
.iter()
.chain(input)
.flat_map(|b| b.into_bits_le())
.collect();
let result: Vec<_> = evaluate_blake2s(&input)?
.into_iter() .into_iter()
.enumerate()
{
let chunk = int.to_bytes(&mut cs.ns(|| format!("Result ToBytes {}", i)))?;
result.extend_from_slice(&chunk);
}
Ok(Blake2sOutputGadget(result))
.flat_map(|int| int.to_bytes().unwrap())
.collect();
Ok(OutputVar(result))
} }
} }
@ -541,27 +387,21 @@ mod test {
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use crate::prf::blake2s::{constraints::blake2s_gadget, Blake2s as B2SPRF};
use crate::prf::blake2s::{constraints::evaluate_blake2s, Blake2s as B2SPRF};
use blake2::VarBlake2s; use blake2::VarBlake2s;
use r1cs_core::ConstraintSystem; use r1cs_core::ConstraintSystem;
use super::Blake2sGadget; use super::Blake2sGadget;
use r1cs_std::{
boolean::AllocatedBit, prelude::*, test_constraint_system::TestConstraintSystem,
};
use r1cs_std::prelude::*;
#[test] #[test]
fn test_blake2s_constraints() { fn test_blake2s_constraints() {
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let input_bits: Vec<_> = (0..512) let input_bits: Vec<_> = (0..512)
.map(|i| {
AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
.unwrap()
.into()
})
.map(|i| Boolean::new_witness(cs.ns(format!("input bit {}", i)), || Ok(true)).unwrap())
.collect(); .collect();
blake2s_gadget(&mut cs, &input_bits).unwrap();
assert!(cs.is_satisfied());
evaluate_blake2s(&input_bits).unwrap();
assert!(cs.is_satisfied().unwrap());
assert_eq!(cs.num_constraints(), 21792); assert_eq!(cs.num_constraints(), 21792);
} }
@ -571,7 +411,7 @@ mod test {
use rand::Rng; use rand::Rng;
let mut rng = XorShiftRng::seed_from_u64(1231275789u64); let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let mut seed = [0u8; 32]; let mut seed = [0u8; 32];
rng.fill(&mut seed); rng.fill(&mut seed);
@ -579,32 +419,25 @@ mod test {
let mut input = [0u8; 32]; let mut input = [0u8; 32];
rng.fill(&mut input); rng.fill(&mut input);
let seed_gadget = Blake2sGadget::new_seed(&mut cs.ns(|| "declare_seed"), &seed);
let input_gadget = UInt8::alloc_vec(&mut cs.ns(|| "declare_input"), &input).unwrap();
let seed_var = Blake2sGadget::new_seed(cs.clone(), &seed);
let input_var = UInt8::new_witness_vec(cs.ns("declare_input"), &input).unwrap();
let out = B2SPRF::evaluate(&seed, &input).unwrap(); let out = B2SPRF::evaluate(&seed, &input).unwrap();
let actual_out_gadget = <Blake2sGadget as PRFGadget<_, Fr>>::OutputGadget::alloc(
&mut cs.ns(|| "declare_output"),
let actual_out_var = <Blake2sGadget as PRFGadget<_, Fr>>::OutputVar::new_witness(
cs.ns("declare_output"),
|| Ok(out), || Ok(out),
) )
.unwrap(); .unwrap();
let output_gadget = Blake2sGadget::check_evaluation_gadget(
&mut cs.ns(|| "eval_blake2s"),
&seed_gadget,
&input_gadget,
)
.unwrap();
output_gadget
.enforce_equal(&mut cs, &actual_out_gadget)
.unwrap();
let output_var = Blake2sGadget::evaluate(&seed_var, &input_var).unwrap();
output_var.enforce_equal(&actual_out_var).unwrap();
if !cs.is_satisfied() {
if !cs.is_satisfied().unwrap() {
println!( println!(
"which is unsatisfied: {:?}", "which is unsatisfied: {:?}",
cs.which_is_unsatisfied().unwrap() cs.which_is_unsatisfied().unwrap()
); );
} }
assert!(cs.is_satisfied());
assert!(cs.is_satisfied().unwrap());
} }
#[test] #[test]
@ -612,27 +445,27 @@ mod test {
// Test that 512 fixed leading bits (constants) // Test that 512 fixed leading bits (constants)
// doesn't result in more constraints. // doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let mut rng = XorShiftRng::seed_from_u64(1231275789u64); let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let input_bits: Vec<_> = (0..512) let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.gen())) .map(|_| Boolean::constant(rng.gen()))
.chain((0..512).map(|i| { .chain((0..512).map(|i| {
AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
.unwrap()
.into()
Boolean::new_witness(cs.ns(format!("input bit {}", i)), || Ok(true)).unwrap()
})) }))
.collect(); .collect();
blake2s_gadget(&mut cs, &input_bits).unwrap();
assert!(cs.is_satisfied());
evaluate_blake2s(&input_bits).unwrap();
assert!(cs.is_satisfied().unwrap());
assert_eq!(cs.num_constraints(), 21792); assert_eq!(cs.num_constraints(), 21792);
} }
#[test] #[test]
fn test_blake2s_constant_constraints() { fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let mut rng = XorShiftRng::seed_from_u64(1231275789u64); let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
blake2s_gadget(&mut cs, &input_bits).unwrap();
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::<Fr>::constant(rng.gen()))
.collect();
evaluate_blake2s(&input_bits).unwrap();
assert_eq!(cs.num_constraints(), 0); assert_eq!(cs.num_constraints(), 0);
} }
@ -651,25 +484,24 @@ mod test {
let mut hash_result = Vec::with_capacity(h.output_size()); let mut hash_result = Vec::with_capacity(h.output_size());
h.variable_result(|res| hash_result.extend_from_slice(res)); h.variable_result(|res| hash_result.extend_from_slice(res));
let mut cs = TestConstraintSystem::<Fr>::new();
let cs = ConstraintSystem::<Fr>::new_ref();
let mut input_bits = vec![]; let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() { for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 { for bit_i in 0..8 {
let cs = cs.ns(|| format!("input bit_gadget {} {}", byte_i, bit_i));
let cs = cs.ns(format!("input bit {} {}", byte_i, bit_i));
input_bits.push( input_bits.push(
AllocatedBit::alloc(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
Boolean::new_witness(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap(),
); );
} }
} }
let r = blake2s_gadget(&mut cs, &input_bits).unwrap();
let r = evaluate_blake2s(&input_bits).unwrap();
assert!(cs.is_satisfied());
assert!(cs.is_satisfied().unwrap());
let mut s = hash_result let mut s = hash_result
.iter() .iter()
@ -679,10 +511,10 @@ mod test {
for b in chunk.to_bits_le() { for b in chunk.to_bits_le() {
match b { match b {
Boolean::Is(b) => { Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
assert!(s.next().unwrap() == b.value().unwrap());
} }
Boolean::Not(b) => { Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
assert!(s.next().unwrap() != b.value().unwrap());
} }
Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(input_len == 0); assert!(input_len == 0);

+ 8
- 11
crypto-primitives/src/prf/constraints.rs

@ -2,22 +2,19 @@ use algebra_core::Field;
use core::fmt::Debug; use core::fmt::Debug;
use crate::{prf::PRF, Vec}; use crate::{prf::PRF, Vec};
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::{ConstraintSystemRef, SynthesisError};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
pub trait PRFGadget<P: PRF, ConstraintF: Field> {
type OutputGadget: EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF>
+ AllocGadget<P::Output, ConstraintF>
pub trait PRFGadget<P: PRF, F: Field> {
type OutputVar: EqGadget<F>
+ ToBytesGadget<F>
+ AllocVar<P::Output, F>
+ R1CSVar<F, Value = P::Output>
+ Clone + Clone
+ Debug; + Debug;
fn new_seed<CS: ConstraintSystem<ConstraintF>>(cs: CS, output: &P::Seed) -> Vec<UInt8>;
fn new_seed(cs: ConstraintSystemRef<F>, seed: &P::Seed) -> Vec<UInt8<F>>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
seed: &[UInt8],
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError>;
fn evaluate(seed: &[UInt8<F>], input: &[UInt8<F>]) -> Result<Self::OutputVar, SynthesisError>;
} }

+ 1
- 1
crypto-primitives/src/prf/mod.rs

@ -13,7 +13,7 @@ pub use self::blake2s::*;
pub trait PRF { pub trait PRF {
type Input: FromBytes + Default; type Input: FromBytes + Default;
type Output: ToBytes + Eq + Clone + Default + Hash;
type Output: ToBytes + Eq + Clone + Debug + Default + Hash;
type Seed: FromBytes + ToBytes + Clone + Default + Debug; type Seed: FromBytes + ToBytes + Clone + Default + Debug;
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError>; fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError>;

+ 9
- 10
crypto-primitives/src/signature/constraints.rs

@ -1,21 +1,20 @@
use algebra_core::Field; use algebra_core::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::SynthesisError;
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
use crate::signature::SignatureScheme; use crate::signature::SignatureScheme;
pub trait SigRandomizePkGadget<S: SignatureScheme, ConstraintF: Field> { pub trait SigRandomizePkGadget<S: SignatureScheme, ConstraintF: Field> {
type ParametersGadget: AllocGadget<S::Parameters, ConstraintF> + Clone;
type ParametersVar: AllocVar<S::Parameters, ConstraintF> + Clone;
type PublicKeyGadget: ToBytesGadget<ConstraintF>
type PublicKeyVar: ToBytesGadget<ConstraintF>
+ EqGadget<ConstraintF> + EqGadget<ConstraintF>
+ AllocGadget<S::PublicKey, ConstraintF>
+ AllocVar<S::PublicKey, ConstraintF>
+ Clone; + Clone;
fn check_randomization_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
public_key: &Self::PublicKeyGadget,
randomness: &[UInt8],
) -> Result<Self::PublicKeyGadget, SynthesisError>;
fn randomize(
parameters: &Self::ParametersVar,
public_key: &Self::PublicKeyVar,
randomness: &[UInt8<ConstraintF>],
) -> Result<Self::PublicKeyVar, SynthesisError>;
} }

+ 5
- 5
crypto-primitives/src/signature/mod.rs

@ -52,9 +52,9 @@ pub trait SignatureScheme {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{signature::schnorr::SchnorrSignature, SignatureScheme};
use crate::signature::{schnorr, *};
use algebra::{ use algebra::{
ed_on_bls12_381::EdwardsAffine as JubJub, groups::Group, test_rng, to_bytes, ToBytes,
ed_on_bls12_381::EdwardsProjective as JubJub, groups::Group, test_rng, to_bytes, ToBytes,
UniformRand, UniformRand,
}; };
use blake2::Blake2s; use blake2::Blake2s;
@ -90,13 +90,13 @@ mod test {
fn schnorr_signature_test() { fn schnorr_signature_test() {
let message = "Hi, I am a Schnorr signature!"; let message = "Hi, I am a Schnorr signature!";
let rng = &mut test_rng(); let rng = &mut test_rng();
sign_and_verify::<SchnorrSignature<JubJub, Blake2s>>(message.as_bytes());
failed_verification::<SchnorrSignature<JubJub, Blake2s>>(
sign_and_verify::<schnorr::Schnorr<JubJub, Blake2s>>(message.as_bytes());
failed_verification::<schnorr::Schnorr<JubJub, Blake2s>>(
message.as_bytes(), message.as_bytes(),
"Bad message".as_bytes(), "Bad message".as_bytes(),
); );
let random_scalar = to_bytes!(<JubJub as Group>::ScalarField::rand(rng)).unwrap(); let random_scalar = to_bytes!(<JubJub as Group>::ScalarField::rand(rng)).unwrap();
randomize_and_verify::<SchnorrSignature<JubJub, Blake2s>>(
randomize_and_verify::<schnorr::Schnorr<JubJub, Blake2s>>(
message.as_bytes(), message.as_bytes(),
&random_scalar.as_slice(), &random_scalar.as_slice(),
); );

+ 87
- 172
crypto-primitives/src/signature/schnorr/constraints.rs

@ -1,242 +1,157 @@
use crate::Vec; use crate::Vec;
use algebra_core::{groups::Group, Field};
use r1cs_core::{ConstraintSystem, SynthesisError};
use algebra_core::{Field, ProjectiveCurve};
use r1cs_core::{Namespace, SynthesisError};
use r1cs_std::prelude::*; use r1cs_std::prelude::*;
use crate::signature::SigRandomizePkGadget; use crate::signature::SigRandomizePkGadget;
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
use crate::signature::schnorr::{SchnorrPublicKey, SchnorrSigParameters, SchnorrSignature};
use crate::signature::schnorr::{Parameters, PublicKey, Schnorr};
use digest::Digest; use digest::Digest;
pub struct SchnorrSigGadgetParameters<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
{
generator: GG,
_group: PhantomData<*const G>,
_engine: PhantomData<*const ConstraintF>,
}
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
impl<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> Clone
for SchnorrSigGadgetParameters<G, ConstraintF, GG>
#[derive(Clone)]
pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
where
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
fn clone(&self) -> Self {
Self {
generator: self.generator.clone(),
_group: PhantomData,
_engine: PhantomData,
}
}
generator: GC,
_curve: PhantomData<C>,
} }
#[derive(Derivative)] #[derive(Derivative)]
#[derivative( #[derivative(
Debug(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"),
Clone(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"),
PartialEq(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"),
Eq(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>")
Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
)] )]
pub struct SchnorrSigGadgetPk<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
pub_key: GG,
#[doc(hidden)]
_group: PhantomData<*const G>,
pub struct PublicKeyVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
where
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{
pub_key: GC,
#[doc(hidden)] #[doc(hidden)]
_engine: PhantomData<*const ConstraintF>,
_group: PhantomData<*const C>,
} }
pub struct SchnorrRandomizePkGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
#[doc(hidden)]
_group: PhantomData<*const G>,
pub struct SchnorrRandomizePkGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
where
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{
#[doc(hidden)] #[doc(hidden)]
_group_gadget: PhantomData<*const GG>,
_group: PhantomData<*const C>,
#[doc(hidden)] #[doc(hidden)]
_engine: PhantomData<*const ConstraintF>,
_group_gadget: PhantomData<*const GC>,
} }
impl<G, GG, D, ConstraintF> SigRandomizePkGadget<SchnorrSignature<G, D>, ConstraintF>
for SchnorrRandomizePkGadget<G, ConstraintF, GG>
impl<C, GC, D> SigRandomizePkGadget<Schnorr<C, D>, ConstraintF<C>>
for SchnorrRandomizePkGadget<C, GC>
where where
G: Group,
GG: GroupGadget<G, ConstraintF>,
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
D: Digest + Send + Sync, D: Digest + Send + Sync,
ConstraintF: Field,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
type ParametersGadget = SchnorrSigGadgetParameters<G, ConstraintF, GG>;
type PublicKeyGadget = SchnorrSigGadgetPk<G, ConstraintF, GG>;
fn check_randomization_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
public_key: &Self::PublicKeyGadget,
randomness: &[UInt8],
) -> Result<Self::PublicKeyGadget, SynthesisError> {
type ParametersVar = ParametersVar<C, GC>;
type PublicKeyVar = PublicKeyVar<C, GC>;
fn randomize(
parameters: &Self::ParametersVar,
public_key: &Self::PublicKeyVar,
randomness: &[UInt8<ConstraintF<C>>],
) -> Result<Self::PublicKeyVar, SynthesisError> {
let base = parameters.generator.clone(); let base = parameters.generator.clone();
let randomness = randomness let randomness = randomness
.iter() .iter()
.flat_map(|b| b.into_bits_le()) .flat_map(|b| b.into_bits_le())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let rand_pk = base.mul_bits(
&mut cs.ns(|| "Compute Randomizer"),
&public_key.pub_key,
randomness.iter(),
)?;
Ok(SchnorrSigGadgetPk {
let rand_pk = &public_key.pub_key + &base.mul_bits(randomness.iter())?;
Ok(PublicKeyVar {
pub_key: rand_pk, pub_key: rand_pk,
_group: PhantomData, _group: PhantomData,
_engine: PhantomData,
}) })
} }
} }
impl<G, ConstraintF, GG, D> AllocGadget<SchnorrSigParameters<G, D>, ConstraintF>
for SchnorrSigGadgetParameters<G, ConstraintF, GG>
impl<C, GC, D> AllocVar<Parameters<C, D>, ConstraintF<C>> for ParametersVar<C, GC>
where where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
D: Digest, D: Digest,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<SchnorrSigParameters<G, D>>,
{
let generator = GG::alloc_constant(cs, val.borrow().generator)?;
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 { Ok(Self {
generator, generator,
_engine: PhantomData,
_group: PhantomData,
})
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrSigParameters<G, D>>,
{
let generator = GG::alloc_checked(cs, || f().map(|pp| pp.borrow().generator))?;
Ok(Self {
generator,
_engine: PhantomData,
_group: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrSigParameters<G, D>>,
{
let generator = GG::alloc_input(cs, || f().map(|pp| pp.borrow().generator))?;
Ok(Self {
generator,
_engine: PhantomData,
_group: PhantomData,
_curve: PhantomData,
}) })
} }
} }
impl<G, ConstraintF, GG> AllocGadget<SchnorrPublicKey<G>, ConstraintF>
for SchnorrSigGadgetPk<G, ConstraintF, GG>
impl<C, GC> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GC>
where where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
val: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<SchnorrPublicKey<G>>,
{
let pub_key = GG::alloc_constant(cs, val.borrow())?;
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 { Ok(Self {
pub_key, pub_key,
_engine: PhantomData,
_group: PhantomData,
})
}
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrPublicKey<G>>,
{
let pub_key = GG::alloc_input(cs, || f().map(|pk| *pk.borrow()))?;
Ok(Self {
pub_key,
_engine: PhantomData,
_group: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrPublicKey<G>>,
{
let pub_key = GG::alloc_input(cs, || f().map(|pk| *pk.borrow()))?;
Ok(Self {
pub_key,
_engine: PhantomData,
_group: PhantomData, _group: PhantomData,
}) })
} }
} }
impl<G, ConstraintF, GG> ConditionalEqGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
impl<C, GC> EqGadget<ConstraintF<C>> for PublicKeyVar<C, GC>
where where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
#[inline] #[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
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, &self,
mut cs: CS,
other: &Self, other: &Self,
condition: &Boolean,
condition: &Boolean<ConstraintF<C>>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
self.pub_key.conditional_enforce_equal(
&mut cs.ns(|| "PubKey equality"),
&other.pub_key,
condition,
)?;
Ok(())
self.pub_key
.conditional_enforce_equal(&other.pub_key, condition)
} }
fn cost() -> usize {
<GG as ConditionalEqGadget<ConstraintF>>::cost()
#[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<G, ConstraintF, GG> EqGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
{
}
impl<G, ConstraintF, GG> ToBytesGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
impl<C, GC> ToBytesGadget<ConstraintF<C>> for PublicKeyVar<C, GC>
where where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{ {
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.pub_key.to_bytes(&mut cs.ns(|| "PubKey To Bytes"))
fn to_bytes(&self) -> Result<Vec<UInt8<ConstraintF<C>>>, SynthesisError> {
self.pub_key.to_bytes()
} }
} }

+ 56
- 52
crypto-primitives/src/signature/schnorr/mod.rs

@ -2,9 +2,8 @@ use crate::{Error, SignatureScheme, Vec};
use algebra_core::{ use algebra_core::{
bytes::ToBytes, bytes::ToBytes,
fields::{Field, PrimeField}, fields::{Field, PrimeField},
groups::Group,
io::{Result as IoResult, Write}, io::{Result as IoResult, Write},
to_bytes, One, ToConstraintField, UniformRand, Zero,
to_bytes, AffineCurve, One, ProjectiveCurve, ToConstraintField, UniformRand, Zero,
}; };
use core::{hash::Hash, marker::PhantomData}; use core::{hash::Hash, marker::PhantomData};
use digest::Digest; use digest::Digest;
@ -13,57 +12,55 @@ use rand::Rng;
#[cfg(feature = "r1cs")] #[cfg(feature = "r1cs")]
pub mod constraints; pub mod constraints;
pub struct SchnorrSignature<G: Group, D: Digest> {
_group: PhantomData<G>,
pub struct Schnorr<C: ProjectiveCurve, D: Digest> {
_group: PhantomData<C>,
_hash: PhantomData<D>, _hash: PhantomData<D>,
} }
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Clone(bound = "G: Group, H: Digest"))]
pub struct SchnorrSigParameters<G: Group, H: Digest> {
#[derivative(Clone(bound = "C: ProjectiveCurve, H: Digest"), Debug)]
pub struct Parameters<C: ProjectiveCurve, H: Digest> {
_hash: PhantomData<H>, _hash: PhantomData<H>,
pub generator: G,
pub generator: C::Affine,
pub salt: [u8; 32], pub salt: [u8; 32],
} }
pub type SchnorrPublicKey<G> = G;
pub type PublicKey<C> = <C as ProjectiveCurve>::Affine;
#[derive(Derivative)]
#[derivative(Clone(bound = "G: Group"), Default(bound = "G: Group"))]
pub struct SchnorrSecretKey<G: Group>(pub G::ScalarField);
#[derive(Clone, Default, Debug)]
pub struct SecretKey<C: ProjectiveCurve>(pub C::ScalarField);
impl<G: Group> ToBytes for SchnorrSecretKey<G> {
impl<C: ProjectiveCurve> ToBytes for SecretKey<C> {
#[inline] #[inline]
fn write<W: Write>(&self, writer: W) -> IoResult<()> { fn write<W: Write>(&self, writer: W) -> IoResult<()> {
self.0.write(writer) self.0.write(writer)
} }
} }
#[derive(Derivative)]
#[derivative(Clone(bound = "G: Group"), Default(bound = "G: Group"))]
pub struct SchnorrSig<G: Group> {
pub prover_response: G::ScalarField,
pub verifier_challenge: G::ScalarField,
#[derive(Clone, Default, Debug)]
pub struct Signature<C: ProjectiveCurve> {
pub prover_response: C::ScalarField,
pub verifier_challenge: C::ScalarField,
} }
impl<G: Group + Hash, D: Digest + Send + Sync> SignatureScheme for SchnorrSignature<G, D>
impl<C: ProjectiveCurve + Hash, D: Digest + Send + Sync> SignatureScheme for Schnorr<C, D>
where where
G::ScalarField: PrimeField,
C::ScalarField: PrimeField,
{ {
type Parameters = SchnorrSigParameters<G, D>;
type PublicKey = G;
type SecretKey = SchnorrSecretKey<G>;
type Signature = SchnorrSig<G>;
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> { fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let setup_time = start_timer!(|| "SchnorrSig::Setup"); let setup_time = start_timer!(|| "SchnorrSig::Setup");
let mut salt = [0u8; 32]; let mut salt = [0u8; 32];
rng.fill_bytes(&mut salt); rng.fill_bytes(&mut salt);
let generator = G::rand(rng);
let generator = C::rand(rng).into();
end_timer!(setup_time); end_timer!(setup_time);
Ok(SchnorrSigParameters {
Ok(Parameters {
_hash: PhantomData, _hash: PhantomData,
generator, generator,
salt, salt,
@ -76,11 +73,11 @@ where
) -> Result<(Self::PublicKey, Self::SecretKey), Error> { ) -> Result<(Self::PublicKey, Self::SecretKey), Error> {
let keygen_time = start_timer!(|| "SchnorrSig::KeyGen"); let keygen_time = start_timer!(|| "SchnorrSig::KeyGen");
let secret_key = G::ScalarField::rand(rng);
let public_key = parameters.generator.mul(&secret_key);
let secret_key = C::ScalarField::rand(rng);
let public_key = parameters.generator.mul(secret_key).into();
end_timer!(keygen_time); end_timer!(keygen_time);
Ok((public_key, SchnorrSecretKey(secret_key)))
Ok((public_key, SecretKey(secret_key)))
} }
fn sign<R: Rng>( fn sign<R: Rng>(
@ -93,10 +90,10 @@ where
// (k, e); // (k, e);
let (random_scalar, verifier_challenge) = loop { let (random_scalar, verifier_challenge) = loop {
// Sample a random scalar `k` from the prime scalar field. // Sample a random scalar `k` from the prime scalar field.
let random_scalar: G::ScalarField = G::ScalarField::rand(rng);
// Commit to the random scalar via r := k · g.
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. // This is the prover's first msg in the Sigma protocol.
let prover_commitment: G = parameters.generator.mul(&random_scalar);
let prover_commitment = parameters.generator.mul(random_scalar).into_affine();
// Hash everything to get verifier challenge. // Hash everything to get verifier challenge.
let mut hash_input = Vec::new(); let mut hash_input = Vec::new();
@ -106,7 +103,7 @@ where
// Compute the supposed verifier response: e := H(salt || r || msg); // Compute the supposed verifier response: e := H(salt || r || msg);
if let Some(verifier_challenge) = if let Some(verifier_challenge) =
G::ScalarField::from_random_bytes(&D::digest(&hash_input))
C::ScalarField::from_random_bytes(&D::digest(&hash_input))
{ {
break (random_scalar, verifier_challenge); break (random_scalar, verifier_challenge);
}; };
@ -114,7 +111,7 @@ where
// k - xe; // k - xe;
let prover_response = random_scalar - &(verifier_challenge * &sk.0); let prover_response = random_scalar - &(verifier_challenge * &sk.0);
let signature = SchnorrSig {
let signature = Signature {
prover_response, prover_response,
verifier_challenge, verifier_challenge,
}; };
@ -131,13 +128,14 @@ where
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let verify_time = start_timer!(|| "SchnorrSig::Verify"); let verify_time = start_timer!(|| "SchnorrSig::Verify");
let SchnorrSig {
let Signature {
prover_response, prover_response,
verifier_challenge, verifier_challenge,
} = signature; } = signature;
let mut claimed_prover_commitment = parameters.generator.mul(prover_response);
let public_key_times_verifier_challenge = pk.mul(verifier_challenge);
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; claimed_prover_commitment += &public_key_times_verifier_challenge;
let claimed_prover_commitment = claimed_prover_commitment.into_affine();
let mut hash_input = Vec::new(); let mut hash_input = Vec::new();
hash_input.extend_from_slice(&parameters.salt); hash_input.extend_from_slice(&parameters.salt);
@ -145,7 +143,7 @@ where
hash_input.extend_from_slice(&message); hash_input.extend_from_slice(&message);
let obtained_verifier_challenge = if let Some(obtained_verifier_challenge) = let obtained_verifier_challenge = if let Some(obtained_verifier_challenge) =
G::ScalarField::from_random_bytes(&D::digest(&hash_input))
C::ScalarField::from_random_bytes(&D::digest(&hash_input))
{ {
obtained_verifier_challenge obtained_verifier_challenge
} else { } else {
@ -162,20 +160,26 @@ where
) -> Result<Self::PublicKey, Error> { ) -> Result<Self::PublicKey, Error> {
let rand_pk_time = start_timer!(|| "SchnorrSig::RandomizePubKey"); let rand_pk_time = start_timer!(|| "SchnorrSig::RandomizePubKey");
let mut randomized_pk = *public_key;
let mut base = parameters.generator;
let mut encoded = G::zero();
for bit in bytes_to_bits(randomness) {
let randomized_pk = *public_key;
let base = parameters.generator;
let mut encoded = C::zero();
let mut found_one = false;
for bit in bytes_to_bits(randomness).into_iter().rev() {
if found_one {
encoded.double_in_place();
} else {
found_one |= bit;
}
if bit { if bit {
encoded += &base;
encoded.add_assign_mixed(&base)
} }
base.double_in_place();
} }
randomized_pk += &encoded;
encoded.add_assign_mixed(&randomized_pk);
end_timer!(rand_pk_time); end_timer!(rand_pk_time);
Ok(randomized_pk)
Ok(encoded.into())
} }
fn randomize_signature( fn randomize_signature(
@ -184,12 +188,12 @@ where
randomness: &[u8], randomness: &[u8],
) -> Result<Self::Signature, Error> { ) -> Result<Self::Signature, Error> {
let rand_signature_time = start_timer!(|| "SchnorrSig::RandomizeSig"); let rand_signature_time = start_timer!(|| "SchnorrSig::RandomizeSig");
let SchnorrSig {
let Signature {
prover_response, prover_response,
verifier_challenge, verifier_challenge,
} = signature; } = signature;
let mut base = G::ScalarField::one();
let mut multiplier = G::ScalarField::zero();
let mut base = C::ScalarField::one();
let mut multiplier = C::ScalarField::zero();
for bit in bytes_to_bits(randomness) { for bit in bytes_to_bits(randomness) {
if bit { if bit {
multiplier += &base; multiplier += &base;
@ -197,7 +201,7 @@ where
base.double_in_place(); base.double_in_place();
} }
let new_sig = SchnorrSig {
let new_sig = Signature {
prover_response: *prover_response - &(*verifier_challenge * &multiplier), prover_response: *prover_response - &(*verifier_challenge * &multiplier),
verifier_challenge: *verifier_challenge, verifier_challenge: *verifier_challenge,
}; };
@ -217,11 +221,11 @@ pub fn bytes_to_bits(bytes: &[u8]) -> Vec {
bits bits
} }
impl<ConstraintF: Field, G: Group + ToConstraintField<ConstraintF>, D: Digest>
ToConstraintField<ConstraintF> for SchnorrSigParameters<G, D>
impl<ConstraintF: Field, C: ProjectiveCurve + ToConstraintField<ConstraintF>, D: Digest>
ToConstraintField<ConstraintF> for Parameters<C, D>
{ {
#[inline] #[inline]
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> { fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
self.generator.to_field_elements()
self.generator.into_projective().to_field_elements()
} }
} }

Loading…
Cancel
Save